2009-06-12 Bill Holmes <billholmes54@gmail.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 //      Mike Kestner  <mkestner@novell.com>
25 //      Everaldo Canuto  <ecanuto@novell.com>
26 //
27
28 using System.Collections;
29 using System.Drawing;
30 using System.Threading;
31
32 namespace System.Windows.Forms {
33
34         /*
35                 When writing this code the Wine project was of great help to
36                 understand the logic behind some Win32 issues. Thanks to them. Jordi,
37         */
38         // UIA Framework Note: This class used by UIA for its mouse action methods.
39         internal class MenuTracker {
40
41                 internal bool active;
42                 internal bool popup_active;
43                 internal bool popdown_menu;
44                 internal bool hotkey_active;
45                 private bool mouse_down = false;
46                 public Menu CurrentMenu;
47                 public Menu TopMenu;
48                 public Control GrabControl;
49                 Point last_motion = Point.Empty;
50                 
51             public MenuTracker (Menu top_menu)
52                 {
53                         TopMenu = CurrentMenu = top_menu;
54                         foreach (MenuItem item in TopMenu.MenuItems)
55                                 AddShortcuts (item);
56                 }
57
58                 enum KeyNavState {
59                         Idle,
60                         Startup,
61                         NoPopups,
62                         Navigating
63                 }
64
65                 KeyNavState keynav_state = KeyNavState.Idle;
66
67                 public bool Navigating {
68                         get { return keynav_state != KeyNavState.Idle || active; }
69                 }
70
71                 internal static Point ScreenToMenu (Menu menu, Point pnt)               
72                 {
73                         int x = pnt.X;
74                         int y = pnt.Y;
75                         XplatUI.ScreenToMenu (menu.Wnd.window.Handle, ref x, ref y);
76                         return new Point (x, y);
77                 }       
78
79                 private void UpdateCursor ()
80                 {
81                         Control child_control = GrabControl.GetRealChildAtPoint (Cursor.Position);
82                         if (child_control != null) {
83                                 if (active)
84                                         XplatUI.SetCursor (child_control.Handle, Cursors.Default.handle);
85                                 else
86                                         XplatUI.SetCursor (child_control.Handle, child_control.Cursor.handle);
87                         }
88                 }
89
90                 void Deactivate ()
91                 {
92                         bool redrawbar = (keynav_state != KeyNavState.Idle) && (TopMenu is MainMenu); 
93
94                         active = false;
95                         popup_active = false;
96                         hotkey_active = false;
97                         if (GrabControl != null)
98                                 GrabControl.ActiveTracker = null;
99                         keynav_state = KeyNavState.Idle;
100                         if (TopMenu is ContextMenu) {
101                                 PopUpWindow puw = TopMenu.Wnd as PopUpWindow;
102                                 DeselectItem (TopMenu.SelectedItem);
103                                 if (puw != null)
104                                         puw.HideWindow ();
105                         } else {
106                                 DeselectItem (TopMenu.SelectedItem);
107                         }
108                         CurrentMenu = TopMenu;
109
110                         if (redrawbar)
111                                 (TopMenu as MainMenu).Draw ();                  
112                 }
113
114                 MenuItem FindItemByCoords (Menu menu, Point pt)
115                 {
116                         if (menu is MainMenu)
117                                 pt = ScreenToMenu (menu, pt);
118                         else
119                                 pt = menu.Wnd.PointToClient (pt);
120                         foreach (MenuItem item in menu.MenuItems) {
121                                 Rectangle rect = item.bounds;
122                                 if (rect.Contains (pt))
123                                         return item;
124                         }
125
126                         return null;
127                 }
128
129                 MenuItem GetItemAtXY (int x, int y)
130                 {
131                         Point pnt = new Point (x, y);
132                         MenuItem item = null;
133                         if (TopMenu.SelectedItem != null)
134                                 item = FindSubItemByCoord (TopMenu.SelectedItem, Control.MousePosition);
135                         if (item == null)
136                                 item = FindItemByCoords (TopMenu, pnt);
137                         return item;
138                 }
139
140                 // UIA Framework Note: Used to expand/collapse MenuItems
141                 public bool OnMouseDown (MouseEventArgs args)
142                 {
143                         MenuItem item = GetItemAtXY (args.X, args.Y);
144
145                         mouse_down = true;
146
147                         if (item == null) {
148                                 Deactivate ();
149                                 return false;
150                         }
151
152                         if ((args.Button & MouseButtons.Left) == 0)
153                                 return true;
154
155                         if (!item.Enabled)
156                                 return true;
157                         
158                         popdown_menu = active && item.VisibleItems;
159                         
160                         if (item.IsPopup || (item.Parent is MainMenu)) {
161                                 active = true;
162                                 item.Parent.InvalidateItem (item);
163                         }
164                         
165                         if ((CurrentMenu == TopMenu) && !popdown_menu)
166                                 SelectItem (item.Parent, item, item.IsPopup);
167                         
168                         GrabControl.ActiveTracker = this;
169                         return true;
170                 }
171
172                 // UIA Framework Note: Used to select MenuItems
173                 public void OnMotion (MouseEventArgs args)
174                 {
175                         // Windows helpfully sends us MOUSEMOVE messages when any key is pressed.
176                         // So if the mouse hasn't actually moved since the last MOUSEMOVE, ignore it.
177                         if (args.Location == last_motion)
178                                 return;
179                                 
180                         last_motion = args.Location;
181                         
182                         MenuItem item = GetItemAtXY (args.X, args.Y);
183
184                         UpdateCursor ();
185
186                         if (CurrentMenu.SelectedItem == item)
187                                 return;
188
189                         GrabControl.ActiveTracker = (active || item != null) ? this : null;
190
191                         if (item == null) {
192                                 MenuItem old_item = CurrentMenu.SelectedItem;
193                                 
194                                 // Return when is a popup with visible subitems for MainMenu 
195                                 if  ((active && old_item.VisibleItems && old_item.IsPopup && (CurrentMenu is MainMenu)))
196                                         return;
197
198                                 // Also returns when keyboard navigating
199                                 if (keynav_state == KeyNavState.Navigating)
200                                         return;
201                                 
202                                 // Select parent menu when move outside of menu item
203                                 if (old_item.Parent is MenuItem) {
204                                         MenuItem new_item = (old_item.Parent as MenuItem);
205                                         if (new_item.IsPopup) {
206                                                 SelectItem (new_item.Parent, new_item, false);
207                                                 return;
208                                         }
209                                 }
210                                 if (CurrentMenu != TopMenu)
211                                         CurrentMenu = CurrentMenu.parent_menu;
212                                                                 
213                                 DeselectItem (old_item);
214                         } else {
215                                 keynav_state = KeyNavState.Idle;
216                                 SelectItem (item.Parent, item, active && item.IsPopup && popup_active && (CurrentMenu.SelectedItem != item));
217                         }
218                 }
219
220                 // UIA Framework Note: Used to expand/collapse MenuItems
221                 public void OnMouseUp (MouseEventArgs args)
222                 {
223                         /* mouse down dont comes from menu */
224                         if (!mouse_down)
225                                 return;
226
227                         mouse_down = false;
228
229                         /* is not left button */
230                         if ((args.Button & MouseButtons.Left) == 0)
231                                 return;
232                         
233                         MenuItem item = GetItemAtXY (args.X, args.Y);
234
235                         /* the user released the mouse button outside the menu */
236                         if (item == null) {
237                                 Deactivate ();
238                                 return;
239                         }
240                         
241                         if (!item.Enabled)
242                                 return;
243                         
244                         /* Deactivate the menu when is topmenu and popdown and */
245                         if (((CurrentMenu == TopMenu) && !(CurrentMenu is ContextMenu) && popdown_menu) || !item.IsPopup) {
246                                 Deactivate ();
247                                 UpdateCursor ();
248                         }
249                         
250                         /* Perform click when is not a popup */
251                         if (!item.IsPopup) {
252                                 DeselectItem (item);
253                                 
254                                 // Raise the form's MenuComplete event
255                                 if (TopMenu != null && TopMenu.Wnd != null) {
256                                         Form f = TopMenu.Wnd.FindForm ();
257                                         
258                                         if (f != null)
259                                                 f.OnMenuComplete (EventArgs.Empty);     
260                                 }
261                                 
262                                 item.PerformClick ();
263                         }
264                 }
265
266                 static public bool TrackPopupMenu (Menu menu, Point pnt)
267                 {
268                         if (menu.MenuItems.Count <= 0)  // No submenus to track
269                                 return true;                            
270
271                         MenuTracker tracker = menu.tracker;
272                         tracker.active = true;
273                         tracker.popup_active = true;
274                         
275                         // Set GrabControl
276                         Control src_ctrl = (tracker.TopMenu as ContextMenu).SourceControl;
277                         tracker.GrabControl = src_ctrl.FindForm ();
278                         if (tracker.GrabControl == null)
279                                 tracker.GrabControl = src_ctrl.FindRootParent ();
280                         tracker.GrabControl.ActiveTracker = tracker;
281                         
282                         menu.Wnd = new PopUpWindow (tracker.GrabControl, menu);
283                         menu.Wnd.Location =  menu.Wnd.PointToClient (pnt);
284                         ((PopUpWindow)menu.Wnd).ShowWindow ();
285
286                         bool no_quit = true;
287
288                         Object queue_id = XplatUI.StartLoop(Thread.CurrentThread);
289
290                         while ((menu.Wnd != null) && menu.Wnd.Visible && no_quit) {
291                                 MSG msg = new MSG ();
292                                 no_quit = XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0);
293
294                                 switch((Msg)msg.message) {
295                                 case Msg.WM_KEYDOWN:
296                                 case Msg.WM_SYSKEYDOWN:
297                                 case Msg.WM_CHAR:
298                                 case Msg.WM_SYSCHAR:
299                                 case Msg.WM_KEYUP:
300                                 case Msg.WM_SYSKEYUP:
301                                         Control c = Control.FromHandle(msg.hwnd);
302                                         if (c != null) {
303                                                 Message m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
304                                                 c.PreProcessControlMessageInternal (ref m);
305                                         }
306                                         break;
307                                 default:
308                                         XplatUI.TranslateMessage (ref msg);
309                                         XplatUI.DispatchMessage (ref msg);
310                                         break;
311                                 }
312                         }
313
314                         if (tracker.GrabControl.IsDisposed)
315                                 return true;
316
317                         if (!no_quit)
318                                 XplatUI.PostQuitMessage(0);
319
320                         if (menu.Wnd != null) {
321                                 menu.Wnd.Dispose ();
322                                 menu.Wnd = null;
323                         }
324
325                         return true;
326                 }
327         
328                 void DeselectItem (MenuItem item)
329                 {
330                         if (item == null)
331                                 return;                         
332                         
333                         item.Selected = false;
334
335                         /* When popup item then close all sub popups and unselect all sub items */
336                         if (item.IsPopup) {
337                                 HideSubPopups (item, TopMenu);
338                                 
339                                 /* Unselect all selected sub itens */
340                                 foreach (MenuItem subitem in item.MenuItems)
341                                         if (subitem.Selected)
342                                                 DeselectItem (subitem);
343                         }
344
345                         Menu menu = item.Parent;
346                         menu.InvalidateItem (item);
347                 }
348
349                 void SelectItem (Menu menu, MenuItem item, bool execute)
350                 {
351                         MenuItem prev_item = CurrentMenu.SelectedItem;
352                         
353                         if (prev_item != item.Parent) {
354                                 DeselectItem (prev_item);
355                                 if ((CurrentMenu != menu) && (prev_item.Parent != item) && (prev_item.Parent is MenuItem)) {
356                                         DeselectItem (prev_item.Parent as MenuItem);
357                                 }
358                         }
359
360                         if (CurrentMenu != menu)
361                                 CurrentMenu = menu;
362                         
363                         item.Selected = true;
364                         menu.InvalidateItem (item);
365                         
366                         if (((CurrentMenu == TopMenu) && execute) || ((CurrentMenu != TopMenu) && popup_active))
367                                 item.PerformSelect ();
368
369                         if ((execute) && ((prev_item == null) || (item != prev_item.Parent)))
370                                 ExecFocusedItem (menu, item);
371                 }
372
373                 //      Used when the user executes the action of an item (press enter, shortcut)
374                 //      or a sub-popup menu has to be shown
375                 void ExecFocusedItem (Menu menu, MenuItem item)
376                 {
377                         if (item == null)
378                                 return;
379
380                         if (!item.Enabled)
381                                 return;
382                                 
383                         if (item.IsPopup) {
384                                 ShowSubPopup (menu, item);
385                         } else {
386                                 Deactivate ();
387                                 item.PerformClick ();
388                         }
389                 }
390
391                 // Create a popup window and show it or only show it if it is already created
392                 void ShowSubPopup (Menu menu, MenuItem item)
393                 {
394                         if (item.Enabled == false)
395                                 return;
396
397                         if (!popdown_menu || !item.VisibleItems) 
398                                 item.PerformPopup ();
399                         
400                         if (item.VisibleItems == false)
401                                 return;
402
403                         if (item.Wnd != null) {
404                                 item.Wnd.Dispose ();
405                         }
406
407                         popup_active = true;
408                         PopUpWindow puw = new PopUpWindow (GrabControl, item);
409                         
410                         Point pnt;
411                         if (menu is MainMenu)
412                                 pnt = new Point (item.X, item.Y + item.Height - 2 - menu.Height);
413                         else
414                                 pnt = new Point (item.X + item.Width - 3, item.Y - 3);
415                         pnt = menu.Wnd.PointToScreen (pnt);
416                         puw.Location = pnt;
417                         item.Wnd = puw;
418
419                         puw.ShowWindow ();
420                 }
421
422                 static public void HideSubPopups (Menu menu, Menu topmenu)
423                 {
424                         foreach (MenuItem item in menu.MenuItems)
425                                 if (item.IsPopup)
426                                         HideSubPopups (item, null);
427
428                         if (menu.Wnd == null)
429                                 return;
430
431                         PopUpWindow puw = menu.Wnd as PopUpWindow;
432                         if (puw != null) {
433                                 puw.Hide ();
434                                 puw.Dispose ();
435                         }
436                         menu.Wnd = null;
437
438 #if NET_2_0
439                         if ((topmenu != null) && (topmenu is MainMenu))
440                                 ((MainMenu) topmenu).OnCollapse (EventArgs.Empty);
441 #endif
442                 }
443
444                 MenuItem FindSubItemByCoord (Menu menu, Point pnt)
445                 {               
446                         foreach (MenuItem item in menu.MenuItems) {
447
448                                 if (item.IsPopup && item.Wnd != null && item.Wnd.Visible && item == menu.SelectedItem) {
449                                         MenuItem result = FindSubItemByCoord (item, pnt);
450                                         if (result != null)
451                                                 return result;
452                                 }
453                                         
454                                 if (menu.Wnd == null || !menu.Wnd.Visible)
455                                         continue;
456
457                                 Rectangle rect = item.bounds;
458                                 Point pnt_client = menu.Wnd.PointToScreen (new Point (item.X, item.Y));
459                                 rect.X = pnt_client.X;
460                                 rect.Y = pnt_client.Y;
461                                 
462                                 if (rect.Contains (pnt) == true)
463                                         return item;
464                         }                       
465                         
466                         return null;
467                 }
468
469                 static MenuItem FindItemByKey (Menu menu, IntPtr key)
470                 {
471                         char key_char = Char.ToUpper ((char) (key.ToInt32() & 0xff));
472                         foreach (MenuItem item in menu.MenuItems) {
473                                 if (item.Mnemonic == key_char)
474                                         return item;
475                         }
476
477                         string key_str = key_char.ToString (); 
478                         foreach (MenuItem item in menu.MenuItems) {
479                                 //if (item.Mnemonic == key_char)
480                                 if (item.Text.StartsWith (key_str))
481                                         return item;
482                         }
483
484                         return null;
485                 }
486
487                 enum ItemNavigation {
488                         First,
489                         Last,
490                         Next,
491                         Previous,
492                 }
493
494                 static MenuItem GetNextItem (Menu menu, ItemNavigation navigation)
495                 {
496                         int pos = 0;
497                         bool selectable_items = false;
498                         MenuItem item;
499
500                         // Check if there is at least a selectable item
501                         for (int i = 0; i < menu.MenuItems.Count; i++) {
502                                 item = menu.MenuItems [i];
503                                 if (item.Separator == false && item.Visible == true) {
504                                         selectable_items = true;
505                                         break;
506                                 }
507                         }
508
509                         if (selectable_items == false)
510                                 return null;
511
512                         switch (navigation) {
513                         case ItemNavigation.First:
514
515                                 /* First item that is not separator and it is visible*/
516                                 for (pos = 0; pos < menu.MenuItems.Count; pos++) {
517                                         item = menu.MenuItems [pos];
518                                         if (item.Separator == false && item.Visible == true)
519                                                 break;
520                                 }
521
522                                 break;
523
524                         case ItemNavigation.Last: // Not used
525                                 break;
526
527                         case ItemNavigation.Next:
528
529                                 pos = menu.SelectedItem == null ? - 1 : menu.SelectedItem.Index;
530
531                                 /* Next item that is not separator and it is visible*/
532                                 for (pos++; pos < menu.MenuItems.Count; pos++) {
533                                         item = menu.MenuItems [pos];
534                                         if (item.Separator == false && item.Visible == true)
535                                                 break;
536                                 }
537
538                                 if (pos >= menu.MenuItems.Count) { /* Jump at the start of the menu */
539                                         pos = 0;
540                                         /* Next item that is not separator and it is visible*/
541                                         for (; pos < menu.MenuItems.Count; pos++) {
542                                                 item = menu.MenuItems [pos];
543                                                 if (item.Separator == false && item.Visible == true)
544                                                         break;
545                                         }
546                                 }
547                                 break;
548
549                         case ItemNavigation.Previous:
550
551                                 if (menu.SelectedItem != null)
552                                         pos = menu.SelectedItem.Index;
553
554                                 /* Previous item that is not separator and it is visible*/
555                                 for (pos--; pos >= 0; pos--) {
556                                         item = menu.MenuItems [pos];
557                                         if (item.Separator == false && item.Visible == true)
558                                                 break;
559                                 }
560
561                                 if (pos < 0 ) { /* Jump at the end of the menu*/
562                                         pos = menu.MenuItems.Count - 1;
563                                         /* Previous item that is not separator and it is visible*/
564                                         for (; pos >= 0; pos--) {
565                                                 item = menu.MenuItems [pos];
566                                                 if (item.Separator == false && item.Visible == true)
567                                                         break;
568                                         }
569                                 }
570
571                                 break;
572
573                         default:
574                                 break;
575                         }
576
577                         return menu.MenuItems [pos];
578                 }
579
580                 void ProcessMenuKey (Msg msg_type)
581                 {
582                         if (TopMenu.MenuItems.Count == 0)
583                                 return;
584
585                         MainMenu main_menu = TopMenu as MainMenu;
586
587                         switch (msg_type) {
588                         case Msg.WM_SYSKEYDOWN:
589                                 switch (keynav_state) {
590                                 case KeyNavState.Idle:
591                                         keynav_state = KeyNavState.Startup;
592                                         hotkey_active = true;
593                                         GrabControl.ActiveTracker = this;
594                                         CurrentMenu = TopMenu;
595                                         main_menu.Draw ();
596                                         break;
597                                 case KeyNavState.Startup:
598                                         break;
599                                 default:
600                                         Deactivate ();
601                                         main_menu.Draw ();
602                                         break;
603                                 }
604                                 break;
605
606                         case Msg.WM_SYSKEYUP:
607                                 switch (keynav_state) {
608                                 case KeyNavState.Idle:
609                                 case KeyNavState.Navigating:
610                                         break;
611                                 case KeyNavState.Startup:
612                                         keynav_state = KeyNavState.NoPopups;
613                                         SelectItem (TopMenu, TopMenu.MenuItems [0], false);
614                                         break;
615                                 default:
616                                         Deactivate ();
617                                         main_menu.Draw ();
618                                         break;
619                                 }
620                                 break;
621                         }
622                 }
623
624                 bool ProcessMnemonic (Message msg, Keys key_data)
625                 {
626                         keynav_state = KeyNavState.Navigating;
627                         MenuItem item = FindItemByKey (CurrentMenu, msg.WParam);
628                         if ((item == null) || (GrabControl == null))
629                                 return false;
630
631                         active = true;
632                         GrabControl.ActiveTracker = this;
633                         
634                         SelectItem (CurrentMenu, item, true);
635                         if (item.IsPopup) {
636                                 CurrentMenu = item;
637                                 SelectItem (item, item.MenuItems [0], false);
638                         }
639                         return true;
640                 }
641
642                 Hashtable shortcuts = new Hashtable ();
643                 
644                 public void AddShortcuts (MenuItem item)
645                 {
646                         foreach (MenuItem child in item.MenuItems) {
647                                 AddShortcuts (child);
648                                 if (child.Shortcut != Shortcut.None)
649                                         shortcuts [(int)child.Shortcut] = child;
650                         }
651
652                         if (item.Shortcut != Shortcut.None)
653                                 shortcuts [(int)item.Shortcut] = item;
654                 }
655
656                 public void RemoveShortcuts (MenuItem item)
657                 {
658                         foreach (MenuItem child in item.MenuItems) {
659                                 RemoveShortcuts (child);
660                                 if (child.Shortcut != Shortcut.None)
661                                         shortcuts.Remove ((int)child.Shortcut);
662                         }
663
664                         if (item.Shortcut != Shortcut.None)
665                                 shortcuts.Remove ((int)item.Shortcut);
666                 }
667
668                 bool ProcessShortcut (Keys keyData)
669                 {
670                         MenuItem item = shortcuts [(int)keyData] as MenuItem;
671                         if (item == null || !item.Enabled)
672                                 return false;
673
674                         if (active)
675                                 Deactivate ();
676                         item.PerformClick ();
677                         return true;
678                 }
679
680                 public bool ProcessKeys (ref Message msg, Keys keyData)
681                 {
682                         // If we get Alt-F4, Windows will ignore it because we have a capture,
683                         // release the capture and the program will exit.  (X11 doesn't care.)
684                         if ((keyData & Keys.Alt) == Keys.Alt && (keyData & Keys.F4) == Keys.F4) {
685                                 if (GrabControl != null)
686                                         GrabControl.ActiveTracker = null;
687                                         
688                                 return false;
689                         }
690                         
691                         if ((Msg)msg.Msg != Msg.WM_SYSKEYUP && ProcessShortcut (keyData))
692                                 return true;
693                         else if ((keyData & Keys.KeyCode) == Keys.Menu && TopMenu is MainMenu) {
694                                 ProcessMenuKey ((Msg) msg.Msg);
695                                 return true;
696                         } else if ((keyData & Keys.Alt) == Keys.Alt)
697                                 return ProcessMnemonic (msg, keyData);
698                         else if ((Msg)msg.Msg == Msg.WM_SYSKEYUP)
699                                 return false;
700                         else if (!Navigating)
701                                 return false;
702
703                         MenuItem item;
704                         
705                         switch (keyData) {
706                         case Keys.Up:
707                                 if (CurrentMenu is MainMenu)
708                                         return true;
709                                 else if (CurrentMenu.MenuItems.Count == 1 && CurrentMenu.parent_menu == TopMenu) {
710                                         DeselectItem (CurrentMenu.SelectedItem);
711                                         CurrentMenu = TopMenu;
712                                         return true;
713                                 }
714                                 item = GetNextItem (CurrentMenu, ItemNavigation.Previous);
715                                 if (item != null)
716                                         SelectItem (CurrentMenu, item, false);
717                                 break;
718                         
719                         case Keys.Down:
720                                 if (CurrentMenu is MainMenu) {
721                                         if (CurrentMenu.SelectedItem != null && CurrentMenu.SelectedItem.IsPopup) {
722                                                 keynav_state = KeyNavState.Navigating;
723                                                 item = CurrentMenu.SelectedItem;
724                                                 ShowSubPopup (CurrentMenu, item);
725                                                 SelectItem (item, item.MenuItems [0], false);
726                                                 CurrentMenu = item;
727                                                 active = true;
728                                                 GrabControl.ActiveTracker = this;
729                                         }
730                                         return true;
731                                 }
732                                 item = GetNextItem (CurrentMenu, ItemNavigation.Next);
733                                 if (item != null)
734                                         SelectItem (CurrentMenu, item, false);
735                                 break;
736                         
737                         case Keys.Right:
738                                 if (CurrentMenu is MainMenu) {
739                                         item = GetNextItem (CurrentMenu, ItemNavigation.Next);
740                                         bool popup = item.IsPopup && keynav_state != KeyNavState.NoPopups;
741                                         SelectItem (CurrentMenu, item, popup);
742                                         if (popup) {
743                                                 SelectItem (item, item.MenuItems [0], false);
744                                                 CurrentMenu = item;
745                                         }
746                                 } else if (CurrentMenu.SelectedItem != null && CurrentMenu.SelectedItem.IsPopup) {
747                                         item = CurrentMenu.SelectedItem;
748                                         ShowSubPopup (CurrentMenu, item);
749                                         SelectItem (item, item.MenuItems [0], false);
750                                         CurrentMenu = item;
751                                 } else {
752                                         //Search up for a main menu
753                                         Menu Prnt = CurrentMenu.parent_menu;
754                                         while (Prnt != null && !(Prnt is MainMenu)) {
755                                                 Prnt = Prnt.parent_menu;
756                                         }
757                                         if (Prnt is MainMenu)
758                                         {
759                                                 item = GetNextItem(Prnt, ItemNavigation.Next);
760                                                 SelectItem(Prnt, item, item.IsPopup);
761                                                 if (item.IsPopup)
762                                                 {
763                                                         SelectItem(item, item.MenuItems[0], false);
764                                                         CurrentMenu = item;
765                                                 }
766                                         }
767                                 }
768                                 break;
769                         
770                         case Keys.Left:
771                                 if (CurrentMenu is MainMenu) {
772                                         item = GetNextItem (CurrentMenu, ItemNavigation.Previous);
773                                         bool popup = item.IsPopup && keynav_state != KeyNavState.NoPopups;
774                                         SelectItem (CurrentMenu, item, popup);
775                                         if (popup) {
776                                                 SelectItem (item, item.MenuItems [0], false);
777                                                 CurrentMenu = item;
778                                         }
779                                 } else if (CurrentMenu.parent_menu is MainMenu) {
780                                         item = GetNextItem (CurrentMenu.parent_menu, ItemNavigation.Previous);
781                                         SelectItem (CurrentMenu.parent_menu, item, item.IsPopup);
782                                         if (item.IsPopup) {
783                                                 SelectItem (item, item.MenuItems [0], false);
784                                                 CurrentMenu = item;
785                                         }
786                                 } else {
787                                         HideSubPopups (CurrentMenu, TopMenu);
788                                         if (CurrentMenu.parent_menu != null)
789                                                 CurrentMenu = CurrentMenu.parent_menu;
790                                 }
791                                 break;
792
793                         case Keys.Return:
794                                 if (CurrentMenu.SelectedItem != null && CurrentMenu.SelectedItem.IsPopup) {
795                                         keynav_state = KeyNavState.Navigating;
796                                         item = CurrentMenu.SelectedItem;
797                                         ShowSubPopup (CurrentMenu, item);
798                                         SelectItem (item, item.MenuItems [0], false);
799                                         CurrentMenu = item;
800                                         active = true;
801                                         GrabControl.ActiveTracker = this;
802                                 } else {
803                                         ExecFocusedItem (CurrentMenu, CurrentMenu.SelectedItem);
804                                 }
805                                 break;
806                                 
807                         case Keys.Escape:
808                                 Deactivate ();
809                                 break;
810
811                         default:
812                                 ProcessMnemonic (msg, keyData);
813                                 break;
814                         }
815
816                         return active;
817                 }
818         }
819
820         internal class PopUpWindow : Control
821         {
822                 private Menu menu;
823                 private Control form;
824
825                 public PopUpWindow (Control form, Menu menu): base ()
826                 {
827                         this.menu = menu;
828                         this.form = form;
829                         SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
830                         SetStyle (ControlStyles.ResizeRedraw | ControlStyles.Opaque, true);
831                         is_visible = false;
832                 }
833
834                 protected override CreateParams CreateParams
835                 {
836                         get {
837                                 CreateParams cp = base.CreateParams;
838                                 cp.Caption = "Menu PopUp";
839                                 cp.Style = unchecked ((int)(WindowStyles.WS_POPUP));
840                                 cp.ExStyle |= (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST);
841                                 return cp;
842                         }
843                 }
844
845                 public void ShowWindow ()
846                 {
847                         XplatUI.SetCursor(form.Handle, Cursors.Default.handle);
848                         RefreshItems ();
849                         Show ();
850                 }
851                 
852                 internal override void OnPaintInternal (PaintEventArgs args)
853                 {
854                         ThemeEngine.Current.DrawPopupMenu (args.Graphics, menu, args.ClipRectangle, ClientRectangle);
855                 }
856                 
857                 public void HideWindow ()
858                 {
859                         XplatUI.SetCursor (form.Handle, form.Cursor.handle);
860                         MenuTracker.HideSubPopups (menu, null);
861                 Hide ();
862                 }
863
864                 protected override void CreateHandle ()
865                 {
866                         base.CreateHandle ();
867                         RefreshItems ();                        
868                 }               
869                 
870                 // Called when the number of items has changed
871                 internal void RefreshItems ()
872                 {
873                         Point pt = new Point (Location.X, Location.Y);
874                         
875                         ThemeEngine.Current.CalcPopupMenuSize (DeviceContext, menu);
876
877                         if ((pt.X + menu.Rect.Width) > SystemInformation.VirtualScreen.Width) {
878                                 if (((pt.X - menu.Rect.Width) > 0) && !(menu.parent_menu is MainMenu))
879                                         pt.X = pt.X - menu.Rect.Width;
880                                 else
881                                         pt.X = SystemInformation.VirtualScreen.Width - menu.Rect.Width;
882
883                                 if (pt.X < 0)
884                                         pt.X = 0;
885                         }
886                         if ((pt.Y + menu.Rect.Height) > SystemInformation.VirtualScreen.Height) {
887                                 if ((pt.Y - menu.Rect.Height) > 0)
888                                         pt.Y = pt.Y - menu.Rect.Height;
889                                 else
890                                         pt.Y = SystemInformation.VirtualScreen.Height - menu.Rect.Height;
891
892                                 if (pt.Y < 0)
893                                         pt.Y = 0;
894                         }
895
896                         Location = pt;
897                         Width = menu.Rect.Width;
898                         Height = menu.Rect.Height;                      
899                 }
900                 
901                 internal override bool ActivateOnShow { get { return false; } }
902         }
903 }
904
905