**** Merged from MCS ****
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / MenuAPI.cs
blobc807b4ca4e89c53747ee68754b9591a99e99129e
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.
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.
22 // Authors:
23 // Jordi Mas i Hernandez, jordi@ximian.com
26 // NOT COMPLETE
28 using System.Drawing;
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
61 public class MENU
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 TRACKER tracker;
71 public MENUITEM SelectedItem; // Currently focused item
72 public bool bMenubar;
74 public MENU ()
76 Wnd = null;
77 hParent = IntPtr.Zero;
78 items = new ArrayList ();
79 Flags = MF.MF_INSERT;
80 Width = Height = FocusedItem = 0;
81 tracker = new TRACKER ();
82 bMenubar = false;
87 public class MENUITEM
89 public MenuItem item;
90 public Rectangle rect;
91 public MF fState;
92 public int wID;
93 public IntPtr hSubMenu;
94 public int xTab;
95 public int pos; /* Position in the menuitems array*/
97 public MENUITEM ()
99 xTab = 0;
100 wID = 0;
101 pos = 0;
102 rect = new Rectangle ();
107 public class TRACKER
109 public IntPtr hCurrentMenu;
110 public IntPtr hTopMenu;
112 public TRACKER ()
114 hCurrentMenu = hTopMenu = IntPtr.Zero;
118 public enum MenuMouseEvent
120 Down,
121 Move,
124 internal enum ItemNavigation
126 First,
127 Last,
128 Next,
129 Previous,
132 internal enum MF
134 MF_INSERT = 0x0,
135 MF_APPEND = 0x100,
136 MF_DELETE = 0x200,
137 MF_REMOVE = 0x1000,
138 MF_BYCOMMAND = 0,
139 MF_BYPOSITION = 0x400,
140 MF_SEPARATOR = 0x800,
141 MF_ENABLED = 0,
142 MF_GRAYED = 1,
143 MF_DISABLED = 2,
144 MF_UNCHECKED = 0,
145 MF_CHECKED = 8,
146 MF_USECHECKBITMAPS = 0x200,
147 MF_STRING = 0,
148 MF_BITMAP = 4,
149 MF_OWNERDRAW = 0x100,
150 MF_POPUP = 0x10,
151 MF_MENUBARBREAK = 0x20,
152 MF_MENUBREAK = 0x40,
153 MF_UNHILITE = 0,
154 MF_HILITE = 0x80,
155 MF_DEFAULT = 0x1000,
156 MF_SYSMENU = 0x2000,
157 MF_HELP = 0x4000,
158 MF_RIGHTJUSTIFY = 0x4000,
159 MF_MENUBAR = 0x8000 // Internal
162 static MenuAPI ()
164 string_format_text.LineAlignment = StringAlignment.Center;
165 string_format_text.Alignment = StringAlignment.Near;
166 string_format_text.HotkeyPrefix = HotkeyPrefix.Show;
168 string_format_shortcut.LineAlignment = StringAlignment.Center;
169 string_format_shortcut.Alignment = StringAlignment.Far;
171 string_format_menubar_text.LineAlignment = StringAlignment.Center;
172 string_format_menubar_text.Alignment = StringAlignment.Center;
173 string_format_menubar_text.HotkeyPrefix = HotkeyPrefix.Show;
176 static public IntPtr StoreMenuID (MENU menu)
178 int id = menu_list.Add (menu);
179 return (IntPtr)(id + 1);
182 static public MENU GetMenuFromID (IntPtr ptr)
184 int id = (int)ptr;
185 id = id - 1;
187 if (menu_list[id] == null) // It has been delete it
188 return null;
190 return (MENU) menu_list[id];
193 static public IntPtr CreateMenu ()
195 MENU menu = new MENU ();
196 return StoreMenuID (menu);
199 static public IntPtr CreatePopupMenu ()
201 MENU popMenu = new MENU ();
202 popMenu.Flags |= MF.MF_POPUP;
203 return StoreMenuID (popMenu);
206 static public int InsertMenuItem (IntPtr hMenu, int uItem, bool fByPosition, MenuItem item, ref IntPtr hSubMenu)
208 int id;
210 if (fByPosition == false)
211 throw new NotImplementedException ();
213 MENU menu = GetMenuFromID (hMenu);
214 if ((uint)uItem > menu.items.Count)
215 uItem = menu.items.Count;
217 MENUITEM menu_item = new MENUITEM ();
218 menu_item.item = item;
220 if (item.IsPopup) {
221 menu_item.hSubMenu = CreatePopupMenu ();
222 MENU submenu = GetMenuFromID (menu_item.hSubMenu);
223 submenu.hParent = hMenu;
225 else
226 menu_item.hSubMenu = IntPtr.Zero;
228 hSubMenu = menu_item.hSubMenu;
229 id = menu.items.Count;
230 menu_item.pos = menu.items.Count;
231 menu.items.Insert (uItem, menu_item);
233 return id;
236 // The Point object contains screen coordinates
237 static public bool TrackPopupMenu (IntPtr hTopMenu, IntPtr hMenu, Point pnt, bool bMenubar, Control Wnd)
240 MENU menu = GetMenuFromID (hMenu);
241 menu.Wnd = new PopUpWindow (hMenu);
242 menu.tracker.hCurrentMenu = hMenu;
243 menu.tracker.hTopMenu = hTopMenu;
245 MENUITEM select_item = GetNextItem (hMenu, ItemNavigation.First);
247 if (select_item != null)
248 MenuAPI.SelectItem (hMenu, select_item, false);
250 menu.Wnd.Location = menu.Wnd.PointToClient (pnt);
251 ((PopUpWindow)menu.Wnd).ShowWindow ();
253 Application.Run ();
255 if (menu.Wnd == null) {
256 menu.Wnd.Dispose ();
257 menu.Wnd = null;
260 return true;
264 Menu drawing API
267 static public void CalcItemSize (Graphics dc, MENUITEM item, int y, int x, bool menuBar)
269 item.rect.Y = y;
270 item.rect.X = x;
272 if (item.item.Visible == false)
273 return;
275 if (item.item.Separator == true) {
276 item.rect.Height = SEPARATOR_HEIGHT / 2;
277 item.rect.Width = -1;
278 return;
281 SizeF size;
282 size = dc.MeasureString (item.item.Text, MENU_FONT);
283 item.rect.Width = (int) size.Width;
284 item.rect.Height = (int) size.Height;
286 if (!menuBar) {
288 if (item.item.Shortcut != Shortcut.None && item.item.ShowShortcut) {
289 item.xTab = SM_CXMENUCHECK + MENU_TAB_SPACE + (int) size.Width;
290 size = dc.MeasureString (" " + item.item.GetShortCutText (), MENU_FONT);
291 item.rect.Width += MENU_TAB_SPACE + (int) size.Width;
294 item.rect.Width += 4 + (SM_CXMENUCHECK * 2);
296 else {
297 item.rect.Width += MENU_BAR_ITEMS_SPACE;
298 x += item.rect.Width;
301 if (item.rect.Height < SM_CYMENU - 1)
302 item.rect.Height = SM_CYMENU - 1;
305 static public void CalcPopupMenuSize (Graphics dc, IntPtr hMenu)
307 int x = 3;
308 int start = 0;
309 int i, n, y, max;
311 MENU menu = GetMenuFromID (hMenu);
312 menu.Height = 0;
314 while (start < menu.items.Count) {
315 y = 2;
316 max = 0;
317 for (i = start; i < menu.items.Count; i++) {
318 MENUITEM item = (MENUITEM) menu.items[i];
320 if ((i != start) && (item.item.Break || item.item.BarBreak))
321 break;
323 CalcItemSize (dc, item, y, x, false);
324 y += item.rect.Height;
326 if (item.rect.Width > max)
327 max = item.rect.Width;
330 // Reemplace the -1 by the menu width (separators)
331 for (n = start; n < i; n++, start++) {
332 MENUITEM item = (MENUITEM) menu.items[n];
333 item.rect.Width = max;
336 if (y > menu.Height)
337 menu.Height = y;
339 x+= max;
342 menu.Width = x;
344 //space for border
345 menu.Width += 2;
346 menu.Height += 2;
348 menu.Width += SM_CXBORDER;
349 menu.Height += SM_CYBORDER;
352 static public void DrawMenuItem (Graphics dc, MENUITEM item, int menu_height, bool menuBar)
354 StringFormat string_format;
356 if (item.item.Visible == false)
357 return;
359 if (menuBar)
360 string_format = string_format_menubar_text;
361 else
362 string_format = string_format_text;
364 if (item.item.Separator == true) {
366 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
367 item.rect.X, item.rect.Y, item.rect.X + item.rect.Width, item.rect.Y);
369 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonHilight),
370 item.rect.X, item.rect.Y + 1, item.rect.X + item.rect.Width, item.rect.Y + 1);
372 return;
375 Rectangle rect_text = item.rect;
377 if (!menuBar)
378 rect_text.X += SM_CXMENUCHECK;
380 if (item.item.BarBreak) { /* Draw vertical break bar*/
382 Rectangle rect = item.rect;
383 rect.Y++;
384 rect.Width = 3;
385 rect.Height = menu_height - 6;
387 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
388 rect.X, rect.Y , rect.X, rect.Y + rect.Height);
390 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonHilight),
391 rect.X + 1, rect.Y , rect.X +1, rect.Y + rect.Height);
395 if ((item.fState & MF.MF_HILITE) == MF.MF_HILITE) {
396 Rectangle rect = item.rect;
397 rect.X++;
398 rect.Width -=2;
400 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush
401 (ThemeEngine.Current.ColorHilight), rect);
404 if (item.item.Enabled) {
406 Color color_text;
408 if ((item.fState & MF.MF_HILITE) == MF.MF_HILITE)
409 color_text = ThemeEngine.Current.ColorHilightText;
410 else
411 color_text = ThemeEngine.Current.ColorMenuText;
414 dc.DrawString (item.item.Text, MENU_FONT,
415 ThemeEngine.Current.ResPool.GetSolidBrush (color_text),
416 rect_text, string_format);
418 if (!menuBar && item.item.Shortcut != Shortcut.None && item.item.ShowShortcut) {
420 string str = item.item.GetShortCutText ();
421 Rectangle rect = rect_text;
422 rect.X = item.xTab;
423 rect.Width -= item.xTab;
425 dc.DrawString (str, MENU_FONT, ThemeEngine.Current.ResPool.GetSolidBrush (color_text),
426 rect, string_format_shortcut);
430 else {
431 ControlPaint.DrawStringDisabled (dc,
432 item.item.Text, MENU_FONT, Color.Black, rect_text,
433 string_format);
436 /* Draw arrow */
437 if (menuBar == false && item.item.IsPopup) {
439 Bitmap bmp = new Bitmap (SM_CXARROWCHECK, SM_CYARROWCHECK);
440 Graphics gr = Graphics.FromImage (bmp);
441 Rectangle rect_arrow = new Rectangle (0, 0, SM_CXARROWCHECK, SM_CYARROWCHECK);
442 ControlPaint.DrawMenuGlyph (gr, rect_arrow, MenuGlyph.Arrow);
443 bmp.MakeTransparent ();
444 dc.DrawImage (bmp, item.rect.X + item.rect.Width - SM_CXARROWCHECK,
445 item.rect.Y + ((item.rect.Height - SM_CYARROWCHECK) /2));
447 gr.Dispose ();
448 bmp.Dispose ();
451 /* Draw checked or radio */
452 if (menuBar == false && item.item.Checked) {
454 Rectangle area = item.rect;
455 Bitmap bmp = new Bitmap (SM_CXMENUCHECK, SM_CYMENUCHECK);
456 Graphics gr = Graphics.FromImage (bmp);
457 Rectangle rect_arrow = new Rectangle (0, 0, SM_CXMENUCHECK, SM_CYMENUCHECK);
459 if (item.item.RadioCheck)
460 ControlPaint.DrawMenuGlyph (gr, rect_arrow, MenuGlyph.Bullet);
461 else
462 ControlPaint.DrawMenuGlyph (gr, rect_arrow, MenuGlyph.Checkmark);
464 bmp.MakeTransparent ();
465 dc.DrawImage (bmp, area.X, item.rect.Y + ((item.rect.Height - SM_CYMENUCHECK) / 2));
467 gr.Dispose ();
468 bmp.Dispose ();
473 static public void DrawPopupMenu (Graphics dc, IntPtr hMenu, Rectangle cliparea, Rectangle rect)
475 MENU menu = GetMenuFromID (hMenu);
477 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush
478 (ThemeEngine.Current.ColorMenu), cliparea);
480 /* Draw menu borders */
481 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorHilightText),
482 rect.X, rect.Y, rect.X + rect.Width, rect.Y);
484 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorHilightText),
485 rect.X, rect.Y, rect.X, rect.Y + rect.Height);
487 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
488 rect.X + rect.Width - 1 , rect.Y , rect.X + rect.Width - 1, rect.Y + rect.Height);
490 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonDkShadow),
491 rect.X + rect.Width, rect.Y , rect.X + rect.Width, rect.Y + rect.Height);
493 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
494 rect.X , rect.Y + rect.Height - 1 , rect.X + rect.Width - 1, rect.Y + rect.Height -1);
496 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonDkShadow),
497 rect.X , rect.Y + rect.Height, rect.X + rect.Width - 1, rect.Y + rect.Height);
499 for (int i = 0; i < menu.items.Count; i++)
500 if (cliparea.IntersectsWith (((MENUITEM) menu.items[i]).rect)) {
501 DrawMenuItem (dc, (MENUITEM) menu.items[i], menu.Height, menu.bMenubar);
506 // Updates the menu rect and returns the height
507 static public int MenuBarCalcSize (Graphics dc, IntPtr hMenu, int width)
509 int x = 0;
510 int i = 0;
511 int y = 0;
512 MENU menu = GetMenuFromID (hMenu);
513 menu.Height = 0;
514 MENUITEM item;
516 while (i < menu.items.Count) {
518 item = (MENUITEM) menu.items[i];
519 CalcItemSize (dc, item, y, x, true);
520 i = i + 1;
522 if (x + item.rect.Width > width) {
523 item.rect.X = 0;
524 y += item.rect.Height;
525 item.rect.Y = y;
526 x = 0;
529 x += item.rect.Width;
530 item.fState |= MF.MF_MENUBAR;
532 if (y + item.rect.Height > menu.Height)
533 menu.Height = item.rect.Height + y;
536 menu.Width = width;
537 return menu.Height;
540 // Draws a menu bar in a Window
541 static public void DrawMenuBar (Graphics dc, IntPtr hMenu, Rectangle rect)
543 MENU menu = GetMenuFromID (hMenu);
544 Rectangle rect_menu = new Rectangle ();
546 if (menu.Height == 0)
547 MenuBarCalcSize (dc, hMenu, rect_menu.Width);
549 rect.Height = menu.Height;
550 rect.Width = menu.Width;
552 for (int i = 0; i < menu.items.Count; i++)
553 DrawMenuItem (dc, (MENUITEM) menu.items[i], menu.Height, true);
557 Menu handeling API
560 static public MENUITEM FindItemByCoords (IntPtr hMenu, Point pt)
562 MENU menu = GetMenuFromID (hMenu);
564 for (int i = 0; i < menu.items.Count; i++) {
565 MENUITEM item = (MENUITEM) menu.items[i];
566 if (item.rect.Contains (pt)) {
567 return item;
571 return null;
574 // Select the item and unselect the previous selecte item
575 static public void SelectItem (IntPtr hMenu, MENUITEM item, bool execute)
577 MENU menu = GetMenuFromID (hMenu);
578 MENUITEM previous_selitem = null;
580 /* Already selected */
581 for (int i = 0; i < menu.items.Count; i++) {
582 MENUITEM it = (MENUITEM) menu.items[i];
584 if ((it.fState & MF.MF_HILITE) == MF.MF_HILITE) {
585 if (item.rect == it.rect)
586 return;
588 /* Unselect previous item*/
589 previous_selitem = it;
590 it.fState = it.fState & ~MF.MF_HILITE;
591 menu.Wnd.Invalidate (previous_selitem.rect);
592 break;
596 // If the previous item had subitems, hide them
597 if (previous_selitem != null && previous_selitem.item.IsPopup)
598 HideSubPopups (hMenu);
600 if (menu.tracker.hCurrentMenu != hMenu)
601 menu.tracker.hCurrentMenu = hMenu;
603 menu.SelectedItem = item;
604 item.fState |= MF.MF_HILITE;
605 menu.Wnd.Invalidate (item.rect);
607 item.item.PerformSelect ();
609 if (execute)
610 ExecFocusedItem (hMenu, item);
614 // Used when the user executes the action of an item (press enter, shortcut)
615 // or a sub-popup menu has to be shown
616 static public void ExecFocusedItem (IntPtr hMenu, MENUITEM item)
618 if (item.item.IsPopup) {
619 ShowSubPopup (hMenu, item.hSubMenu, item);
621 else {
622 // Execute function
626 // Create a popup window and show it or only show it if it is already created
627 static public void ShowSubPopup (IntPtr hParent, IntPtr hMenu, MENUITEM item)
629 MENU menu = GetMenuFromID (hMenu);
631 if (item.item.Enabled == false)
632 return;
634 MENU menu_parent = GetMenuFromID (hParent);
635 ((PopUpWindow)menu_parent.Wnd).LostFocus ();
636 menu.tracker.hCurrentMenu = hMenu;
638 if (menu.Wnd == null) {
639 Point pnt = new Point ();
641 menu.Wnd = new PopUpWindow (hMenu);
642 pnt.X = item.rect.X + item.rect.Width;
643 pnt.Y = item.rect.Y + 1;
644 pnt = menu_parent.Wnd.PointToScreen (pnt);
645 menu.Wnd.Location = pnt;
648 MENUITEM select_item = GetNextItem (hMenu, ItemNavigation.First);
650 if (select_item != null)
651 MenuAPI.SelectItem (hMenu, select_item, false);
653 ((PopUpWindow)menu.Wnd).ShowWindow ();
654 menu.Wnd.Refresh ();
658 /* Hides all the submenus open in a menu */
659 static public void HideSubPopups (IntPtr hMenu)
661 MENU menu = GetMenuFromID (hMenu);
662 MENUITEM item;
664 for (int i = 0; i < menu.items.Count; i++) {
665 item = (MENUITEM) menu.items[i];
666 if (!item.item.IsPopup)
667 continue;
669 MENU sub_menu = GetMenuFromID (item.hSubMenu);
671 if (sub_menu.Wnd != null) {
672 HideSubPopups (item.hSubMenu);
673 ((PopUpWindow)sub_menu.Wnd).Hide ();
678 static public void DestroyMenu (IntPtr hMenu)
680 if (hMenu == IntPtr.Zero)
681 return;
683 MENU menu = GetMenuFromID (hMenu);
684 MENUITEM item;
686 for (int i = 0; i < menu.items.Count; i++) {
687 item = (MENUITEM) menu.items[i];
688 if (item.item.IsPopup) {
689 MENU sub_menu = GetMenuFromID (item.hSubMenu);
690 if (sub_menu != null && sub_menu.Wnd != null) {
691 HideSubPopups (item.hSubMenu);
692 DestroyMenu (item.hSubMenu);
697 // Do not destroy the window of a Menubar
698 if (menu.Wnd != null && menu.bMenubar == false) {
699 ((PopUpWindow)menu.Wnd).Dispose ();
700 menu.Wnd = null;
702 /* Unreference from the array list */
703 menu_list[((int)hMenu)-1] = null;
707 static public void SetMenuBarWindow (IntPtr hMenu, Control wnd)
709 MENU menu = GetMenuFromID (hMenu);
710 menu.Wnd = wnd;
711 menu.bMenubar = true;
714 static private void MenuBarMove (IntPtr hMenu, MENUITEM item)
716 MENU menu = GetMenuFromID (hMenu);
717 Point pnt = new Point (item.rect.X, item.rect.Y + item.rect.Height);
718 pnt = menu.Wnd.PointToScreen (pnt);
720 MenuAPI.SelectItem (hMenu, item, false);
721 HideSubPopups (menu.tracker.hCurrentMenu);
722 menu.tracker.hCurrentMenu = hMenu;
723 MenuAPI.TrackPopupMenu (hMenu, item.hSubMenu, pnt, false, null);
726 // Function that process all menubar mouse events
727 static public void TrackBarMouseEvent (IntPtr hMenu, Control wnd, MouseEventArgs e, MenuMouseEvent eventype)
729 MENU menu = GetMenuFromID (hMenu);
731 switch (eventype) {
732 case MenuMouseEvent.Down: {
734 MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
736 if (item != null && menu.SelectedItem != item)
737 MenuBarMove (hMenu, item);
739 break;
742 case MenuMouseEvent.Move: { /* Coordinates in screen position*/
744 if (menu.tracker.hCurrentMenu != IntPtr.Zero) {
746 Point pnt = new Point (e.X, e.Y);
747 pnt = menu.Wnd.PointToClient (pnt);
749 MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, pnt);
751 if (item != null && menu.SelectedItem != item)
752 MenuBarMove (hMenu, item);
754 break;
757 default:
758 break;
762 static public MENUITEM FindItemByKey (IntPtr hMenu, IntPtr key)
764 MENU menu = GetMenuFromID (hMenu);
765 MENUITEM item;
767 char key_char = (char ) (key.ToInt32() & 0xff);
768 key_char = Char.ToUpper (key_char);
770 for (int i = 0; i < menu.items.Count; i++) {
771 item = (MENUITEM) menu.items[i];
773 if (item.item.Mnemonic == '\0')
774 continue;
776 if (item.item.Mnemonic == key_char)
777 return item;
780 return null;
783 // Get the next or previous selectable item on a menu
784 static public MENUITEM GetNextItem (IntPtr hMenu, ItemNavigation navigation)
786 MENU menu = GetMenuFromID (hMenu);
787 int pos = 0;
788 bool selectable_items = false;
789 MENUITEM item;
791 // Check if there is at least a selectable item
792 for (int i = 0; i < menu.items.Count; i++) {
793 item = (MENUITEM)menu.items[i];
794 if (item.item.Separator == false && item.item.Visible == true) {
795 selectable_items = true;
796 break;
800 if (selectable_items == false)
801 return null;
803 switch (navigation) {
804 case ItemNavigation.First: {
805 pos = 0;
807 /* Next item that is not separator and it is visible*/
808 for (; pos < menu.items.Count; pos++) {
809 item = (MENUITEM)menu.items[pos];
810 if (item.item.Separator == false && item.item.Visible == true)
811 break;
814 if (pos >= menu.items.Count) { /* Jump at the start of the menu */
815 pos = 0;
816 /* Next item that is not separator and it is visible*/
817 for (; pos < menu.items.Count; pos++) {
818 item = (MENUITEM)menu.items[pos];
819 if (item.item.Separator == false && item.item.Visible == true)
820 break;
824 break;
827 case ItemNavigation.Last: { // Not used
828 break;
831 case ItemNavigation.Next: {
833 if (menu.SelectedItem != null)
834 pos = menu.SelectedItem.pos;
836 /* Next item that is not separator and it is visible*/
837 for (pos++; pos < menu.items.Count; pos++) {
838 item = (MENUITEM)menu.items[pos];
839 if (item.item.Separator == false && item.item.Visible == true)
840 break;
843 if (pos >= menu.items.Count) { /* Jump at the start of the menu */
844 pos = 0;
845 /* Next item that is not separator and it is visible*/
846 for (; pos < menu.items.Count; pos++) {
847 item = (MENUITEM)menu.items[pos];
848 if (item.item.Separator == false && item.item.Visible == true)
849 break;
852 break;
855 case ItemNavigation.Previous: {
857 if (menu.SelectedItem != null)
858 pos = menu.SelectedItem.pos;
860 /* Previous item that is not separator and it is visible*/
861 for (pos--; pos >= 0; pos--) {
862 item = (MENUITEM)menu.items[pos];
863 if (item.item.Separator == false && item.item.Visible == true)
864 break;
867 if (pos < 0 ) { /* Jump at the end of the menu*/
868 pos = menu.items.Count - 1;
869 /* Previous item that is not separator and it is visible*/
870 for (; pos >= 0; pos--) {
871 item = (MENUITEM)menu.items[pos];
872 if (item.item.Separator == false && item.item.Visible == true)
873 break;
877 break;
880 default:
881 break;
884 return (MENUITEM)menu.items[pos];
887 static public bool ProcessKeys (IntPtr hMenu, ref Message msg, Keys keyData)
889 MENU menu = GetMenuFromID (hMenu);
890 MENUITEM item;
892 switch (keyData) {
893 case Keys.Up: {
894 item = GetNextItem (hMenu, ItemNavigation.Previous);
895 if (item != null)
896 MenuAPI.SelectItem (hMenu, item, false);
898 break;
901 case Keys.Down: {
902 item = GetNextItem (hMenu, ItemNavigation.Next);
904 if (item != null)
905 MenuAPI.SelectItem (hMenu, item, false);
906 break;
909 /* Menubar selects and opens next. Popups next or open*/
910 case Keys.Right: {
912 // Try to Expand popup first
913 if (menu.SelectedItem.item.IsPopup) {
914 ShowSubPopup (hMenu, menu.SelectedItem.hSubMenu, menu.SelectedItem);
915 } else {
917 MENU parent = null;
918 if (menu.hParent != IntPtr.Zero)
919 parent = GetMenuFromID (menu.hParent);
921 if (parent != null && parent.bMenubar == true) {
922 MENUITEM select_item = GetNextItem (menu.hParent, ItemNavigation.Next);
923 MenuBarMove (menu.hParent, select_item);
927 break;
930 case Keys.Left: {
932 // Try to Collapse popup first
933 if (menu.SelectedItem.item.IsPopup) {
935 } else {
937 MENU parent = null;
938 if (menu.hParent != IntPtr.Zero)
939 parent = GetMenuFromID (menu.hParent);
941 if (parent != null && parent.bMenubar == true) {
942 MENUITEM select_item = GetNextItem (menu.hParent, ItemNavigation.Previous);
943 MenuBarMove (menu.hParent, select_item);
947 break;
950 default:
951 break;
954 /* Try if it is a menu hot key */
955 item = MenuAPI.FindItemByKey (hMenu, msg.WParam);
958 if (item != null) {
959 MenuAPI.SelectItem (hMenu, item, false);
960 return true;
963 return false;
969 class PopUpWindow
972 internal class PopUpWindow : Control
974 private IntPtr hMenu;
976 public PopUpWindow (IntPtr hMenu): base ()
978 this.hMenu = hMenu;
979 MouseDown += new MouseEventHandler (OnMouseDownPUW);
980 MouseMove += new MouseEventHandler (OnMouseMovePUW);
981 MouseUp += new MouseEventHandler (OnMouseUpPUW);
982 Paint += new PaintEventHandler (OnPaintPUW);
983 SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
984 SetStyle (ControlStyles.ResizeRedraw | ControlStyles.Opaque, true);
987 protected override CreateParams CreateParams
989 get {
990 CreateParams cp = base.CreateParams;
991 cp.Style = unchecked ((int)(WindowStyles.WS_POPUP | WindowStyles.WS_VISIBLE | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_CLIPCHILDREN));
992 cp.ExStyle |= (int)WindowStyles.WS_EX_TOOLWINDOW;
993 return cp;
997 public void ShowWindow ()
999 Show ();
1000 Refresh ();
1003 public void Destroy ()
1005 Capture = false;
1006 DestroyHandle ();
1009 public void LostFocus ()
1011 Capture = false;
1014 protected override void OnResize(EventArgs e)
1016 base.OnResize (e);
1019 private void OnPaintPUW (Object o, PaintEventArgs pevent)
1021 if (Width <= 0 || Height <= 0 || Visible == false)
1022 return;
1024 Draw (pevent.ClipRectangle);
1025 pevent.Graphics.DrawImage (ImageBuffer, pevent.ClipRectangle, pevent.ClipRectangle, GraphicsUnit.Pixel);
1028 private void OnMouseDownPUW (object sender, MouseEventArgs e)
1030 /* Click outside the client area*/
1031 if (ClientRectangle.Contains (e.X, e.Y) == false) {
1032 Capture = false;
1033 Hide ();
1037 private void OnMouseUpPUW (object sender, MouseEventArgs e)
1039 /* Click outside the client area*/
1040 MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
1041 MenuAPI.MENU menu = MenuAPI.GetMenuFromID (hMenu);
1043 if (item != null && item.item.Enabled) {
1044 item.item.PerformClick ();
1045 MenuAPI.DestroyMenu (menu.tracker.hTopMenu);
1047 Capture = false;
1048 Refresh ();
1053 private void OnMouseMovePUW (object sender, MouseEventArgs e)
1055 MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
1056 MenuAPI.MENU menu = MenuAPI.GetMenuFromID (hMenu);
1058 if (item != null) {
1059 MenuAPI.SelectItem (hMenu, item, true);
1060 } else {
1062 MenuAPI.MENU menu_parent = null;
1064 if (menu.tracker.hTopMenu != IntPtr.Zero)
1065 menu_parent = MenuAPI.GetMenuFromID (menu.tracker.hTopMenu);
1067 if (menu_parent!=null && menu_parent.bMenubar) {
1069 MenuAPI.TrackBarMouseEvent (menu.tracker.hTopMenu,
1070 this, new MouseEventArgs(e.Button, e.Clicks, MousePosition.X, MousePosition.Y, e.Delta),
1071 MenuAPI.MenuMouseEvent.Move);
1076 protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
1078 return MenuAPI.ProcessKeys (hMenu, ref msg, keyData);
1081 protected override void CreateHandle ()
1083 base.CreateHandle ();
1085 MenuAPI.MENU menu = MenuAPI.GetMenuFromID (hMenu);
1086 MenuAPI.CalcPopupMenuSize (DeviceContext, hMenu);
1088 Width = menu.Width;
1089 Height = menu.Height;
1093 private void Draw (Rectangle clip)
1095 MenuAPI.DrawPopupMenu (DeviceContext, hMenu, clip, ClientRectangle);