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