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 TRACKER tracker
;
71 public MENUITEM SelectedItem
; // Currently focused item
77 hParent
= IntPtr
.Zero
;
78 items
= new ArrayList ();
80 Width
= Height
= FocusedItem
= 0;
81 tracker
= new TRACKER ();
90 public Rectangle rect
;
93 public IntPtr hSubMenu
;
95 public int pos
; /* Position in the menuitems array*/
102 rect
= new Rectangle ();
109 public IntPtr hCurrentMenu
;
110 public IntPtr hTopMenu
;
114 hCurrentMenu
= hTopMenu
= IntPtr
.Zero
;
118 public enum MenuMouseEvent
124 internal enum ItemNavigation
139 MF_BYPOSITION
= 0x400,
140 MF_SEPARATOR
= 0x800,
146 MF_USECHECKBITMAPS
= 0x200,
149 MF_OWNERDRAW
= 0x100,
151 MF_MENUBARBREAK
= 0x20,
158 MF_RIGHTJUSTIFY
= 0x4000,
159 MF_MENUBAR
= 0x8000 // Internal
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
)
187 if (menu_list
[id
] == null) // It has been delete it
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
)
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
;
221 menu_item
.hSubMenu
= CreatePopupMenu ();
222 MENU submenu
= GetMenuFromID (menu_item
.hSubMenu
);
223 submenu
.hParent
= hMenu
;
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
);
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 ();
255 if (menu
.Wnd
== null) {
267 static public void CalcItemSize (Graphics dc
, MENUITEM item
, int y
, int x
, bool menuBar
)
272 if (item
.item
.Visible
== false)
275 if (item
.item
.Separator
== true) {
276 item
.rect
.Height
= SEPARATOR_HEIGHT
/ 2;
277 item
.rect
.Width
= -1;
282 size
= dc
.MeasureString (item
.item
.Text
, MENU_FONT
);
283 item
.rect
.Width
= (int) size
.Width
;
284 item
.rect
.Height
= (int) size
.Height
;
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);
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
)
311 MENU menu
= GetMenuFromID (hMenu
);
314 while (start
< menu
.items
.Count
) {
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
))
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
;
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)
360 string_format
= string_format_menubar_text
;
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);
375 Rectangle rect_text
= item
.rect
;
378 rect_text
.X
+= SM_CXMENUCHECK
;
380 if (item
.item
.BarBreak
) { /* Draw vertical break bar*/
382 Rectangle rect
= item
.rect
;
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
;
400 dc
.FillRectangle (ThemeEngine
.Current
.ResPool
.GetSolidBrush
401 (ThemeEngine
.Current
.ColorHilight
), rect
);
404 if (item
.item
.Enabled
) {
408 if ((item
.fState
& MF
.MF_HILITE
) == MF
.MF_HILITE
)
409 color_text
= ThemeEngine
.Current
.ColorHilightText
;
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
;
423 rect
.Width
-= item
.xTab
;
425 dc
.DrawString (str
, MENU_FONT
, ThemeEngine
.Current
.ResPool
.GetSolidBrush (color_text
),
426 rect
, string_format_shortcut
);
431 ControlPaint
.DrawStringDisabled (dc
,
432 item
.item
.Text
, MENU_FONT
, Color
.Black
, rect_text
,
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));
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
);
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));
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
)
512 MENU menu
= GetMenuFromID (hMenu
);
516 while (i
< menu
.items
.Count
) {
518 item
= (MENUITEM
) menu
.items
[i
];
519 CalcItemSize (dc
, item
, y
, x
, true);
522 if (x
+ item
.rect
.Width
> width
) {
524 y
+= item
.rect
.Height
;
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
;
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);
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
)) {
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
)
588 /* Unselect previous item*/
589 previous_selitem
= it
;
590 it
.fState
= it
.fState
& ~MF
.MF_HILITE
;
591 menu
.Wnd
.Invalidate (previous_selitem
.rect
);
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 ();
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
);
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)
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 ();
658 /* Hides all the submenus open in a menu */
659 static public void HideSubPopups (IntPtr hMenu
)
661 MENU menu
= GetMenuFromID (hMenu
);
664 for (int i
= 0; i
< menu
.items
.Count
; i
++) {
665 item
= (MENUITEM
) menu
.items
[i
];
666 if (!item
.item
.IsPopup
)
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
)
683 MENU menu
= GetMenuFromID (hMenu
);
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 ();
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
);
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
);
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
);
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
);
762 static public MENUITEM
FindItemByKey (IntPtr hMenu
, IntPtr key
)
764 MENU menu
= GetMenuFromID (hMenu
);
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')
776 if (item
.item
.Mnemonic
== key_char
)
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
);
788 bool selectable_items
= false;
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;
800 if (selectable_items
== false)
803 switch (navigation
) {
804 case ItemNavigation
.First
: {
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)
814 if (pos
>= menu
.items
.Count
) { /* Jump at the start of the menu */
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)
827 case ItemNavigation
.Last
: { // Not used
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)
843 if (pos
>= menu
.items
.Count
) { /* Jump at the start of the menu */
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)
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)
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)
884 return (MENUITEM
)menu
.items
[pos
];
887 static public bool ProcessKeys (IntPtr hMenu
, ref Message msg
, Keys keyData
)
889 MENU menu
= GetMenuFromID (hMenu
);
894 item
= GetNextItem (hMenu
, ItemNavigation
.Previous
);
896 MenuAPI
.SelectItem (hMenu
, item
, false);
902 item
= GetNextItem (hMenu
, ItemNavigation
.Next
);
905 MenuAPI
.SelectItem (hMenu
, item
, false);
909 /* Menubar selects and opens next. Popups next or open*/
912 // Try to Expand popup first
913 if (menu
.SelectedItem
.item
.IsPopup
) {
914 ShowSubPopup (hMenu
, menu
.SelectedItem
.hSubMenu
, menu
.SelectedItem
);
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
);
932 // Try to Collapse popup first
933 if (menu
.SelectedItem
.item
.IsPopup
) {
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
);
954 /* Try if it is a menu hot key */
955 item
= MenuAPI
.FindItemByKey (hMenu
, msg
.WParam
);
959 MenuAPI
.SelectItem (hMenu
, item
, false);
972 internal class PopUpWindow
: Control
974 private IntPtr hMenu
;
976 public PopUpWindow (IntPtr hMenu
): base ()
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
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
;
997 public void ShowWindow ()
1003 public void Destroy ()
1009 public void LostFocus ()
1014 protected override void OnResize(EventArgs e
)
1019 private void OnPaintPUW (Object o
, PaintEventArgs pevent
)
1021 if (Width
<= 0 || Height
<= 0 || Visible
== false)
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) {
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
);
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
);
1059 MenuAPI
.SelectItem (hMenu
, item
, true);
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
);
1089 Height
= menu
.Height
;
1093 private void Draw (Rectangle clip
)
1095 MenuAPI
.DrawPopupMenu (DeviceContext
, hMenu
, clip
, ClientRectangle
);