* TreeView.cs: Don't draw the selected node when we lose
[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 menu = null;
215                         MSG msg = new MSG();
216                         bool no_quit = true;
217
218                         if (hMenu == IntPtr.Zero)       // No submenus to track
219                                 return true;                            
220
221                         menu = GetMenuFromID (hMenu);
222                         
223                         menu.Wnd = new PopUpWindow (hMenu, tracker);
224                         tracker.hCurrentMenu = hMenu;
225                         tracker.hTopMenu = hTopMenu;
226
227                         MENUITEM select_item = GetNextItem (hMenu, ItemNavigation.First);
228
229                         if (select_item != null) {
230                                 MenuAPI.SelectItem (hMenu, select_item, false, tracker);
231                         }
232
233                         menu.Wnd.Location =  menu.Wnd.PointToClient (pnt);
234                                 
235                         if (menu.menu.IsDirty) {                                
236                                 menu.items.Clear ();
237                                 menu.menu.CreateItems ();
238                                 ((PopUpWindow)menu.Wnd).RefreshItems ();
239                                 menu.menu.IsDirty = false;
240                         }
241                         ((PopUpWindow)menu.Wnd).ShowWindow ();
242
243                         while ((menu.Wnd != null) && menu.Wnd.Visible && no_quit) {
244                                 no_quit = XplatUI.GetMessage(ref msg, IntPtr.Zero, 0, 0);
245                                 XplatUI.TranslateMessage(ref msg);
246                                 XplatUI.DispatchMessage(ref msg);                               
247                         }
248
249                         if (!no_quit) {
250                                 XplatUI.Exit();
251                         }
252
253                         if (menu.Wnd == null) {
254                                 menu.Wnd.Dispose ();
255                                 menu.Wnd = null;
256                         }
257
258                         return true;
259                 }
260         
261                 /*
262                         Menu handeling API
263                 */
264                 
265                 static public Point ClientAreaPointToScreen (MENU menu, Point pnt)              
266                 {
267                         Point   rslt;
268                         int     x;
269                         int     y;
270
271                         pnt.Y -= menu.Y;
272
273                         x = pnt.X;
274                         y = pnt.Y;
275
276                         XplatUI.MenuToScreen(menu.Wnd.window.Handle, ref x, ref y);
277
278                         rslt = new Point(x, y);
279
280                         return rslt;
281                 }
282                 
283                 static public Point ClientAreaPointToClient (MENU menu, Point pnt)              
284                 {
285                         Point   rslt;
286                         int     x;
287                         int     y;
288
289                         x = pnt.X;
290                         y = pnt.Y;
291
292                         XplatUI.ScreenToMenu(menu.Wnd.window.Handle, ref x, ref y);
293
294                         rslt = new Point(x, y);
295
296                         rslt.Y += menu.Y;
297
298                         return rslt;
299                 }       
300
301                 static public MENUITEM FindItemByCoords (IntPtr hMenu, Point pt)
302                 {
303                         MENU menu = GetMenuFromID (hMenu);
304
305                         for (int i = 0; i < menu.items.Count; i++) {
306                                 MENUITEM item = (MENUITEM) menu.items[i];
307                                 if (item.rect.Contains (pt)) {
308                                         return item;
309                                 }
310                         }
311
312                         return null;
313                 }
314                 
315                 // Get the current selected item
316                 static public MENUITEM GetSelected (IntPtr hMenu)
317                 {
318                         MENU menu = GetMenuFromID (hMenu);                      
319                         MENUITEM it;
320                         
321                         /* Loop all items */
322                         for (int i = 0; i < menu.items.Count; i++) {
323                                 it = (MENUITEM) menu.items[i];                  
324                                 if ((it.item.Status & DrawItemState.Selected) == DrawItemState.Selected) {
325                                         return it;
326                                 }                               
327                         }
328                         
329                         return null;
330                 }
331
332                 static internal void DrawMenuBar (IntPtr hMenu)                 
333                 {
334                         MENU menu = GetMenuFromID (hMenu);
335                         DrawMenuBar (hMenu, new Rectangle (menu.X, menu.Y, menu.Width, menu.Height));                                   
336                 }
337
338                 // Little helper
339                 static internal void DrawMenuBar (IntPtr hMenu, Rectangle rect)
340                 {
341                         Graphics g;
342                         MENU menu = GetMenuFromID (hMenu);
343
344                         menu.X = rect.X;
345                         menu.Y = rect.Y;
346                         rect.Height = menu.Height;
347
348                         g = XplatUI.GetMenuDC(menu.Wnd.window.Handle, IntPtr.Zero);
349                         ThemeEngine.Current.DrawMenuBar (g, hMenu, rect);
350                         XplatUI.ReleaseMenuDC(menu.Wnd.window.Handle, g);
351                 }
352                 
353                 static public void UnSelectItem (IntPtr hMenu, MENUITEM item)
354                 {                       
355                         MENU menu = GetMenuFromID (hMenu);
356                         
357                         if (item == null)
358                                 return;                         
359                         
360                         item.item.Status = item.item.Status &~ DrawItemState.Selected;
361                                                 
362                         if (menu.bMenubar) {
363                                 DrawMenuBar (hMenu);
364                         } else {
365                                 menu.Wnd.Invalidate (item.rect);
366                         }
367                 }
368
369                 // Select the item and unselect the previous selecte item
370                 static public void SelectItem (IntPtr hMenu, MENUITEM item, bool execute, TRACKER tracker)
371                 {
372                         MENU menu = GetMenuFromID (hMenu);
373                         MENUITEM previous_selitem = GetSelected (hMenu);
374                         
375                         /* Already selected */
376                         if (previous_selitem != null && item.rect == previous_selitem.rect) {
377                                 return;
378                         }
379
380                         UnSelectItem (hMenu, previous_selitem);
381                         
382                         // If the previous item had subitems, hide them
383                         if (previous_selitem != null && previous_selitem.item.IsPopup)
384                                 HideSubPopups (hMenu);
385
386                         if (tracker.hCurrentMenu != hMenu) {
387                                 menu.Wnd.Capture = true;
388                                 tracker.hCurrentMenu = hMenu;
389                         }
390                         
391                         menu.SelectedItem = item;
392                         item.item.Status |= DrawItemState.Selected;                     
393                         
394                         if (menu.bMenubar) {
395                                 DrawMenuBar (hMenu);
396                         } else {
397                                 menu.Wnd.Invalidate (item.rect);
398                         }
399
400                         item.item.PerformSelect ();                                     
401                         
402                         if (execute)
403                                 ExecFocusedItem (hMenu, item, tracker);
404                 }
405
406
407                 //      Used when the user executes the action of an item (press enter, shortcut)
408                 //      or a sub-popup menu has to be shown
409                 static public void ExecFocusedItem (IntPtr hMenu, MENUITEM item, TRACKER tracker)
410                 {
411                         if (item.item.Enabled == false)
412                                 return;                  
413                                 
414                         if (item.item.IsPopup) {                                
415                                 ShowSubPopup (hMenu, item.hSubMenu, item, tracker);                             
416                         }
417                         else {
418                                 // Execute function
419                         }
420                 }
421
422                 // Create a popup window and show it or only show it if it is already created
423                 static public void ShowSubPopup (IntPtr hParent, IntPtr hMenu, MENUITEM item, TRACKER tracker)
424                 {
425                         MENU menu = GetMenuFromID (hMenu);
426                         Point pnt = new Point ();
427
428                         if (item.item.Enabled == false)
429                                 return;
430
431                         MENU menu_parent = GetMenuFromID (hParent);
432                         ((PopUpWindow)menu_parent.Wnd).LostFocus ();
433                         tracker.hCurrentMenu = hMenu;
434
435                         if (menu.Wnd != null)
436                                 menu.Wnd.Dispose ();
437                                 
438                         menu.Wnd = new PopUpWindow (hMenu, tracker);
439                         
440                         pnt.X = item.rect.X + item.rect.Width;
441                         pnt.Y = item.rect.Y + 1;
442                         pnt = menu_parent.Wnd.PointToScreen (pnt);
443                         menu.Wnd.Location = pnt;
444
445                         MENUITEM select_item = GetNextItem (hMenu, ItemNavigation.First);
446
447                         if (select_item != null)
448                                 MenuAPI.SelectItem (hMenu, select_item, false, tracker);
449
450                         ((PopUpWindow)menu.Wnd).ShowWindow ();
451                 }
452
453                 /* Hides all the submenus open in a menu */
454                 static public void HideSubPopups (IntPtr hMenu)
455                 {
456                         MENU menu = GetMenuFromID (hMenu);
457                         MENUITEM item;                                          
458                         
459                         for (int i = 0; i < menu.items.Count; i++) {
460                                 item = (MENUITEM) menu.items[i];
461                                 if (!item.item.IsPopup)
462                                         continue;
463
464                                 MENU sub_menu = GetMenuFromID (item.hSubMenu);
465
466                                 if (sub_menu.Wnd != null) {
467                                         HideSubPopups (item.hSubMenu);                                  
468                                         ((PopUpWindow)sub_menu.Wnd).Hide ();
469                                 }
470                         }
471                 }
472
473                 static public void DestroyMenu (IntPtr hMenu)
474                 {
475                         if (hMenu == IntPtr.Zero)
476                                 return;                         
477
478                         MENU menu = GetMenuFromID (hMenu);
479                         MENUITEM item;
480
481                         for (int i = 0; i < menu.items.Count; i++) {
482                                 item = (MENUITEM) menu.items[i];
483                                 if (item.item.IsPopup) {
484                                         MENU sub_menu = GetMenuFromID (item.hSubMenu);
485                                         if (sub_menu != null && sub_menu.Wnd != null) 
486                                                 HideSubPopups (item.hSubMenu);
487                                                 
488                                         DestroyMenu (item.hSubMenu);                                    
489                                 }
490                         }
491
492                         // Do not destroy the window of a Menubar
493                         if (menu.Wnd != null && menu.bMenubar == false) {
494                                 ((PopUpWindow)menu.Wnd).Dispose ();
495                                 menu.Wnd = null;                        
496                         }
497                         
498                         /* Unreference from the array list */
499                         menu_list[((int)hMenu)-1] = null;
500                 }
501                 
502                 // Find item by screen coordinates
503                 static public bool FindSubItemByCoord (IntPtr hMenu, Point pnt, ref IntPtr hMenuItem, ref MENUITEM itemfound)
504                 {               
505                         Point pnt_client;       
506                         Rectangle rect;
507                         MENU menu = GetMenuFromID (hMenu);
508                         MENUITEM item;
509                         
510                         for (int i = 0; i < menu.items.Count; i++) {
511                                 item = (MENUITEM) menu.items[i];
512                                 
513                                 if (item.item.IsPopup)
514                                         if (FindSubItemByCoord (item.hSubMenu, pnt, ref hMenuItem, ref itemfound) == true)
515                                                 return true;
516                                         
517                                 if (menu.Wnd == null) // Menu has not been created yet
518                                         continue;                                       
519                                                                 
520                                 rect = item.rect;
521                                 pnt_client = menu.Wnd.PointToScreen (new Point (item.rect.X, item.rect.Y));
522                                 rect.X = pnt_client.X;
523                                 rect.Y = pnt_client.Y;
524                                 
525                                 if (rect.Contains (pnt) == true) {
526                                         itemfound = item;
527                                         hMenuItem = hMenu;
528                                         return true;
529                                 }
530                         }                       
531                         
532                         return false;
533                 }
534
535                 static public void SetMenuBarWindow (IntPtr hMenu, Control wnd)
536                 {
537                         MENU menu = GetMenuFromID (hMenu);
538                         menu.Wnd = wnd;
539                         menu.bMenubar = true;
540                 }
541
542                 static private void MenuBarMove (IntPtr hMenu, MENUITEM item, TRACKER tracker)
543                 {
544                         MENU menu = GetMenuFromID (hMenu);
545                         Point pnt = new Point (item.rect.X, item.rect.Y + item.rect.Height + 1);
546                         pnt = ClientAreaPointToScreen (menu, pnt);
547                         MenuAPI.SelectItem (hMenu, item, false, tracker);
548                         HideSubPopups (tracker.hCurrentMenu);
549                         tracker.hCurrentMenu = hMenu;
550                         MenuAPI.TrackPopupMenu (hMenu, item.hSubMenu, pnt, false, null);
551                 }
552
553                 // Function that process all menubar mouse events. Coordinates in screen position
554                 static public void TrackBarMouseEvent (IntPtr hMenu, Control wnd, MouseEventArgs e, MenuMouseEvent eventype, TRACKER tracker)
555                 {
556                         MENU menu = GetMenuFromID (hMenu);
557
558                         switch (eventype) {
559                                 case MenuMouseEvent.Down: {
560                                         Point pnt = new Point (e.X, e.Y);
561                                         pnt = ClientAreaPointToClient (menu, pnt);
562
563                                         MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, pnt);
564
565                                         if (item != null) {
566                                                 MENU top_menu = GetMenuFromID (tracker.hTopMenu);
567                                                 
568                                                 top_menu.bTracking = true;                                      
569                                                 MenuBarMove (hMenu, item, tracker);
570                 
571                                                 if (item != null) {
572                                                         item.item.PerformClick ();                      
573                                                 }
574                                         }
575
576                                         break;
577                                 }
578
579                                 case MenuMouseEvent.Move: {
580
581                                         if (tracker.hTopMenu != IntPtr.Zero && tracker.hCurrentMenu != IntPtr.Zero) {
582                                                 
583                                                 MENU top_menu = GetMenuFromID (tracker.hTopMenu);
584                                                 
585                                                 if (top_menu.bTracking == false)
586                                                         break;
587
588                                                 Point pnt = new Point (e.X, e.Y);
589                                                 //pnt = menu.Wnd.PointToClient (pnt);
590                                                 pnt = ClientAreaPointToClient (menu, pnt);
591
592                                                 MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, pnt);
593
594                                                 if (item != null && menu.SelectedItem != item)
595                                                         MenuBarMove (hMenu, item, tracker);
596                                         }
597                                         break;
598                                 }
599
600                                 default:
601                                         break;
602                         }
603                 }
604
605                 static public MENUITEM FindItemByKey (IntPtr hMenu, IntPtr key)
606                 {
607                         MENU menu = GetMenuFromID (hMenu);
608                         MENUITEM item;
609
610                         char key_char = (char ) (key.ToInt32() & 0xff);
611                         key_char = Char.ToUpper (key_char);
612
613                         for (int i = 0; i < menu.items.Count; i++) {
614                                 item = (MENUITEM) menu.items[i];
615
616                                 if (item.item.Mnemonic == '\0')
617                                         continue;
618
619                                 if (item.item.Mnemonic == key_char)
620                                         return item;
621                         }
622
623                         return null;
624                 }
625
626                 // Get the next or previous selectable item on a menu
627                 static public MENUITEM GetNextItem (IntPtr hMenu, ItemNavigation navigation)
628                 {
629                         MENU menu = GetMenuFromID (hMenu);
630                         int pos = 0;
631                         bool selectable_items = false;
632                         MENUITEM item;
633
634                         // Check if there is at least a selectable item
635                         for (int i = 0; i < menu.items.Count; i++) {
636                                 item = (MENUITEM)menu.items[i];
637                                 if (item.item.Separator == false && item.item.Visible == true) {
638                                         selectable_items = true;
639                                         break;
640                                 }
641                         }
642
643                         if (selectable_items == false)
644                                 return null;
645
646                         switch (navigation) {
647                         case ItemNavigation.First: {
648                                 pos = 0;
649
650                                 /* Next item that is not separator and it is visible*/
651                                 for (; pos < menu.items.Count; pos++) {
652                                         item = (MENUITEM)menu.items[pos];
653                                         if (item.item.Separator == false && item.item.Visible == true)
654                                                 break;
655                                 }
656
657                                 if (pos >= menu.items.Count) { /* Jump at the start of the menu */
658                                         pos = 0;
659                                         /* Next item that is not separator and it is visible*/
660                                         for (; pos < menu.items.Count; pos++) {
661                                                 item = (MENUITEM)menu.items[pos];
662                                                 if (item.item.Separator == false && item.item.Visible == true)
663                                                         break;
664                                         }
665                                 }
666
667                                 break;
668                         }
669
670                         case ItemNavigation.Last: { // Not used
671                                 break;
672                         }
673
674                         case ItemNavigation.Next: {
675
676                                 if (menu.SelectedItem != null)
677                                         pos = menu.SelectedItem.pos;
678
679                                 /* Next item that is not separator and it is visible*/
680                                 for (pos++; pos < menu.items.Count; pos++) {
681                                         item = (MENUITEM)menu.items[pos];
682                                         if (item.item.Separator == false && item.item.Visible == true)
683                                                 break;
684                                 }
685
686                                 if (pos >= menu.items.Count) { /* Jump at the start of the menu */
687                                         pos = 0;
688                                         /* Next item that is not separator and it is visible*/
689                                         for (; pos < menu.items.Count; pos++) {
690                                                 item = (MENUITEM)menu.items[pos];
691                                                 if (item.item.Separator == false && item.item.Visible == true)
692                                                         break;
693                                         }
694                                 }
695                                 break;
696                         }
697
698                         case ItemNavigation.Previous: {
699
700                                 if (menu.SelectedItem != null)
701                                         pos = menu.SelectedItem.pos;
702
703                                 /* Previous item that is not separator and it is visible*/
704                                 for (pos--; pos >= 0; pos--) {
705                                         item = (MENUITEM)menu.items[pos];
706                                         if (item.item.Separator == false && item.item.Visible == true)
707                                                 break;
708                                 }
709
710                                 if (pos < 0 ) { /* Jump at the end of the menu*/
711                                         pos = menu.items.Count - 1;
712                                         /* Previous item that is not separator and it is visible*/
713                                         for (; pos >= 0; pos--) {
714                                                 item = (MENUITEM)menu.items[pos];
715                                                 if (item.item.Separator == false && item.item.Visible == true)
716                                                         break;
717                                         }
718                                 }
719
720                                 break;
721                         }
722
723                         default:
724                                 break;
725                         }
726
727                         return (MENUITEM)menu.items[pos];
728                 }
729
730                 static public bool ProcessKeys (IntPtr hMenu, ref Message msg, Keys keyData, TRACKER tracker)
731                 {
732                         MENU menu = GetMenuFromID (hMenu);
733                         MENUITEM item;
734
735                         switch (keyData) {
736                                 case Keys.Up: {
737                                         item = GetNextItem (hMenu, ItemNavigation.Previous);
738                                         if (item != null)
739                                                 MenuAPI.SelectItem (hMenu, item, false, tracker);
740
741                                         break;
742                                 }
743
744                                 case Keys.Down: {
745                                         item = GetNextItem (hMenu, ItemNavigation.Next);
746
747                                         if (item != null)
748                                                 MenuAPI.SelectItem (hMenu, item, false, tracker);
749                                         break;
750                                 }
751
752                                 /* Menubar selects and opens next. Popups next or open*/
753                                 case Keys.Right: {
754
755                                         // Try to Expand popup first
756                                         if (menu.SelectedItem.item.IsPopup) {
757                                                 ShowSubPopup (hMenu, menu.SelectedItem.hSubMenu, menu.SelectedItem, tracker);
758                                         } else {
759
760                                                 MENU parent = null;
761                                                 if (menu.hParent != IntPtr.Zero)
762                                                         parent = GetMenuFromID (menu.hParent);
763
764                                                 if (parent != null && parent.bMenubar == true) {
765                                                         MENUITEM select_item = GetNextItem (menu.hParent, ItemNavigation.Next);
766                                                         MenuBarMove (menu.hParent, select_item, tracker);
767                                                 }
768                                         }
769
770                                         break;
771                                 }
772
773                                 case Keys.Left: {
774
775                                         // Try to Collapse popup first
776                                         if (menu.SelectedItem.item.IsPopup) {
777
778                                         } else {
779
780                                                 MENU parent = null;
781                                                 if (menu.hParent != IntPtr.Zero)
782                                                         parent = GetMenuFromID (menu.hParent);
783
784                                                 if (parent != null && parent.bMenubar == true) {
785                                                         MENUITEM select_item = GetNextItem (menu.hParent, ItemNavigation.Previous);
786                                                         MenuBarMove (menu.hParent, select_item, tracker);
787                                                 }
788                                         }
789
790                                         break;
791                                 }
792                                 
793                                 case Keys.Return: {                                     
794                                         MenuAPI.ExecFocusedItem (hMenu, menu.SelectedItem, tracker);
795                                         break;
796                                 }
797
798                                 default:
799                                         break;
800                         }
801
802                         /* Try if it is a menu hot key */
803                         item = MenuAPI.FindItemByKey (hMenu, msg.WParam);
804
805                         if (item != null) {
806                                 MenuAPI.SelectItem (hMenu, item, false, tracker);
807                                 return true;
808                         }
809
810                         return false;
811                 }
812         }
813
814         /*
815
816                 class PopUpWindow
817
818         */
819         internal class PopUpWindow : Control
820         {
821                 private IntPtr hMenu;
822                 private MenuAPI.TRACKER tracker;
823
824                 public PopUpWindow (IntPtr hMenu, MenuAPI.TRACKER tracker): base ()
825                 {
826                         this.hMenu = hMenu;
827                         this.tracker = tracker;
828                         MouseDown += new MouseEventHandler (OnMouseDownPUW);
829                         MouseMove += new MouseEventHandler (OnMouseMovePUW);
830                         MouseUp += new MouseEventHandler (OnMouseUpPUW);
831                         Paint += new PaintEventHandler (OnPaintPUW);
832                         SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
833                         SetStyle (ControlStyles.ResizeRedraw | ControlStyles.Opaque, true);
834                         is_visible = false;
835                 }
836
837                 protected override CreateParams CreateParams
838                 {
839                         get {
840                                 CreateParams cp = base.CreateParams;
841                                 cp.Caption = "Menu PopUp";
842                                 cp.Style = unchecked ((int)(WindowStyles.WS_POPUP));
843                                 cp.ExStyle |= (int)(WindowStyles.WS_EX_TOOLWINDOW | WindowStyles.WS_EX_TOPMOST);
844                                 return cp;
845                         }
846                 }
847
848                 public void ShowWindow ()
849                 {
850                         Show ();
851                         Capture = true;                 
852                         RefreshItems ();
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                         ThemeEngine.Current.DrawPopupMenu  (pevent.Graphics, hMenu, pevent.ClipRectangle, ClientRectangle);
869                 }
870                 
871                 public void HideWindow ()
872                 {
873                         Capture = false;
874                         Hide ();
875                         MenuAPI.MENU top_menu = MenuAPI.GetMenuFromID (tracker.hTopMenu);
876                         top_menu.bTracking = false;
877                         
878                         MenuAPI.HideSubPopups (tracker.hTopMenu);
879                         
880                         if (top_menu.bMenubar) {
881                                 MenuAPI.MENUITEM item = MenuAPI.GetSelected (tracker.hTopMenu);
882                         
883                                 if (item != null) {
884                                         MenuAPI.UnSelectItem (tracker.hTopMenu, item);
885                                 }
886                         } else { // Context Menu                                
887                                 ((PopUpWindow)top_menu.Wnd).Hide ();
888                         }
889                 }
890
891                 private void OnMouseDownPUW (object sender, MouseEventArgs e)
892                 {                       
893                         /* Click outside the client area*/
894                         if (ClientRectangle.Contains (e.X, e.Y) == false) {
895                                 HideWindow ();
896                                 return;
897                         }
898                         
899                         MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
900                         
901                         if (item != null) {
902                                 MenuAPI.ExecFocusedItem (hMenu, item, tracker);                         
903                         }
904                 }
905
906                 private void OnMouseUpPUW (object sender, MouseEventArgs e)
907                 {
908                         /* Click in an item area*/
909                         MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
910
911                         if (item != null) {
912                                 if (item.item.Enabled) {
913                                         HideWindow ();
914                                 }
915                                 item.item.PerformClick ();
916                         }
917                 }
918
919                 private void OnMouseMovePUW (object sender, MouseEventArgs e)
920                 {       
921                         MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
922                         
923                         if (item != null) {                             
924                                 MenuAPI.SelectItem (hMenu, item, false, tracker);
925                         } else {                                        
926
927                                 MenuAPI.MENU menu_parent = null;
928
929                                 if (tracker.hTopMenu != IntPtr.Zero)
930                                         menu_parent = MenuAPI.GetMenuFromID (tracker.hTopMenu);
931                                         
932                                 if (menu_parent == null) 
933                                         return;
934                                 
935                                 if (menu_parent.bMenubar) {
936                                         MenuAPI.TrackBarMouseEvent (tracker.hTopMenu,
937                                                 this, new MouseEventArgs(e.Button, e.Clicks, MousePosition.X, MousePosition.Y, e.Delta),
938                                                 MenuAPI.MenuMouseEvent.Move, tracker);
939                                 }                               
940                                         
941                                 IntPtr hMenuItem = IntPtr.Zero;
942                                 MenuAPI.MENUITEM item_found = null;
943                                 
944                                 if (MenuAPI.FindSubItemByCoord (tracker.hTopMenu, MousePosition, ref hMenuItem, ref item_found) == false)
945                                         return;                         
946                                                         
947                                 MenuAPI.SelectItem (hMenuItem, item_found, false, tracker);                             
948                         }
949                 }
950
951                 protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
952                 {       
953                         return MenuAPI.ProcessKeys (hMenu, ref msg, keyData, tracker);
954                 }
955
956                 protected override void CreateHandle ()
957                 {
958                         base.CreateHandle ();
959                         RefreshItems ();                        
960                 }               
961                 
962                 // Called when the number of items has changed
963                 internal void RefreshItems ()
964                 {
965                         MenuAPI.MENU menu = MenuAPI.GetMenuFromID (hMenu);
966                         ThemeEngine.Current.CalcPopupMenuSize (DeviceContext, hMenu);
967
968                         if ((Location.X + menu.Width) > SystemInformation.WorkingArea.Width) {
969                                 Location = new Point (Location.X - menu.Width, Location.Y);
970                         }
971                         if ((Location.Y + menu.Height) > SystemInformation.WorkingArea.Height) {
972                                 Location = new Point (Location.X, Location.Y - menu.Height);
973                         }
974
975                         Width = menu.Width;
976                         Height = menu.Height;                   
977                 }
978         }
979
980 }
981