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