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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
20 // Copyright (c) 2004 Novell, Inc.
23 // Jordi Mas i Hernandez, jordi@ximian.com
29 using System.Drawing.Text;
30 using System.Collections;
32 namespace System.Windows.Forms
36 This class mimics the Win32 API Menu functionality
38 When writing this code the Wine project was of great help to
39 understand the logic behind some Win32 issues. Thanks to them. Jordi,
41 internal class MenuAPI
43 static StringFormat string_format_text = new StringFormat ();
44 static StringFormat string_format_shortcut = new StringFormat ();
45 static StringFormat string_format_menubar_text = new StringFormat ();
46 static ArrayList menu_list = new ArrayList ();
47 static Font MENU_FONT = new Font (FontFamily.GenericSansSerif, 8.25f);
48 static int POPUP_ARROW_WITDH;
49 static int POPUP_ARROW_HEIGHT;
50 const int SEPARATOR_HEIGHT = 5;
51 const int SM_CXBORDER = 1;
52 const int SM_CYBORDER = 1;
53 const int SM_CXMENUCHECK = 14; // Width of the menu check
54 const int SM_CYMENUCHECK = 14; // Height of the menu check
55 const int SM_CXARROWCHECK = 16; // Width of the arrow
56 const int SM_CYARROWCHECK = 16; // Height of the arrow
57 const int SM_CYMENU = 18; // Minimum height of a menu
58 const int MENU_TAB_SPACE = 8; // Pixels added to the width of an item because of a tab
59 const int MENU_BAR_ITEMS_SPACE = 8; // Space between menu bar items
63 public MF Flags; // Menu flags (MF_POPUP, MF_SYSMENU)
64 public int Width; // Width of the whole menu
65 public int Height; // Height of the whole menu
66 public Control Wnd; // In a Popup menu is the PopupWindow and in a MenuBar the Form
67 public ArrayList items; // Array of menu items
68 public int FocusedItem; // Currently focused item
69 public IntPtr hParent;
70 public MENUITEM SelectedItem; // Currently selected item
72 public bool bTracking;
73 public Menu menu; // SWF.Menu
75 public MENU (Menu menu_obj)
78 hParent = IntPtr.Zero;
79 items = new ArrayList ();
81 Width = Height = FocusedItem = 0;
92 public Rectangle rect;
95 public IntPtr hSubMenu;
97 public int pos; /* Position in the menuitems array*/
104 rect = new Rectangle ();
111 public IntPtr hCurrentMenu;
112 public IntPtr hTopMenu;
116 hCurrentMenu = hTopMenu = IntPtr.Zero;
120 public enum MenuMouseEvent
126 internal enum ItemNavigation
141 MF_BYPOSITION = 0x400,
142 MF_SEPARATOR = 0x800,
148 MF_USECHECKBITMAPS = 0x200,
151 MF_OWNERDRAW = 0x100,
153 MF_MENUBARBREAK = 0x20,
160 MF_RIGHTJUSTIFY = 0x4000,
161 MF_MENUBAR = 0x8000 // Internal
166 string_format_text.LineAlignment = StringAlignment.Center;
167 string_format_text.Alignment = StringAlignment.Near;
168 string_format_text.HotkeyPrefix = HotkeyPrefix.Show;
170 string_format_shortcut.LineAlignment = StringAlignment.Center;
171 string_format_shortcut.Alignment = StringAlignment.Far;
173 string_format_menubar_text.LineAlignment = StringAlignment.Center;
174 string_format_menubar_text.Alignment = StringAlignment.Center;
175 string_format_menubar_text.HotkeyPrefix = HotkeyPrefix.Show;
178 static public IntPtr StoreMenuID (MENU menu)
180 int id = menu_list.Add (menu);
181 return (IntPtr)(id + 1);
184 static public MENU GetMenuFromID (IntPtr ptr)
189 if (menu_list[id] == null) // It has been delete it
192 return (MENU) menu_list[id];
195 static public IntPtr CreateMenu (Menu menu_obj)
197 MENU menu = new MENU (menu_obj);
198 return StoreMenuID (menu);
201 static public IntPtr CreatePopupMenu (Menu menu_obj)
203 MENU popMenu = new MENU (menu_obj);
204 popMenu.Flags |= MF.MF_POPUP;
205 return StoreMenuID (popMenu);
208 static public int InsertMenuItem (IntPtr hMenu, int uItem, bool fByPosition, MenuItem item, ref IntPtr hSubMenu)
212 if (fByPosition == false)
213 throw new NotImplementedException ();
215 MENU menu = GetMenuFromID (hMenu);
216 if ((uint)uItem > menu.items.Count)
217 uItem = menu.items.Count;
219 MENUITEM menu_item = new MENUITEM ();
220 menu_item.item = item;
223 menu_item.hSubMenu = CreatePopupMenu (menu_item.item);
224 MENU submenu = GetMenuFromID (menu_item.hSubMenu);
225 submenu.hParent = hMenu;
228 menu_item.hSubMenu = IntPtr.Zero;
230 hSubMenu = menu_item.hSubMenu;
231 id = menu.items.Count;
232 menu_item.pos = menu.items.Count;
233 menu.items.Insert (uItem, menu_item);
238 // The Point object contains screen coordinates
239 static public bool TrackPopupMenu (IntPtr hTopMenu, IntPtr hMenu, Point pnt, bool bMenubar, Control Wnd)
241 TRACKER tracker = new TRACKER ();
242 MENU top_menu = GetMenuFromID (hTopMenu);
245 if (hMenu == IntPtr.Zero) // No submenus to track
248 menu = GetMenuFromID (hMenu);
250 Console.WriteLine ("TrackPopupMenu hTopMenu: {0} hMenu:{1} bMenubar:{2} top_menu.bMenubar: {3} menu.bMenubar: {4}",hTopMenu,
251 hMenu, bMenubar, top_menu.bMenubar, menu.bMenubar);
254 menu.Wnd = new PopUpWindow (hMenu, tracker);
255 tracker.hCurrentMenu = hMenu;
256 tracker.hTopMenu = hTopMenu;
258 MENUITEM select_item = GetNextItem (hMenu, ItemNavigation.First);
260 if (select_item != null) {
261 MenuAPI.SelectItem (hMenu, select_item, false, tracker);
264 // Make sure the menu is always visible and does not 'leave' the screen
265 // What is menu.Width/Height? It seemed to be 0/0
266 if ((pnt.X + menu.Wnd.Width) > SystemInformation.WorkingArea.Width) {
267 pnt.X -= menu.Wnd.Width;
270 if ((pnt.X + menu.Wnd.Height) > SystemInformation.WorkingArea.Height) {
271 pnt.Y -= menu.Wnd.Height;
274 menu.Wnd.Location = menu.Wnd.PointToClient (pnt);
276 if (menu.menu.IsDirty) {
278 menu.menu.CreateItems ();
279 ((PopUpWindow)menu.Wnd).RefreshItems ();
280 menu.menu.IsDirty = false;
283 ((PopUpWindow)menu.Wnd).ShowWindow ();
287 if (menu.Wnd == null) {
299 static public void CalcItemSize (Graphics dc, MENUITEM item, int y, int x, bool menuBar)
304 if (item.item.Visible == false)
307 if (item.item.Separator == true) {
308 item.rect.Height = SEPARATOR_HEIGHT / 2;
309 item.rect.Width = -1;
314 size = dc.MeasureString (item.item.Text, MENU_FONT);
315 item.rect.Width = (int) size.Width;
316 item.rect.Height = (int) size.Height;
320 if (item.item.Shortcut != Shortcut.None && item.item.ShowShortcut) {
321 item.xTab = SM_CXMENUCHECK + MENU_TAB_SPACE + (int) size.Width;
322 size = dc.MeasureString (" " + item.item.GetShortCutText (), MENU_FONT);
323 item.rect.Width += MENU_TAB_SPACE + (int) size.Width;
326 item.rect.Width += 4 + (SM_CXMENUCHECK * 2);
329 item.rect.Width += MENU_BAR_ITEMS_SPACE;
330 x += item.rect.Width;
333 if (item.rect.Height < SM_CYMENU - 1)
334 item.rect.Height = SM_CYMENU - 1;
337 static public void CalcPopupMenuSize (Graphics dc, IntPtr hMenu)
343 MENU menu = GetMenuFromID (hMenu);
346 while (start < menu.items.Count) {
349 for (i = start; i < menu.items.Count; i++) {
350 MENUITEM item = (MENUITEM) menu.items[i];
352 if ((i != start) && (item.item.Break || item.item.BarBreak))
355 CalcItemSize (dc, item, y, x, false);
356 y += item.rect.Height;
358 if (item.rect.Width > max)
359 max = item.rect.Width;
362 // Reemplace the -1 by the menu width (separators)
363 for (n = start; n < i; n++, start++) {
364 MENUITEM item = (MENUITEM) menu.items[n];
365 item.rect.Width = max;
380 menu.Width += SM_CXBORDER;
381 menu.Height += SM_CYBORDER;
384 static public void DrawMenuItem (Graphics dc, MENUITEM item, int menu_height, bool menuBar)
386 StringFormat string_format;
388 if (item.item.Visible == false)
392 string_format = string_format_menubar_text;
394 string_format = string_format_text;
396 if (item.item.Separator == true) {
398 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
399 item.rect.X, item.rect.Y, item.rect.X + item.rect.Width, item.rect.Y);
401 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonHilight),
402 item.rect.X, item.rect.Y + 1, item.rect.X + item.rect.Width, item.rect.Y + 1);
407 Rectangle rect_text = item.rect;
410 rect_text.X += SM_CXMENUCHECK;
412 if (item.item.BarBreak) { /* Draw vertical break bar*/
414 Rectangle rect = item.rect;
417 rect.Height = menu_height - 6;
419 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
420 rect.X, rect.Y , rect.X, rect.Y + rect.Height);
422 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonHilight),
423 rect.X + 1, rect.Y , rect.X +1, rect.Y + rect.Height);
427 if ((item.fState & MF.MF_HILITE) == MF.MF_HILITE) {
428 Rectangle rect = item.rect;
432 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush
433 (ThemeEngine.Current.ColorHilight), rect);
436 if (item.item.Enabled) {
440 if ((item.fState & MF.MF_HILITE) == MF.MF_HILITE)
441 color_text = ThemeEngine.Current.ColorHilightText;
443 color_text = ThemeEngine.Current.ColorMenuText;
446 dc.DrawString (item.item.Text, MENU_FONT,
447 ThemeEngine.Current.ResPool.GetSolidBrush (color_text),
448 rect_text, string_format);
450 if (!menuBar && item.item.Shortcut != Shortcut.None && item.item.ShowShortcut) {
452 string str = item.item.GetShortCutText ();
453 Rectangle rect = rect_text;
455 rect.Width -= item.xTab;
457 dc.DrawString (str, MENU_FONT, ThemeEngine.Current.ResPool.GetSolidBrush (color_text),
458 rect, string_format_shortcut);
463 ControlPaint.DrawStringDisabled (dc,
464 item.item.Text, MENU_FONT, Color.Black, rect_text,
469 if (menuBar == false && item.item.IsPopup) {
471 Bitmap bmp = new Bitmap (SM_CXARROWCHECK, SM_CYARROWCHECK);
472 Graphics gr = Graphics.FromImage (bmp);
473 Rectangle rect_arrow = new Rectangle (0, 0, SM_CXARROWCHECK, SM_CYARROWCHECK);
474 ControlPaint.DrawMenuGlyph (gr, rect_arrow, MenuGlyph.Arrow);
475 bmp.MakeTransparent ();
476 dc.DrawImage (bmp, item.rect.X + item.rect.Width - SM_CXARROWCHECK,
477 item.rect.Y + ((item.rect.Height - SM_CYARROWCHECK) /2));
483 /* Draw checked or radio */
484 if (menuBar == false && item.item.Checked) {
486 Rectangle area = item.rect;
487 Bitmap bmp = new Bitmap (SM_CXMENUCHECK, SM_CYMENUCHECK);
488 Graphics gr = Graphics.FromImage (bmp);
489 Rectangle rect_arrow = new Rectangle (0, 0, SM_CXMENUCHECK, SM_CYMENUCHECK);
491 if (item.item.RadioCheck)
492 ControlPaint.DrawMenuGlyph (gr, rect_arrow, MenuGlyph.Bullet);
494 ControlPaint.DrawMenuGlyph (gr, rect_arrow, MenuGlyph.Checkmark);
496 bmp.MakeTransparent ();
497 dc.DrawImage (bmp, area.X, item.rect.Y + ((item.rect.Height - SM_CYMENUCHECK) / 2));
505 static public void DrawPopupMenu (Graphics dc, IntPtr hMenu, Rectangle cliparea, Rectangle rect)
507 MENU menu = GetMenuFromID (hMenu);
509 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush
510 (ThemeEngine.Current.ColorMenu), cliparea);
512 /* Draw menu borders */
513 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorHilightText),
514 rect.X, rect.Y, rect.X + rect.Width, rect.Y);
516 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorHilightText),
517 rect.X, rect.Y, rect.X, rect.Y + rect.Height);
519 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
520 rect.X + rect.Width - 1 , rect.Y , rect.X + rect.Width - 1, rect.Y + rect.Height);
522 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonDkShadow),
523 rect.X + rect.Width, rect.Y , rect.X + rect.Width, rect.Y + rect.Height);
525 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
526 rect.X , rect.Y + rect.Height - 1 , rect.X + rect.Width - 1, rect.Y + rect.Height -1);
528 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonDkShadow),
529 rect.X , rect.Y + rect.Height, rect.X + rect.Width - 1, rect.Y + rect.Height);
531 for (int i = 0; i < menu.items.Count; i++)
532 if (cliparea.IntersectsWith (((MENUITEM) menu.items[i]).rect)) {
533 DrawMenuItem (dc, (MENUITEM) menu.items[i], menu.Height, menu.bMenubar);
538 // Updates the menu rect and returns the height
539 static public int MenuBarCalcSize (Graphics dc, IntPtr hMenu, int width)
544 MENU menu = GetMenuFromID (hMenu);
548 while (i < menu.items.Count) {
550 item = (MENUITEM) menu.items[i];
551 CalcItemSize (dc, item, y, x, true);
554 if (x + item.rect.Width > width) {
556 y += item.rect.Height;
561 x += item.rect.Width;
562 item.fState |= MF.MF_MENUBAR;
564 if (y + item.rect.Height > menu.Height)
565 menu.Height = item.rect.Height + y;
572 // Draws a menu bar in a Window
573 static public void DrawMenuBar (Graphics dc, IntPtr hMenu, Rectangle rect)
575 MENU menu = GetMenuFromID (hMenu);
576 Rectangle rect_menu = new Rectangle ();
578 if (menu.Height == 0)
579 MenuBarCalcSize (dc, hMenu, rect_menu.Width);
581 rect.Height = menu.Height;
582 rect.Width = menu.Width;
584 for (int i = 0; i < menu.items.Count; i++)
585 DrawMenuItem (dc, (MENUITEM) menu.items[i], menu.Height, true);
592 static public MENUITEM FindItemByCoords (IntPtr hMenu, Point pt)
594 MENU menu = GetMenuFromID (hMenu);
596 for (int i = 0; i < menu.items.Count; i++) {
597 MENUITEM item = (MENUITEM) menu.items[i];
598 if (item.rect.Contains (pt)) {
606 // Get the current selected item
607 static public MENUITEM GetSelected (IntPtr hMenu)
609 MENU menu = GetMenuFromID (hMenu);
613 for (int i = 0; i < menu.items.Count; i++) {
614 it = (MENUITEM) menu.items[i];
615 if ((it.fState & MF.MF_HILITE) == MF.MF_HILITE) {
623 static public void UnSelectItem (IntPtr hMenu, MENUITEM item)
625 MENU menu = GetMenuFromID (hMenu);
630 item.fState = item.fState & ~MF.MF_HILITE;
631 menu.Wnd.Invalidate (item.rect);
634 // Select the item and unselect the previous selecte item
635 static public void SelectItem (IntPtr hMenu, MENUITEM item, bool execute, TRACKER tracker)
637 MENU menu = GetMenuFromID (hMenu);
638 MENUITEM previous_selitem = GetSelected (hMenu);
640 /* Already selected */
641 if (previous_selitem != null && item.rect == previous_selitem.rect) {
645 UnSelectItem (hMenu, previous_selitem);
647 // If the previous item had subitems, hide them
648 if (previous_selitem != null && previous_selitem.item.IsPopup)
649 HideSubPopups (hMenu);
651 if (tracker.hCurrentMenu != hMenu) {
652 menu.Wnd.Capture = true;
653 tracker.hCurrentMenu = hMenu;
656 menu.SelectedItem = item;
657 item.fState |= MF.MF_HILITE;
658 menu.Wnd.Invalidate (item.rect);
660 item.item.PerformSelect ();
663 ExecFocusedItem (hMenu, item, tracker);
667 // Used when the user executes the action of an item (press enter, shortcut)
668 // or a sub-popup menu has to be shown
669 static public void ExecFocusedItem (IntPtr hMenu, MENUITEM item, TRACKER tracker)
671 if (item.item.Enabled == false)
674 if (item.item.IsPopup) {
675 ShowSubPopup (hMenu, item.hSubMenu, item, tracker);
682 // Create a popup window and show it or only show it if it is already created
683 static public void ShowSubPopup (IntPtr hParent, IntPtr hMenu, MENUITEM item, TRACKER tracker)
685 MENU menu = GetMenuFromID (hMenu);
686 Point pnt = new Point ();
688 if (item.item.Enabled == false)
691 MENU menu_parent = GetMenuFromID (hParent);
692 ((PopUpWindow)menu_parent.Wnd).LostFocus ();
693 tracker.hCurrentMenu = hMenu;
695 if (menu.Wnd == null)
696 menu.Wnd = new PopUpWindow (hMenu, tracker);
698 pnt.X = item.rect.X + item.rect.Width;
699 pnt.Y = item.rect.Y + 1;
700 pnt = menu_parent.Wnd.PointToScreen (pnt);
701 menu.Wnd.Location = pnt;
703 MENUITEM select_item = GetNextItem (hMenu, ItemNavigation.First);
705 if (select_item != null)
706 MenuAPI.SelectItem (hMenu, select_item, false, tracker);
708 ((PopUpWindow)menu.Wnd).ShowWindow ();
709 Console.WriteLine ("ShowSubPopup end {0} {1}", ((PopUpWindow)menu.Wnd).Location,
710 ((PopUpWindow)menu.Wnd).Size);
713 /* Hides all the submenus open in a menu */
714 static public void HideSubPopups (IntPtr hMenu)
716 MENU menu = GetMenuFromID (hMenu);
719 for (int i = 0; i < menu.items.Count; i++) {
720 item = (MENUITEM) menu.items[i];
721 if (!item.item.IsPopup)
724 MENU sub_menu = GetMenuFromID (item.hSubMenu);
726 if (sub_menu.Wnd != null) {
727 HideSubPopups (item.hSubMenu);
728 ((PopUpWindow)sub_menu.Wnd).Hide ();
733 static public void DestroyMenu (IntPtr hMenu)
735 if (hMenu == IntPtr.Zero)
738 MENU menu = GetMenuFromID (hMenu);
741 for (int i = 0; i < menu.items.Count; i++) {
742 item = (MENUITEM) menu.items[i];
743 if (item.item.IsPopup) {
744 MENU sub_menu = GetMenuFromID (item.hSubMenu);
745 if (sub_menu != null && sub_menu.Wnd != null)
746 HideSubPopups (item.hSubMenu);
748 DestroyMenu (item.hSubMenu);
752 // Do not destroy the window of a Menubar
753 if (menu.Wnd != null && menu.bMenubar == false) {
754 ((PopUpWindow)menu.Wnd).Dispose ();
758 /* Unreference from the array list */
759 menu_list[((int)hMenu)-1] = null;
762 // Find item by screen coordinates
763 static public bool FindSubItemByCoord (IntPtr hMenu, Point pnt, ref IntPtr hMenuItem, ref MENUITEM itemfound)
767 MENU menu = GetMenuFromID (hMenu);
770 for (int i = 0; i < menu.items.Count; i++) {
771 item = (MENUITEM) menu.items[i];
773 if (item.item.IsPopup)
774 if (FindSubItemByCoord (item.hSubMenu, pnt, ref hMenuItem, ref itemfound) == true)
777 if (menu.Wnd == null) // Menu has not been created yet
781 pnt_client = menu.Wnd.PointToScreen (new Point (item.rect.X, item.rect.Y));
782 rect.X = pnt_client.X;
783 rect.Y = pnt_client.Y;
785 if (rect.Contains (pnt) == true) {
795 static public void SetMenuBarWindow (IntPtr hMenu, Control wnd)
797 MENU menu = GetMenuFromID (hMenu);
799 menu.bMenubar = true;
802 static private void MenuBarMove (IntPtr hMenu, MENUITEM item, TRACKER tracker)
804 MENU menu = GetMenuFromID (hMenu);
805 Point pnt = new Point (item.rect.X, item.rect.Y + item.rect.Height);
806 pnt = menu.Wnd.PointToScreen (pnt);
808 MenuAPI.SelectItem (hMenu, item, false, tracker);
809 HideSubPopups (tracker.hCurrentMenu);
810 tracker.hCurrentMenu = hMenu;
811 MenuAPI.TrackPopupMenu (hMenu, item.hSubMenu, pnt, false, null);
814 // Function that process all menubar mouse events
815 static public void TrackBarMouseEvent (IntPtr hMenu, Control wnd, MouseEventArgs e, MenuMouseEvent eventype, TRACKER tracker)
817 MENU menu = GetMenuFromID (hMenu);
820 case MenuMouseEvent.Down: {
822 MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
825 MENU top_menu = GetMenuFromID (tracker.hTopMenu);
827 top_menu.bTracking = true;
828 MenuBarMove (hMenu, item, tracker);
831 item.item.PerformClick ();
838 case MenuMouseEvent.Move: { /* Coordinates in screen position*/
840 if (tracker.hTopMenu != IntPtr.Zero && tracker.hCurrentMenu != IntPtr.Zero) {
842 MENU top_menu = GetMenuFromID (tracker.hTopMenu);
844 if (top_menu.bTracking == false)
847 Point pnt = new Point (e.X, e.Y);
848 pnt = menu.Wnd.PointToClient (pnt);
850 MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, pnt);
852 if (item != null && menu.SelectedItem != item)
853 MenuBarMove (hMenu, item, tracker);
863 static public MENUITEM FindItemByKey (IntPtr hMenu, IntPtr key)
865 MENU menu = GetMenuFromID (hMenu);
868 char key_char = (char ) (key.ToInt32() & 0xff);
869 key_char = Char.ToUpper (key_char);
871 for (int i = 0; i < menu.items.Count; i++) {
872 item = (MENUITEM) menu.items[i];
874 if (item.item.Mnemonic == '\0')
877 if (item.item.Mnemonic == key_char)
884 // Get the next or previous selectable item on a menu
885 static public MENUITEM GetNextItem (IntPtr hMenu, ItemNavigation navigation)
887 MENU menu = GetMenuFromID (hMenu);
889 bool selectable_items = false;
892 // Check if there is at least a selectable item
893 for (int i = 0; i < menu.items.Count; i++) {
894 item = (MENUITEM)menu.items[i];
895 if (item.item.Separator == false && item.item.Visible == true) {
896 selectable_items = true;
901 if (selectable_items == false)
904 switch (navigation) {
905 case ItemNavigation.First: {
908 /* Next item that is not separator and it is visible*/
909 for (; pos < menu.items.Count; pos++) {
910 item = (MENUITEM)menu.items[pos];
911 if (item.item.Separator == false && item.item.Visible == true)
915 if (pos >= menu.items.Count) { /* Jump at the start of the menu */
917 /* Next item that is not separator and it is visible*/
918 for (; pos < menu.items.Count; pos++) {
919 item = (MENUITEM)menu.items[pos];
920 if (item.item.Separator == false && item.item.Visible == true)
928 case ItemNavigation.Last: { // Not used
932 case ItemNavigation.Next: {
934 if (menu.SelectedItem != null)
935 pos = menu.SelectedItem.pos;
937 /* Next item that is not separator and it is visible*/
938 for (pos++; pos < menu.items.Count; pos++) {
939 item = (MENUITEM)menu.items[pos];
940 if (item.item.Separator == false && item.item.Visible == true)
944 if (pos >= menu.items.Count) { /* Jump at the start of the menu */
946 /* Next item that is not separator and it is visible*/
947 for (; pos < menu.items.Count; pos++) {
948 item = (MENUITEM)menu.items[pos];
949 if (item.item.Separator == false && item.item.Visible == true)
956 case ItemNavigation.Previous: {
958 if (menu.SelectedItem != null)
959 pos = menu.SelectedItem.pos;
961 /* Previous item that is not separator and it is visible*/
962 for (pos--; pos >= 0; pos--) {
963 item = (MENUITEM)menu.items[pos];
964 if (item.item.Separator == false && item.item.Visible == true)
968 if (pos < 0 ) { /* Jump at the end of the menu*/
969 pos = menu.items.Count - 1;
970 /* Previous item that is not separator and it is visible*/
971 for (; pos >= 0; pos--) {
972 item = (MENUITEM)menu.items[pos];
973 if (item.item.Separator == false && item.item.Visible == true)
985 return (MENUITEM)menu.items[pos];
988 static public bool ProcessKeys (IntPtr hMenu, ref Message msg, Keys keyData, TRACKER tracker)
990 MENU menu = GetMenuFromID (hMenu);
995 item = GetNextItem (hMenu, ItemNavigation.Previous);
997 MenuAPI.SelectItem (hMenu, item, false, tracker);
1003 item = GetNextItem (hMenu, ItemNavigation.Next);
1006 MenuAPI.SelectItem (hMenu, item, false, tracker);
1010 /* Menubar selects and opens next. Popups next or open*/
1013 // Try to Expand popup first
1014 if (menu.SelectedItem.item.IsPopup) {
1015 ShowSubPopup (hMenu, menu.SelectedItem.hSubMenu, menu.SelectedItem, tracker);
1019 if (menu.hParent != IntPtr.Zero)
1020 parent = GetMenuFromID (menu.hParent);
1022 if (parent != null && parent.bMenubar == true) {
1023 MENUITEM select_item = GetNextItem (menu.hParent, ItemNavigation.Next);
1024 MenuBarMove (menu.hParent, select_item, tracker);
1033 // Try to Collapse popup first
1034 if (menu.SelectedItem.item.IsPopup) {
1039 if (menu.hParent != IntPtr.Zero)
1040 parent = GetMenuFromID (menu.hParent);
1042 if (parent != null && parent.bMenubar == true) {
1043 MENUITEM select_item = GetNextItem (menu.hParent, ItemNavigation.Previous);
1044 MenuBarMove (menu.hParent, select_item, tracker);
1052 Console.WriteLine ("Return key: "+ menu.SelectedItem.item.Text);
1053 MenuAPI.ExecFocusedItem (hMenu, menu.SelectedItem, tracker);
1061 /* Try if it is a menu hot key */
1062 item = MenuAPI.FindItemByKey (hMenu, msg.WParam);
1065 MenuAPI.SelectItem (hMenu, item, false, tracker);
1078 internal class PopUpWindow : Control
1080 private IntPtr hMenu;
1081 private MenuAPI.TRACKER tracker;
1083 public PopUpWindow (IntPtr hMenu, MenuAPI.TRACKER tracker): base ()
1086 this.tracker = tracker;
1087 MouseDown += new MouseEventHandler (OnMouseDownPUW);
1088 MouseMove += new MouseEventHandler (OnMouseMovePUW);
1089 MouseUp += new MouseEventHandler (OnMouseUpPUW);
1090 Paint += new PaintEventHandler (OnPaintPUW);
1091 SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
1092 SetStyle (ControlStyles.ResizeRedraw | ControlStyles.Opaque, true);
1096 protected override CreateParams CreateParams
1099 CreateParams cp = base.CreateParams;
1100 cp.Caption = "Menu PopUp";
1101 cp.Style = unchecked ((int)(WindowStyles.WS_POPUP));
1102 cp.ExStyle |= (int)(WindowStyles.WS_EX_TOOLWINDOW | WindowStyles.WS_EX_TOPMOST);
1107 public void ShowWindow ()
1114 public new void LostFocus ()
1119 protected override void OnResize (EventArgs e)
1124 private void OnPaintPUW (Object o, PaintEventArgs pevent)
1126 if (Width <= 0 || Height <= 0 || Visible == false)
1129 Draw (pevent.ClipRectangle);
1130 pevent.Graphics.DrawImage (ImageBuffer, pevent.ClipRectangle, pevent.ClipRectangle, GraphicsUnit.Pixel);
1133 public void HideWindow ()
1137 MenuAPI.MENU top_menu = MenuAPI.GetMenuFromID (tracker.hTopMenu);
1138 top_menu.bTracking = false;
1140 MenuAPI.HideSubPopups (tracker.hTopMenu);
1142 if (top_menu.bMenubar) {
1143 MenuAPI.MENUITEM item = MenuAPI.GetSelected (tracker.hTopMenu);
1146 MenuAPI.UnSelectItem (tracker.hTopMenu, item);
1148 } else { // Context Menu
1149 Console.WriteLine ("HideWindow context menu {0} ", top_menu.hParent);
1150 ((PopUpWindow)top_menu.Wnd).Hide ();
1154 private void OnMouseDownPUW (object sender, MouseEventArgs e)
1156 /* Click outside the client area*/
1157 if (ClientRectangle.Contains (e.X, e.Y) == false) {
1162 private void OnMouseUpPUW (object sender, MouseEventArgs e)
1164 /* Click in an item area*/
1165 MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
1168 item.item.PerformClick ();
1173 private void OnMouseMovePUW (object sender, MouseEventArgs e)
1175 MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
1178 MenuAPI.SelectItem (hMenu, item, true, tracker);
1181 MenuAPI.MENU menu_parent = null;
1183 if (tracker.hTopMenu != IntPtr.Zero)
1184 menu_parent = MenuAPI.GetMenuFromID (tracker.hTopMenu);
1186 if (menu_parent == null)
1189 if (menu_parent.bMenubar) {
1190 MenuAPI.TrackBarMouseEvent (tracker.hTopMenu,
1191 this, new MouseEventArgs(e.Button, e.Clicks, MousePosition.X, MousePosition.Y, e.Delta),
1192 MenuAPI.MenuMouseEvent.Move, tracker);
1195 IntPtr hMenuItem = IntPtr.Zero;
1196 MenuAPI.MENUITEM item_found = null;
1198 if (MenuAPI.FindSubItemByCoord (tracker.hTopMenu, MousePosition, ref hMenuItem, ref item_found) == false)
1201 MenuAPI.SelectItem (hMenuItem, item_found, false, tracker);
1205 protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
1207 return MenuAPI.ProcessKeys (hMenu, ref msg, keyData, tracker);
1210 protected override void CreateHandle ()
1212 base.CreateHandle ();
1216 // Called when the number of items has changed
1217 internal void RefreshItems ()
1219 MenuAPI.MENU menu = MenuAPI.GetMenuFromID (hMenu);
1220 MenuAPI.CalcPopupMenuSize (DeviceContext, hMenu);
1223 Height = menu.Height;
1226 private void Draw (Rectangle clip)
1228 MenuAPI.DrawPopupMenu (DeviceContext, hMenu, clip, ClientRectangle);