2005-04-12 Dick Porter <dick@ximian.com>
[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-2005 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.Collections;
30
31 namespace System.Windows.Forms
32 {
33
34         /*
35                 This class mimics the Win32 API Menu functionality
36
37                 When writing this code the Wine project was of great help to
38                 understand the logic behind some Win32 issues. Thanks to them. Jordi,
39         */
40         internal class MenuAPI
41         {
42                 static ArrayList menu_list = new ArrayList ();          
43
44                 public class MENU
45                 {
46                         public MF               Flags;          // Menu flags (MF_POPUP, MF_SYSMENU)
47                         public int              X;              // Used in MenuBar only
48                         public int              Y;              // Used in MenuBar only
49                         public int              Width;          // Width of the whole menu
50                         public int              Height;         // Height of the whole menu
51                         public Control          Wnd;            // In a Popup menu is the PopupWindow and in a MenuBar the Form
52                         public ArrayList        items;          // Array of menu items
53                         public int              FocusedItem;    // Currently focused item
54                         public IntPtr           hParent;                        
55                         public MENUITEM         SelectedItem;   // Currently selected item
56                         public bool             bMenubar;
57                         public bool             bTracking;
58                         public Menu             menu;           // SWF.Menu 
59
60                         public MENU (Menu menu_obj)
61                         {
62                                 Wnd = null;
63                                 hParent = IntPtr.Zero;
64                                 items = new ArrayList ();
65                                 Flags = MF.MF_INSERT;
66                                 Width = Height = FocusedItem = X = Y = 0;
67                                 bMenubar = false;
68                                 bTracking = false;
69                                 menu = menu_obj;
70                         }
71                 }
72
73                 public class MENUITEM
74                 {
75                         public  MenuItem        item;
76                         public  Rectangle       rect;                   
77                         public  int             wID;
78                         public  IntPtr          hSubMenu;                       
79                         public  int             pos;    /* Position in the menuitems array*/
80
81                         public MENUITEM ()
82                         {                               
83                                 wID = 0;
84                                 pos = 0;
85                                 rect = new Rectangle ();
86                         }
87
88                 };
89
90                 public class TRACKER
91                 {
92                         public  IntPtr  hCurrentMenu;
93                         public  IntPtr  hTopMenu;
94
95                         public TRACKER ()
96                         {
97                                 hCurrentMenu = hTopMenu = IntPtr.Zero;
98                         }
99                 };
100
101                 public enum MenuMouseEvent
102                 {
103                         Down,
104                         Move,
105                 }
106
107                 internal enum ItemNavigation
108                 {
109                         First,
110                         Last,
111                         Next,
112                         Previous,
113                 }
114
115                 internal enum MF
116                 {
117                         MF_INSERT           = 0x0,
118                         MF_APPEND           = 0x100,
119                         MF_DELETE           = 0x200,
120                         MF_REMOVE           = 0x1000,
121                         MF_BYCOMMAND        = 0,
122                         MF_BYPOSITION       = 0x400,
123                         MF_SEPARATOR        = 0x800,
124                         MF_ENABLED          = 0,
125                         MF_GRAYED           = 1,
126                         MF_DISABLED         = 2,
127                         MF_UNCHECKED        = 0,
128                         MF_CHECKED          = 8,
129                         MF_USECHECKBITMAPS  = 0x200,
130                         MF_STRING           = 0,
131                         MF_BITMAP           = 4,
132                         MF_OWNERDRAW        = 0x100,
133                         MF_POPUP            = 0x10,
134                         MF_MENUBARBREAK     = 0x20,
135                         MF_MENUBREAK        = 0x40,
136                         MF_UNHILITE         = 0,
137                         MF_HILITE           = 0x80,
138                         MF_DEFAULT          = 0x1000,
139                         MF_SYSMENU          = 0x2000,
140                         MF_HELP             = 0x4000,
141                         MF_RIGHTJUSTIFY     = 0x4000,
142                         MF_MENUBAR          = 0x8000    // Internal
143                 }
144
145                 static MenuAPI ()
146                 {
147                         
148                 }               
149                 
150                 static public IntPtr StoreMenuID (MENU menu)
151                 {
152                         int id = menu_list.Add (menu);
153                         return (IntPtr)(id + 1);
154                 }
155
156                 static public MENU GetMenuFromID (IntPtr ptr)
157                 {
158                         int id = (int)ptr;
159                         id = id - 1;
160
161                         if (menu_list[id] == null)      // It has been delete it
162                                 return null;
163
164                         return (MENU) menu_list[id];
165                 }
166
167                 static public IntPtr CreateMenu (Menu menu_obj)
168                 {
169                         MENU menu = new MENU (menu_obj);                        
170                         return StoreMenuID (menu);
171                 }
172
173                 static public IntPtr CreatePopupMenu (Menu menu_obj)
174                 {
175                         MENU popMenu = new MENU (menu_obj);
176                         popMenu.Flags |= MF.MF_POPUP;                   
177                         return StoreMenuID (popMenu);
178                 }
179
180                 static public int InsertMenuItem (IntPtr hMenu, int uItem, bool fByPosition, MenuItem item, ref IntPtr hSubMenu)
181                 {
182                         int id;
183                         
184                         if (fByPosition == false)
185                                 throw new NotImplementedException ();
186
187                         MENU menu = GetMenuFromID (hMenu);
188                         if ((uint)uItem > menu.items.Count)
189                                 uItem =  menu.items.Count;
190
191                         MENUITEM menu_item = new MENUITEM ();
192                         menu_item.item = item;
193
194                         if (item.IsPopup) {
195                                 menu_item.hSubMenu = CreatePopupMenu (menu_item.item);
196                                 MENU submenu = GetMenuFromID (menu_item.hSubMenu);
197                                 submenu.hParent = hMenu;
198                         }
199                         else
200                                 menu_item.hSubMenu = IntPtr.Zero;
201
202                         hSubMenu = menu_item.hSubMenu;
203                         id = menu.items.Count;
204                         menu_item.pos = menu.items.Count;
205                         menu.items.Insert (uItem, menu_item);
206
207                         return id;
208                 }
209
210                 // The Point object contains screen coordinates
211                 static public bool TrackPopupMenu (IntPtr hTopMenu, IntPtr hMenu, Point pnt, bool bMenubar, Control Wnd)
212                 {
213                         TRACKER tracker = new TRACKER ();                       
214                         MENU top_menu = GetMenuFromID (hTopMenu);
215                         MENU menu = null;
216
217                         if (hMenu == IntPtr.Zero)       // No submenus to track
218                                 return true;                            
219
220                         menu = GetMenuFromID (hMenu);                   
221                         
222                         menu.Wnd = new PopUpWindow (hMenu, tracker);
223                         tracker.hCurrentMenu = hMenu;
224                         tracker.hTopMenu = hTopMenu;
225
226                         MENUITEM select_item = GetNextItem (hMenu, ItemNavigation.First);
227
228                         if (select_item != null) {
229                                 MenuAPI.SelectItem (hMenu, select_item, false, tracker);
230                         }
231
232                         // Make sure the menu is always visible and does not 'leave' the screen
233                         // What is menu.Width/Height? It seemed to be 0/0
234                         if ((pnt.X + menu.Wnd.Width) > SystemInformation.WorkingArea.Width) {
235                                 pnt.X -= menu.Wnd.Width;
236                         }
237
238                         if ((pnt.X + menu.Wnd.Height) > SystemInformation.WorkingArea.Height) {
239                                 pnt.Y -= menu.Wnd.Height;
240                         }
241
242                         menu.Wnd.Location =  menu.Wnd.PointToClient (pnt);
243                                 
244                         if (menu.menu.IsDirty) {                                
245                                 menu.items.Clear ();
246                                 menu.menu.CreateItems ();
247                                 ((PopUpWindow)menu.Wnd).RefreshItems ();
248                                 menu.menu.IsDirty = false;
249                         }
250                         
251                         ((PopUpWindow)menu.Wnd).ShowWindow ();
252
253                         Application.Run ();
254
255                         if (menu.Wnd == null) {
256                                 menu.Wnd.Dispose ();
257                                 menu.Wnd = null;
258                         }
259
260                         return true;
261                 }
262         
263                 /*
264                         Menu handeling API
265                 */
266                 
267                 static public Point ClientAreaPointToScreen (MENU menu, Point pnt)              
268                 {
269                         Point   rslt;
270                         int     x;
271                         int     y;
272
273                         pnt.Y -= menu.Y;
274
275                         x = pnt.X;
276                         y = pnt.Y;
277
278                         XplatUI.MenuToScreen(menu.Wnd.window.Handle, ref x, ref y);
279
280                         rslt = new Point(x, y);
281
282                         return rslt;
283                 }
284                 
285                 static public Point ClientAreaPointToClient (MENU menu, Point pnt)              
286                 {
287                         Point   rslt;
288                         int     x;
289                         int     y;
290
291                         x = pnt.X;
292                         y = pnt.Y;
293
294                         XplatUI.ScreenToMenu(menu.Wnd.window.Handle, ref x, ref y);
295
296                         rslt = new Point(x, y);
297
298                         rslt.Y += menu.Y;
299
300                         return rslt;
301                 }       
302
303                 static public MENUITEM FindItemByCoords (IntPtr hMenu, Point pt)
304                 {
305                         MENU menu = GetMenuFromID (hMenu);
306
307                         for (int i = 0; i < menu.items.Count; i++) {
308                                 MENUITEM item = (MENUITEM) menu.items[i];
309                                 if (item.rect.Contains (pt)) {
310                                         return item;
311                                 }
312                         }
313
314                         return null;
315                 }
316                 
317                 // Get the current selected item
318                 static public MENUITEM GetSelected (IntPtr hMenu)
319                 {
320                         MENU menu = GetMenuFromID (hMenu);                      
321                         MENUITEM it;
322                         
323                         /* Loop all items */
324                         for (int i = 0; i < menu.items.Count; i++) {
325                                 it = (MENUITEM) menu.items[i];                  
326                                 if ((it.item.Status & DrawItemState.Selected) == DrawItemState.Selected) {
327                                         return it;
328                                 }                               
329                         }
330                         
331                         return null;
332                 }
333
334                 static internal void DrawMenuBar (IntPtr hMenu)                 
335                 {
336                         MENU menu = GetMenuFromID (hMenu);
337                         DrawMenuBar (hMenu, new Rectangle (menu.X, menu.Y, menu.Width, menu.Height));                                   
338                 }
339
340                 // Little helper
341                 static internal void DrawMenuBar (IntPtr hMenu, Rectangle rect)
342                 {
343                         Graphics g;
344                         MENU menu = GetMenuFromID (hMenu);
345
346                         menu.X = rect.X;
347                         menu.Y = rect.Y;
348                         rect.Height = menu.Height;
349
350                         g = XplatUI.GetMenuDC(menu.Wnd.window.Handle, IntPtr.Zero);
351                         ThemeEngine.Current.DrawMenuBar (g, hMenu, rect);
352                         XplatUI.ReleaseMenuDC(menu.Wnd.window.Handle, g);
353                 }
354                 
355                 static public void UnSelectItem (IntPtr hMenu, MENUITEM item)
356                 {                       
357                         MENU menu = GetMenuFromID (hMenu);
358                         
359                         if (item == null)
360                                 return;                         
361                         
362                         item.item.Status = item.item.Status &~ DrawItemState.Selected;
363                                                 
364                         if (menu.bMenubar) {
365                                 DrawMenuBar (hMenu);
366                         } else {
367                                 menu.Wnd.Invalidate (item.rect);
368                         }
369                 }
370
371                 // Select the item and unselect the previous selecte item
372                 static public void SelectItem (IntPtr hMenu, MENUITEM item, bool execute, TRACKER tracker)
373                 {
374                         MENU menu = GetMenuFromID (hMenu);
375                         MENUITEM previous_selitem = GetSelected (hMenu);
376                         
377                         /* Already selected */
378                         if (previous_selitem != null && item.rect == previous_selitem.rect) {
379                                 return;
380                         }
381
382                         UnSelectItem (hMenu, previous_selitem);
383                         
384                         // If the previous item had subitems, hide them
385                         if (previous_selitem != null && previous_selitem.item.IsPopup)
386                                 HideSubPopups (hMenu);
387
388                         if (tracker.hCurrentMenu != hMenu) {
389                                 menu.Wnd.Capture = true;
390                                 tracker.hCurrentMenu = hMenu;
391                         }
392                         
393                         menu.SelectedItem = item;
394                         item.item.Status |= DrawItemState.Selected;                     
395                         
396                         if (menu.bMenubar) {
397                                 DrawMenuBar (hMenu);
398                         } else {
399                                 menu.Wnd.Invalidate (item.rect);
400                         }
401
402                         item.item.PerformSelect ();                                     
403                         
404                         if (execute)
405                                 ExecFocusedItem (hMenu, item, tracker);
406                 }
407
408
409                 //      Used when the user executes the action of an item (press enter, shortcut)
410                 //      or a sub-popup menu has to be shown
411                 static public void ExecFocusedItem (IntPtr hMenu, MENUITEM item, TRACKER tracker)
412                 {
413                         if (item.item.Enabled == false)
414                                 return;
415                          
416                                 
417                         if (item.item.IsPopup) {                                
418                                 ShowSubPopup (hMenu, item.hSubMenu, item, tracker);                             
419                         }
420                         else {
421                                 // Execute function
422                         }
423                 }
424
425                 // Create a popup window and show it or only show it if it is already created
426                 static public void ShowSubPopup (IntPtr hParent, IntPtr hMenu, MENUITEM item, TRACKER tracker)
427                 {
428                         MENU menu = GetMenuFromID (hMenu);
429                         Point pnt = new Point ();
430
431                         if (item.item.Enabled == false)
432                                 return;
433
434                         MENU menu_parent = GetMenuFromID (hParent);
435                         ((PopUpWindow)menu_parent.Wnd).LostFocus ();
436                         tracker.hCurrentMenu = hMenu;
437
438                         if (menu.Wnd == null)
439                                 menu.Wnd = new PopUpWindow (hMenu, tracker);
440                         
441                         pnt.X = item.rect.X + item.rect.Width;
442                         pnt.Y = item.rect.Y + 1;
443                         pnt = menu_parent.Wnd.PointToScreen (pnt);
444                         menu.Wnd.Location = pnt;
445
446                         MENUITEM select_item = GetNextItem (hMenu, ItemNavigation.First);
447
448                         if (select_item != null)
449                                 MenuAPI.SelectItem (hMenu, select_item, false, tracker);
450
451                         ((PopUpWindow)menu.Wnd).ShowWindow ();
452                 }
453
454                 /* Hides all the submenus open in a menu */
455                 static public void HideSubPopups (IntPtr hMenu)
456                 {
457                         MENU menu = GetMenuFromID (hMenu);
458                         MENUITEM item;                                          
459                         
460                         for (int i = 0; i < menu.items.Count; i++) {
461                                 item = (MENUITEM) menu.items[i];
462                                 if (!item.item.IsPopup)
463                                         continue;
464
465                                 MENU sub_menu = GetMenuFromID (item.hSubMenu);
466
467                                 if (sub_menu.Wnd != null) {
468                                         HideSubPopups (item.hSubMenu);                                  
469                                         ((PopUpWindow)sub_menu.Wnd).Hide ();
470                                 }
471                         }
472                 }
473
474                 static public void DestroyMenu (IntPtr hMenu)
475                 {
476                         if (hMenu == IntPtr.Zero)
477                                 return;                         
478
479                         MENU menu = GetMenuFromID (hMenu);
480                         MENUITEM item;
481
482                         for (int i = 0; i < menu.items.Count; i++) {
483                                 item = (MENUITEM) menu.items[i];
484                                 if (item.item.IsPopup) {
485                                         MENU sub_menu = GetMenuFromID (item.hSubMenu);
486                                         if (sub_menu != null && sub_menu.Wnd != null) 
487                                                 HideSubPopups (item.hSubMenu);
488                                                 
489                                         DestroyMenu (item.hSubMenu);                                    
490                                 }
491                         }
492
493                         // Do not destroy the window of a Menubar
494                         if (menu.Wnd != null && menu.bMenubar == false) {
495                                 ((PopUpWindow)menu.Wnd).Dispose ();
496                                 menu.Wnd = null;                        
497                         }
498                         
499                         /* Unreference from the array list */
500                         menu_list[((int)hMenu)-1] = null;
501                 }
502                 
503                 // Find item by screen coordinates
504                 static public bool FindSubItemByCoord (IntPtr hMenu, Point pnt, ref IntPtr hMenuItem, ref MENUITEM itemfound)
505                 {               
506                         Point pnt_client;       
507                         Rectangle rect;
508                         MENU menu = GetMenuFromID (hMenu);
509                         MENUITEM item;
510                         
511                         for (int i = 0; i < menu.items.Count; i++) {
512                                 item = (MENUITEM) menu.items[i];
513                                 
514                                 if (item.item.IsPopup)
515                                         if (FindSubItemByCoord (item.hSubMenu, pnt, ref hMenuItem, ref itemfound) == true)
516                                                 return true;
517                                         
518                                 if (menu.Wnd == null) // Menu has not been created yet
519                                         continue;                                       
520                                                                 
521                                 rect = item.rect;
522                                 pnt_client = menu.Wnd.PointToScreen (new Point (item.rect.X, item.rect.Y));
523                                 rect.X = pnt_client.X;
524                                 rect.Y = pnt_client.Y;
525                                 
526                                 if (rect.Contains (pnt) == true) {
527                                         itemfound = item;
528                                         hMenuItem = hMenu;
529                                         return true;
530                                 }
531                         }                       
532                         
533                         return false;
534                 }
535
536                 static public void SetMenuBarWindow (IntPtr hMenu, Control wnd)
537                 {
538                         MENU menu = GetMenuFromID (hMenu);
539                         menu.Wnd = wnd;
540                         menu.bMenubar = true;
541                 }
542
543                 static private void MenuBarMove (IntPtr hMenu, MENUITEM item, TRACKER tracker)
544                 {
545                         MENU menu = GetMenuFromID (hMenu);
546                         Point pnt = new Point (item.rect.X, item.rect.Y + item.rect.Height + 1);
547                         pnt = ClientAreaPointToScreen (menu, pnt);
548                         MenuAPI.SelectItem (hMenu, item, false, tracker);
549                         HideSubPopups (tracker.hCurrentMenu);
550                         tracker.hCurrentMenu = hMenu;
551                         MenuAPI.TrackPopupMenu (hMenu, item.hSubMenu, pnt, false, null);
552                 }
553
554                 // Function that process all menubar mouse events. Coordinates in screen position
555                 static public void TrackBarMouseEvent (IntPtr hMenu, Control wnd, MouseEventArgs e, MenuMouseEvent eventype, TRACKER tracker)
556                 {
557                         MENU menu = GetMenuFromID (hMenu);
558
559                         switch (eventype) {
560                                 case MenuMouseEvent.Down: {
561                                         Point pnt = new Point (e.X, e.Y);
562                                         pnt = ClientAreaPointToClient (menu, pnt);
563
564                                         MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, pnt);
565
566                                         if (item != null) {
567                                                 MENU top_menu = GetMenuFromID (tracker.hTopMenu);
568                                                 
569                                                 top_menu.bTracking = true;                                      
570                                                 MenuBarMove (hMenu, item, tracker);
571                 
572                                                 if (item != null) {
573                                                         item.item.PerformClick ();                      
574                                                 }
575                                         }
576
577                                         break;
578                                 }
579
580                                 case MenuMouseEvent.Move: {
581
582                                         if (tracker.hTopMenu != IntPtr.Zero && tracker.hCurrentMenu != IntPtr.Zero) {
583                                                 
584                                                 MENU top_menu = GetMenuFromID (tracker.hTopMenu);
585                                                 
586                                                 if (top_menu.bTracking == false)
587                                                         break;
588
589                                                 Point pnt = new Point (e.X, e.Y);
590                                                 //pnt = menu.Wnd.PointToClient (pnt);
591                                                 pnt = ClientAreaPointToClient (menu, pnt);
592
593                                                 MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, pnt);
594
595                                                 if (item != null && menu.SelectedItem != item)
596                                                         MenuBarMove (hMenu, item, tracker);
597                                         }
598                                         break;
599                                 }
600
601                                 default:
602                                         break;
603                         }
604                 }
605
606                 static public MENUITEM FindItemByKey (IntPtr hMenu, IntPtr key)
607                 {
608                         MENU menu = GetMenuFromID (hMenu);
609                         MENUITEM item;
610
611                         char key_char = (char ) (key.ToInt32() & 0xff);
612                         key_char = Char.ToUpper (key_char);
613
614                         for (int i = 0; i < menu.items.Count; i++) {
615                                 item = (MENUITEM) menu.items[i];
616
617                                 if (item.item.Mnemonic == '\0')
618                                         continue;
619
620                                 if (item.item.Mnemonic == key_char)
621                                         return item;
622                         }
623
624                         return null;
625                 }
626
627                 // Get the next or previous selectable item on a menu
628                 static public MENUITEM GetNextItem (IntPtr hMenu, ItemNavigation navigation)
629                 {
630                         MENU menu = GetMenuFromID (hMenu);
631                         int pos = 0;
632                         bool selectable_items = false;
633                         MENUITEM item;
634
635                         // Check if there is at least a selectable item
636                         for (int i = 0; i < menu.items.Count; i++) {
637                                 item = (MENUITEM)menu.items[i];
638                                 if (item.item.Separator == false && item.item.Visible == true) {
639                                         selectable_items = true;
640                                         break;
641                                 }
642                         }
643
644                         if (selectable_items == false)
645                                 return null;
646
647                         switch (navigation) {
648                         case ItemNavigation.First: {
649                                 pos = 0;
650
651                                 /* Next item that is not separator and it is visible*/
652                                 for (; pos < menu.items.Count; pos++) {
653                                         item = (MENUITEM)menu.items[pos];
654                                         if (item.item.Separator == false && item.item.Visible == true)
655                                                 break;
656                                 }
657
658                                 if (pos >= menu.items.Count) { /* Jump at the start of the menu */
659                                         pos = 0;
660                                         /* Next item that is not separator and it is visible*/
661                                         for (; pos < menu.items.Count; pos++) {
662                                                 item = (MENUITEM)menu.items[pos];
663                                                 if (item.item.Separator == false && item.item.Visible == true)
664                                                         break;
665                                         }
666                                 }
667
668                                 break;
669                         }
670
671                         case ItemNavigation.Last: { // Not used
672                                 break;
673                         }
674
675                         case ItemNavigation.Next: {
676
677                                 if (menu.SelectedItem != null)
678                                         pos = menu.SelectedItem.pos;
679
680                                 /* Next item that is not separator and it is visible*/
681                                 for (pos++; pos < menu.items.Count; pos++) {
682                                         item = (MENUITEM)menu.items[pos];
683                                         if (item.item.Separator == false && item.item.Visible == true)
684                                                 break;
685                                 }
686
687                                 if (pos >= menu.items.Count) { /* Jump at the start of the menu */
688                                         pos = 0;
689                                         /* Next item that is not separator and it is visible*/
690                                         for (; pos < menu.items.Count; pos++) {
691                                                 item = (MENUITEM)menu.items[pos];
692                                                 if (item.item.Separator == false && item.item.Visible == true)
693                                                         break;
694                                         }
695                                 }
696                                 break;
697                         }
698
699                         case ItemNavigation.Previous: {
700
701                                 if (menu.SelectedItem != null)
702                                         pos = menu.SelectedItem.pos;
703
704                                 /* Previous item that is not separator and it is visible*/
705                                 for (pos--; pos >= 0; pos--) {
706                                         item = (MENUITEM)menu.items[pos];
707                                         if (item.item.Separator == false && item.item.Visible == true)
708                                                 break;
709                                 }
710
711                                 if (pos < 0 ) { /* Jump at the end of the menu*/
712                                         pos = menu.items.Count - 1;
713                                         /* Previous item that is not separator and it is visible*/
714                                         for (; pos >= 0; pos--) {
715                                                 item = (MENUITEM)menu.items[pos];
716                                                 if (item.item.Separator == false && item.item.Visible == true)
717                                                         break;
718                                         }
719                                 }
720
721                                 break;
722                         }
723
724                         default:
725                                 break;
726                         }
727
728                         return (MENUITEM)menu.items[pos];
729                 }
730
731                 static public bool ProcessKeys (IntPtr hMenu, ref Message msg, Keys keyData, TRACKER tracker)
732                 {
733                         MENU menu = GetMenuFromID (hMenu);
734                         MENUITEM item;
735
736                         switch (keyData) {
737                                 case Keys.Up: {
738                                         item = GetNextItem (hMenu, ItemNavigation.Previous);
739                                         if (item != null)
740                                                 MenuAPI.SelectItem (hMenu, item, false, tracker);
741
742                                         break;
743                                 }
744
745                                 case Keys.Down: {
746                                         item = GetNextItem (hMenu, ItemNavigation.Next);
747
748                                         if (item != null)
749                                                 MenuAPI.SelectItem (hMenu, item, false, tracker);
750                                         break;
751                                 }
752
753                                 /* Menubar selects and opens next. Popups next or open*/
754                                 case Keys.Right: {
755
756                                         // Try to Expand popup first
757                                         if (menu.SelectedItem.item.IsPopup) {
758                                                 ShowSubPopup (hMenu, menu.SelectedItem.hSubMenu, menu.SelectedItem, tracker);
759                                         } else {
760
761                                                 MENU parent = null;
762                                                 if (menu.hParent != IntPtr.Zero)
763                                                         parent = GetMenuFromID (menu.hParent);
764
765                                                 if (parent != null && parent.bMenubar == true) {
766                                                         MENUITEM select_item = GetNextItem (menu.hParent, ItemNavigation.Next);
767                                                         MenuBarMove (menu.hParent, select_item, tracker);
768                                                 }
769                                         }
770
771                                         break;
772                                 }
773
774                                 case Keys.Left: {
775
776                                         // Try to Collapse popup first
777                                         if (menu.SelectedItem.item.IsPopup) {
778
779                                         } else {
780
781                                                 MENU parent = null;
782                                                 if (menu.hParent != IntPtr.Zero)
783                                                         parent = GetMenuFromID (menu.hParent);
784
785                                                 if (parent != null && parent.bMenubar == true) {
786                                                         MENUITEM select_item = GetNextItem (menu.hParent, ItemNavigation.Previous);
787                                                         MenuBarMove (menu.hParent, select_item, tracker);
788                                                 }
789                                         }
790
791                                         break;
792                                 }
793                                 
794                                 case Keys.Return: {                                     
795                                         MenuAPI.ExecFocusedItem (hMenu, menu.SelectedItem, tracker);
796                                         break;
797                                 }
798
799                                 default:
800                                         break;
801                         }
802
803                         /* Try if it is a menu hot key */
804                         item = MenuAPI.FindItemByKey (hMenu, msg.WParam);
805
806                         if (item != null) {
807                                 MenuAPI.SelectItem (hMenu, item, false, tracker);
808                                 return true;
809                         }
810
811                         return false;
812                 }
813         }
814
815         /*
816
817                 class PopUpWindow
818
819         */
820         internal class PopUpWindow : Control
821         {
822                 private IntPtr hMenu;
823                 private MenuAPI.TRACKER tracker;
824
825                 public PopUpWindow (IntPtr hMenu, MenuAPI.TRACKER tracker): base ()
826                 {
827                         this.hMenu = hMenu;
828                         this.tracker = tracker;
829                         MouseDown += new MouseEventHandler (OnMouseDownPUW);
830                         MouseMove += new MouseEventHandler (OnMouseMovePUW);
831                         MouseUp += new MouseEventHandler (OnMouseUpPUW);
832                         Paint += new PaintEventHandler (OnPaintPUW);
833                         SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
834                         SetStyle (ControlStyles.ResizeRedraw | ControlStyles.Opaque, true);
835                         is_visible = false;
836                 }
837
838                 protected override CreateParams CreateParams
839                 {
840                         get {
841                                 CreateParams cp = base.CreateParams;
842                                 cp.Caption = "Menu PopUp";
843                                 cp.Style = unchecked ((int)(WindowStyles.WS_POPUP));
844                                 cp.ExStyle |= (int)(WindowStyles.WS_EX_TOOLWINDOW | WindowStyles.WS_EX_TOPMOST);
845                                 return cp;
846                         }
847                 }
848
849                 public void ShowWindow ()
850                 {
851                         Show ();
852                         Capture = true;                 
853                         Refresh ();
854                 }
855                 
856                 public new void LostFocus ()
857                 {                       
858                         Capture = false;
859                 }
860
861                 protected override void OnResize (EventArgs e)
862                 {
863                         base.OnResize (e);
864                 }
865
866                 private void OnPaintPUW (Object o, PaintEventArgs pevent)
867                 {
868                         if (Width <= 0 || Height <=  0 || Visible == false)
869                                 return;
870
871                         Draw (pevent.ClipRectangle);
872                         pevent.Graphics.DrawImage (ImageBuffer, pevent.ClipRectangle, pevent.ClipRectangle, GraphicsUnit.Pixel);
873                 }
874                 
875                 public void HideWindow ()
876                 {
877                         Capture = false;
878                         Hide ();
879                         MenuAPI.MENU top_menu = MenuAPI.GetMenuFromID (tracker.hTopMenu);
880                         top_menu.bTracking = false;
881                         
882                         MenuAPI.HideSubPopups (tracker.hTopMenu);
883                         
884                         if (top_menu.bMenubar) {
885                                 MenuAPI.MENUITEM item = MenuAPI.GetSelected (tracker.hTopMenu);
886                         
887                                 if (item != null) {
888                                         MenuAPI.UnSelectItem (tracker.hTopMenu, item);
889                                 }
890                         } else { // Context Menu                                
891                                 ((PopUpWindow)top_menu.Wnd).Hide ();
892                         }
893                 }
894
895                 private void OnMouseDownPUW (object sender, MouseEventArgs e)
896                 {                       
897                         /* Click outside the client area*/
898                         if (ClientRectangle.Contains (e.X, e.Y) == false) {
899                                 HideWindow ();
900                         }
901                         
902                         MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
903                         
904                         if (item != null) {
905                                 MenuAPI.ExecFocusedItem (hMenu, item, tracker);                         
906                         }
907                 }
908
909                 private void OnMouseUpPUW (object sender, MouseEventArgs e)
910                 {
911                         /* Click in an item area*/
912                         MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
913
914                         if (item != null) {
915                                 if (item.item.Enabled) {
916                                         HideWindow ();
917                                 }
918                                 item.item.PerformClick ();
919                         }
920                 }
921
922                 private void OnMouseMovePUW (object sender, MouseEventArgs e)
923                 {       
924                         MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
925                         
926                         if (item != null) {                             
927                                 MenuAPI.SelectItem (hMenu, item, false, tracker);
928                         } else {                                        
929
930                                 MenuAPI.MENU menu_parent = null;
931
932                                 if (tracker.hTopMenu != IntPtr.Zero)
933                                         menu_parent = MenuAPI.GetMenuFromID (tracker.hTopMenu);
934                                         
935                                 if (menu_parent == null) 
936                                         return;
937                                 
938                                 if (menu_parent.bMenubar) {
939                                         MenuAPI.TrackBarMouseEvent (tracker.hTopMenu,
940                                                 this, new MouseEventArgs(e.Button, e.Clicks, MousePosition.X, MousePosition.Y, e.Delta),
941                                                 MenuAPI.MenuMouseEvent.Move, tracker);
942                                 }                               
943                                         
944                                 IntPtr hMenuItem = IntPtr.Zero;
945                                 MenuAPI.MENUITEM item_found = null;
946                                 
947                                 if (MenuAPI.FindSubItemByCoord (tracker.hTopMenu, MousePosition, ref hMenuItem, ref item_found) == false)
948                                         return;                         
949                                                         
950                                 MenuAPI.SelectItem (hMenuItem, item_found, false, tracker);                             
951                         }
952                 }
953
954                 protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
955                 {       
956                         return MenuAPI.ProcessKeys (hMenu, ref msg, keyData, tracker);
957                 }
958
959                 protected override void CreateHandle ()
960                 {
961                         base.CreateHandle ();
962                         RefreshItems ();                        
963                 }               
964                 
965                 // Called when the number of items has changed
966                 internal void RefreshItems ()
967                 {
968                         MenuAPI.MENU menu = MenuAPI.GetMenuFromID (hMenu);
969                         ThemeEngine.Current.CalcPopupMenuSize (DeviceContext, hMenu);
970
971                         Width = menu.Width;
972                         Height = menu.Height;                   
973                 }
974
975                 private void Draw (Rectangle clip)
976                 {
977                         ThemeEngine.Current.DrawPopupMenu  (DeviceContext, hMenu, clip, ClientRectangle);
978                 }
979         }
980
981 }
982