* TabControl.cs: Show the tooltip depending on the value
[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                                 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                         // We should process Alt+key only if we don't have an active menu,
683                         // and hide it otherwise.
684                         if ((keyData & Keys.Alt) == Keys.Alt && active) {
685                                 Deactivate ();
686                                 return false;
687                         }
688
689                         // If we get Alt-F4, Windows will ignore it because we have a capture,
690                         // release the capture and the program will exit.  (X11 doesn't care.)
691                         if ((keyData & Keys.Alt) == Keys.Alt && (keyData & Keys.F4) == Keys.F4) {
692                                 if (GrabControl != null)
693                                         GrabControl.ActiveTracker = null;
694                                         
695                                 return false;
696                         }
697                         
698                         if ((Msg)msg.Msg != Msg.WM_SYSKEYUP && ProcessShortcut (keyData))
699                                 return true;
700                         else if ((keyData & Keys.KeyCode) == Keys.Menu && TopMenu is MainMenu) {
701                                 ProcessMenuKey ((Msg) msg.Msg);
702                                 return true;
703                         } else if ((keyData & Keys.Alt) == Keys.Alt)
704                                 return ProcessMnemonic (msg, keyData);
705                         else if ((Msg)msg.Msg == Msg.WM_SYSKEYUP)
706                                 return false;
707                         else if (!Navigating)
708                                 return false;
709
710                         MenuItem item;
711                         
712                         switch (keyData) {
713                         case Keys.Up:
714                                 if (CurrentMenu is MainMenu)
715                                         return true;
716                                 else if (CurrentMenu.MenuItems.Count == 1 && CurrentMenu.parent_menu == TopMenu) {
717                                         DeselectItem (CurrentMenu.SelectedItem);
718                                         CurrentMenu = TopMenu;
719                                         return true;
720                                 }
721                                 item = GetNextItem (CurrentMenu, ItemNavigation.Previous);
722                                 if (item != null)
723                                         SelectItem (CurrentMenu, item, false);
724                                 break;
725                         
726                         case Keys.Down:
727                                 if (CurrentMenu is MainMenu) {
728                                         if (CurrentMenu.SelectedItem != null && CurrentMenu.SelectedItem.IsPopup) {
729                                                 keynav_state = KeyNavState.Navigating;
730                                                 item = CurrentMenu.SelectedItem;
731                                                 ShowSubPopup (CurrentMenu, item);
732                                                 SelectItem (item, item.MenuItems [0], false);
733                                                 CurrentMenu = item;
734                                                 active = true;
735                                                 GrabControl.ActiveTracker = this;
736                                         }
737                                         return true;
738                                 }
739                                 item = GetNextItem (CurrentMenu, ItemNavigation.Next);
740                                 if (item != null)
741                                         SelectItem (CurrentMenu, item, false);
742                                 break;
743                         
744                         case Keys.Right:
745                                 if (CurrentMenu is MainMenu) {
746                                         item = GetNextItem (CurrentMenu, ItemNavigation.Next);
747                                         bool popup = item.IsPopup && keynav_state != KeyNavState.NoPopups;
748                                         SelectItem (CurrentMenu, item, popup);
749                                         if (popup) {
750                                                 SelectItem (item, item.MenuItems [0], false);
751                                                 CurrentMenu = item;
752                                         }
753                                 } else if (CurrentMenu.SelectedItem != null && CurrentMenu.SelectedItem.IsPopup) {
754                                         item = CurrentMenu.SelectedItem;
755                                         ShowSubPopup (CurrentMenu, item);
756                                         SelectItem (item, item.MenuItems [0], false);
757                                         CurrentMenu = item;
758                                 } else {
759                                         //Search up for a main menu
760                                         Menu Prnt = CurrentMenu.parent_menu;
761                                         while (Prnt != null && !(Prnt is MainMenu)) {
762                                                 Prnt = Prnt.parent_menu;
763                                         }
764                                         if (Prnt is MainMenu)
765                                         {
766                                                 item = GetNextItem(Prnt, ItemNavigation.Next);
767                                                 SelectItem(Prnt, item, item.IsPopup);
768                                                 if (item.IsPopup)
769                                                 {
770                                                         SelectItem(item, item.MenuItems[0], false);
771                                                         CurrentMenu = item;
772                                                 }
773                                         }
774                                 }
775                                 break;
776                         
777                         case Keys.Left:
778                                 if (CurrentMenu is MainMenu) {
779                                         item = GetNextItem (CurrentMenu, ItemNavigation.Previous);
780                                         bool popup = item.IsPopup && keynav_state != KeyNavState.NoPopups;
781                                         SelectItem (CurrentMenu, item, popup);
782                                         if (popup) {
783                                                 SelectItem (item, item.MenuItems [0], false);
784                                                 CurrentMenu = item;
785                                         }
786                                 } else if (CurrentMenu.parent_menu is MainMenu) {
787                                         item = GetNextItem (CurrentMenu.parent_menu, ItemNavigation.Previous);
788                                         SelectItem (CurrentMenu.parent_menu, item, item.IsPopup);
789                                         if (item.IsPopup) {
790                                                 SelectItem (item, item.MenuItems [0], false);
791                                                 CurrentMenu = item;
792                                         }
793                                 } else if (!(CurrentMenu is ContextMenu)) { // ContextMenu root remains active.
794                                         HideSubPopups (CurrentMenu, TopMenu);
795                                         if (CurrentMenu.parent_menu != null)
796                                                 CurrentMenu = CurrentMenu.parent_menu;
797                                 }
798                                 break;
799
800                         case Keys.Return:
801                                 if (CurrentMenu.SelectedItem != null && CurrentMenu.SelectedItem.IsPopup) {
802                                         keynav_state = KeyNavState.Navigating;
803                                         item = CurrentMenu.SelectedItem;
804                                         ShowSubPopup (CurrentMenu, item);
805                                         SelectItem (item, item.MenuItems [0], false);
806                                         CurrentMenu = item;
807                                         active = true;
808                                         GrabControl.ActiveTracker = this;
809                                 } else {
810                                         ExecFocusedItem (CurrentMenu, CurrentMenu.SelectedItem);
811                                 }
812                                 break;
813                                 
814                         case Keys.Escape:
815                                 Deactivate ();
816                                 break;
817
818                         default:
819                                 ProcessMnemonic (msg, keyData);
820                                 break;
821                         }
822
823                         return active;
824                 }
825         }
826
827         internal class PopUpWindow : Control
828         {
829                 private Menu menu;
830                 private Control form;
831
832                 public PopUpWindow (Control form, Menu menu): base ()
833                 {
834                         this.menu = menu;
835                         this.form = form;
836                         SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
837                         SetStyle (ControlStyles.ResizeRedraw | ControlStyles.Opaque, true);
838                         is_visible = false;
839                 }
840
841                 protected override CreateParams CreateParams
842                 {
843                         get {
844                                 CreateParams cp = base.CreateParams;
845                                 cp.Caption = "Menu PopUp";
846                                 cp.Style = unchecked ((int)(WindowStyles.WS_POPUP));
847                                 cp.ExStyle |= (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST);
848                                 return cp;
849                         }
850                 }
851
852                 public void ShowWindow ()
853                 {
854                         XplatUI.SetCursor(form.Handle, Cursors.Default.handle);
855                         RefreshItems ();
856                         Show ();
857                 }
858                 
859                 internal override void OnPaintInternal (PaintEventArgs args)
860                 {
861                         ThemeEngine.Current.DrawPopupMenu (args.Graphics, menu, args.ClipRectangle, ClientRectangle);
862                 }
863                 
864                 public void HideWindow ()
865                 {
866                         XplatUI.SetCursor (form.Handle, form.Cursor.handle);
867                         MenuTracker.HideSubPopups (menu, null);
868                 Hide ();
869                 }
870
871                 protected override void CreateHandle ()
872                 {
873                         base.CreateHandle ();
874                         RefreshItems ();                        
875                 }               
876                 
877                 // Called when the number of items has changed
878                 internal void RefreshItems ()
879                 {
880                         Point pt = new Point (Location.X, Location.Y);
881                         
882                         ThemeEngine.Current.CalcPopupMenuSize (DeviceContext, menu);
883
884                         if ((pt.X + menu.Rect.Width) > SystemInformation.VirtualScreen.Width) {
885                                 if (((pt.X - menu.Rect.Width) > 0) && !(menu.parent_menu is MainMenu))
886                                         pt.X = pt.X - menu.Rect.Width;
887                                 else
888                                         pt.X = SystemInformation.VirtualScreen.Width - menu.Rect.Width;
889
890                                 if (pt.X < 0)
891                                         pt.X = 0;
892                         }
893                         if ((pt.Y + menu.Rect.Height) > SystemInformation.VirtualScreen.Height) {
894                                 if ((pt.Y - menu.Rect.Height) > 0)
895                                         pt.Y = pt.Y - menu.Rect.Height;
896                                 else
897                                         pt.Y = SystemInformation.VirtualScreen.Height - menu.Rect.Height;
898
899                                 if (pt.Y < 0)
900                                         pt.Y = 0;
901                         }
902
903                         Location = pt;
904                         Width = menu.Rect.Width;
905                         Height = menu.Rect.Height;                      
906                 }
907                 
908                 internal override bool ActivateOnShow { get { return false; } }
909         }
910 }
911
912