4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
43 #include "wine/port.h"
54 #include "wine/winbase16.h"
55 #include "wine/winuser16.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
59 #include "wine/exception.h"
62 #include "user_private.h"
63 #include "wine/debug.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
66 WINE_DECLARE_DEBUG_CHANNEL(accel
);
68 /* internal popup menu window messages */
70 #define MM_SETMENUHANDLE (WM_USER + 0)
71 #define MM_GETMENUHANDLE (WM_USER + 1)
73 /* Menu item structure */
75 /* ----------- MENUITEMINFO Stuff ----------- */
76 UINT fType
; /* Item type. */
77 UINT fState
; /* Item state. */
78 UINT_PTR wID
; /* Item id. */
79 HMENU hSubMenu
; /* Pop-up menu. */
80 HBITMAP hCheckBit
; /* Bitmap when checked. */
81 HBITMAP hUnCheckBit
; /* Bitmap when unchecked. */
82 LPWSTR text
; /* Item text. */
83 ULONG_PTR dwItemData
; /* Application defined. */
84 LPWSTR dwTypeData
; /* depends on fMask */
85 HBITMAP hbmpItem
; /* bitmap */
86 /* ----------- Wine stuff ----------- */
87 RECT rect
; /* Item area (relative to menu window) */
88 UINT xTab
; /* X position of text after Tab */
89 SIZE bmpsize
; /* size needed for the HBMMENU_CALLBACK
93 /* Popup menu structure */
95 WORD wFlags
; /* Menu flags (MF_POPUP, MF_SYSMENU) */
96 WORD wMagic
; /* Magic number */
97 WORD Width
; /* Width of the whole menu */
98 WORD Height
; /* Height of the whole menu */
99 UINT nItems
; /* Number of items in the menu */
100 HWND hWnd
; /* Window containing the menu */
101 MENUITEM
*items
; /* Array of menu items */
102 UINT FocusedItem
; /* Currently focused item */
103 HWND hwndOwner
; /* window receiving the messages for ownerdraw */
104 BOOL bTimeToHide
; /* Request hiding when receiving a second click in the top-level menu item */
105 BOOL bScrolling
; /* Scroll arrows are active */
106 UINT nScrollPos
; /* Current scroll position */
107 UINT nTotalHeight
; /* Total height of menu items inside menu */
108 /* ------------ MENUINFO members ------ */
109 DWORD dwStyle
; /* Extended menu style */
110 UINT cyMax
; /* max height of the whole menu, 0 is screen height */
111 HBRUSH hbrBack
; /* brush for menu background */
112 DWORD dwContextHelpID
;
113 DWORD dwMenuData
; /* application defined value */
114 HMENU hSysMenuOwner
; /* Handle to the dummy sys menu holder */
115 SIZE maxBmpSize
; /* Maximum size of the bitmap items */
116 } POPUPMENU
, *LPPOPUPMENU
;
118 /* internal flags for menu tracking */
120 #define TF_ENDMENU 0x10000
121 #define TF_SUSPENDPOPUP 0x20000
122 #define TF_SKIPREMOVE 0x40000
127 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
128 HMENU hTopMenu
; /* initial menu */
129 HWND hOwnerWnd
; /* where notifications are sent */
133 #define MENU_MAGIC 0x554d /* 'MU' */
138 /* Internal MENU_TrackMenu() flags */
139 #define TPM_INTERNAL 0xF0000000
140 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
141 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
143 /* Space between 2 columns */
144 #define MENU_COL_SPACE 4
146 /* top and bottom margins for popup menus */
147 #define MENU_TOP_MARGIN 3
148 #define MENU_BOTTOM_MARGIN 2
150 /* maximum allowed depth of any branch in the menu tree.
151 * This value is slightly larger than in windows (25) to
152 * stay on the safe side. */
153 #define MAXMENUDEPTH 30
155 /* (other menu->FocusedItem values give the position of the focused item) */
156 #define NO_SELECTED_ITEM 0xffff
158 #define MENU_ITEM_TYPE(flags) \
159 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
161 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
162 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
163 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
165 #define IS_SYSTEM_MENU(menu) \
166 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
168 #define MENUITEMINFO_TYPE_MASK \
169 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
170 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
171 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
172 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
173 #define STATE_MASK (~TYPE_MASK)
174 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
176 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
178 static SIZE menucharsize
;
179 static UINT ODitemheight
; /* default owner drawn item height */
181 /* Use global popup window because there's no way 2 menus can
182 * be tracked at the same time. */
183 static HWND top_popup
;
184 static HMENU top_popup_hmenu
;
186 /* Flag set by EndMenu() to force an exit from menu tracking */
187 static BOOL fEndMenu
= FALSE
;
189 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
);
191 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
);
193 static BOOL
SetMenuItemInfo_common( MENUITEM
*, const MENUITEMINFOW
*, BOOL
);
195 /*********************************************************************
196 * menu class descriptor
198 const struct builtin_class_descr MENU_builtin_class
=
200 (LPCWSTR
)POPUPMENU_CLASS_ATOM
, /* name */
201 CS_DROPSHADOW
| CS_SAVEBITS
| CS_DBLCLKS
, /* style */
202 NULL
, /* procA (winproc is Unicode only) */
203 PopupMenuWndProc
, /* procW */
204 sizeof(HMENU
), /* extra */
205 IDC_ARROW
, /* cursor */
206 (HBRUSH
)(COLOR_MENU
+1) /* brush */
210 /***********************************************************************
211 * debug_print_menuitem
213 * Print a menuitem in readable form.
216 #define debug_print_menuitem(pre, mp, post) \
217 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
219 #define MENUOUT(text) \
220 TRACE("%s%s", (count++ ? "," : ""), (text))
222 #define MENUFLAG(bit,text) \
224 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
227 static void do_debug_print_menuitem(const char *prefix
, const MENUITEM
*mp
,
230 static const char * const hbmmenus
[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
231 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
232 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
233 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
234 TRACE("%s ", prefix
);
236 UINT flags
= mp
->fType
;
237 TRACE( "{ ID=0x%lx", mp
->wID
);
239 TRACE( ", Sub=%p", mp
->hSubMenu
);
243 MENUFLAG( MFT_SEPARATOR
, "sep");
244 MENUFLAG( MFT_OWNERDRAW
, "own");
245 MENUFLAG( MFT_BITMAP
, "bit");
246 MENUFLAG(MF_POPUP
, "pop");
247 MENUFLAG(MFT_MENUBARBREAK
, "barbrk");
248 MENUFLAG(MFT_MENUBREAK
, "brk");
249 MENUFLAG(MFT_RADIOCHECK
, "radio");
250 MENUFLAG(MFT_RIGHTORDER
, "rorder");
251 MENUFLAG(MF_SYSMENU
, "sys");
252 MENUFLAG(MFT_RIGHTJUSTIFY
, "right"); /* same as MF_HELP */
254 TRACE( "+0x%x", flags
);
260 MENUFLAG(MFS_GRAYED
, "grey");
261 MENUFLAG(MFS_DEFAULT
, "default");
262 MENUFLAG(MFS_DISABLED
, "dis");
263 MENUFLAG(MFS_CHECKED
, "check");
264 MENUFLAG(MFS_HILITE
, "hi");
265 MENUFLAG(MF_USECHECKBITMAPS
, "usebit");
266 MENUFLAG(MF_MOUSESELECT
, "mouse");
268 TRACE( "+0x%x", flags
);
271 TRACE( ", Chk=%p", mp
->hCheckBit
);
273 TRACE( ", Unc=%p", mp
->hUnCheckBit
);
275 TRACE( ", Text=%s", debugstr_w(mp
->text
));
277 TRACE( ", ItemData=0x%08lx", mp
->dwItemData
);
280 if( IS_MAGIC_BITMAP(mp
->hbmpItem
))
281 TRACE( ", hbitmap=%s", hbmmenus
[ (INT_PTR
)mp
->hbmpItem
+ 1]);
283 TRACE( ", hbitmap=%p", mp
->hbmpItem
);
288 TRACE(" %s\n", postfix
);
295 /***********************************************************************
298 * Validate the given menu handle and returns the menu structure pointer.
300 static POPUPMENU
*MENU_GetMenu(HMENU hMenu
)
302 POPUPMENU
*menu
= USER_HEAP_LIN_ADDR(hMenu
);
303 if (!menu
|| menu
->wMagic
!= MENU_MAGIC
)
305 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu
, menu
, menu
? menu
->wMagic
:0);
311 /***********************************************************************
314 * Get the system menu of a window
316 static HMENU
get_win_sys_menu( HWND hwnd
)
319 WND
*win
= WIN_GetPtr( hwnd
);
320 if (win
&& win
!= WND_OTHER_PROCESS
&& win
!= WND_DESKTOP
)
323 WIN_ReleasePtr( win
);
328 /***********************************************************************
331 static HFONT
get_menu_font( BOOL bold
)
333 static HFONT hMenuFont
, hMenuFontBold
;
335 HFONT ret
= bold
? hMenuFontBold
: hMenuFont
;
339 NONCLIENTMETRICSW ncm
;
342 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
343 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSW
), &ncm
, 0);
347 ncm
.lfMenuFont
.lfWeight
+= 300;
348 if (ncm
.lfMenuFont
.lfWeight
> 1000) ncm
.lfMenuFont
.lfWeight
= 1000;
350 if (!(ret
= CreateFontIndirectW( &ncm
.lfMenuFont
))) return 0;
351 prev
= InterlockedCompareExchangePointer( (void **)(bold
? &hMenuFontBold
: &hMenuFont
),
355 /* another thread beat us to it */
363 /***********************************************************************
366 static HBITMAP
get_arrow_bitmap(void)
368 static HBITMAP arrow_bitmap
;
370 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
374 /***********************************************************************
375 * get_down_arrow_bitmap
377 static HBITMAP
get_down_arrow_bitmap(void)
379 static HBITMAP arrow_bitmap
;
381 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW
));
385 /***********************************************************************
386 * get_down_arrow_inactive_bitmap
388 static HBITMAP
get_down_arrow_inactive_bitmap(void)
390 static HBITMAP arrow_bitmap
;
392 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI
));
396 /***********************************************************************
397 * get_up_arrow_bitmap
399 static HBITMAP
get_up_arrow_bitmap(void)
401 static HBITMAP arrow_bitmap
;
403 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW
));
407 /***********************************************************************
408 * get_up_arrow_inactive_bitmap
410 static HBITMAP
get_up_arrow_inactive_bitmap(void)
412 static HBITMAP arrow_bitmap
;
414 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI
));
418 /***********************************************************************
421 * Return the default system menu.
423 static HMENU
MENU_CopySysPopup(void)
425 static const WCHAR sysmenuW
[] = {'S','Y','S','M','E','N','U',0};
426 HMENU hMenu
= LoadMenuW(user32_module
, sysmenuW
);
430 MENUITEMINFOW miteminfo
;
431 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
432 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
433 /* decorate the menu with bitmaps */
434 minfo
.cbSize
= sizeof( MENUINFO
);
435 minfo
.dwStyle
= MNS_CHECKORBMP
;
436 minfo
.fMask
= MIM_STYLE
;
437 SetMenuInfo( hMenu
, &minfo
);
438 miteminfo
.cbSize
= sizeof( MENUITEMINFOW
);
439 miteminfo
.fMask
= MIIM_BITMAP
;
440 miteminfo
.hbmpItem
= HBMMENU_POPUP_CLOSE
;
441 SetMenuItemInfoW( hMenu
, SC_CLOSE
, FALSE
, &miteminfo
);
442 miteminfo
.hbmpItem
= HBMMENU_POPUP_RESTORE
;
443 SetMenuItemInfoW( hMenu
, SC_RESTORE
, FALSE
, &miteminfo
);
444 miteminfo
.hbmpItem
= HBMMENU_POPUP_MAXIMIZE
;
445 SetMenuItemInfoW( hMenu
, SC_MAXIMIZE
, FALSE
, &miteminfo
);
446 miteminfo
.hbmpItem
= HBMMENU_POPUP_MINIMIZE
;
447 SetMenuItemInfoW( hMenu
, SC_MINIMIZE
, FALSE
, &miteminfo
);
448 SetMenuDefaultItem(hMenu
, SC_CLOSE
, FALSE
);
451 ERR("Unable to load default system menu\n" );
453 TRACE("returning %p.\n", hMenu
);
459 /**********************************************************************
462 * Create a copy of the system menu. System menu in Windows is
463 * a special menu bar with the single entry - system menu popup.
464 * This popup is presented to the outside world as a "system menu".
465 * However, the real system menu handle is sometimes seen in the
466 * WM_MENUSELECT parameters (and Word 6 likes it this way).
468 static HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
472 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
473 if ((hMenu
= CreateMenu()))
475 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
476 menu
->wFlags
= MF_SYSMENU
;
477 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
478 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
481 hPopupMenu
= MENU_CopySysPopup();
485 if (GetClassLongW(hWnd
, GCL_STYLE
) & CS_NOCLOSE
)
486 DeleteMenu(hPopupMenu
, SC_CLOSE
, MF_BYCOMMAND
);
488 InsertMenuW( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
489 (UINT_PTR
)hPopupMenu
, NULL
);
491 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
492 menu
->items
[0].fState
= 0;
493 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
495 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
498 DestroyMenu( hMenu
);
500 ERR("failed to load system menu!\n");
505 /***********************************************************************
506 * MENU_InitSysMenuPopup
508 * Grey the appropriate items in System menu.
510 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
514 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
515 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
516 gray
= ((style
& WS_MAXIMIZE
) != 0);
517 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
518 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
519 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
520 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
521 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
522 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
523 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
524 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
526 /* The menu item must keep its state if it's disabled */
528 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
532 /******************************************************************************
534 * UINT MENU_GetStartOfNextColumn(
537 *****************************************************************************/
539 static UINT
MENU_GetStartOfNextColumn(
542 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
546 return NO_SELECTED_ITEM
;
548 i
= menu
->FocusedItem
+ 1;
549 if( i
== NO_SELECTED_ITEM
)
552 for( ; i
< menu
->nItems
; ++i
) {
553 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
557 return NO_SELECTED_ITEM
;
561 /******************************************************************************
563 * UINT MENU_GetStartOfPrevColumn(
566 *****************************************************************************/
568 static UINT
MENU_GetStartOfPrevColumn(
571 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
575 return NO_SELECTED_ITEM
;
577 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
578 return NO_SELECTED_ITEM
;
580 /* Find the start of the column */
582 for(i
= menu
->FocusedItem
; i
!= 0 &&
583 !(menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
587 return NO_SELECTED_ITEM
;
589 for(--i
; i
!= 0; --i
) {
590 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
594 TRACE("ret %d.\n", i
);
601 /***********************************************************************
604 * Find a menu item. Return a pointer on the item, and modifies *hmenu
605 * in case the item was in a sub-menu.
607 static MENUITEM
*MENU_FindItem( HMENU
*hmenu
, UINT
*nPos
, UINT wFlags
)
610 MENUITEM
*fallback
= NULL
;
611 UINT fallback_pos
= 0;
614 if ((*hmenu
== (HMENU
)0xffff) || (!(menu
= MENU_GetMenu(*hmenu
)))) return NULL
;
615 if (wFlags
& MF_BYPOSITION
)
617 if (*nPos
>= menu
->nItems
) return NULL
;
618 return &menu
->items
[*nPos
];
622 MENUITEM
*item
= menu
->items
;
623 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
625 if (item
->fType
& MF_POPUP
)
627 HMENU hsubmenu
= item
->hSubMenu
;
628 MENUITEM
*subitem
= MENU_FindItem( &hsubmenu
, nPos
, wFlags
);
634 else if (item
->wID
== *nPos
)
636 /* fallback to this item if nothing else found */
641 else if (item
->wID
== *nPos
)
650 *nPos
= fallback_pos
;
655 /***********************************************************************
658 * Find a Sub menu. Return the position of the submenu, and modifies
659 * *hmenu in case it is found in another sub-menu.
660 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
662 UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
667 if (((*hmenu
)==(HMENU
)0xffff) ||
668 (!(menu
= MENU_GetMenu(*hmenu
))))
669 return NO_SELECTED_ITEM
;
671 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
672 if(!(item
->fType
& MF_POPUP
)) continue;
673 if (item
->hSubMenu
== hSubTarget
) {
677 HMENU hsubmenu
= item
->hSubMenu
;
678 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
679 if (pos
!= NO_SELECTED_ITEM
) {
685 return NO_SELECTED_ITEM
;
688 /***********************************************************************
691 static void MENU_FreeItemData( MENUITEM
* item
)
694 HeapFree( GetProcessHeap(), 0, item
->text
);
697 /***********************************************************************
698 * MENU_AdjustMenuItemRect
700 * Adjust menu item rectangle according to scrolling state.
703 MENU_AdjustMenuItemRect(const POPUPMENU
*menu
, LPRECT rect
)
705 if (menu
->bScrolling
)
707 UINT arrow_bitmap_height
;
710 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp
), &bmp
);
711 arrow_bitmap_height
= bmp
.bmHeight
;
712 rect
->top
+= arrow_bitmap_height
- menu
->nScrollPos
;
713 rect
->bottom
+= arrow_bitmap_height
- menu
->nScrollPos
;
718 /***********************************************************************
719 * MENU_FindItemByCoords
721 * Find the item at the specified coordinates (screen coords). Does
722 * not work for child windows and therefore should not be called for
723 * an arbitrary system menu.
725 static MENUITEM
*MENU_FindItemByCoords( const POPUPMENU
*menu
,
726 POINT pt
, UINT
*pos
)
732 if (!GetWindowRect(menu
->hWnd
, &rect
)) return NULL
;
736 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
739 MENU_AdjustMenuItemRect(menu
, &rect
);
740 if (PtInRect(&rect
, pt
))
750 /***********************************************************************
753 * Find the menu item selected by a key press.
754 * Return item id, -1 if none, -2 if we should close the menu.
756 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
757 WCHAR key
, BOOL forceMenuChar
)
759 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key
, key
, hmenu
);
761 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
765 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
766 MENUITEM
*item
= menu
->items
;
773 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
777 WCHAR
*p
= item
->text
- 2;
780 p
= strchrW (p
+ 2, '&');
782 while (p
!= NULL
&& p
[1] == '&');
783 if (p
&& (toupperW(p
[1]) == toupperW(key
))) return i
;
787 menuchar
= SendMessageW( hwndOwner
, WM_MENUCHAR
,
788 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
789 if (HIWORD(menuchar
) == 2) return LOWORD(menuchar
);
790 if (HIWORD(menuchar
) == 1) return (UINT
)(-2);
796 /***********************************************************************
797 * MENU_GetBitmapItemSize
799 * Get the size of a bitmap item.
801 static void MENU_GetBitmapItemSize( MENUITEM
*lpitem
, SIZE
*size
,
805 HBITMAP bmp
= lpitem
->hbmpItem
;
807 size
->cx
= size
->cy
= 0;
809 /* check if there is a magic menu item associated with this item */
810 switch( (INT_PTR
) bmp
)
812 case (INT_PTR
)HBMMENU_CALLBACK
:
814 MEASUREITEMSTRUCT measItem
;
815 measItem
.CtlType
= ODT_MENU
;
817 measItem
.itemID
= lpitem
->wID
;
818 measItem
.itemWidth
= lpitem
->rect
.right
- lpitem
->rect
.left
;
819 measItem
.itemHeight
= lpitem
->rect
.bottom
- lpitem
->rect
.top
;
820 measItem
.itemData
= lpitem
->dwItemData
;
821 SendMessageW( hwndOwner
, WM_MEASUREITEM
, lpitem
->wID
, (LPARAM
)&measItem
);
822 size
->cx
= measItem
.itemWidth
;
823 size
->cy
= measItem
.itemHeight
;
827 case (INT_PTR
)HBMMENU_SYSTEM
:
828 if (lpitem
->dwItemData
)
830 bmp
= (HBITMAP
)lpitem
->dwItemData
;
834 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
835 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
836 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
837 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
838 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
839 size
->cx
= GetSystemMetrics( SM_CYMENU
) - 4;
842 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
843 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
844 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
845 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
846 size
->cx
= GetSystemMetrics( SM_CXMENUSIZE
);
847 size
->cy
= GetSystemMetrics( SM_CYMENUSIZE
);
850 if (GetObjectW(bmp
, sizeof(bm
), &bm
))
852 size
->cx
= bm
.bmWidth
;
853 size
->cy
= bm
.bmHeight
;
857 /***********************************************************************
858 * MENU_DrawBitmapItem
860 * Draw a bitmap item.
862 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
,
863 HMENU hmenu
, HWND hwndOwner
, UINT odaction
, BOOL menuBar
)
869 int w
= rect
->right
- rect
->left
;
870 int h
= rect
->bottom
- rect
->top
;
873 HBITMAP hbmToDraw
= lpitem
->hbmpItem
;
876 /* Check if there is a magic menu item associated with this item */
877 if (IS_MAGIC_BITMAP(hbmToDraw
))
883 switch((INT_PTR
)hbmToDraw
)
885 case (INT_PTR
)HBMMENU_SYSTEM
:
886 if (lpitem
->dwItemData
)
888 bmp
= (HBITMAP
)lpitem
->dwItemData
;
889 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
893 static HBITMAP hBmpSysMenu
;
895 if (!hBmpSysMenu
) hBmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
897 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
898 /* only use right half of the bitmap */
899 bmp_xoffset
= bm
.bmWidth
/ 2;
900 bm
.bmWidth
-= bmp_xoffset
;
903 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
904 flags
= DFCS_CAPTIONRESTORE
;
906 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
907 flags
= DFCS_CAPTIONMIN
;
909 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
910 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
912 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
913 flags
= DFCS_CAPTIONCLOSE
;
915 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
916 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
918 case (INT_PTR
)HBMMENU_CALLBACK
:
920 DRAWITEMSTRUCT drawItem
;
921 drawItem
.CtlType
= ODT_MENU
;
923 drawItem
.itemID
= lpitem
->wID
;
924 drawItem
.itemAction
= odaction
;
925 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
926 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
927 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
928 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
929 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
930 drawItem
.hwndItem
= (HWND
)hmenu
;
932 drawItem
.itemData
= lpitem
->dwItemData
;
933 drawItem
.rcItem
= *rect
;
934 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
938 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
941 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
944 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
947 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
951 FIXME("Magic %p not implemented\n", hbmToDraw
);
956 /* draw the magic bitmaps using marlett font characters */
957 /* FIXME: fontsize and the position (x,y) could probably be better */
958 HFONT hfont
, hfontsav
;
959 LOGFONTW logfont
= { 0, 0, 0, 0, FW_NORMAL
,
960 0, 0, 0, SYMBOL_CHARSET
, 0, 0, 0, 0,
961 { 'M','a','r','l','e','t','t',0 } };
962 logfont
.lfHeight
= min( h
, w
) - 5 ;
963 TRACE(" height %d rect %s\n", logfont
.lfHeight
, wine_dbgstr_rect( rect
));
964 hfont
= CreateFontIndirectW( &logfont
);
965 hfontsav
= SelectObject(hdc
, hfont
);
966 TextOutW( hdc
, rect
->left
, rect
->top
+ 2, &bmchr
, 1);
967 SelectObject(hdc
, hfontsav
);
968 DeleteObject( hfont
);
973 InflateRect( &r
, -1, -1 );
974 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
975 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
980 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
983 hdcMem
= CreateCompatibleDC( hdc
);
984 SelectObject( hdcMem
, bmp
);
986 /* handle fontsize > bitmap_height */
987 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
989 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
990 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmpItem
)
991 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
992 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
997 /***********************************************************************
1000 * Calculate the size of the menu item and store it in lpitem->rect.
1002 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
1003 INT orgX
, INT orgY
, BOOL menuBar
, POPUPMENU
* lppop
)
1006 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1007 UINT arrow_bitmap_width
;
1011 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
1012 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
1013 (menuBar
? " (MenuBar)" : ""));
1015 GetObjectW( get_arrow_bitmap(), sizeof(bm
), &bm
);
1016 arrow_bitmap_width
= bm
.bmWidth
;
1018 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1019 if( !menucharsize
.cx
) {
1020 menucharsize
.cx
= GdiGetCharDimensions( hdc
, NULL
, &menucharsize
.cy
);
1021 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1022 * but it is unlikely an application will depend on that */
1023 ODitemheight
= HIWORD( GetDialogBaseUnits());
1026 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
1028 if (lpitem
->fType
& MF_OWNERDRAW
)
1030 MEASUREITEMSTRUCT mis
;
1031 mis
.CtlType
= ODT_MENU
;
1033 mis
.itemID
= lpitem
->wID
;
1034 mis
.itemData
= lpitem
->dwItemData
;
1035 mis
.itemHeight
= ODitemheight
;
1037 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
1038 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1039 * width of a menufont character to the width of an owner-drawn menu.
1041 lpitem
->rect
.right
+= mis
.itemWidth
+ 2 * menucharsize
.cx
;
1043 /* under at least win95 you seem to be given a standard
1044 height for the menu and the height value is ignored */
1045 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
1047 lpitem
->rect
.bottom
+= mis
.itemHeight
;
1049 TRACE("id=%04lx size=%dx%d\n",
1050 lpitem
->wID
, lpitem
->rect
.right
-lpitem
->rect
.left
,
1051 lpitem
->rect
.bottom
-lpitem
->rect
.top
);
1055 if (lpitem
->fType
& MF_SEPARATOR
)
1057 lpitem
->rect
.bottom
+= GetSystemMetrics( SM_CYMENUSIZE
)/2;
1059 lpitem
->rect
.right
+= arrow_bitmap_width
+ menucharsize
.cx
;
1067 if (lpitem
->hbmpItem
) {
1070 MENU_GetBitmapItemSize(lpitem
, &size
, hwndOwner
);
1071 /* Keep the size of the bitmap in callback mode to be able
1072 * to draw it correctly */
1073 lpitem
->bmpsize
= size
;
1074 lppop
->maxBmpSize
.cx
= max( lppop
->maxBmpSize
.cx
, size
.cx
);
1075 lppop
->maxBmpSize
.cy
= max( lppop
->maxBmpSize
.cy
, size
.cy
);
1076 lpitem
->rect
.right
+= size
.cx
+ 2;
1077 itemheight
= size
.cy
+ 2;
1079 if( !(lppop
->dwStyle
& MNS_NOCHECK
))
1080 lpitem
->rect
.right
+= check_bitmap_width
;
1081 lpitem
->rect
.right
+= 4 + menucharsize
.cx
;
1082 lpitem
->xTab
= lpitem
->rect
.right
;
1083 lpitem
->rect
.right
+= arrow_bitmap_width
;
1084 } else if (lpitem
->hbmpItem
) { /* menuBar */
1087 MENU_GetBitmapItemSize( lpitem
, &size
, hwndOwner
);
1088 lpitem
->bmpsize
= size
;
1089 lpitem
->rect
.right
+= size
.cx
;
1090 if( lpitem
->text
) lpitem
->rect
.right
+= 2;
1091 itemheight
= size
.cy
;
1094 /* it must be a text item - unless it's the system menu */
1095 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->text
) {
1096 HFONT hfontOld
= NULL
;
1097 RECT rc
= lpitem
->rect
;
1098 LONG txtheight
, txtwidth
;
1100 if ( lpitem
->fState
& MFS_DEFAULT
) {
1101 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1104 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1105 DT_SINGLELINE
|DT_CALCRECT
);
1106 lpitem
->rect
.right
+= rc
.right
- rc
.left
;
1107 itemheight
= max( max( itemheight
, txtheight
),
1108 GetSystemMetrics( SM_CYMENU
) - 1);
1109 lpitem
->rect
.right
+= 2 * menucharsize
.cx
;
1111 if ((p
= strchrW( lpitem
->text
, '\t' )) != NULL
) {
1114 int n
= (int)( p
- lpitem
->text
);
1115 /* Item contains a tab (only meaningful in popup menus) */
1116 /* get text size before the tab */
1117 txtheight
= DrawTextW( hdc
, lpitem
->text
, n
, &rc
,
1118 DT_SINGLELINE
|DT_CALCRECT
);
1119 txtwidth
= rc
.right
- rc
.left
;
1120 p
+= 1; /* advance past the Tab */
1121 /* get text size after the tab */
1122 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1123 DT_SINGLELINE
|DT_CALCRECT
);
1124 lpitem
->xTab
+= txtwidth
;
1125 txtheight
= max( txtheight
, tmpheight
);
1126 txtwidth
+= menucharsize
.cx
+ /* space for the tab */
1127 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1129 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1130 DT_SINGLELINE
|DT_CALCRECT
);
1131 txtwidth
= rc
.right
- rc
.left
;
1132 lpitem
->xTab
+= txtwidth
;
1134 lpitem
->rect
.right
+= 2 + txtwidth
;
1135 itemheight
= max( itemheight
,
1136 max( txtheight
+ 2, menucharsize
.cy
+ 4));
1138 if (hfontOld
) SelectObject (hdc
, hfontOld
);
1139 } else if( menuBar
) {
1140 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
1142 lpitem
->rect
.bottom
+= itemheight
;
1143 TRACE("%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1147 /***********************************************************************
1148 * MENU_GetMaxPopupHeight
1151 MENU_GetMaxPopupHeight(const POPUPMENU
*lppop
)
1154 return lppop
->cyMax
;
1155 return GetSystemMetrics(SM_CYSCREEN
) - GetSystemMetrics(SM_CYBORDER
);
1159 /***********************************************************************
1160 * MENU_PopupMenuCalcSize
1162 * Calculate the size of a popup menu.
1164 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
)
1169 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
, maxHeight
;
1171 lppop
->Width
= lppop
->Height
= 0;
1172 if (lppop
->nItems
== 0) return;
1175 SelectObject( hdc
, get_menu_font(FALSE
));
1180 lppop
->maxBmpSize
.cx
= 0;
1181 lppop
->maxBmpSize
.cy
= 0;
1183 while (start
< lppop
->nItems
)
1185 lpitem
= &lppop
->items
[start
];
1187 if( lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1188 orgX
+= MENU_COL_SPACE
;
1189 orgY
= MENU_TOP_MARGIN
;
1191 maxTab
= maxTabWidth
= 0;
1192 /* Parse items until column break or end of menu */
1193 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1196 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1198 MENU_CalcItemSize( hdc
, lpitem
, lppop
->hwndOwner
, orgX
, orgY
, FALSE
, lppop
);
1199 maxX
= max( maxX
, lpitem
->rect
.right
);
1200 orgY
= lpitem
->rect
.bottom
;
1201 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1203 maxTab
= max( maxTab
, lpitem
->xTab
);
1204 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1208 /* Finish the column (set all items to the largest width found) */
1209 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
1210 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1212 lpitem
->rect
.right
= maxX
;
1213 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1214 lpitem
->xTab
= maxTab
;
1217 lppop
->Height
= max( lppop
->Height
, orgY
);
1220 lppop
->Width
= maxX
;
1222 /* space for 3d border */
1223 lppop
->Height
+= MENU_BOTTOM_MARGIN
;
1226 /* Adjust popup height if it exceeds maximum */
1227 maxHeight
= MENU_GetMaxPopupHeight(lppop
);
1228 lppop
->nTotalHeight
= lppop
->Height
- MENU_TOP_MARGIN
;
1229 if (lppop
->Height
>= maxHeight
)
1231 lppop
->Height
= maxHeight
;
1232 lppop
->bScrolling
= TRUE
;
1236 lppop
->bScrolling
= FALSE
;
1239 ReleaseDC( 0, hdc
);
1243 /***********************************************************************
1244 * MENU_MenuBarCalcSize
1246 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1247 * height is off by 1 pixel which causes lengthy window relocations when
1248 * active document window is maximized/restored.
1250 * Calculate the size of the menu bar.
1252 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1253 LPPOPUPMENU lppop
, HWND hwndOwner
)
1256 UINT start
, i
, helpPos
;
1257 int orgX
, orgY
, maxY
;
1259 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1260 if (lppop
->nItems
== 0) return;
1261 TRACE("lprect %p %s\n", lprect
, wine_dbgstr_rect( lprect
));
1262 lppop
->Width
= lprect
->right
- lprect
->left
;
1264 maxY
= lprect
->top
+1;
1267 lppop
->maxBmpSize
.cx
= 0;
1268 lppop
->maxBmpSize
.cy
= 0;
1269 while (start
< lppop
->nItems
)
1271 lpitem
= &lppop
->items
[start
];
1272 orgX
= lprect
->left
;
1275 /* Parse items until line break or end of menu */
1276 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1278 if ((helpPos
== ~0U) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1280 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1282 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
1283 debug_print_menuitem (" item: ", lpitem
, "");
1284 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
, lppop
);
1286 if (lpitem
->rect
.right
> lprect
->right
)
1288 if (i
!= start
) break;
1289 else lpitem
->rect
.right
= lprect
->right
;
1291 maxY
= max( maxY
, lpitem
->rect
.bottom
);
1292 orgX
= lpitem
->rect
.right
;
1295 /* Finish the line (set all items to the largest height found) */
1296 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= maxY
;
1299 lprect
->bottom
= maxY
;
1300 lppop
->Height
= lprect
->bottom
- lprect
->top
;
1302 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1303 /* the last item (if several lines, only move the last line) */
1304 if (helpPos
== ~0U) return;
1305 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1306 orgY
= lpitem
->rect
.top
;
1307 orgX
= lprect
->right
;
1308 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1309 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1310 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1311 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1312 lpitem
->rect
.right
= orgX
;
1313 orgX
= lpitem
->rect
.left
;
1318 /***********************************************************************
1319 * MENU_DrawScrollArrows
1321 * Draw scroll arrows.
1324 MENU_DrawScrollArrows(const POPUPMENU
*lppop
, HDC hdc
)
1326 HDC hdcMem
= CreateCompatibleDC(hdc
);
1327 HBITMAP hOrigBitmap
;
1328 UINT arrow_bitmap_width
, arrow_bitmap_height
;
1332 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1333 arrow_bitmap_width
= bmp
.bmWidth
;
1334 arrow_bitmap_height
= bmp
.bmHeight
;
1337 if (lppop
->nScrollPos
)
1338 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_bitmap());
1340 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_inactive_bitmap());
1343 rect
.right
= lppop
->Width
;
1344 rect
.bottom
= arrow_bitmap_height
;
1345 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1346 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2, 0,
1347 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1348 rect
.top
= lppop
->Height
- arrow_bitmap_height
;
1349 rect
.bottom
= lppop
->Height
;
1350 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1351 if (lppop
->nScrollPos
< lppop
->nTotalHeight
- (MENU_GetMaxPopupHeight(lppop
) - 2 * arrow_bitmap_height
))
1352 SelectObject(hdcMem
, get_down_arrow_bitmap());
1354 SelectObject(hdcMem
, get_down_arrow_inactive_bitmap());
1355 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2,
1356 lppop
->Height
- arrow_bitmap_height
,
1357 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1358 SelectObject(hdcMem
, hOrigBitmap
);
1363 /***********************************************************************
1366 * Draws the popup-menu arrow.
1368 static void draw_popup_arrow( HDC hdc
, RECT rect
, UINT arrow_bitmap_width
,
1369 UINT arrow_bitmap_height
)
1371 HDC hdcMem
= CreateCompatibleDC( hdc
);
1372 HBITMAP hOrigBitmap
;
1374 hOrigBitmap
= SelectObject( hdcMem
, get_arrow_bitmap() );
1375 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1376 (rect
.top
+ rect
.bottom
- arrow_bitmap_height
) / 2,
1377 arrow_bitmap_width
, arrow_bitmap_height
,
1378 hdcMem
, 0, 0, SRCCOPY
);
1379 SelectObject( hdcMem
, hOrigBitmap
);
1382 /***********************************************************************
1385 * Draw a single menu item.
1387 static void MENU_DrawMenuItem( HWND hwnd
, HMENU hmenu
, HWND hwndOwner
, HDC hdc
, MENUITEM
*lpitem
,
1388 UINT height
, BOOL menuBar
, UINT odaction
)
1391 BOOL flat_menu
= FALSE
;
1393 UINT arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
1394 POPUPMENU
*menu
= MENU_GetMenu(hmenu
);
1397 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1401 GetObjectW( get_arrow_bitmap(), sizeof(bmp
), &bmp
);
1402 arrow_bitmap_width
= bmp
.bmWidth
;
1403 arrow_bitmap_height
= bmp
.bmHeight
;
1406 if (lpitem
->fType
& MF_SYSMENU
)
1408 if( !IsIconic(hwnd
) )
1409 NC_DrawSysButton( hwnd
, hdc
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
) );
1413 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1414 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
1418 if (lpitem
->fState
& MF_HILITE
)
1420 if(menuBar
&& !flat_menu
) {
1421 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1422 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1424 if(lpitem
->fState
& MF_GRAYED
)
1425 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1427 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1428 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1433 if (lpitem
->fState
& MF_GRAYED
)
1434 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1436 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1437 SetBkColor( hdc
, GetSysColor( bkgnd
) );
1440 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1441 rect
= lpitem
->rect
;
1442 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu
), &rect
);
1444 if (lpitem
->fType
& MF_OWNERDRAW
)
1447 ** Experimentation under Windows reveals that an owner-drawn
1448 ** menu is given the rectangle which includes the space it requested
1449 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1450 ** and a popup-menu arrow. This is the value of lpitem->rect.
1451 ** Windows will leave all drawing to the application except for
1452 ** the popup-menu arrow. Windows always draws that itself, after
1453 ** the menu owner has finished drawing.
1457 dis
.CtlType
= ODT_MENU
;
1459 dis
.itemID
= lpitem
->wID
;
1460 dis
.itemData
= lpitem
->dwItemData
;
1462 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1463 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1464 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1465 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1466 dis
.hwndItem
= (HWND
)hmenu
;
1469 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1470 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner
,
1471 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1472 dis
.hDC
, wine_dbgstr_rect( &dis
.rcItem
));
1473 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1474 /* Draw the popup-menu arrow */
1475 if (lpitem
->fType
& MF_POPUP
)
1476 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1477 arrow_bitmap_height
);
1481 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
1483 if (lpitem
->fState
& MF_HILITE
)
1487 InflateRect (&rect
, -1, -1);
1488 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
1489 InflateRect (&rect
, 1, 1);
1490 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1495 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1497 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1501 FillRect( hdc
, &rect
, GetSysColorBrush(bkgnd
) );
1503 SetBkMode( hdc
, TRANSPARENT
);
1505 /* vertical separator */
1506 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1511 rc
.left
-= MENU_COL_SPACE
/ 2 + 1;
1513 rc
.bottom
= height
- 3;
1516 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1517 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1518 LineTo( hdc
, rc
.left
, rc
.bottom
);
1519 SelectObject( hdc
, oldPen
);
1522 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1525 /* horizontal separator */
1526 if (lpitem
->fType
& MF_SEPARATOR
)
1533 rc
.top
= ( rc
.top
+ rc
.bottom
) / 2;
1536 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1537 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1538 LineTo( hdc
, rc
.right
, rc
.top
);
1539 SelectObject( hdc
, oldPen
);
1542 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1546 /* helper lines for debugging */
1547 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1548 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1549 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1550 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1553 if (lpitem
->hbmpItem
) {
1554 /* calculate the bitmap rectangle in coordinates relative
1555 * to the item rectangle */
1557 if( lpitem
->hbmpItem
== HBMMENU_CALLBACK
)
1560 bmprc
.left
= lpitem
->text
? menucharsize
.cx
: 0;
1562 else if (menu
->dwStyle
& MNS_NOCHECK
)
1564 else if (menu
->dwStyle
& MNS_CHECKORBMP
)
1567 bmprc
.left
= 4 + GetSystemMetrics(SM_CXMENUCHECK
);
1568 bmprc
.right
= bmprc
.left
+ lpitem
->bmpsize
.cx
;
1569 if( menuBar
&& !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1572 bmprc
.top
= (rect
.bottom
- rect
.top
-
1573 lpitem
->bmpsize
.cy
) / 2;
1574 bmprc
.bottom
= bmprc
.top
+ lpitem
->bmpsize
.cy
;
1580 INT y
= rect
.top
+ rect
.bottom
;
1582 int checked
= FALSE
;
1583 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1584 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1585 /* Draw the check mark
1588 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1590 if( !(menu
->dwStyle
& MNS_NOCHECK
)) {
1591 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
:
1592 lpitem
->hUnCheckBit
;
1593 if (bm
) /* we have a custom bitmap */
1595 HDC hdcMem
= CreateCompatibleDC( hdc
);
1597 SelectObject( hdcMem
, bm
);
1598 BitBlt( hdc
, rc
.left
, (y
- check_bitmap_height
) / 2,
1599 check_bitmap_width
, check_bitmap_height
,
1600 hdcMem
, 0, 0, SRCCOPY
);
1604 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1607 HBITMAP bm
= CreateBitmap( check_bitmap_width
,
1608 check_bitmap_height
, 1, 1, NULL
);
1609 HDC hdcMem
= CreateCompatibleDC( hdc
);
1611 SelectObject( hdcMem
, bm
);
1612 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1613 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1614 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1615 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1616 BitBlt( hdc
, rc
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1617 hdcMem
, 0, 0, SRCCOPY
);
1623 if( lpitem
->hbmpItem
&&
1624 !( checked
&& (menu
->dwStyle
& MNS_CHECKORBMP
))) {
1626 /* some applications make this assumption on the DC's origin */
1627 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1628 MENU_DrawBitmapItem(hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1630 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1632 /* Draw the popup-menu arrow */
1633 if (lpitem
->fType
& MF_POPUP
)
1634 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1635 arrow_bitmap_height
);
1637 if( !(menu
->dwStyle
& MNS_NOCHECK
))
1638 rect
.left
+= check_bitmap_width
;
1639 rect
.right
-= arrow_bitmap_width
;
1641 else if( lpitem
->hbmpItem
)
1642 { /* Draw the bitmap */
1645 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1646 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1648 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1650 /* process text if present */
1656 UINT uFormat
= (menuBar
) ?
1657 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1658 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1660 if( !(menu
->dwStyle
& MNS_CHECKORBMP
))
1661 rect
.left
+= menu
->maxBmpSize
.cx
;
1663 if ( lpitem
->fState
& MFS_DEFAULT
)
1665 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1669 if( lpitem
->hbmpItem
)
1670 rect
.left
+= lpitem
->bmpsize
.cx
;
1671 if( !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1672 rect
.left
+= menucharsize
.cx
;
1673 rect
.right
-= menucharsize
.cx
;
1676 for (i
= 0; lpitem
->text
[i
]; i
++)
1677 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1680 if(lpitem
->fState
& MF_GRAYED
)
1682 if (!(lpitem
->fState
& MF_HILITE
) )
1684 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1685 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1686 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1687 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1689 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1692 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1694 /* paint the shortcut text */
1695 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1697 if (lpitem
->text
[i
] == '\t')
1699 rect
.left
= lpitem
->xTab
;
1700 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1704 rect
.right
= lpitem
->xTab
;
1705 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1708 if(lpitem
->fState
& MF_GRAYED
)
1710 if (!(lpitem
->fState
& MF_HILITE
) )
1712 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1713 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1714 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1715 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1717 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1719 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1723 SelectObject (hdc
, hfontOld
);
1728 /***********************************************************************
1729 * MENU_DrawPopupMenu
1731 * Paint a popup menu.
1733 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1735 HBRUSH hPrevBrush
= 0;
1738 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1740 GetClientRect( hwnd
, &rect
);
1742 if((hPrevBrush
= SelectObject( hdc
, GetSysColorBrush(COLOR_MENU
) ))
1743 && (SelectObject( hdc
, get_menu_font(FALSE
))))
1747 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1749 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1753 BOOL flat_menu
= FALSE
;
1755 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1757 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1759 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1761 if( (menu
= MENU_GetMenu( hmenu
)))
1763 /* draw menu items */
1770 for( u
= menu
->nItems
; u
> 0; u
--, item
++)
1771 MENU_DrawMenuItem( hwnd
, hmenu
, menu
->hwndOwner
, hdc
,
1772 item
, menu
->Height
, FALSE
, ODA_DRAWENTIRE
);
1774 /* draw scroll arrows */
1775 if (menu
->bScrolling
)
1776 MENU_DrawScrollArrows(menu
, hdc
);
1780 SelectObject( hdc
, hPrevBrush
);
1785 /***********************************************************************
1788 * Paint a menu bar. Returns the height of the menu bar.
1789 * called from [windows/nonclient.c]
1791 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
,
1796 HMENU hMenu
= GetMenu(hwnd
);
1798 lppop
= MENU_GetMenu( hMenu
);
1799 if (lppop
== NULL
|| lprect
== NULL
)
1801 return GetSystemMetrics(SM_CYMENU
);
1806 hfontOld
= SelectObject( hDC
, get_menu_font(FALSE
));
1808 if (lppop
->Height
== 0)
1809 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
1811 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
1813 if (hfontOld
) SelectObject( hDC
, hfontOld
);
1814 return lppop
->Height
;
1817 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1821 /***********************************************************************
1824 * Display a popup menu.
1826 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
, UINT flags
,
1827 INT x
, INT y
, INT xanchor
, INT yanchor
)
1835 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1836 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1838 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1839 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1841 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1842 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1845 /* store the owner for DrawItem */
1846 menu
->hwndOwner
= hwndOwner
;
1848 menu
->nScrollPos
= 0;
1849 MENU_PopupMenuCalcSize( menu
);
1851 /* adjust popup menu pos so that it fits within the desktop */
1853 width
= menu
->Width
+ GetSystemMetrics(SM_CXBORDER
);
1854 height
= menu
->Height
+ GetSystemMetrics(SM_CYBORDER
);
1856 /* FIXME: should use item rect */
1859 monitor
= MonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
1860 info
.cbSize
= sizeof(info
);
1861 GetMonitorInfoW( monitor
, &info
);
1863 if( flags
& TPM_RIGHTALIGN
) x
-= width
;
1864 if( flags
& TPM_CENTERALIGN
) x
-= width
/ 2;
1866 if( flags
& TPM_BOTTOMALIGN
) y
-= height
;
1867 if( flags
& TPM_VCENTERALIGN
) y
-= height
/ 2;
1869 if( x
+ width
> info
.rcWork
.right
)
1871 if( xanchor
&& x
>= width
- xanchor
)
1872 x
-= width
- xanchor
;
1874 if( x
+ width
> info
.rcWork
.right
)
1875 x
= info
.rcWork
.right
- width
;
1877 if( x
< info
.rcWork
.left
) x
= info
.rcWork
.left
;
1879 if( y
+ height
> info
.rcWork
.bottom
)
1881 if( yanchor
&& y
>= height
+ yanchor
)
1882 y
-= height
+ yanchor
;
1884 if( y
+ height
> info
.rcWork
.bottom
)
1885 y
= info
.rcWork
.bottom
- height
;
1887 if( y
< info
.rcWork
.top
) y
= info
.rcWork
.top
;
1889 /* NOTE: In Windows, top menu popup is not owned. */
1890 menu
->hWnd
= CreateWindowExW( 0, (LPCWSTR
)POPUPMENU_CLASS_ATOM
, NULL
,
1891 WS_POPUP
, x
, y
, width
, height
,
1892 hwndOwner
, 0, (HINSTANCE
)GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1894 if( !menu
->hWnd
) return FALSE
;
1896 top_popup
= menu
->hWnd
;
1897 top_popup_hmenu
= hmenu
;
1899 /* Display the window */
1901 SetWindowPos( menu
->hWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1902 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1903 UpdateWindow( menu
->hWnd
);
1908 /***********************************************************************
1909 * MENU_EnsureMenuItemVisible
1912 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop
, UINT wIndex
, HDC hdc
)
1914 if (lppop
->bScrolling
)
1916 MENUITEM
*item
= &lppop
->items
[wIndex
];
1917 UINT nMaxHeight
= MENU_GetMaxPopupHeight(lppop
);
1918 UINT nOldPos
= lppop
->nScrollPos
;
1920 UINT arrow_bitmap_height
;
1923 GetClientRect(lppop
->hWnd
, &rc
);
1925 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1926 arrow_bitmap_height
= bmp
.bmHeight
;
1928 rc
.top
+= arrow_bitmap_height
;
1929 rc
.bottom
-= arrow_bitmap_height
+ MENU_BOTTOM_MARGIN
;
1931 nMaxHeight
-= GetSystemMetrics(SM_CYBORDER
) + 2 * arrow_bitmap_height
;
1932 if (item
->rect
.bottom
> lppop
->nScrollPos
+ nMaxHeight
)
1935 lppop
->nScrollPos
= item
->rect
.bottom
- nMaxHeight
;
1936 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1937 MENU_DrawScrollArrows(lppop
, hdc
);
1939 else if (item
->rect
.top
- MENU_TOP_MARGIN
< lppop
->nScrollPos
)
1941 lppop
->nScrollPos
= item
->rect
.top
- MENU_TOP_MARGIN
;
1942 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1943 MENU_DrawScrollArrows(lppop
, hdc
);
1949 /***********************************************************************
1952 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1953 BOOL sendMenuSelect
, HMENU topmenu
)
1958 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1960 lppop
= MENU_GetMenu( hmenu
);
1961 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1963 if (lppop
->FocusedItem
== wIndex
) return;
1964 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1965 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1967 top_popup
= lppop
->hWnd
;
1968 top_popup_hmenu
= hmenu
;
1971 SelectObject( hdc
, get_menu_font(FALSE
));
1973 /* Clear previous highlighted item */
1974 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1976 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1977 MENU_DrawMenuItem(lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,&lppop
->items
[lppop
->FocusedItem
],
1978 lppop
->Height
, !(lppop
->wFlags
& MF_POPUP
),
1982 /* Highlight new item (if any) */
1983 lppop
->FocusedItem
= wIndex
;
1984 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1986 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
1987 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
1988 MENU_EnsureMenuItemVisible(lppop
, wIndex
, hdc
);
1989 MENU_DrawMenuItem( lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,
1990 &lppop
->items
[wIndex
], lppop
->Height
,
1991 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
1995 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
1996 SendMessageW( hwndOwner
, WM_MENUSELECT
,
1997 MAKEWPARAM(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
1998 ip
->fType
| ip
->fState
|
1999 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
2002 else if (sendMenuSelect
) {
2005 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
2006 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
2007 MENUITEM
*ip
= &ptm
->items
[pos
];
2008 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKEWPARAM(pos
,
2009 ip
->fType
| ip
->fState
|
2010 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
2014 ReleaseDC( lppop
->hWnd
, hdc
);
2018 /***********************************************************************
2019 * MENU_MoveSelection
2021 * Moves currently selected item according to the offset parameter.
2022 * If there is no selection then it should select the last item if
2023 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2025 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
2030 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
2032 menu
= MENU_GetMenu( hmenu
);
2033 if ((!menu
) || (!menu
->items
)) return;
2035 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2037 if( menu
->nItems
== 1 ) return; else
2038 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
2040 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2042 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2047 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
2048 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
2049 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2051 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2057 /**********************************************************************
2060 * Insert (allocate) a new item into a menu.
2062 static MENUITEM
*MENU_InsertItem( HMENU hMenu
, UINT pos
, UINT flags
)
2067 if (!(menu
= MENU_GetMenu(hMenu
)))
2070 /* Find where to insert new item */
2072 if (flags
& MF_BYPOSITION
) {
2073 if (pos
> menu
->nItems
)
2076 if (!MENU_FindItem( &hMenu
, &pos
, flags
))
2079 if (!(menu
= MENU_GetMenu( hMenu
)))
2084 /* Make sure that MDI system buttons stay on the right side.
2085 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2086 * regardless of their id.
2088 while (pos
> 0 && (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
>= (INT_PTR
)HBMMENU_SYSTEM
&&
2089 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
<= (INT_PTR
)HBMMENU_MBAR_CLOSE_D
)
2092 TRACE("inserting at %u by pos %u\n", pos
, flags
& MF_BYPOSITION
);
2094 /* Create new items array */
2096 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
2099 WARN("allocation failed\n" );
2102 if (menu
->nItems
> 0)
2104 /* Copy the old array into the new one */
2105 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
2106 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
2107 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
2108 HeapFree( GetProcessHeap(), 0, menu
->items
);
2110 menu
->items
= newItems
;
2112 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
2113 menu
->Height
= 0; /* force size recalculate */
2114 return &newItems
[pos
];
2118 /**********************************************************************
2119 * MENU_ParseResource
2121 * Parse a standard menu resource and add items to the menu.
2122 * Return a pointer to the end of the resource.
2124 * NOTE: flags is equivalent to the mtOption field
2126 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
2134 flags
= GET_WORD(res
);
2135 end_flag
= flags
& MF_END
;
2136 /* Remove MF_END because it has the same value as MF_HILITE */
2138 res
+= sizeof(WORD
);
2139 if (!(flags
& MF_POPUP
))
2142 res
+= sizeof(WORD
);
2145 if (!unicode
) res
+= strlen(str
) + 1;
2146 else res
+= (strlenW((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
2147 if (flags
& MF_POPUP
)
2149 HMENU hSubMenu
= CreatePopupMenu();
2150 if (!hSubMenu
) return NULL
;
2151 if (!(res
= MENU_ParseResource( res
, hSubMenu
, unicode
)))
2153 if (!unicode
) AppendMenuA( hMenu
, flags
, (UINT_PTR
)hSubMenu
, str
);
2154 else AppendMenuW( hMenu
, flags
, (UINT_PTR
)hSubMenu
, (LPCWSTR
)str
);
2156 else /* Not a popup */
2158 if (!unicode
) AppendMenuA( hMenu
, flags
, id
, *str
? str
: NULL
);
2159 else AppendMenuW( hMenu
, flags
, id
,
2160 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
2162 } while (!end_flag
);
2167 /**********************************************************************
2168 * MENUEX_ParseResource
2170 * Parse an extended menu resource and add items to the menu.
2171 * Return a pointer to the end of the resource.
2173 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2179 mii
.cbSize
= sizeof(mii
);
2180 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
2181 mii
.fType
= GET_DWORD(res
);
2182 res
+= sizeof(DWORD
);
2183 mii
.fState
= GET_DWORD(res
);
2184 res
+= sizeof(DWORD
);
2185 mii
.wID
= GET_DWORD(res
);
2186 res
+= sizeof(DWORD
);
2187 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
2188 res
+= sizeof(WORD
);
2189 /* Align the text on a word boundary. */
2190 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2191 mii
.dwTypeData
= (LPWSTR
) res
;
2192 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2193 /* Align the following fields on a dword boundary. */
2194 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2196 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2197 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
2199 if (resinfo
& 1) { /* Pop-up? */
2200 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2201 res
+= sizeof(DWORD
);
2202 mii
.hSubMenu
= CreatePopupMenu();
2205 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2206 DestroyMenu(mii
.hSubMenu
);
2209 mii
.fMask
|= MIIM_SUBMENU
;
2210 mii
.fType
|= MF_POPUP
;
2212 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2214 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2215 mii
.wID
, mii
.fType
);
2216 mii
.fType
|= MF_SEPARATOR
;
2218 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2219 } while (!(resinfo
& MF_END
));
2224 /***********************************************************************
2227 * Return the handle of the selected sub-popup menu (if any).
2229 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2234 menu
= MENU_GetMenu( hmenu
);
2236 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2238 item
= &menu
->items
[menu
->FocusedItem
];
2239 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2240 return item
->hSubMenu
;
2245 /***********************************************************************
2246 * MENU_HideSubPopups
2248 * Hide the sub-popup menus of this menu.
2250 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2251 BOOL sendMenuSelect
, UINT wFlags
)
2253 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2255 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2257 if (menu
&& top_popup
)
2263 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2265 item
= &menu
->items
[menu
->FocusedItem
];
2266 if (!(item
->fType
& MF_POPUP
) ||
2267 !(item
->fState
& MF_MOUSESELECT
)) return;
2268 item
->fState
&= ~MF_MOUSESELECT
;
2269 hsubmenu
= item
->hSubMenu
;
2272 if (!(submenu
= MENU_GetMenu( hsubmenu
))) return;
2273 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
, wFlags
);
2274 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2275 DestroyWindow( submenu
->hWnd
);
2278 if (!(wFlags
& TPM_NONOTIFY
))
2279 SendMessageW( hwndOwner
, WM_UNINITMENUPOPUP
, (WPARAM
)hsubmenu
,
2280 MAKELPARAM(0, IS_SYSTEM_MENU(submenu
)) );
2285 /***********************************************************************
2288 * Display the sub-menu of the selected item of this menu.
2289 * Return the handle of the submenu, or hmenu if no submenu to display.
2291 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2292 BOOL selectFirst
, UINT wFlags
)
2299 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2301 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2303 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2305 item
= &menu
->items
[menu
->FocusedItem
];
2306 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2309 /* message must be sent before using item,
2310 because nearly everything may be changed by the application ! */
2312 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2313 if (!(wFlags
& TPM_NONOTIFY
))
2314 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2315 MAKELPARAM( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2317 item
= &menu
->items
[menu
->FocusedItem
];
2320 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2321 if (!(item
->fState
& MF_HILITE
))
2323 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2324 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2326 SelectObject( hdc
, get_menu_font(FALSE
));
2328 item
->fState
|= MF_HILITE
;
2329 MENU_DrawMenuItem( menu
->hWnd
, hmenu
, hwndOwner
, hdc
, item
, menu
->Height
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2330 ReleaseDC( menu
->hWnd
, hdc
);
2332 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2335 item
->fState
|= MF_MOUSESELECT
;
2337 if (IS_SYSTEM_MENU(menu
))
2339 MENU_InitSysMenuPopup(item
->hSubMenu
,
2340 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2341 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2343 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2344 rect
.top
= rect
.bottom
;
2345 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2346 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2350 GetWindowRect( menu
->hWnd
, &rect
);
2351 if (menu
->wFlags
& MF_POPUP
)
2353 RECT rc
= item
->rect
;
2355 MENU_AdjustMenuItemRect(menu
, &rc
);
2357 /* The first item in the popup menu has to be at the
2358 same y position as the focused menu item */
2359 rect
.left
+= rc
.right
- GetSystemMetrics(SM_CXBORDER
);
2360 rect
.top
+= rc
.top
- MENU_TOP_MARGIN
;
2361 rect
.right
= rc
.left
- rc
.right
+ GetSystemMetrics(SM_CXBORDER
);
2362 rect
.bottom
= rc
.top
- rc
.bottom
- MENU_TOP_MARGIN
2363 - MENU_BOTTOM_MARGIN
- GetSystemMetrics(SM_CYBORDER
);
2367 rect
.left
+= item
->rect
.left
;
2368 rect
.top
+= item
->rect
.bottom
;
2369 rect
.right
= item
->rect
.right
- item
->rect
.left
;
2370 rect
.bottom
= item
->rect
.bottom
- item
->rect
.top
;
2374 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
, 0,
2375 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2377 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2378 return item
->hSubMenu
;
2383 /**********************************************************************
2386 HWND
MENU_IsMenuActive(void)
2391 /**********************************************************************
2394 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2396 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2398 void MENU_EndMenu( HWND hwnd
)
2401 menu
= top_popup_hmenu
? MENU_GetMenu( top_popup_hmenu
) : NULL
;
2402 if (menu
&& hwnd
== menu
->hwndOwner
) EndMenu();
2405 /***********************************************************************
2408 * Walks menu chain trying to find a menu pt maps to.
2410 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2412 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2413 UINT item
= menu
->FocusedItem
;
2416 /* try subpopup first (if any) */
2417 ret
= (item
!= NO_SELECTED_ITEM
&&
2418 (menu
->items
[item
].fType
& MF_POPUP
) &&
2419 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2420 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2422 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2424 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2425 if( menu
->wFlags
& MF_POPUP
)
2427 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2429 else if (ht
== HTSYSMENU
)
2430 ret
= get_win_sys_menu( menu
->hWnd
);
2431 else if (ht
== HTMENU
)
2432 ret
= GetMenu( menu
->hWnd
);
2437 /***********************************************************************
2438 * MENU_ExecFocusedItem
2440 * Execute a menu item (for instance when user pressed Enter).
2441 * Return the wID of the executed item. Otherwise, -1 indicating
2442 * that no menu item was executed, -2 if a popup is shown;
2443 * Have to receive the flags for the TrackPopupMenu options to avoid
2444 * sending unwanted message.
2447 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2450 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2452 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2454 if (!menu
|| !menu
->nItems
||
2455 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2457 item
= &menu
->items
[menu
->FocusedItem
];
2459 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu
, item
->wID
, item
->hSubMenu
, item
->fType
);
2461 if (!(item
->fType
& MF_POPUP
))
2463 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2465 /* If TPM_RETURNCMD is set you return the id, but
2466 do not send a message to the owner */
2467 if(!(wFlags
& TPM_RETURNCMD
))
2469 if( menu
->wFlags
& MF_SYSMENU
)
2470 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2471 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2474 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2475 DWORD dwStyle
= menu
->dwStyle
| (topmenu
? topmenu
->dwStyle
: 0);
2477 if (dwStyle
& MNS_NOTIFYBYPOS
)
2478 PostMessageW( pmt
->hOwnerWnd
, WM_MENUCOMMAND
, menu
->FocusedItem
,
2481 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2489 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2496 /***********************************************************************
2497 * MENU_SwitchTracking
2499 * Helper function for menu navigation routines.
2501 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
, UINT wFlags
)
2503 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2504 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2506 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2508 if( pmt
->hTopMenu
!= hPtMenu
&&
2509 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2511 /* both are top level menus (system and menu-bar) */
2512 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2513 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2514 pmt
->hTopMenu
= hPtMenu
;
2516 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2517 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2521 /***********************************************************************
2524 * Return TRUE if we can go on with menu tracking.
2526 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2528 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2533 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2536 if( IS_SYSTEM_MENU(ptmenu
) )
2537 item
= ptmenu
->items
;
2539 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2543 if( ptmenu
->FocusedItem
!= id
)
2544 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2546 /* If the popup menu is not already "popped" */
2547 if(!(item
->fState
& MF_MOUSESELECT
))
2549 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2554 /* Else the click was on the menu bar, finish the tracking */
2559 /***********************************************************************
2562 * Return the value of MENU_ExecFocusedItem if
2563 * the selected item was not a popup. Else open the popup.
2564 * A -1 return value indicates that we go on with menu tracking.
2567 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2569 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2574 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2577 if( IS_SYSTEM_MENU(ptmenu
) )
2578 item
= ptmenu
->items
;
2580 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2582 if( item
&& (ptmenu
->FocusedItem
== id
))
2584 debug_print_menuitem ("FocusedItem: ", item
, "");
2586 if( !(item
->fType
& MF_POPUP
) )
2588 INT executedMenuId
= MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2589 if (executedMenuId
== -1 || executedMenuId
== -2) return -1;
2590 return executedMenuId
;
2593 /* If we are dealing with the top-level menu */
2594 /* and this is a click on an already "popped" item: */
2595 /* Stop the menu tracking and close the opened submenus */
2596 if((pmt
->hTopMenu
== hPtMenu
) && ptmenu
->bTimeToHide
)
2599 ptmenu
->bTimeToHide
= TRUE
;
2605 /***********************************************************************
2608 * Return TRUE if we can go on with menu tracking.
2610 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2612 UINT id
= NO_SELECTED_ITEM
;
2613 POPUPMENU
*ptmenu
= NULL
;
2617 ptmenu
= MENU_GetMenu( hPtMenu
);
2618 if( IS_SYSTEM_MENU(ptmenu
) )
2621 MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2624 if( id
== NO_SELECTED_ITEM
)
2626 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2627 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2630 else if( ptmenu
->FocusedItem
!= id
)
2632 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2633 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2639 /***********************************************************************
2642 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2644 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
, UINT wFlags
)
2646 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2649 /* When skipping left, we need to do something special after the
2651 if (vk
== VK_LEFT
&& menu
->FocusedItem
== 0)
2655 /* When skipping right, for the non-system menu, we need to
2656 handle the last non-special menu item (ie skip any window
2657 icons such as MDI maximize, restore or close) */
2658 else if ((vk
== VK_RIGHT
) && !IS_SYSTEM_MENU(menu
))
2660 UINT i
= menu
->FocusedItem
+ 1;
2661 while (i
< menu
->nItems
) {
2662 if ((menu
->items
[i
].wID
>= SC_SIZE
&&
2663 menu
->items
[i
].wID
<= SC_RESTORE
)) {
2667 if (i
== menu
->nItems
) {
2671 /* When skipping right, we need to cater for the system menu */
2672 else if ((vk
== VK_RIGHT
) && IS_SYSTEM_MENU(menu
))
2674 if (menu
->FocusedItem
== (menu
->nItems
- 1)) {
2681 MDINEXTMENU next_menu
;
2686 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2687 next_menu
.hmenuNext
= 0;
2688 next_menu
.hwndNext
= 0;
2689 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2691 TRACE("%p [%p] -> %p [%p]\n",
2692 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2694 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2696 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2697 hNewWnd
= pmt
->hOwnerWnd
;
2698 if( IS_SYSTEM_MENU(menu
) )
2700 /* switch to the menu bar */
2702 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2706 menu
= MENU_GetMenu( hNewMenu
);
2707 id
= menu
->nItems
- 1;
2709 /* Skip backwards over any system predefined icons,
2710 eg. MDI close, restore etc icons */
2712 (menu
->items
[id
].wID
>= SC_SIZE
&&
2713 menu
->items
[id
].wID
<= SC_RESTORE
)) id
--;
2716 else if (style
& WS_SYSMENU
)
2718 /* switch to the system menu */
2719 hNewMenu
= get_win_sys_menu( hNewWnd
);
2723 else /* application returned a new menu to switch to */
2725 hNewMenu
= next_menu
.hmenuNext
;
2726 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2728 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2730 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2732 if (style
& WS_SYSMENU
&&
2733 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2735 /* get the real system menu */
2736 hNewMenu
= get_win_sys_menu(hNewWnd
);
2738 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2740 /* FIXME: Not sure what to do here;
2741 * perhaps try to track hNewMenu as a popup? */
2743 TRACE(" -- got confused.\n");
2750 if( hNewMenu
!= pmt
->hTopMenu
)
2752 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2754 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2755 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2758 if( hNewWnd
!= pmt
->hOwnerWnd
)
2760 pmt
->hOwnerWnd
= hNewWnd
;
2761 set_capture_window( pmt
->hOwnerWnd
, GUI_INMENUMODE
, NULL
);
2764 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2765 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2772 /***********************************************************************
2775 * The idea is not to show the popup if the next input message is
2776 * going to hide it anyway.
2778 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT16 uMsg
)
2782 msg
.hwnd
= pmt
->hOwnerWnd
;
2784 PeekMessageW( &msg
, 0, uMsg
, uMsg
, PM_NOYIELD
| PM_REMOVE
);
2785 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2790 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2791 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2793 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2794 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2795 if( msg
.message
== WM_KEYDOWN
&&
2796 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2798 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2805 /* failures go through this */
2806 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2810 /***********************************************************************
2813 * Handle a VK_ESCAPE key event in a menu.
2815 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2817 BOOL bEndMenu
= TRUE
;
2819 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2821 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2823 if (menu
->wFlags
& MF_POPUP
)
2825 HMENU hmenutmp
, hmenuprev
;
2827 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2829 /* close topmost popup */
2830 while (hmenutmp
!= pmt
->hCurrentMenu
)
2832 hmenuprev
= hmenutmp
;
2833 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2836 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2837 pmt
->hCurrentMenu
= hmenuprev
;
2845 /***********************************************************************
2848 * Handle a VK_LEFT key event in a menu.
2850 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
)
2853 HMENU hmenutmp
, hmenuprev
;
2856 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2857 menu
= MENU_GetMenu( hmenutmp
);
2859 /* Try to move 1 column left (if possible) */
2860 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2861 NO_SELECTED_ITEM
) {
2863 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2868 /* close topmost popup */
2869 while (hmenutmp
!= pmt
->hCurrentMenu
)
2871 hmenuprev
= hmenutmp
;
2872 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2875 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2876 pmt
->hCurrentMenu
= hmenuprev
;
2878 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2880 /* move menu bar selection if no more popups are left */
2882 if( !MENU_DoNextMenu( pmt
, VK_LEFT
, wFlags
) )
2883 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2885 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2887 /* A sublevel menu was displayed - display the next one
2888 * unless there is another displacement coming up */
2890 if( !MENU_SuspendPopup( pmt
, WM_KEYDOWN
) )
2891 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2892 pmt
->hTopMenu
, TRUE
, wFlags
);
2898 /***********************************************************************
2901 * Handle a VK_RIGHT key event in a menu.
2903 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
)
2906 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2909 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2911 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2912 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2914 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2916 /* If already displaying a popup, try to display sub-popup */
2918 hmenutmp
= pmt
->hCurrentMenu
;
2919 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2921 /* if subpopup was displayed then we are done */
2922 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2925 /* Check to see if there's another column */
2926 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2927 NO_SELECTED_ITEM
) {
2928 TRACE("Going to %d.\n", nextcol
);
2929 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2934 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2936 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2938 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2939 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
2940 } else hmenutmp
= 0;
2942 /* try to move to the next item */
2943 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
, wFlags
) )
2944 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
2946 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2947 if( !MENU_SuspendPopup(pmt
, WM_KEYDOWN
) )
2948 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2949 pmt
->hTopMenu
, TRUE
, wFlags
);
2953 static void CALLBACK
release_capture( BOOL __normal
)
2955 set_capture_window( 0, GUI_INMENUMODE
, NULL
);
2958 /***********************************************************************
2961 * Menu tracking code.
2963 static BOOL
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
2964 HWND hwnd
, const RECT
*lprect
)
2969 INT executedMenuId
= -1;
2971 BOOL enterIdleSent
= FALSE
;
2975 mt
.hCurrentMenu
= hmenu
;
2976 mt
.hTopMenu
= hmenu
;
2977 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
2981 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2982 hmenu
, wFlags
, x
, y
, hwnd
, wine_dbgstr_rect( lprect
));
2985 if (!(menu
= MENU_GetMenu( hmenu
)))
2987 WARN("Invalid menu handle %p\n", hmenu
);
2988 SetLastError(ERROR_INVALID_MENU_HANDLE
);
2992 if (wFlags
& TPM_BUTTONDOWN
)
2994 /* Get the result in order to start the tracking or not */
2995 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
2996 fEndMenu
= !fRemove
;
2999 if (wFlags
& TF_ENDMENU
) fEndMenu
= TRUE
;
3001 /* owner may not be visible when tracking a popup, so use the menu itself */
3002 capture_win
= (wFlags
& TPM_POPUPMENU
) ? menu
->hWnd
: mt
.hOwnerWnd
;
3003 set_capture_window( capture_win
, GUI_INMENUMODE
, NULL
);
3005 __TRY
while (!fEndMenu
)
3007 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3008 if (!menu
) /* sometimes happens if I do a window manager close */
3011 /* we have to keep the message in the queue until it's
3012 * clear that menu loop is not over yet. */
3016 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
3018 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
3019 /* remove the message from the queue */
3020 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3026 HWND win
= menu
->wFlags
& MF_POPUP
? menu
->hWnd
: 0;
3027 enterIdleSent
= TRUE
;
3028 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
3034 /* check if EndMenu() tried to cancel us, by posting this message */
3035 if(msg
.message
== WM_CANCELMODE
)
3037 /* we are now out of the loop */
3040 /* remove the message from the queue */
3041 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3043 /* break out of internal loop, ala ESCAPE */
3047 TranslateMessage( &msg
);
3050 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
3051 enterIdleSent
=FALSE
;
3054 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
3057 * Use the mouse coordinates in lParam instead of those in the MSG
3058 * struct to properly handle synthetic messages. They are already
3059 * in screen coordinates.
3061 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
3062 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
3064 /* Find a menu for this mouse event */
3065 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
3069 /* no WM_NC... messages in captured state */
3071 case WM_RBUTTONDBLCLK
:
3072 case WM_RBUTTONDOWN
:
3073 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3075 case WM_LBUTTONDBLCLK
:
3076 case WM_LBUTTONDOWN
:
3077 /* If the message belongs to the menu, removes it from the queue */
3078 /* Else, end menu tracking */
3079 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3080 fEndMenu
= !fRemove
;
3084 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3087 /* Check if a menu was selected by the mouse */
3090 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
3091 TRACE("executedMenuId %d\n", executedMenuId
);
3093 /* End the loop if executedMenuId is an item ID */
3094 /* or if the job was done (executedMenuId = 0). */
3095 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3097 /* No menu was selected by the mouse */
3098 /* if the function was called by TrackPopupMenu, continue
3099 with the menu tracking. If not, stop it */
3101 fEndMenu
= ((wFlags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3106 /* the selected menu item must be changed every time */
3107 /* the mouse moves. */
3110 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
3112 } /* switch(msg.message) - mouse */
3114 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3116 fRemove
= TRUE
; /* Keyboard messages are always removed */
3129 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3130 NO_SELECTED_ITEM
, FALSE
, 0 );
3131 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3132 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
3136 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3138 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3139 if (!(menu
->wFlags
& MF_POPUP
))
3140 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
3141 else /* otherwise try to move selection */
3142 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3143 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3147 MENU_KeyLeft( &mt
, wFlags
);
3151 MENU_KeyRight( &mt
, wFlags
);
3155 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
3161 hi
.cbSize
= sizeof(HELPINFO
);
3162 hi
.iContextType
= HELPINFO_MENUITEM
;
3163 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
3166 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
3167 hi
.hItemHandle
= hmenu
;
3168 hi
.dwContextId
= menu
->dwContextHelpID
;
3169 hi
.MousePos
= msg
.pt
;
3170 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3177 break; /* WM_KEYDOWN */
3184 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
3186 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3187 fEndMenu
= (executedMenuId
!= -2);
3192 /* Hack to avoid control chars. */
3193 /* We will find a better way real soon... */
3194 if (msg
.wParam
< 32) break;
3196 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3197 LOWORD(msg
.wParam
), FALSE
);
3198 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3199 else if (pos
== (UINT
)-1) MessageBeep(0);
3202 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
3204 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3205 fEndMenu
= (executedMenuId
!= -2);
3209 } /* switch(msg.message) - kbd */
3213 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3214 DispatchMessageW( &msg
);
3218 if (!fEndMenu
) fRemove
= TRUE
;
3220 /* finally remove message from the queue */
3222 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
3223 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3224 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
3226 __FINALLY( release_capture
)
3228 /* If dropdown is still painted and the close box is clicked on
3229 then the menu will be destroyed as part of the DispatchMessage above.
3230 This will then invalidate the menu handle in mt.hTopMenu. We should
3231 check for this first. */
3232 if( IsMenu( mt
.hTopMenu
) )
3234 menu
= MENU_GetMenu( mt
.hTopMenu
);
3236 if( IsWindow( mt
.hOwnerWnd
) )
3238 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
, wFlags
);
3240 if (menu
&& (menu
->wFlags
& MF_POPUP
))
3242 DestroyWindow( menu
->hWnd
);
3245 if (!(wFlags
& TPM_NONOTIFY
))
3246 SendMessageW( mt
.hOwnerWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)mt
.hTopMenu
,
3247 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3249 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
3250 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKEWPARAM(0,0xffff), 0 );
3253 /* Reset the variable for hiding menu */
3254 if( menu
) menu
->bTimeToHide
= FALSE
;
3257 /* The return value is only used by TrackPopupMenu */
3258 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3259 if (executedMenuId
== -1) executedMenuId
= 0;
3260 return executedMenuId
;
3263 /***********************************************************************
3266 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3270 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3274 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3275 if (!(wFlags
& TPM_NONOTIFY
))
3276 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3278 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3280 if (!(wFlags
& TPM_NONOTIFY
))
3282 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3283 /* If an app changed/recreated menu bar entries in WM_INITMENU
3284 * menu sizes will be recalculated once the menu created/shown.
3288 /* This makes the menus of applications built with Delphi work.
3289 * It also enables menus to be displayed in more than one window,
3290 * but there are some bugs left that need to be fixed in this case.
3292 if ((menu
= MENU_GetMenu( hMenu
))) menu
->hWnd
= hWnd
;
3296 /***********************************************************************
3299 static BOOL
MENU_ExitTracking(HWND hWnd
)
3301 TRACE("hwnd=%p\n", hWnd
);
3303 SendMessageW( hWnd
, WM_EXITMENULOOP
, 0, 0 );
3306 top_popup_hmenu
= NULL
;
3310 /***********************************************************************
3311 * MENU_TrackMouseMenuBar
3313 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3315 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3317 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3318 UINT wFlags
= TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3320 TRACE("wnd=%p ht=0x%04x %s\n", hWnd
, ht
, wine_dbgstr_point( &pt
));
3324 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3325 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3326 MENU_ExitTracking(hWnd
);
3331 /***********************************************************************
3332 * MENU_TrackKbdMenuBar
3334 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3336 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3338 UINT uItem
= NO_SELECTED_ITEM
;
3340 UINT wFlags
= TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3342 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3344 /* find window that has a menu */
3346 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd
, GWL_STYLE
)))
3347 if (!(hwnd
= GetAncestor( hwnd
, GA_PARENT
))) return;
3349 /* check if we have to track a system menu */
3351 hTrackMenu
= GetMenu( hwnd
);
3352 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3354 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3355 hTrackMenu
= get_win_sys_menu( hwnd
);
3357 wParam
|= HTSYSMENU
; /* prevent item lookup */
3360 if (!IsMenu( hTrackMenu
)) return;
3362 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3364 if( wChar
&& wChar
!= ' ' )
3366 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3367 if ( uItem
>= (UINT
)(-2) )
3369 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3370 /* schedule end of menu tracking */
3371 wFlags
|= TF_ENDMENU
;
3376 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3378 if (!(wParam
& HTSYSMENU
) || wChar
== ' ')
3380 if( uItem
== NO_SELECTED_ITEM
)
3381 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3383 PostMessageW( hwnd
, WM_KEYDOWN
, VK_DOWN
, 0L );
3387 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3388 MENU_ExitTracking( hwnd
);
3391 /**********************************************************************
3392 * TrackPopupMenuEx (USER32.@)
3394 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3395 HWND hWnd
, LPTPMPARAMS lpTpm
)
3399 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3400 hMenu
, wFlags
, x
, y
, hWnd
, lpTpm
,
3401 lpTpm
? wine_dbgstr_rect( &lpTpm
->rcExclude
) : "-" );
3403 /* Parameter check */
3404 /* FIXME: this check is performed several times, here and in the called
3405 functions. That could be optimized */
3406 if (!MENU_GetMenu( hMenu
))
3408 SetLastError( ERROR_INVALID_MENU_HANDLE
);
3412 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3414 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3415 if (!(wFlags
& TPM_NONOTIFY
))
3416 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3418 if (MENU_ShowPopup( hWnd
, hMenu
, 0, wFlags
, x
, y
, 0, 0 ))
3419 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
,
3420 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3421 MENU_ExitTracking(hWnd
);
3426 /**********************************************************************
3427 * TrackPopupMenu (USER32.@)
3429 * Like the win32 API, the function return the command ID only if the
3430 * flag TPM_RETURNCMD is on.
3433 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3434 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3436 return TrackPopupMenuEx( hMenu
, wFlags
, x
, y
, hWnd
, NULL
);
3439 /***********************************************************************
3442 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3444 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3446 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3452 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3453 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3457 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3458 return MA_NOACTIVATE
;
3463 BeginPaint( hwnd
, &ps
);
3464 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3465 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3466 EndPaint( hwnd
, &ps
);
3473 /* zero out global pointer in case resident popup window was destroyed. */
3474 if (hwnd
== top_popup
) {
3476 top_popup_hmenu
= NULL
;
3484 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3487 SetWindowLongPtrW( hwnd
, 0, 0 );
3490 case MM_SETMENUHANDLE
:
3491 SetWindowLongPtrW( hwnd
, 0, wParam
);
3494 case MM_GETMENUHANDLE
:
3495 return GetWindowLongPtrW( hwnd
, 0 );
3498 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3504 /***********************************************************************
3505 * MENU_GetMenuBarHeight
3507 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3509 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3510 INT orgX
, INT orgY
)
3516 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3518 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3520 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3521 SelectObject( hdc
, get_menu_font(FALSE
));
3522 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3523 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3524 ReleaseDC( hwnd
, hdc
);
3525 return lppop
->Height
;
3529 /*******************************************************************
3530 * ChangeMenuA (USER32.@)
3532 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3533 UINT id
, UINT flags
)
3535 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3536 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3538 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3539 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3541 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3542 flags
& MF_BYPOSITION
? pos
: id
,
3543 flags
& ~MF_REMOVE
);
3544 /* Default: MF_INSERT */
3545 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3549 /*******************************************************************
3550 * ChangeMenuW (USER32.@)
3552 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3553 UINT id
, UINT flags
)
3555 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3556 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3558 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3559 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3561 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3562 flags
& MF_BYPOSITION
? pos
: id
,
3563 flags
& ~MF_REMOVE
);
3564 /* Default: MF_INSERT */
3565 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3569 /*******************************************************************
3570 * CheckMenuItem (USER32.@)
3572 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3577 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3578 ret
= item
->fState
& MF_CHECKED
;
3579 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3580 else item
->fState
&= ~MF_CHECKED
;
3585 /**********************************************************************
3586 * EnableMenuItem (USER32.@)
3588 BOOL WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3594 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3596 /* Get the Popupmenu to access the owner menu */
3597 if (!(menu
= MENU_GetMenu(hMenu
)))
3600 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3603 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3604 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3606 /* If the close item in the system menu change update the close button */
3607 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3609 if (menu
->hSysMenuOwner
!= 0)
3612 POPUPMENU
* parentMenu
;
3614 /* Get the parent menu to access*/
3615 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3618 /* Refresh the frame to reflect the change */
3619 GetWindowRect(parentMenu
->hWnd
, &rc
);
3620 MapWindowPoints(0, parentMenu
->hWnd
, (POINT
*)&rc
, 2);
3622 RedrawWindow(parentMenu
->hWnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3630 /*******************************************************************
3631 * GetMenuStringA (USER32.@)
3633 INT WINAPI
GetMenuStringA(
3634 HMENU hMenu
, /* [in] menuhandle */
3635 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3636 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3637 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3638 UINT wFlags
/* [in] MF_ flags */
3642 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3643 if (str
&& nMaxSiz
) str
[0] = '\0';
3644 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3645 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3648 if (!item
->text
) return 0;
3649 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3650 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3652 TRACE("returning %s\n", debugstr_a(str
));
3657 /*******************************************************************
3658 * GetMenuStringW (USER32.@)
3660 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3661 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3665 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3666 if (str
&& nMaxSiz
) str
[0] = '\0';
3667 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3668 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3671 if (!str
|| !nMaxSiz
) return item
->text
? strlenW(item
->text
) : 0;
3672 if( !(item
->text
)) {
3676 lstrcpynW( str
, item
->text
, nMaxSiz
);
3677 TRACE("returning %s\n", debugstr_w(str
));
3678 return strlenW(str
);
3682 /**********************************************************************
3683 * HiliteMenuItem (USER32.@)
3685 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3689 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3690 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3691 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3692 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3693 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
, 0 );
3694 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3699 /**********************************************************************
3700 * GetMenuState (USER32.@)
3702 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3705 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3706 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3707 debug_print_menuitem (" item: ", item
, "");
3708 if (item
->fType
& MF_POPUP
)
3710 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3711 if (!menu
) return -1;
3712 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3716 /* We used to (from way back then) mask the result to 0xff. */
3717 /* I don't know why and it seems wrong as the documented */
3718 /* return flag MF_SEPARATOR is outside that mask. */
3719 return (item
->fType
| item
->fState
);
3724 /**********************************************************************
3725 * GetMenuItemCount (USER32.@)
3727 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3729 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3730 if (!menu
) return -1;
3731 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3732 return menu
->nItems
;
3736 /**********************************************************************
3737 * GetMenuItemID (USER32.@)
3739 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3743 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return -1;
3744 if (lpmi
->fType
& MF_POPUP
) return -1;
3750 /**********************************************************************
3753 * Uses flags, id and text ptr, passed by InsertMenu() and
3754 * ModifyMenu() to setup a MenuItemInfo structure.
3756 static void MENU_mnu2mnuii( UINT flags
, UINT_PTR id
, LPCWSTR str
,
3757 LPMENUITEMINFOW pmii
)
3759 ZeroMemory( pmii
, sizeof( MENUITEMINFOW
));
3760 pmii
->cbSize
= sizeof( MENUITEMINFOW
);
3761 pmii
->fMask
= MIIM_STATE
| MIIM_ID
| MIIM_FTYPE
;
3762 /* setting bitmap clears text and vice versa */
3763 if( IS_STRING_ITEM(flags
)) {
3764 pmii
->fMask
|= MIIM_STRING
| MIIM_BITMAP
;
3766 flags
|= MF_SEPARATOR
;
3767 /* Item beginning with a backspace is a help item */
3768 /* FIXME: wrong place, this is only true in win16 */
3769 else if( *str
== '\b') {
3773 pmii
->dwTypeData
= (LPWSTR
)str
;
3774 } else if( flags
& MFT_BITMAP
){
3775 pmii
->fMask
|= MIIM_BITMAP
| MIIM_STRING
;
3776 pmii
->hbmpItem
= HBITMAP_32(LOWORD(str
));
3778 if( flags
& MF_OWNERDRAW
){
3779 pmii
->fMask
|= MIIM_DATA
;
3780 pmii
->dwItemData
= (ULONG_PTR
) str
;
3782 if( flags
& MF_POPUP
) {
3783 pmii
->fMask
|= MIIM_SUBMENU
;
3784 pmii
->hSubMenu
= (HMENU
)id
;
3786 if( flags
& MF_SEPARATOR
) flags
|= MF_GRAYED
| MF_DISABLED
;
3787 pmii
->fState
= flags
& MENUITEMINFO_STATE_MASK
& ~MFS_DEFAULT
;
3788 pmii
->fType
= flags
& MENUITEMINFO_TYPE_MASK
;
3789 pmii
->wID
= (UINT
)id
;
3793 /*******************************************************************
3794 * InsertMenuW (USER32.@)
3796 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3797 UINT_PTR id
, LPCWSTR str
)
3802 if (IS_STRING_ITEM(flags
) && str
)
3803 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3804 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3805 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3806 hMenu
, pos
, flags
, id
, str
);
3808 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3809 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
3810 if (!(SetMenuItemInfo_common( item
, &mii
, TRUE
)))
3812 RemoveMenu( hMenu
, pos
, flags
);
3816 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3821 /*******************************************************************
3822 * InsertMenuA (USER32.@)
3824 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3825 UINT_PTR id
, LPCSTR str
)
3829 if (IS_STRING_ITEM(flags
) && str
)
3831 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3832 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3835 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3836 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3837 HeapFree( GetProcessHeap(), 0, newstr
);
3841 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3845 /*******************************************************************
3846 * AppendMenuA (USER32.@)
3848 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3849 UINT_PTR id
, LPCSTR data
)
3851 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3855 /*******************************************************************
3856 * AppendMenuW (USER32.@)
3858 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3859 UINT_PTR id
, LPCWSTR data
)
3861 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3865 /**********************************************************************
3866 * RemoveMenu (USER32.@)
3868 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3873 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3874 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3875 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3879 MENU_FreeItemData( item
);
3881 if (--menu
->nItems
== 0)
3883 HeapFree( GetProcessHeap(), 0, menu
->items
);
3888 while(nPos
< menu
->nItems
)
3894 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3895 menu
->nItems
* sizeof(MENUITEM
) );
3901 /**********************************************************************
3902 * DeleteMenu (USER32.@)
3904 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3906 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3907 if (!item
) return FALSE
;
3908 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3909 /* nPos is now the position of the item */
3910 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3915 /*******************************************************************
3916 * ModifyMenuW (USER32.@)
3918 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3919 UINT_PTR id
, LPCWSTR str
)
3924 if (IS_STRING_ITEM(flags
))
3925 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3927 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
3929 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
))) return FALSE
;
3930 MENU_GetMenu(hMenu
)->Height
= 0; /* force size recalculate */
3931 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
3932 return SetMenuItemInfo_common( item
, &mii
, TRUE
);
3936 /*******************************************************************
3937 * ModifyMenuA (USER32.@)
3939 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3940 UINT_PTR id
, LPCSTR str
)
3944 if (IS_STRING_ITEM(flags
) && str
)
3946 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3947 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3950 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3951 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
3952 HeapFree( GetProcessHeap(), 0, newstr
);
3956 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3960 /**********************************************************************
3961 * CreatePopupMenu (USER32.@)
3963 HMENU WINAPI
CreatePopupMenu(void)
3968 if (!(hmenu
= CreateMenu())) return 0;
3969 menu
= MENU_GetMenu( hmenu
);
3970 menu
->wFlags
|= MF_POPUP
;
3971 menu
->bTimeToHide
= FALSE
;
3976 /**********************************************************************
3977 * GetMenuCheckMarkDimensions (USER.417)
3978 * GetMenuCheckMarkDimensions (USER32.@)
3980 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
3982 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
3986 /**********************************************************************
3987 * SetMenuItemBitmaps (USER32.@)
3989 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
3990 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
3994 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3996 if (!hNewCheck
&& !hNewUnCheck
)
3998 item
->fState
&= ~MF_USECHECKBITMAPS
;
4000 else /* Install new bitmaps */
4002 item
->hCheckBit
= hNewCheck
;
4003 item
->hUnCheckBit
= hNewUnCheck
;
4004 item
->fState
|= MF_USECHECKBITMAPS
;
4010 /**********************************************************************
4011 * CreateMenu (USER32.@)
4013 HMENU WINAPI
CreateMenu(void)
4017 if (!(hMenu
= USER_HEAP_ALLOC( sizeof(POPUPMENU
) ))) return 0;
4018 menu
= USER_HEAP_LIN_ADDR(hMenu
);
4020 ZeroMemory(menu
, sizeof(POPUPMENU
));
4021 menu
->wMagic
= MENU_MAGIC
;
4022 menu
->FocusedItem
= NO_SELECTED_ITEM
;
4023 menu
->bTimeToHide
= FALSE
;
4025 TRACE("return %p\n", hMenu
);
4031 /**********************************************************************
4032 * DestroyMenu (USER32.@)
4034 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
4036 LPPOPUPMENU lppop
= MENU_GetMenu(hMenu
);
4038 TRACE("(%p)\n", hMenu
);
4041 if (!lppop
) return FALSE
;
4043 lppop
->wMagic
= 0; /* Mark it as destroyed */
4045 /* DestroyMenu should not destroy system menu popup owner */
4046 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
4048 DestroyWindow( lppop
->hWnd
);
4052 if (lppop
->items
) /* recursively destroy submenus */
4055 MENUITEM
*item
= lppop
->items
;
4056 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
4058 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
4059 MENU_FreeItemData( item
);
4061 HeapFree( GetProcessHeap(), 0, lppop
->items
);
4063 USER_HEAP_FREE( hMenu
);
4068 /**********************************************************************
4069 * GetSystemMenu (USER32.@)
4071 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
4073 WND
*wndPtr
= WIN_GetPtr( hWnd
);
4076 if (wndPtr
== WND_DESKTOP
) return 0;
4077 if (wndPtr
== WND_OTHER_PROCESS
)
4079 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
4083 if (wndPtr
->hSysMenu
&& bRevert
)
4085 DestroyMenu(wndPtr
->hSysMenu
);
4086 wndPtr
->hSysMenu
= 0;
4089 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
4090 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
4092 if( wndPtr
->hSysMenu
)
4095 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
4097 /* Store the dummy sysmenu handle to facilitate the refresh */
4098 /* of the close button if the SC_CLOSE item change */
4099 menu
= MENU_GetMenu(retvalue
);
4101 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
4103 WIN_ReleasePtr( wndPtr
);
4105 return bRevert
? 0 : retvalue
;
4109 /*******************************************************************
4110 * SetSystemMenu (USER32.@)
4112 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
4114 WND
*wndPtr
= WIN_GetPtr( hwnd
);
4116 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
4118 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
4119 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
4120 WIN_ReleasePtr( wndPtr
);
4127 /**********************************************************************
4128 * GetMenu (USER32.@)
4130 HMENU WINAPI
GetMenu( HWND hWnd
)
4132 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
4133 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4137 /**********************************************************************
4138 * GetMenuBarInfo (USER32.@)
4140 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4142 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4146 /**********************************************************************
4149 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4150 * SetWindowPos call that would result if SetMenu were called directly.
4152 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4154 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4156 if (hMenu
&& !IsMenu(hMenu
))
4158 WARN("hMenu %p is not a menu handle\n", hMenu
);
4161 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4164 hWnd
= WIN_GetFullHandle( hWnd
);
4165 if (GetCapture() == hWnd
)
4166 set_capture_window( 0, GUI_INMENUMODE
, NULL
); /* release the capture */
4172 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
4174 lpmenu
->hWnd
= hWnd
;
4175 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
4177 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4182 /**********************************************************************
4183 * SetMenu (USER32.@)
4185 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4187 if(!MENU_SetMenu(hWnd
, hMenu
))
4190 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4191 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4196 /**********************************************************************
4197 * GetSubMenu (USER32.@)
4199 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4203 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return 0;
4204 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
4205 return lpmi
->hSubMenu
;
4209 /**********************************************************************
4210 * DrawMenuBar (USER32.@)
4212 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4215 HMENU hMenu
= GetMenu(hWnd
);
4217 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4219 if (!hMenu
|| !(lppop
= MENU_GetMenu( hMenu
))) return FALSE
;
4221 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4222 lppop
->hwndOwner
= hWnd
;
4223 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4224 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4228 /***********************************************************************
4229 * DrawMenuBarTemp (USER32.@)
4233 * called by W98SE desk.cpl Control Panel Applet
4235 * Not 100% sure about the param names, but close.
4237 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4242 BOOL flat_menu
= FALSE
;
4244 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4247 hMenu
= GetMenu(hwnd
);
4250 hFont
= get_menu_font(FALSE
);
4252 lppop
= MENU_GetMenu( hMenu
);
4253 if (lppop
== NULL
|| lprect
== NULL
)
4255 retvalue
= GetSystemMetrics(SM_CYMENU
);
4259 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4261 hfontOld
= SelectObject( hDC
, hFont
);
4263 if (lppop
->Height
== 0)
4264 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4266 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4268 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4270 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4271 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4272 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4274 if (lppop
->nItems
== 0)
4276 retvalue
= GetSystemMetrics(SM_CYMENU
);
4280 for (i
= 0; i
< lppop
->nItems
; i
++)
4282 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
4283 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
4285 retvalue
= lppop
->Height
;
4288 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4292 /***********************************************************************
4293 * EndMenu (USER.187)
4294 * EndMenu (USER32.@)
4296 BOOL WINAPI
EndMenu(void)
4298 /* if we are in the menu code, and it is active */
4299 if (!fEndMenu
&& top_popup
)
4301 /* terminate the menu handling code */
4304 /* needs to be posted to wakeup the internal menu handler */
4305 /* which will now terminate the menu, in the event that */
4306 /* the main window was minimized, or lost focus, so we */
4307 /* don't end up with an orphaned menu */
4308 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4314 /***********************************************************************
4315 * LookupMenuHandle (USER.217)
4317 HMENU16 WINAPI
LookupMenuHandle16( HMENU16 hmenu
, INT16 id
)
4319 HMENU hmenu32
= HMENU_32(hmenu
);
4321 if (!MENU_FindItem( &hmenu32
, &id32
, MF_BYCOMMAND
)) return 0;
4322 else return HMENU_16(hmenu32
);
4326 /**********************************************************************
4327 * LoadMenu (USER.150)
4329 HMENU16 WINAPI
LoadMenu16( HINSTANCE16 instance
, LPCSTR name
)
4335 if (HIWORD(name
) && name
[0] == '#') name
= ULongToPtr(atoi( name
+ 1 ));
4336 if (!name
) return 0;
4338 instance
= GetExePtr( instance
);
4339 if (!(hRsrc
= FindResource16( instance
, name
, (LPSTR
)RT_MENU
))) return 0;
4340 if (!(handle
= LoadResource16( instance
, hRsrc
))) return 0;
4341 hMenu
= LoadMenuIndirect16(LockResource16(handle
));
4342 FreeResource16( handle
);
4347 /*****************************************************************
4348 * LoadMenuA (USER32.@)
4350 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4352 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4353 if (!hrsrc
) return 0;
4354 return LoadMenuIndirectA( LoadResource( instance
, hrsrc
));
4358 /*****************************************************************
4359 * LoadMenuW (USER32.@)
4361 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4363 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4364 if (!hrsrc
) return 0;
4365 return LoadMenuIndirectW( LoadResource( instance
, hrsrc
));
4369 /**********************************************************************
4370 * LoadMenuIndirect (USER.220)
4372 HMENU16 WINAPI
LoadMenuIndirect16( LPCVOID
template )
4375 WORD version
, offset
;
4376 LPCSTR p
= template;
4378 TRACE("(%p)\n", template );
4379 version
= GET_WORD(p
);
4383 WARN("version must be 0 for Win16\n" );
4386 offset
= GET_WORD(p
);
4387 p
+= sizeof(WORD
) + offset
;
4388 if (!(hMenu
= CreateMenu())) return 0;
4389 if (!MENU_ParseResource( p
, hMenu
, FALSE
))
4391 DestroyMenu( hMenu
);
4394 return HMENU_16(hMenu
);
4398 /**********************************************************************
4399 * LoadMenuIndirectW (USER32.@)
4401 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4404 WORD version
, offset
;
4405 LPCSTR p
= template;
4407 version
= GET_WORD(p
);
4409 TRACE("%p, ver %d\n", template, version
);
4412 case 0: /* standard format is version of 0 */
4413 offset
= GET_WORD(p
);
4414 p
+= sizeof(WORD
) + offset
;
4415 if (!(hMenu
= CreateMenu())) return 0;
4416 if (!MENU_ParseResource( p
, hMenu
, TRUE
))
4418 DestroyMenu( hMenu
);
4422 case 1: /* extended format is version of 1 */
4423 offset
= GET_WORD(p
);
4424 p
+= sizeof(WORD
) + offset
;
4425 if (!(hMenu
= CreateMenu())) return 0;
4426 if (!MENUEX_ParseResource( p
, hMenu
))
4428 DestroyMenu( hMenu
);
4433 ERR("version %d not supported.\n", version
);
4439 /**********************************************************************
4440 * LoadMenuIndirectA (USER32.@)
4442 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4444 return LoadMenuIndirectW( template );
4448 /**********************************************************************
4451 BOOL WINAPI
IsMenu(HMENU hmenu
)
4453 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4457 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4463 /**********************************************************************
4464 * GetMenuItemInfo_common
4467 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4468 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4470 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4472 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4475 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
4479 if( lpmii
->fMask
& MIIM_TYPE
) {
4480 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4481 WARN("invalid combination of fMask bits used\n");
4482 /* this does not happen on Win9x/ME */
4483 SetLastError( ERROR_INVALID_PARAMETER
);
4486 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4487 if( menu
->hbmpItem
) lpmii
->fType
|= MFT_BITMAP
;
4488 lpmii
->hbmpItem
= menu
->hbmpItem
; /* not on Win9x/ME */
4489 if( lpmii
->fType
& MFT_BITMAP
) {
4490 lpmii
->dwTypeData
= (LPWSTR
) menu
->hbmpItem
;
4492 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4493 /* this does not happen on Win9x/ME */
4494 lpmii
->dwTypeData
= 0;
4499 /* copy the text string */
4500 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4502 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4505 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4507 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4513 len
= strlenW(menu
->text
);
4514 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4515 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4519 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
,
4520 0, NULL
, NULL
) - 1;
4521 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4522 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4523 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4524 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4526 /* if we've copied a substring we return its length */
4527 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4528 if (lpmii
->cch
<= len
+ 1)
4533 /* return length of string */
4534 /* not on Win9x/ME if fType & MFT_BITMAP */
4540 if (lpmii
->fMask
& MIIM_FTYPE
)
4541 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4543 if (lpmii
->fMask
& MIIM_BITMAP
)
4544 lpmii
->hbmpItem
= menu
->hbmpItem
;
4546 if (lpmii
->fMask
& MIIM_STATE
)
4547 lpmii
->fState
= menu
->fState
& MENUITEMINFO_STATE_MASK
;
4549 if (lpmii
->fMask
& MIIM_ID
)
4550 lpmii
->wID
= menu
->wID
;
4552 if (lpmii
->fMask
& MIIM_SUBMENU
)
4553 lpmii
->hSubMenu
= menu
->hSubMenu
;
4555 /* hSubMenu is always cleared
4556 * (not on Win9x/ME ) */
4557 lpmii
->hSubMenu
= 0;
4560 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4561 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4562 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4564 if (lpmii
->fMask
& MIIM_DATA
)
4565 lpmii
->dwItemData
= menu
->dwItemData
;
4570 /**********************************************************************
4571 * GetMenuItemInfoA (USER32.@)
4573 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4574 LPMENUITEMINFOA lpmii
)
4578 if( lpmii
->cbSize
!= sizeof( mii
) &&
4579 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4580 SetLastError( ERROR_INVALID_PARAMETER
);
4583 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4584 mii
.cbSize
= sizeof( mii
);
4585 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4586 (LPMENUITEMINFOW
)&mii
, FALSE
);
4587 mii
.cbSize
= lpmii
->cbSize
;
4588 memcpy( lpmii
, &mii
, mii
.cbSize
);
4592 /**********************************************************************
4593 * GetMenuItemInfoW (USER32.@)
4595 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4596 LPMENUITEMINFOW lpmii
)
4600 if( lpmii
->cbSize
!= sizeof( mii
) &&
4601 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4602 SetLastError( ERROR_INVALID_PARAMETER
);
4605 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4606 mii
.cbSize
= sizeof( mii
);
4607 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4608 mii
.cbSize
= lpmii
->cbSize
;
4609 memcpy( lpmii
, &mii
, mii
.cbSize
);
4614 /* set a menu item text from a ASCII or Unicode string */
4615 static inline void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4621 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4622 strcpyW( menu
->text
, text
);
4626 LPCSTR str
= (LPCSTR
)text
;
4627 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4628 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4629 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4634 /**********************************************************************
4637 * detect if there are loops in the menu tree (or the depth is too large)
4639 static int MENU_depth( POPUPMENU
*pmenu
, int depth
)
4645 if( depth
> MAXMENUDEPTH
) return depth
;
4646 item
= pmenu
->items
;
4647 for( i
= 0; i
< pmenu
->nItems
&& depth
<= MAXMENUDEPTH
; i
++, item
++){
4648 POPUPMENU
*psubmenu
= MENU_GetMenu( item
->hSubMenu
);
4650 int bdepth
= MENU_depth( psubmenu
, depth
);
4651 if( bdepth
> depth
) depth
= bdepth
;
4658 /**********************************************************************
4659 * SetMenuItemInfo_common
4661 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4662 * MIIM_BITMAP and MIIM_STRING flags instead.
4665 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4666 const MENUITEMINFOW
*lpmii
,
4669 if (!menu
) return FALSE
;
4671 debug_print_menuitem("SetMenuItemInfo_common from: ", menu
, "");
4673 if (lpmii
->fMask
& MIIM_FTYPE
) {
4674 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4675 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4677 if (lpmii
->fMask
& MIIM_STRING
) {
4678 /* free the string when used */
4679 HeapFree(GetProcessHeap(), 0, menu
->text
);
4680 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4683 if (lpmii
->fMask
& MIIM_STATE
)
4684 /* Other menu items having MFS_DEFAULT are not converted
4686 menu
->fState
= lpmii
->fState
& MENUITEMINFO_STATE_MASK
;
4688 if (lpmii
->fMask
& MIIM_ID
)
4689 menu
->wID
= lpmii
->wID
;
4691 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4692 menu
->hSubMenu
= lpmii
->hSubMenu
;
4693 if (menu
->hSubMenu
) {
4694 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4696 if( MENU_depth( subMenu
, 0) > MAXMENUDEPTH
) {
4697 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4701 subMenu
->wFlags
|= MF_POPUP
;
4702 menu
->fType
|= MF_POPUP
;
4704 SetLastError( ERROR_INVALID_PARAMETER
);
4709 menu
->fType
&= ~MF_POPUP
;
4712 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4714 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4715 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4717 if (lpmii
->fMask
& MIIM_DATA
)
4718 menu
->dwItemData
= lpmii
->dwItemData
;
4720 if (lpmii
->fMask
& MIIM_BITMAP
)
4721 menu
->hbmpItem
= lpmii
->hbmpItem
;
4723 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
4724 menu
->fType
|= MFT_SEPARATOR
;
4726 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4730 /**********************************************************************
4731 * MENU_NormalizeMenuItemInfoStruct
4733 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4734 * check, copy and extend the MENUITEMINFO struct from the version that the application
4735 * supplied to the version used by wine source. */
4736 static BOOL
MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW
*pmii_in
,
4737 MENUITEMINFOW
*pmii_out
)
4739 /* do we recognize the size? */
4740 if( pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) &&
4741 pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) - sizeof( pmii_in
->hbmpItem
)) {
4742 SetLastError( ERROR_INVALID_PARAMETER
);
4745 /* copy the fields that we have */
4746 memcpy( pmii_out
, pmii_in
, pmii_in
->cbSize
);
4747 /* if the hbmpItem member is missing then extend */
4748 if( pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
)) {
4749 pmii_out
->cbSize
= sizeof( MENUITEMINFOW
);
4750 pmii_out
->hbmpItem
= NULL
;
4752 /* test for invalid bit combinations */
4753 if( (pmii_out
->fMask
& MIIM_TYPE
&&
4754 pmii_out
->fMask
& (MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) ||
4755 (pmii_out
->fMask
& MIIM_FTYPE
&& pmii_out
->fType
& MFT_BITMAP
)) {
4756 WARN("invalid combination of fMask bits used\n");
4757 /* this does not happen on Win9x/ME */
4758 SetLastError( ERROR_INVALID_PARAMETER
);
4761 /* convert old style (MIIM_TYPE) to the new */
4762 if( pmii_out
->fMask
& MIIM_TYPE
){
4763 pmii_out
->fMask
|= MIIM_FTYPE
;
4764 if( IS_STRING_ITEM(pmii_out
->fType
)){
4765 pmii_out
->fMask
|= MIIM_STRING
;
4766 } else if( (pmii_out
->fType
) & MFT_BITMAP
){
4767 pmii_out
->fMask
|= MIIM_BITMAP
;
4768 pmii_out
->hbmpItem
= HBITMAP_32(LOWORD(pmii_out
->dwTypeData
));
4774 /**********************************************************************
4775 * SetMenuItemInfoA (USER32.@)
4777 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4778 const MENUITEMINFOA
*lpmii
)
4782 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4784 if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
4786 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4790 /**********************************************************************
4791 * SetMenuItemInfoW (USER32.@)
4793 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4794 const MENUITEMINFOW
*lpmii
)
4798 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4800 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
4801 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
,
4802 &item
, bypos
? MF_BYPOSITION
: 0), &mii
, TRUE
);
4805 /**********************************************************************
4806 * SetMenuDefaultItem (USER32.@)
4809 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4815 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4817 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4819 /* reset all default-item flags */
4821 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4823 item
->fState
&= ~MFS_DEFAULT
;
4826 /* no default item */
4835 if ( uItem
>= menu
->nItems
) return FALSE
;
4836 item
[uItem
].fState
|= MFS_DEFAULT
;
4841 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4843 if (item
->wID
== uItem
)
4845 item
->fState
|= MFS_DEFAULT
;
4854 /**********************************************************************
4855 * GetMenuDefaultItem (USER32.@)
4857 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4863 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4865 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4867 /* find default item */
4871 if (! item
) return -1;
4873 while ( !( item
->fState
& MFS_DEFAULT
) )
4876 if (i
>= menu
->nItems
) return -1;
4879 /* default: don't return disabled items */
4880 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4882 /* search rekursiv when needed */
4883 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4886 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4887 if ( -1 != ret
) return ret
;
4889 /* when item not found in submenu, return the popup item */
4891 return ( bypos
) ? i
: item
->wID
;
4896 /**********************************************************************
4897 * InsertMenuItemA (USER32.@)
4899 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4900 const MENUITEMINFOA
*lpmii
)
4905 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4907 if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
4909 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4910 return SetMenuItemInfo_common(item
, &mii
, FALSE
);
4914 /**********************************************************************
4915 * InsertMenuItemW (USER32.@)
4917 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4918 const MENUITEMINFOW
*lpmii
)
4923 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4925 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
4927 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4928 return SetMenuItemInfo_common(item
, &mii
, TRUE
);
4931 /**********************************************************************
4932 * CheckMenuRadioItem (USER32.@)
4935 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
4936 UINT first
, UINT last
, UINT check
,
4941 MENUITEM
*mi_first
= NULL
, *mi_check
;
4942 HMENU m_first
, m_check
;
4944 for (i
= first
; i
<= last
; i
++)
4951 mi_first
= MENU_FindItem(&m_first
, &pos
, bypos
);
4952 if (!mi_first
) continue;
4953 mi_check
= mi_first
;
4959 mi_check
= MENU_FindItem(&m_check
, &pos
, bypos
);
4960 if (!mi_check
) continue;
4963 if (m_first
!= m_check
) continue;
4964 if (mi_check
->fType
== MFT_SEPARATOR
) continue;
4968 mi_check
->fType
|= MFT_RADIOCHECK
;
4969 mi_check
->fState
|= MFS_CHECKED
;
4974 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4975 mi_check
->fState
&= ~MFS_CHECKED
;
4983 /**********************************************************************
4984 * GetMenuItemRect (USER32.@)
4986 * ATTENTION: Here, the returned values in rect are the screen
4987 * coordinates of the item just like if the menu was
4988 * always on the upper left side of the application.
4991 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
4994 POPUPMENU
*itemMenu
;
4998 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
5000 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
5001 referenceHwnd
= hwnd
;
5005 itemMenu
= MENU_GetMenu(hMenu
);
5006 if (itemMenu
== NULL
)
5009 if(itemMenu
->hWnd
== 0)
5011 referenceHwnd
= itemMenu
->hWnd
;
5014 if ((rect
== NULL
) || (item
== NULL
))
5019 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
5024 /**********************************************************************
5025 * SetMenuInfo (USER32.@)
5028 * actually use the items to draw the menu
5029 * (recalculate and/or redraw)
5031 static BOOL
menu_SetMenuInfo( HMENU hMenu
, LPCMENUINFO lpmi
)
5034 if( !(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
5036 if (lpmi
->fMask
& MIM_BACKGROUND
)
5037 menu
->hbrBack
= lpmi
->hbrBack
;
5039 if (lpmi
->fMask
& MIM_HELPID
)
5040 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
5042 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5043 menu
->cyMax
= lpmi
->cyMax
;
5045 if (lpmi
->fMask
& MIM_MENUDATA
)
5046 menu
->dwMenuData
= lpmi
->dwMenuData
;
5048 if (lpmi
->fMask
& MIM_STYLE
)
5049 menu
->dwStyle
= lpmi
->dwStyle
;
5051 if( lpmi
->fMask
& MIM_APPLYTOSUBMENUS
) {
5053 MENUITEM
*item
= menu
->items
;
5054 for( i
= menu
->nItems
; i
; i
--, item
++)
5055 if( item
->fType
& MF_POPUP
)
5056 menu_SetMenuInfo( item
->hSubMenu
, lpmi
);
5061 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
5063 TRACE("(%p %p)\n", hMenu
, lpmi
);
5064 if( lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu_SetMenuInfo( hMenu
, lpmi
))) {
5065 if( lpmi
->fMask
& MIM_STYLE
) {
5066 if (lpmi
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
5067 if (lpmi
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
5068 if (lpmi
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
5072 SetLastError( ERROR_INVALID_PARAMETER
);
5076 /**********************************************************************
5077 * GetMenuInfo (USER32.@)
5083 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
5086 TRACE("(%p %p)\n", hMenu
, lpmi
);
5088 if (lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
5091 if (lpmi
->fMask
& MIM_BACKGROUND
)
5092 lpmi
->hbrBack
= menu
->hbrBack
;
5094 if (lpmi
->fMask
& MIM_HELPID
)
5095 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
5097 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5098 lpmi
->cyMax
= menu
->cyMax
;
5100 if (lpmi
->fMask
& MIM_MENUDATA
)
5101 lpmi
->dwMenuData
= menu
->dwMenuData
;
5103 if (lpmi
->fMask
& MIM_STYLE
)
5104 lpmi
->dwStyle
= menu
->dwStyle
;
5108 SetLastError( ERROR_INVALID_PARAMETER
);
5113 /**********************************************************************
5114 * SetMenuContextHelpId (USER32.@)
5116 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
5120 TRACE("(%p 0x%08x)\n", hMenu
, dwContextHelpID
);
5122 if ((menu
= MENU_GetMenu(hMenu
)))
5124 menu
->dwContextHelpID
= dwContextHelpID
;
5131 /**********************************************************************
5132 * GetMenuContextHelpId (USER32.@)
5134 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
5138 TRACE("(%p)\n", hMenu
);
5140 if ((menu
= MENU_GetMenu(hMenu
)))
5142 return menu
->dwContextHelpID
;
5147 /**********************************************************************
5148 * MenuItemFromPoint (USER32.@)
5150 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
5152 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
5155 /*FIXME: Do we have to handle hWnd here? */
5156 if (!menu
) return -1;
5157 if (!MENU_FindItemByCoords(menu
, ptScreen
, &pos
)) return -1;
5162 /**********************************************************************
5163 * translate_accelerator
5165 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
5166 BYTE fVirt
, WORD key
, WORD cmd
)
5171 if (wParam
!= key
) return FALSE
;
5173 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
5174 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
5175 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
5177 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
5179 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
5181 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
5187 if(fVirt
& FVIRTKEY
)
5189 TRACE_(accel
)("found accel for virt_key %04lx (scan %04x)\n",
5190 wParam
, 0xff & HIWORD(lParam
));
5192 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
5193 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5197 if (!(lParam
& 0x01000000)) /* no special_key */
5199 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5200 { /* ^^ ALT pressed */
5201 TRACE_(accel
)("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
5210 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5214 HMENU hMenu
, hSubMenu
, hSysMenu
;
5215 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5217 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5218 hSysMenu
= get_win_sys_menu( hWnd
);
5220 /* find menu item and ask application to initialize it */
5221 /* 1. in the system menu */
5222 hSubMenu
= hSysMenu
;
5224 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5228 if (!IsWindowEnabled(hWnd
))
5232 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5233 if(hSubMenu
!= hSysMenu
)
5235 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5236 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5237 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5239 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5242 else /* 2. in the window's menu */
5246 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5250 if (!IsWindowEnabled(hWnd
))
5254 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5255 if(hSubMenu
!= hMenu
)
5257 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5258 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5259 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5261 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5268 if (uSysStat
!= (UINT
)-1)
5270 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5277 if (uStat
!= (UINT
)-1)
5283 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5295 if( mesg
==WM_COMMAND
)
5297 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5298 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5300 else if( mesg
==WM_SYSCOMMAND
)
5302 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5303 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5307 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5308 * #0: unknown (please report!)
5309 * #1: for WM_KEYUP,WM_SYSKEYUP
5310 * #2: mouse is captured
5311 * #3: window is disabled
5312 * #4: it's a disabled system menu option
5313 * #5: it's a menu option, but window is iconic
5314 * #6: it's a menu option, but disabled
5316 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5318 ERR_(accel
)(" unknown reason - please report!\n");
5323 /**********************************************************************
5324 * TranslateAcceleratorA (USER32.@)
5325 * TranslateAccelerator (USER32.@)
5327 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5330 LPACCEL16 lpAccelTbl
;
5334 if (!hWnd
|| !msg
) return 0;
5336 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
5338 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
5342 wParam
= msg
->wParam
;
5344 switch (msg
->message
)
5353 char ch
= LOWORD(wParam
);
5355 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5356 wParam
= MAKEWPARAM(wch
, HIWORD(wParam
));
5364 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5365 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5369 if (translate_accelerator( hWnd
, msg
->message
, wParam
, msg
->lParam
,
5370 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
5372 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);
5377 /**********************************************************************
5378 * TranslateAcceleratorW (USER32.@)
5380 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5383 LPACCEL16 lpAccelTbl
;
5386 if (!hWnd
|| !msg
) return 0;
5388 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
5390 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
5394 switch (msg
->message
)
5406 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5407 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5411 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5412 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
5414 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);