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.
51 #include "wine/server.h"
52 #include "wine/exception.h"
55 #include "user_private.h"
56 #include "wine/debug.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
60 /* internal flags for menu tracking */
62 #define TF_ENDMENU 0x10000
63 #define TF_SUSPENDPOPUP 0x20000
64 #define TF_SKIPREMOVE 0x40000
65 #define TF_RCVD_BTN_UP 0x80000
70 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
71 HMENU hTopMenu
; /* initial menu */
72 HWND hOwnerWnd
; /* where notifications are sent */
79 /* Internal MENU_TrackMenu() flags */
80 #define TPM_INTERNAL 0xF0000000
81 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
82 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
84 /* Space between 2 columns */
85 #define MENU_COL_SPACE 4
87 /* Margins for popup menus */
90 /* (other menu->FocusedItem values give the position of the focused item) */
91 #define NO_SELECTED_ITEM 0xffff
93 #define MENU_ITEM_TYPE(flags) \
94 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
96 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
97 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
98 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
100 #define IS_SYSTEM_MENU(menu) \
101 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
103 #define MENUITEMINFO_TYPE_MASK \
104 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
105 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
106 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
107 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
108 #define STATE_MASK (~TYPE_MASK)
109 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
111 static SIZE menucharsize
;
112 static UINT ODitemheight
; /* default owner drawn item height */
114 /* Use global popup window because there's no way 2 menus can
115 * be tracked at the same time. */
116 static HWND top_popup
;
117 static HMENU top_popup_hmenu
;
119 /* Flag set by EndMenu() to force an exit from menu tracking */
120 static BOOL fEndMenu
= FALSE
;
122 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
);
124 static BOOL
is_win_menu_disallowed(HWND hwnd
)
126 return (GetWindowLongW(hwnd
, GWL_STYLE
) & (WS_CHILD
| WS_POPUP
)) == WS_CHILD
;
129 /*********************************************************************
130 * menu class descriptor
132 const struct builtin_class_descr MENU_builtin_class
=
134 (LPCWSTR
)POPUPMENU_CLASS_ATOM
, /* name */
135 CS_DROPSHADOW
| CS_SAVEBITS
| CS_DBLCLKS
, /* style */
136 WINPROC_MENU
, /* proc */
137 sizeof(HMENU
), /* extra */
138 IDC_ARROW
, /* cursor */
139 (HBRUSH
)(COLOR_MENU
+1) /* brush */
143 /***********************************************************************
144 * debug_print_menuitem
146 * Print a menuitem in readable form.
149 #define debug_print_menuitem(pre, mp, post) \
150 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
152 #define MENUOUT(text) \
153 TRACE("%s%s", (count++ ? "," : ""), (text))
155 #define MENUFLAG(bit,text) \
157 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
160 static void do_debug_print_menuitem(const char *prefix
, const MENUITEM
*mp
,
163 static const char * const hbmmenus
[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
164 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
165 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
166 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
167 TRACE("%s ", prefix
);
169 UINT flags
= mp
->fType
;
170 TRACE( "{ ID=0x%lx", mp
->wID
);
172 TRACE( ", Sub=%p", mp
->hSubMenu
);
176 MENUFLAG( MFT_SEPARATOR
, "sep");
177 MENUFLAG( MFT_OWNERDRAW
, "own");
178 MENUFLAG( MFT_BITMAP
, "bit");
179 MENUFLAG(MF_POPUP
, "pop");
180 MENUFLAG(MFT_MENUBARBREAK
, "barbrk");
181 MENUFLAG(MFT_MENUBREAK
, "brk");
182 MENUFLAG(MFT_RADIOCHECK
, "radio");
183 MENUFLAG(MFT_RIGHTORDER
, "rorder");
184 MENUFLAG(MF_SYSMENU
, "sys");
185 MENUFLAG(MFT_RIGHTJUSTIFY
, "right"); /* same as MF_HELP */
187 TRACE( "+0x%x", flags
);
193 MENUFLAG(MFS_GRAYED
, "grey");
194 MENUFLAG(MFS_DEFAULT
, "default");
195 MENUFLAG(MFS_DISABLED
, "dis");
196 MENUFLAG(MFS_CHECKED
, "check");
197 MENUFLAG(MFS_HILITE
, "hi");
198 MENUFLAG(MF_USECHECKBITMAPS
, "usebit");
199 MENUFLAG(MF_MOUSESELECT
, "mouse");
201 TRACE( "+0x%x", flags
);
204 TRACE( ", Chk=%p", mp
->hCheckBit
);
206 TRACE( ", Unc=%p", mp
->hUnCheckBit
);
208 TRACE( ", Text=%s", debugstr_w(mp
->text
));
210 TRACE( ", ItemData=0x%08lx", mp
->dwItemData
);
213 if( IS_MAGIC_BITMAP(mp
->hbmpItem
))
214 TRACE( ", hbitmap=%s", hbmmenus
[ (INT_PTR
)mp
->hbmpItem
+ 1]);
216 TRACE( ", hbitmap=%p", mp
->hbmpItem
);
221 TRACE(" %s\n", postfix
);
228 /***********************************************************************
231 * Validate the given menu handle and returns the menu structure pointer.
233 static POPUPMENU
*MENU_GetMenu(HMENU hMenu
)
235 POPUPMENU
*menu
= get_user_handle_ptr( hMenu
, NTUSER_OBJ_MENU
);
237 if (menu
== OBJ_OTHER_PROCESS
)
239 WARN( "other process menu %p?\n", hMenu
);
242 if (menu
) release_user_handle_ptr( menu
); /* FIXME! */
243 else WARN("invalid menu handle=%p\n", hMenu
);
247 static POPUPMENU
*grab_menu_ptr(HMENU hMenu
)
249 POPUPMENU
*menu
= get_user_handle_ptr( hMenu
, NTUSER_OBJ_MENU
);
251 if (menu
== OBJ_OTHER_PROCESS
)
253 WARN("other process menu %p?\n", hMenu
);
260 WARN("invalid menu handle=%p\n", hMenu
);
264 static void release_menu_ptr(POPUPMENU
*menu
)
269 release_user_handle_ptr(menu
);
273 /***********************************************************************
276 * Get the system menu of a window
278 static HMENU
get_win_sys_menu( HWND hwnd
)
281 WND
*win
= WIN_GetPtr( hwnd
);
282 if (win
&& win
!= WND_OTHER_PROCESS
&& win
!= WND_DESKTOP
)
285 WIN_ReleasePtr( win
);
290 /***********************************************************************
293 static HFONT
get_menu_font( BOOL bold
)
295 static HFONT hMenuFont
, hMenuFontBold
;
297 HFONT ret
= bold
? hMenuFontBold
: hMenuFont
;
301 NONCLIENTMETRICSW ncm
;
304 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
305 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSW
), &ncm
, 0);
309 ncm
.lfMenuFont
.lfWeight
+= 300;
310 if (ncm
.lfMenuFont
.lfWeight
> 1000) ncm
.lfMenuFont
.lfWeight
= 1000;
312 if (!(ret
= CreateFontIndirectW( &ncm
.lfMenuFont
))) return 0;
313 prev
= InterlockedCompareExchangePointer( (void **)(bold
? &hMenuFontBold
: &hMenuFont
),
317 /* another thread beat us to it */
325 /***********************************************************************
328 static HBITMAP
get_arrow_bitmap(void)
330 static HBITMAP arrow_bitmap
;
332 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
336 static inline UINT
get_scroll_arrow_height(const POPUPMENU
*menu
)
338 return menucharsize
.cy
+ 4;
341 /***********************************************************************
344 * Return the default system menu.
346 static HMENU
MENU_CopySysPopup(BOOL mdi
)
348 HMENU hMenu
= LoadMenuW(user32_module
, mdi
? L
"SYSMENUMDI" : L
"SYSMENU");
352 MENUITEMINFOW miteminfo
;
353 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
354 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
355 /* decorate the menu with bitmaps */
356 minfo
.cbSize
= sizeof( MENUINFO
);
357 minfo
.dwStyle
= MNS_CHECKORBMP
;
358 minfo
.fMask
= MIM_STYLE
;
359 SetMenuInfo( hMenu
, &minfo
);
360 miteminfo
.cbSize
= sizeof( MENUITEMINFOW
);
361 miteminfo
.fMask
= MIIM_BITMAP
;
362 miteminfo
.hbmpItem
= HBMMENU_POPUP_CLOSE
;
363 SetMenuItemInfoW( hMenu
, SC_CLOSE
, FALSE
, &miteminfo
);
364 miteminfo
.hbmpItem
= HBMMENU_POPUP_RESTORE
;
365 SetMenuItemInfoW( hMenu
, SC_RESTORE
, FALSE
, &miteminfo
);
366 miteminfo
.hbmpItem
= HBMMENU_POPUP_MAXIMIZE
;
367 SetMenuItemInfoW( hMenu
, SC_MAXIMIZE
, FALSE
, &miteminfo
);
368 miteminfo
.hbmpItem
= HBMMENU_POPUP_MINIMIZE
;
369 SetMenuItemInfoW( hMenu
, SC_MINIMIZE
, FALSE
, &miteminfo
);
370 NtUserSetMenuDefaultItem( hMenu
, SC_CLOSE
, FALSE
);
373 ERR("Unable to load default system menu\n" );
375 TRACE("returning %p (mdi=%d).\n", hMenu
, mdi
);
381 /**********************************************************************
384 * Create a copy of the system menu. System menu in Windows is
385 * a special menu bar with the single entry - system menu popup.
386 * This popup is presented to the outside world as a "system menu".
387 * However, the real system menu handle is sometimes seen in the
388 * WM_MENUSELECT parameters (and Word 6 likes it this way).
390 HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
394 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
395 if ((hMenu
= CreateMenu()))
397 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
398 menu
->wFlags
= MF_SYSMENU
;
399 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
400 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
404 if (GetWindowLongW(hWnd
, GWL_EXSTYLE
) & WS_EX_MDICHILD
)
405 hPopupMenu
= MENU_CopySysPopup(TRUE
);
407 hPopupMenu
= MENU_CopySysPopup(FALSE
);
412 if (GetClassLongW(hWnd
, GCL_STYLE
) & CS_NOCLOSE
)
413 NtUserDeleteMenu( hPopupMenu
, SC_CLOSE
, MF_BYCOMMAND
);
415 InsertMenuW( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
416 (UINT_PTR
)hPopupMenu
, NULL
);
418 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
419 menu
->items
[0].fState
= 0;
420 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
422 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
425 NtUserDestroyMenu( hMenu
);
427 ERR("failed to load system menu!\n");
432 /***********************************************************************
433 * MENU_InitSysMenuPopup
435 * Grey the appropriate items in System menu.
437 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
441 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
442 NtUserEnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
443 gray
= ((style
& WS_MAXIMIZE
) != 0);
444 NtUserEnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
445 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
446 NtUserEnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
447 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
448 NtUserEnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
449 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
450 NtUserEnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
451 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
453 /* The menu item must keep its state if it's disabled */
455 NtUserEnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
459 /******************************************************************************
461 * UINT MENU_GetStartOfNextColumn(
464 *****************************************************************************/
466 static UINT
MENU_GetStartOfNextColumn(
469 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
473 return NO_SELECTED_ITEM
;
475 i
= menu
->FocusedItem
+ 1;
476 if( i
== NO_SELECTED_ITEM
)
479 for( ; i
< menu
->nItems
; ++i
) {
480 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
484 return NO_SELECTED_ITEM
;
488 /******************************************************************************
490 * UINT MENU_GetStartOfPrevColumn(
493 *****************************************************************************/
495 static UINT
MENU_GetStartOfPrevColumn(
498 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
502 return NO_SELECTED_ITEM
;
504 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
505 return NO_SELECTED_ITEM
;
507 /* Find the start of the column */
509 for(i
= menu
->FocusedItem
; i
!= 0 &&
510 !(menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
514 return NO_SELECTED_ITEM
;
516 for(--i
; i
!= 0; --i
) {
517 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
521 TRACE("ret %d.\n", i
);
526 static POPUPMENU
*find_menu_item(HMENU hmenu
, UINT id
, UINT flags
, UINT
*pos
)
528 UINT fallback_pos
= ~0u, i
;
531 menu
= grab_menu_ptr(hmenu
);
535 if (flags
& MF_BYPOSITION
)
537 if (id
>= menu
->nItems
)
539 release_menu_ptr(menu
);
548 MENUITEM
*item
= menu
->items
;
549 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
551 if (item
->fType
& MF_POPUP
)
553 POPUPMENU
*submenu
= find_menu_item(item
->hSubMenu
, id
, flags
, pos
);
557 release_menu_ptr(menu
);
560 else if (item
->wID
== id
)
562 /* fallback to this item if nothing else found */
566 else if (item
->wID
== id
)
574 if (fallback_pos
!= ~0u)
578 release_menu_ptr(menu
);
585 /***********************************************************************
588 * Find a Sub menu. Return the position of the submenu, and modifies
589 * *hmenu in case it is found in another sub-menu.
590 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
592 static UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
597 if (((*hmenu
)==(HMENU
)0xffff) ||
598 (!(menu
= MENU_GetMenu(*hmenu
))))
599 return NO_SELECTED_ITEM
;
601 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
602 if(!(item
->fType
& MF_POPUP
)) continue;
603 if (item
->hSubMenu
== hSubTarget
) {
607 HMENU hsubmenu
= item
->hSubMenu
;
608 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
609 if (pos
!= NO_SELECTED_ITEM
) {
615 return NO_SELECTED_ITEM
;
618 /***********************************************************************
619 * MENU_AdjustMenuItemRect
621 * Adjust menu item rectangle according to scrolling state.
624 MENU_AdjustMenuItemRect(const POPUPMENU
*menu
, LPRECT rect
)
626 INT scroll_offset
= menu
->bScrolling
? menu
->nScrollPos
: 0;
628 OffsetRect( rect
, menu
->items_rect
.left
, menu
->items_rect
.top
- scroll_offset
);
633 ht_nowhere
, /* outside the menu */
634 ht_border
, /* anywhere that's not an item or a scroll arrow */
635 ht_item
, /* a menu item */
636 ht_scroll_up
, /* scroll up arrow */
637 ht_scroll_down
/* scroll down arrow */
640 /***********************************************************************
641 * MENU_FindItemByCoords
643 * Find the item at the specified coordinates (screen coords). Does
644 * not work for child windows and therefore should not be called for
645 * an arbitrary system menu.
647 * Returns a hittest code. *pos will contain the position of the
648 * item or NO_SELECTED_ITEM. If the hittest code is ht_scroll_up
649 * or ht_scroll_down then *pos will contain the position of the
650 * item that's just outside the items_rect - ie, the one that would
651 * be scrolled completely into view.
653 static enum hittest
MENU_FindItemByCoords( const POPUPMENU
*menu
, POINT pt
, UINT
*pos
)
658 enum hittest ht
= ht_border
;
660 *pos
= NO_SELECTED_ITEM
;
662 if (!GetWindowRect(menu
->hWnd
, &rect
) || !PtInRect(&rect
, pt
))
665 if (GetWindowLongW( menu
->hWnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) pt
.x
= rect
.right
- 1 - pt
.x
;
666 else pt
.x
-= rect
.left
;
669 if (!PtInRect(&menu
->items_rect
, pt
))
671 if (!menu
->bScrolling
|| pt
.x
< menu
->items_rect
.left
|| pt
.x
>= menu
->items_rect
.right
)
674 /* On a scroll arrow. Update pt so that it points to the item just outside items_rect */
675 if (pt
.y
< menu
->items_rect
.top
)
678 pt
.y
= menu
->items_rect
.top
- 1;
683 pt
.y
= menu
->items_rect
.bottom
;
688 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
691 MENU_AdjustMenuItemRect(menu
, &rect
);
692 if (PtInRect(&rect
, pt
))
695 if (ht
!= ht_scroll_up
&& ht
!= ht_scroll_down
) ht
= ht_item
;
704 /***********************************************************************
707 * Find the menu item selected by a key press.
708 * Return item id, -1 if none, -2 if we should close the menu.
710 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
711 WCHAR key
, BOOL forceMenuChar
)
713 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key
, key
, hmenu
);
715 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
719 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
720 MENUITEM
*item
= menu
->items
;
726 BOOL cjk
= GetSystemMetrics( SM_DBCSENABLED
);
728 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
732 const WCHAR
*p
= item
->text
- 2;
735 const WCHAR
*q
= p
+ 2;
737 if (!p
&& cjk
) p
= wcschr (q
, '\036'); /* Japanese Win16 */
739 while (p
!= NULL
&& p
[1] == '&');
740 if (p
&& (towupper(p
[1]) == towupper(key
))) return i
;
744 menuchar
= SendMessageW( hwndOwner
, WM_MENUCHAR
,
745 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
746 if (HIWORD(menuchar
) == MNC_EXECUTE
) return LOWORD(menuchar
);
747 if (HIWORD(menuchar
) == MNC_CLOSE
) return (UINT
)(-2);
753 /***********************************************************************
754 * MENU_GetBitmapItemSize
756 * Get the size of a bitmap item.
758 static void MENU_GetBitmapItemSize( MENUITEM
*lpitem
, SIZE
*size
,
762 HBITMAP bmp
= lpitem
->hbmpItem
;
764 size
->cx
= size
->cy
= 0;
766 /* check if there is a magic menu item associated with this item */
767 switch( (INT_PTR
) bmp
)
769 case (INT_PTR
)HBMMENU_CALLBACK
:
771 MEASUREITEMSTRUCT measItem
;
772 measItem
.CtlType
= ODT_MENU
;
774 measItem
.itemID
= lpitem
->wID
;
775 measItem
.itemWidth
= lpitem
->rect
.right
- lpitem
->rect
.left
;
776 measItem
.itemHeight
= lpitem
->rect
.bottom
- lpitem
->rect
.top
;
777 measItem
.itemData
= lpitem
->dwItemData
;
778 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&measItem
);
779 size
->cx
= measItem
.itemWidth
;
780 size
->cy
= measItem
.itemHeight
;
784 case (INT_PTR
)HBMMENU_SYSTEM
:
785 if (lpitem
->dwItemData
)
787 bmp
= (HBITMAP
)lpitem
->dwItemData
;
791 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
792 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
793 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
794 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
795 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
796 size
->cx
= GetSystemMetrics( SM_CYMENU
) - 4;
799 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
800 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
801 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
802 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
803 size
->cx
= GetSystemMetrics( SM_CXMENUSIZE
);
804 size
->cy
= GetSystemMetrics( SM_CYMENUSIZE
);
807 if (GetObjectW(bmp
, sizeof(bm
), &bm
))
809 size
->cx
= bm
.bmWidth
;
810 size
->cy
= bm
.bmHeight
;
814 /***********************************************************************
815 * MENU_DrawBitmapItem
817 * Draw a bitmap item.
819 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
,
820 POPUPMENU
*menu
, HWND hwndOwner
, UINT odaction
)
826 int w
= rect
->right
- rect
->left
;
827 int h
= rect
->bottom
- rect
->top
;
830 HBITMAP hbmToDraw
= lpitem
->hbmpItem
;
833 /* Check if there is a magic menu item associated with this item */
834 if (IS_MAGIC_BITMAP(hbmToDraw
))
840 switch((INT_PTR
)hbmToDraw
)
842 case (INT_PTR
)HBMMENU_SYSTEM
:
843 if (lpitem
->dwItemData
)
845 bmp
= (HBITMAP
)lpitem
->dwItemData
;
846 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
850 static HBITMAP hBmpSysMenu
;
852 if (!hBmpSysMenu
) hBmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
854 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
855 /* only use right half of the bitmap */
856 bmp_xoffset
= bm
.bmWidth
/ 2;
857 bm
.bmWidth
-= bmp_xoffset
;
860 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
861 flags
= DFCS_CAPTIONRESTORE
;
863 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
864 flags
= DFCS_CAPTIONMIN
;
866 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
867 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
869 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
870 flags
= DFCS_CAPTIONCLOSE
;
872 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
873 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
875 case (INT_PTR
)HBMMENU_CALLBACK
:
877 DRAWITEMSTRUCT drawItem
;
878 drawItem
.CtlType
= ODT_MENU
;
880 drawItem
.itemID
= lpitem
->wID
;
881 drawItem
.itemAction
= odaction
;
882 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
883 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
884 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
885 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
886 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
887 drawItem
.hwndItem
= (HWND
)menu
->obj
.handle
;
889 drawItem
.itemData
= lpitem
->dwItemData
;
890 drawItem
.rcItem
= *rect
;
891 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
895 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
898 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
901 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
904 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
908 FIXME("Magic %p not implemented\n", hbmToDraw
);
913 /* draw the magic bitmaps using marlett font characters */
914 /* FIXME: fontsize and the position (x,y) could probably be better */
915 HFONT hfont
, hfontsav
;
916 LOGFONTW logfont
= { 0, 0, 0, 0, FW_NORMAL
, 0, 0, 0, SYMBOL_CHARSET
, 0, 0, 0, 0, L
"Marlett" };
917 logfont
.lfHeight
= min( h
, w
) - 5 ;
918 TRACE(" height %d rect %s\n", logfont
.lfHeight
, wine_dbgstr_rect( rect
));
919 hfont
= CreateFontIndirectW( &logfont
);
920 hfontsav
= SelectObject(hdc
, hfont
);
921 TextOutW( hdc
, rect
->left
, rect
->top
+ 2, &bmchr
, 1);
922 SelectObject(hdc
, hfontsav
);
923 DeleteObject( hfont
);
928 InflateRect( &r
, -1, -1 );
929 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
930 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
935 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
938 hdcMem
= CreateCompatibleDC( hdc
);
939 SelectObject( hdcMem
, bmp
);
941 /* handle fontsize > bitmap_height */
942 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
944 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
945 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmpItem
)
946 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
947 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
952 /***********************************************************************
955 * Calculate the size of the menu item and store it in lpitem->rect.
957 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
958 INT orgX
, INT orgY
, BOOL menuBar
, POPUPMENU
* lppop
)
961 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
962 UINT arrow_bitmap_width
;
966 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
967 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
968 (menuBar
? " (MenuBar)" : ""));
970 GetObjectW( get_arrow_bitmap(), sizeof(bm
), &bm
);
971 arrow_bitmap_width
= bm
.bmWidth
;
973 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
974 if( !menucharsize
.cx
) {
975 menucharsize
.cx
= GdiGetCharDimensions( hdc
, NULL
, &menucharsize
.cy
);
976 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
977 * but it is unlikely an application will depend on that */
978 ODitemheight
= HIWORD( GetDialogBaseUnits());
981 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
983 if (lpitem
->fType
& MF_OWNERDRAW
)
985 MEASUREITEMSTRUCT mis
;
986 mis
.CtlType
= ODT_MENU
;
988 mis
.itemID
= lpitem
->wID
;
989 mis
.itemData
= lpitem
->dwItemData
;
990 mis
.itemHeight
= ODitemheight
;
992 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
993 /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
994 * width of a menufont character to the width of an owner-drawn menu.
996 lpitem
->rect
.right
+= mis
.itemWidth
+ 2 * menucharsize
.cx
;
998 /* under at least win95 you seem to be given a standard
999 height for the menu and the height value is ignored */
1000 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
1002 lpitem
->rect
.bottom
+= mis
.itemHeight
;
1004 TRACE("id=%04lx size=%dx%d\n",
1005 lpitem
->wID
, lpitem
->rect
.right
-lpitem
->rect
.left
,
1006 lpitem
->rect
.bottom
-lpitem
->rect
.top
);
1010 if (lpitem
->fType
& MF_SEPARATOR
)
1012 lpitem
->rect
.bottom
+= GetSystemMetrics( SM_CYMENUSIZE
)/2;
1014 lpitem
->rect
.right
+= arrow_bitmap_width
+ menucharsize
.cx
;
1022 if (lpitem
->hbmpItem
) {
1025 MENU_GetBitmapItemSize(lpitem
, &size
, hwndOwner
);
1026 /* Keep the size of the bitmap in callback mode to be able
1027 * to draw it correctly */
1028 lpitem
->bmpsize
= size
;
1029 lppop
->textOffset
= max( lppop
->textOffset
, size
.cx
);
1030 lpitem
->rect
.right
+= size
.cx
+ 2;
1031 itemheight
= size
.cy
+ 2;
1033 if( !(lppop
->dwStyle
& MNS_NOCHECK
))
1034 lpitem
->rect
.right
+= check_bitmap_width
;
1035 lpitem
->rect
.right
+= 4 + menucharsize
.cx
;
1036 lpitem
->xTab
= lpitem
->rect
.right
;
1037 lpitem
->rect
.right
+= arrow_bitmap_width
;
1038 } else if (lpitem
->hbmpItem
) { /* menuBar */
1041 MENU_GetBitmapItemSize( lpitem
, &size
, hwndOwner
);
1042 lpitem
->bmpsize
= size
;
1043 lpitem
->rect
.right
+= size
.cx
;
1044 if( lpitem
->text
) lpitem
->rect
.right
+= 2;
1045 itemheight
= size
.cy
;
1048 /* it must be a text item - unless it's the system menu */
1049 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->text
) {
1050 HFONT hfontOld
= NULL
;
1051 RECT rc
= lpitem
->rect
;
1052 LONG txtheight
, txtwidth
;
1054 if ( lpitem
->fState
& MFS_DEFAULT
) {
1055 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1058 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1059 DT_SINGLELINE
|DT_CALCRECT
);
1060 lpitem
->rect
.right
+= rc
.right
- rc
.left
;
1061 itemheight
= max( max( itemheight
, txtheight
),
1062 GetSystemMetrics( SM_CYMENU
) - 1);
1063 lpitem
->rect
.right
+= 2 * menucharsize
.cx
;
1065 if ((p
= wcschr( lpitem
->text
, '\t' )) != NULL
) {
1068 int n
= (int)( p
- lpitem
->text
);
1069 /* Item contains a tab (only meaningful in popup menus) */
1070 /* get text size before the tab */
1071 txtheight
= DrawTextW( hdc
, lpitem
->text
, n
, &rc
,
1072 DT_SINGLELINE
|DT_CALCRECT
);
1073 txtwidth
= rc
.right
- rc
.left
;
1074 p
+= 1; /* advance past the Tab */
1075 /* get text size after the tab */
1076 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1077 DT_SINGLELINE
|DT_CALCRECT
);
1078 lpitem
->xTab
+= txtwidth
;
1079 txtheight
= max( txtheight
, tmpheight
);
1080 txtwidth
+= menucharsize
.cx
+ /* space for the tab */
1081 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1083 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1084 DT_SINGLELINE
|DT_CALCRECT
);
1085 txtwidth
= rc
.right
- rc
.left
;
1086 lpitem
->xTab
+= txtwidth
;
1088 lpitem
->rect
.right
+= 2 + txtwidth
;
1089 itemheight
= max( itemheight
,
1090 max( txtheight
+ 2, menucharsize
.cy
+ 4));
1092 if (hfontOld
) SelectObject (hdc
, hfontOld
);
1093 } else if( menuBar
) {
1094 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
1096 lpitem
->rect
.bottom
+= itemheight
;
1097 TRACE("%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1100 /***********************************************************************
1101 * MENU_PopupMenuCalcSize
1103 * Calculate the size of a popup menu.
1105 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
, UINT max_height
)
1110 BOOL textandbmp
= FALSE
, multi_col
= FALSE
;
1111 int orgX
, orgY
, maxTab
, maxTabWidth
;
1113 lppop
->Width
= lppop
->Height
= 0;
1114 SetRectEmpty(&lppop
->items_rect
);
1116 if (lppop
->nItems
== 0) return;
1119 SelectObject( hdc
, get_menu_font(FALSE
));
1123 lppop
->textOffset
= 0;
1125 while (start
< lppop
->nItems
)
1127 lpitem
= &lppop
->items
[start
];
1128 orgX
= lppop
->items_rect
.right
;
1129 if( lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1130 orgX
+= MENU_COL_SPACE
;
1131 orgY
= lppop
->items_rect
.top
;
1133 maxTab
= maxTabWidth
= 0;
1134 /* Parse items until column break or end of menu */
1135 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1137 if (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1140 if (i
!= start
) break;
1143 MENU_CalcItemSize( hdc
, lpitem
, lppop
->hwndOwner
, orgX
, orgY
, FALSE
, lppop
);
1144 lppop
->items_rect
.right
= max( lppop
->items_rect
.right
, lpitem
->rect
.right
);
1145 orgY
= lpitem
->rect
.bottom
;
1146 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1148 maxTab
= max( maxTab
, lpitem
->xTab
);
1149 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1151 if( lpitem
->text
&& lpitem
->hbmpItem
) textandbmp
= TRUE
;
1154 /* Finish the column (set all items to the largest width found) */
1155 lppop
->items_rect
.right
= max( lppop
->items_rect
.right
, maxTab
+ maxTabWidth
);
1156 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1158 lpitem
->rect
.right
= lppop
->items_rect
.right
;
1159 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1160 lpitem
->xTab
= maxTab
;
1163 lppop
->items_rect
.bottom
= max( lppop
->items_rect
.bottom
, orgY
);
1166 /* if none of the items have both text and bitmap then
1167 * the text and bitmaps are all aligned on the left. If there is at
1168 * least one item with both text and bitmap then bitmaps are
1169 * on the left and texts left aligned with the right hand side
1171 if( !textandbmp
) lppop
->textOffset
= 0;
1173 lppop
->nTotalHeight
= lppop
->items_rect
.bottom
;
1175 /* space for the border */
1176 OffsetRect(&lppop
->items_rect
, MENU_MARGIN
, MENU_MARGIN
);
1177 lppop
->Height
= lppop
->items_rect
.bottom
+ MENU_MARGIN
;
1178 lppop
->Width
= lppop
->items_rect
.right
+ MENU_MARGIN
;
1180 /* Adjust popup height if it exceeds maximum */
1181 if (lppop
->Height
>= max_height
)
1183 lppop
->Height
= max_height
;
1184 lppop
->bScrolling
= !multi_col
;
1185 /* When the scroll arrows are present, don't add the top/bottom margin as well */
1186 if (lppop
->bScrolling
)
1188 lppop
->items_rect
.top
= get_scroll_arrow_height(lppop
);
1189 lppop
->items_rect
.bottom
= lppop
->Height
- get_scroll_arrow_height(lppop
);
1194 lppop
->bScrolling
= FALSE
;
1197 NtUserReleaseDC( 0, hdc
);
1201 /***********************************************************************
1202 * MENU_MenuBarCalcSize
1204 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1205 * height is off by 1 pixel which causes lengthy window relocations when
1206 * active document window is maximized/restored.
1208 * Calculate the size of the menu bar.
1210 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1211 LPPOPUPMENU lppop
, HWND hwndOwner
)
1214 UINT start
, i
, helpPos
;
1217 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1218 if (lppop
->nItems
== 0) return;
1219 TRACE("lprect %p %s\n", lprect
, wine_dbgstr_rect( lprect
));
1220 /* Start with a 1 pixel top border.
1221 This corresponds to the difference between SM_CYMENU and SM_CYMENUSIZE. */
1222 SetRect(&lppop
->items_rect
, 0, 0, lprect
->right
- lprect
->left
, 1);
1225 lppop
->textOffset
= 0;
1226 while (start
< lppop
->nItems
)
1228 lpitem
= &lppop
->items
[start
];
1229 orgX
= lppop
->items_rect
.left
;
1230 orgY
= lppop
->items_rect
.bottom
;
1232 /* Parse items until line break or end of menu */
1233 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1235 if ((helpPos
== ~0U) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1237 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1239 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
1240 debug_print_menuitem (" item: ", lpitem
, "");
1241 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
, lppop
);
1243 if (lpitem
->rect
.right
> lppop
->items_rect
.right
)
1245 if (i
!= start
) break;
1246 else lpitem
->rect
.right
= lppop
->items_rect
.right
;
1248 lppop
->items_rect
.bottom
= max( lppop
->items_rect
.bottom
, lpitem
->rect
.bottom
);
1249 orgX
= lpitem
->rect
.right
;
1252 /* Finish the line (set all items to the largest height found) */
1253 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= lppop
->items_rect
.bottom
;
1256 OffsetRect(&lppop
->items_rect
, lprect
->left
, lprect
->top
);
1257 lppop
->Width
= lppop
->items_rect
.right
- lppop
->items_rect
.left
;
1258 lppop
->Height
= lppop
->items_rect
.bottom
- lppop
->items_rect
.top
;
1259 lprect
->bottom
= lppop
->items_rect
.bottom
;
1261 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1262 /* the last item (if several lines, only move the last line) */
1263 if (helpPos
== ~0U) return;
1264 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1265 orgY
= lpitem
->rect
.top
;
1266 orgX
= lprect
->right
- lprect
->left
;
1267 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1268 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1269 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1270 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1271 lpitem
->rect
.right
= orgX
;
1272 orgX
= lpitem
->rect
.left
;
1276 static void draw_scroll_arrow(HDC hdc
, int x
, int top
, int height
, BOOL up
, BOOL enabled
)
1278 RECT rect
, light_rect
;
1279 HBRUSH brush
= GetSysColorBrush( enabled
? COLOR_BTNTEXT
: COLOR_BTNSHADOW
);
1280 HBRUSH light
= GetSysColorBrush( COLOR_3DLIGHT
);
1287 SetRect( &rect
, x
+ 1, top
, x
+ 2, top
+ 1);
1288 FillRect( hdc
, &rect
, light
);
1293 SetRect( &rect
, x
, top
, x
+ 1, top
+ 1);
1296 FillRect( hdc
, &rect
, brush
);
1297 if (!enabled
&& !up
&& height
)
1299 SetRect( &light_rect
, rect
.right
, rect
.top
, rect
.right
+ 2, rect
.bottom
);
1300 FillRect( hdc
, &light_rect
, light
);
1302 InflateRect( &rect
, 1, 0 );
1303 OffsetRect( &rect
, 0, up
? 1 : -1 );
1309 FillRect( hdc
, &rect
, light
);
1313 /***********************************************************************
1314 * MENU_DrawScrollArrows
1316 * Draw scroll arrows.
1319 MENU_DrawScrollArrows(const POPUPMENU
*menu
, HDC hdc
)
1321 UINT full_height
= get_scroll_arrow_height( menu
);
1322 UINT arrow_height
= full_height
/ 3;
1323 BOOL at_end
= menu
->nScrollPos
+ menu
->items_rect
.bottom
- menu
->items_rect
.top
== menu
->nTotalHeight
;
1325 draw_scroll_arrow( hdc
, menu
->Width
/ 3, arrow_height
, arrow_height
,
1326 TRUE
, menu
->nScrollPos
!= 0);
1327 draw_scroll_arrow( hdc
, menu
->Width
/ 3, menu
->Height
- 2 * arrow_height
, arrow_height
,
1332 /***********************************************************************
1335 * Draws the popup-menu arrow.
1337 static void draw_popup_arrow( HDC hdc
, RECT rect
, UINT arrow_bitmap_width
,
1338 UINT arrow_bitmap_height
)
1340 HDC hdcMem
= CreateCompatibleDC( hdc
);
1341 HBITMAP hOrigBitmap
;
1343 hOrigBitmap
= SelectObject( hdcMem
, get_arrow_bitmap() );
1344 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1345 (rect
.top
+ rect
.bottom
- arrow_bitmap_height
) / 2,
1346 arrow_bitmap_width
, arrow_bitmap_height
,
1347 hdcMem
, 0, 0, SRCCOPY
);
1348 SelectObject( hdcMem
, hOrigBitmap
);
1351 /***********************************************************************
1354 * Draw a single menu item.
1356 static void MENU_DrawMenuItem( HWND hwnd
, POPUPMENU
*menu
, HWND hwndOwner
, HDC hdc
,
1357 MENUITEM
*lpitem
, BOOL menuBar
, UINT odaction
)
1360 BOOL flat_menu
= FALSE
;
1362 UINT arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
1363 HRGN old_clip
= NULL
, clip
;
1365 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1369 GetObjectW( get_arrow_bitmap(), sizeof(bmp
), &bmp
);
1370 arrow_bitmap_width
= bmp
.bmWidth
;
1371 arrow_bitmap_height
= bmp
.bmHeight
;
1374 if (lpitem
->fType
& MF_SYSMENU
)
1376 if( !IsIconic(hwnd
) )
1377 NC_DrawSysButton( hwnd
, hdc
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
) );
1381 TRACE( "rect=%s\n", wine_dbgstr_rect( &lpitem
->rect
) );
1382 rect
= lpitem
->rect
;
1383 MENU_AdjustMenuItemRect( menu
, &rect
);
1384 if (!IntersectRect( &bmprc
, &rect
, &menu
->items_rect
)) /* bmprc is used as a dummy */
1387 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1388 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
1392 if (lpitem
->fState
& MF_HILITE
)
1394 if(menuBar
&& !flat_menu
) {
1395 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1396 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1398 if(lpitem
->fState
& MF_GRAYED
)
1399 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1401 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1402 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1407 if (lpitem
->fState
& MF_GRAYED
)
1408 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1410 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1411 SetBkColor( hdc
, GetSysColor( bkgnd
) );
1414 old_clip
= CreateRectRgn( 0, 0, 0, 0 );
1415 if (GetClipRgn( hdc
, old_clip
) <= 0)
1417 DeleteObject( old_clip
);
1420 clip
= CreateRectRgnIndirect( &menu
->items_rect
);
1421 ExtSelectClipRgn( hdc
, clip
, RGN_AND
);
1422 DeleteObject( clip
);
1424 if (lpitem
->fType
& MF_OWNERDRAW
)
1427 ** Experimentation under Windows reveals that an owner-drawn
1428 ** menu is given the rectangle which includes the space it requested
1429 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1430 ** and a popup-menu arrow. This is the value of lpitem->rect.
1431 ** Windows will leave all drawing to the application except for
1432 ** the popup-menu arrow. Windows always draws that itself, after
1433 ** the menu owner has finished drawing.
1436 COLORREF old_bk
, old_text
;
1438 dis
.CtlType
= ODT_MENU
;
1440 dis
.itemID
= lpitem
->wID
;
1441 dis
.itemData
= lpitem
->dwItemData
;
1443 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1444 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1445 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1446 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1447 dis
.hwndItem
= (HWND
)menu
->obj
.handle
;
1450 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1451 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner
,
1452 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1453 dis
.hDC
, wine_dbgstr_rect( &dis
.rcItem
));
1454 old_bk
= GetBkColor( hdc
);
1455 old_text
= GetTextColor( hdc
);
1456 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1457 /* Draw the popup-menu arrow */
1458 SetBkColor( hdc
, old_bk
);
1459 SetTextColor( hdc
, old_text
);
1460 if (lpitem
->fType
& MF_POPUP
)
1461 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1462 arrow_bitmap_height
);
1466 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) goto done
;
1468 if (lpitem
->fState
& MF_HILITE
)
1472 InflateRect (&rect
, -1, -1);
1473 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
1474 InflateRect (&rect
, 1, 1);
1475 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1480 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1482 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1486 FillRect( hdc
, &rect
, GetSysColorBrush(bkgnd
) );
1488 SetBkMode( hdc
, TRANSPARENT
);
1490 /* vertical separator */
1491 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1496 rc
.left
-= MENU_COL_SPACE
/ 2 + 1;
1498 rc
.bottom
= menu
->Height
- 3;
1501 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1502 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1503 LineTo( hdc
, rc
.left
, rc
.bottom
);
1504 SelectObject( hdc
, oldPen
);
1507 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1510 /* horizontal separator */
1511 if (lpitem
->fType
& MF_SEPARATOR
)
1516 InflateRect( &rc
, -1, 0 );
1517 rc
.top
= ( rc
.top
+ rc
.bottom
) / 2;
1520 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1521 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1522 LineTo( hdc
, rc
.right
, rc
.top
);
1523 SelectObject( hdc
, oldPen
);
1526 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1530 /* helper lines for debugging */
1531 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1532 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1533 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1534 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1537 if (lpitem
->hbmpItem
) {
1538 /* calculate the bitmap rectangle in coordinates relative
1539 * to the item rectangle */
1541 if( lpitem
->hbmpItem
== HBMMENU_CALLBACK
)
1544 bmprc
.left
= lpitem
->text
? menucharsize
.cx
: 0;
1546 else if (menu
->dwStyle
& MNS_NOCHECK
)
1548 else if (menu
->dwStyle
& MNS_CHECKORBMP
)
1551 bmprc
.left
= 4 + GetSystemMetrics(SM_CXMENUCHECK
);
1552 bmprc
.right
= bmprc
.left
+ lpitem
->bmpsize
.cx
;
1553 if( menuBar
&& !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1556 bmprc
.top
= (rect
.bottom
- rect
.top
-
1557 lpitem
->bmpsize
.cy
) / 2;
1558 bmprc
.bottom
= bmprc
.top
+ lpitem
->bmpsize
.cy
;
1564 INT y
= rect
.top
+ rect
.bottom
;
1565 BOOL checked
= FALSE
;
1566 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1567 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1568 /* Draw the check mark
1571 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1573 if (!(menu
->dwStyle
& MNS_NOCHECK
))
1575 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
:
1576 lpitem
->hUnCheckBit
;
1577 if (bm
) /* we have a custom bitmap */
1579 HDC hdcMem
= CreateCompatibleDC( hdc
);
1581 SelectObject( hdcMem
, bm
);
1582 BitBlt( hdc
, rect
.left
, (y
- check_bitmap_height
) / 2,
1583 check_bitmap_width
, check_bitmap_height
,
1584 hdcMem
, 0, 0, SRCCOPY
);
1588 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1591 HBITMAP bm
= CreateBitmap( check_bitmap_width
,
1592 check_bitmap_height
, 1, 1, NULL
);
1593 HDC hdcMem
= CreateCompatibleDC( hdc
);
1595 SelectObject( hdcMem
, bm
);
1596 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1597 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1598 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1599 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1600 BitBlt( hdc
, rect
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1601 hdcMem
, 0, 0, SRCCOPY
);
1607 if (lpitem
->hbmpItem
&& !(checked
&& (menu
->dwStyle
& MNS_CHECKORBMP
)))
1610 /* some applications make this assumption on the DC's origin */
1611 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1612 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, menu
, hwndOwner
, odaction
);
1613 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1615 /* Draw the popup-menu arrow */
1616 if (lpitem
->fType
& MF_POPUP
)
1617 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1618 arrow_bitmap_height
);
1620 if( !(menu
->dwStyle
& MNS_NOCHECK
))
1621 rect
.left
+= check_bitmap_width
;
1622 rect
.right
-= arrow_bitmap_width
;
1624 else if (lpitem
->hbmpItem
)
1625 { /* Draw the bitmap */
1628 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1629 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, menu
, hwndOwner
, odaction
);
1630 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1632 /* process text if present */
1638 UINT uFormat
= (menuBar
) ?
1639 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1640 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1642 if( !(menu
->dwStyle
& MNS_CHECKORBMP
))
1643 rect
.left
+= menu
->textOffset
;
1645 if ( lpitem
->fState
& MFS_DEFAULT
)
1647 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1651 if( lpitem
->hbmpItem
)
1652 rect
.left
+= lpitem
->bmpsize
.cx
;
1653 if( !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1654 rect
.left
+= menucharsize
.cx
;
1655 rect
.right
-= menucharsize
.cx
;
1658 for (i
= 0; lpitem
->text
[i
]; i
++)
1659 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1662 if(lpitem
->fState
& MF_GRAYED
)
1664 if (!(lpitem
->fState
& MF_HILITE
) )
1666 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1667 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1668 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1669 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1671 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1674 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1676 /* paint the shortcut text */
1677 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1679 if (lpitem
->text
[i
] == '\t')
1681 rect
.left
= lpitem
->xTab
;
1682 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1686 rect
.right
= lpitem
->xTab
;
1687 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1690 if(lpitem
->fState
& MF_GRAYED
)
1692 if (!(lpitem
->fState
& MF_HILITE
) )
1694 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1695 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1696 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1697 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1699 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1701 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1705 SelectObject (hdc
, hfontOld
);
1709 ExtSelectClipRgn( hdc
, old_clip
, RGN_COPY
);
1710 if (old_clip
) DeleteObject( old_clip
);
1714 /***********************************************************************
1715 * MENU_DrawPopupMenu
1717 * Paint a popup menu.
1719 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1721 HBRUSH hPrevBrush
, brush
= GetSysColorBrush( COLOR_MENU
);
1723 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
1725 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1727 GetClientRect( hwnd
, &rect
);
1729 if (menu
&& menu
->hbrBack
) brush
= menu
->hbrBack
;
1730 if ((hPrevBrush
= SelectObject( hdc
, brush
))
1731 && SelectObject( hdc
, get_menu_font(FALSE
) ))
1735 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1737 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1740 BOOL flat_menu
= FALSE
;
1742 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1744 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1746 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1750 TRACE("hmenu %p Style %08x\n", hmenu
, menu
->dwStyle
);
1751 /* draw menu items */
1758 for (u
= menu
->nItems
; u
> 0; u
--, item
++)
1759 MENU_DrawMenuItem( hwnd
, menu
, menu
->hwndOwner
, hdc
,
1760 item
, FALSE
, ODA_DRAWENTIRE
);
1762 /* draw scroll arrows */
1763 if (menu
->bScrolling
)
1764 MENU_DrawScrollArrows(menu
, hdc
);
1768 SelectObject( hdc
, hPrevBrush
);
1773 /***********************************************************************
1776 * Paint a menu bar. Returns the height of the menu bar.
1777 * called from [windows/nonclient.c]
1779 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
)
1782 HMENU hMenu
= GetMenu(hwnd
);
1784 lppop
= MENU_GetMenu( hMenu
);
1785 if (lppop
== NULL
|| lprect
== NULL
)
1787 return GetSystemMetrics(SM_CYMENU
);
1790 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1794 /***********************************************************************
1797 * Popup menu initialization before WM_ENTERMENULOOP.
1799 static BOOL
MENU_InitPopup( HWND hwndOwner
, HMENU hmenu
, UINT flags
)
1804 TRACE("owner=%p hmenu=%p\n", hwndOwner
, hmenu
);
1806 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1808 /* store the owner for DrawItem */
1809 if (!IsWindow( hwndOwner
))
1811 SetLastError( ERROR_INVALID_WINDOW_HANDLE
);
1814 menu
->hwndOwner
= hwndOwner
;
1816 if (flags
& TPM_LAYOUTRTL
)
1817 ex_style
= WS_EX_LAYOUTRTL
;
1819 /* NOTE: In Windows, top menu popup is not owned. */
1820 menu
->hWnd
= CreateWindowExW( ex_style
, (LPCWSTR
)POPUPMENU_CLASS_ATOM
, NULL
,
1821 WS_POPUP
, 0, 0, 0, 0,
1822 hwndOwner
, 0, (HINSTANCE
)GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1824 if( !menu
->hWnd
) return FALSE
;
1829 /***********************************************************************
1832 * Display a popup menu.
1834 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
, UINT flags
,
1835 INT x
, INT y
, INT xanchor
, INT yanchor
)
1843 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1844 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1846 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1847 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1849 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1850 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1853 menu
->nScrollPos
= 0;
1855 /* FIXME: should use item rect */
1858 monitor
= MonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
1859 info
.cbSize
= sizeof(info
);
1860 GetMonitorInfoW( monitor
, &info
);
1862 max_height
= info
.rcWork
.bottom
- info
.rcWork
.top
;
1864 max_height
= min( max_height
, menu
->cyMax
);
1866 MENU_PopupMenuCalcSize( menu
, max_height
);
1868 /* adjust popup menu pos so that it fits within the desktop */
1870 if (flags
& TPM_LAYOUTRTL
)
1871 flags
^= TPM_RIGHTALIGN
;
1873 if( flags
& TPM_RIGHTALIGN
) x
-= menu
->Width
;
1874 if( flags
& TPM_CENTERALIGN
) x
-= menu
->Width
/ 2;
1876 if( flags
& TPM_BOTTOMALIGN
) y
-= menu
->Height
;
1877 if( flags
& TPM_VCENTERALIGN
) y
-= menu
->Height
/ 2;
1879 if( x
+ menu
->Width
> info
.rcWork
.right
)
1881 if( xanchor
&& x
>= menu
->Width
- xanchor
)
1882 x
-= menu
->Width
- xanchor
;
1884 if( x
+ menu
->Width
> info
.rcWork
.right
)
1885 x
= info
.rcWork
.right
- menu
->Width
;
1887 if( x
< info
.rcWork
.left
) x
= info
.rcWork
.left
;
1889 if( y
+ menu
->Height
> info
.rcWork
.bottom
)
1891 if( yanchor
&& y
>= menu
->Height
+ yanchor
)
1892 y
-= menu
->Height
+ yanchor
;
1894 if( y
+ menu
->Height
> info
.rcWork
.bottom
)
1895 y
= info
.rcWork
.bottom
- menu
->Height
;
1897 if( y
< info
.rcWork
.top
) y
= info
.rcWork
.top
;
1900 top_popup
= menu
->hWnd
;
1901 top_popup_hmenu
= hmenu
;
1903 /* Display the window */
1905 NtUserSetWindowPos( menu
->hWnd
, HWND_TOPMOST
, x
, y
, menu
->Width
, menu
->Height
,
1906 SWP_SHOWWINDOW
| SWP_NOACTIVATE
);
1907 UpdateWindow( menu
->hWnd
);
1912 /***********************************************************************
1913 * MENU_EnsureMenuItemVisible
1916 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop
, UINT wIndex
, HDC hdc
)
1918 if (lppop
->bScrolling
)
1920 MENUITEM
*item
= &lppop
->items
[wIndex
];
1921 UINT nOldPos
= lppop
->nScrollPos
;
1922 const RECT
*rc
= &lppop
->items_rect
;
1923 UINT scroll_height
= rc
->bottom
- rc
->top
;
1925 if (item
->rect
.bottom
> lppop
->nScrollPos
+ scroll_height
)
1927 lppop
->nScrollPos
= item
->rect
.bottom
- scroll_height
;
1928 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, rc
, rc
);
1930 else if (item
->rect
.top
< lppop
->nScrollPos
)
1932 lppop
->nScrollPos
= item
->rect
.top
;
1933 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, rc
, rc
);
1936 /* Invalidate the scroll arrows if necessary */
1937 if (nOldPos
!= lppop
->nScrollPos
)
1939 RECT arrow_rect
= lppop
->items_rect
;
1940 if (nOldPos
== 0 || lppop
->nScrollPos
== 0)
1943 arrow_rect
.bottom
= lppop
->items_rect
.top
;
1944 InvalidateRect(lppop
->hWnd
, &arrow_rect
, FALSE
);
1946 if (nOldPos
+ scroll_height
== lppop
->nTotalHeight
||
1947 lppop
->nScrollPos
+ scroll_height
== lppop
->nTotalHeight
)
1949 arrow_rect
.top
= lppop
->items_rect
.bottom
;
1950 arrow_rect
.bottom
= lppop
->Height
;
1951 InvalidateRect(lppop
->hWnd
, &arrow_rect
, FALSE
);
1958 /***********************************************************************
1961 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1962 BOOL sendMenuSelect
, HMENU topmenu
)
1967 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1969 lppop
= MENU_GetMenu( hmenu
);
1970 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1972 if (lppop
->FocusedItem
== wIndex
) return;
1973 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1974 else hdc
= NtUserGetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1976 top_popup
= lppop
->hWnd
;
1977 top_popup_hmenu
= hmenu
;
1980 SelectObject( hdc
, get_menu_font(FALSE
));
1982 /* Clear previous highlighted item */
1983 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1985 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1986 MENU_DrawMenuItem( lppop
->hWnd
, lppop
, hwndOwner
, hdc
, &lppop
->items
[lppop
->FocusedItem
],
1987 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
1990 /* Highlight new item (if any) */
1991 lppop
->FocusedItem
= wIndex
;
1992 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1994 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
1995 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
1996 MENU_EnsureMenuItemVisible(lppop
, wIndex
, hdc
);
1997 MENU_DrawMenuItem( lppop
->hWnd
, lppop
, hwndOwner
, hdc
, &lppop
->items
[wIndex
],
1998 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
2002 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
2003 SendMessageW( hwndOwner
, WM_MENUSELECT
,
2004 MAKEWPARAM(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
2005 ip
->fType
| ip
->fState
|
2006 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
2009 else if (sendMenuSelect
) {
2012 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
2013 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
2014 MENUITEM
*ip
= &ptm
->items
[pos
];
2015 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKEWPARAM(pos
,
2016 ip
->fType
| ip
->fState
|
2017 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
2021 NtUserReleaseDC( lppop
->hWnd
, hdc
);
2025 /***********************************************************************
2026 * MENU_MoveSelection
2028 * Moves currently selected item according to the offset parameter.
2029 * If there is no selection then it should select the last item if
2030 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2032 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
2037 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
2039 menu
= MENU_GetMenu( hmenu
);
2040 if ((!menu
) || (!menu
->items
)) return;
2042 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2044 if( menu
->nItems
== 1 ) return; else
2045 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
2047 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2049 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2054 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
2055 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
2056 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2058 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2064 /**********************************************************************
2065 * MENU_ParseResource
2067 * Parse a standard menu resource and add items to the menu.
2068 * Return a pointer to the end of the resource.
2070 * NOTE: flags is equivalent to the mtOption field
2072 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
)
2080 flags
= GET_WORD(res
);
2081 end_flag
= flags
& MF_END
;
2082 /* Remove MF_END because it has the same value as MF_HILITE */
2084 res
+= sizeof(WORD
);
2085 if (!(flags
& MF_POPUP
))
2088 res
+= sizeof(WORD
);
2091 res
+= (lstrlenW(str
) + 1) * sizeof(WCHAR
);
2092 if (flags
& MF_POPUP
)
2094 HMENU hSubMenu
= CreatePopupMenu();
2095 if (!hSubMenu
) return NULL
;
2096 if (!(res
= MENU_ParseResource( res
, hSubMenu
))) return NULL
;
2097 AppendMenuW( hMenu
, flags
, (UINT_PTR
)hSubMenu
, str
);
2099 else /* Not a popup */
2101 AppendMenuW( hMenu
, flags
, id
, *str
? str
: NULL
);
2103 } while (!end_flag
);
2108 /**********************************************************************
2109 * MENUEX_ParseResource
2111 * Parse an extended menu resource and add items to the menu.
2112 * Return a pointer to the end of the resource.
2114 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2120 mii
.cbSize
= sizeof(mii
);
2121 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
2122 mii
.fType
= GET_DWORD(res
);
2123 res
+= sizeof(DWORD
);
2124 mii
.fState
= GET_DWORD(res
);
2125 res
+= sizeof(DWORD
);
2126 mii
.wID
= GET_DWORD(res
);
2127 res
+= sizeof(DWORD
);
2128 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
2129 res
+= sizeof(WORD
);
2130 /* Align the text on a word boundary. */
2131 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2132 mii
.dwTypeData
= (LPWSTR
) res
;
2133 res
+= (1 + lstrlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2134 /* Align the following fields on a dword boundary. */
2135 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2137 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2138 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
2140 if (resinfo
& 1) { /* Pop-up? */
2141 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2142 res
+= sizeof(DWORD
);
2143 mii
.hSubMenu
= CreatePopupMenu();
2146 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2147 NtUserDestroyMenu( mii
.hSubMenu
);
2150 mii
.fMask
|= MIIM_SUBMENU
;
2151 mii
.fType
|= MF_POPUP
;
2153 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2155 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2156 mii
.wID
, mii
.fType
);
2157 mii
.fType
|= MF_SEPARATOR
;
2159 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2160 } while (!(resinfo
& MF_END
));
2165 /***********************************************************************
2168 * Return the handle of the selected sub-popup menu (if any).
2170 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2175 menu
= MENU_GetMenu( hmenu
);
2177 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2179 item
= &menu
->items
[menu
->FocusedItem
];
2180 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2181 return item
->hSubMenu
;
2186 /***********************************************************************
2187 * MENU_HideSubPopups
2189 * Hide the sub-popup menus of this menu.
2191 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2192 BOOL sendMenuSelect
, UINT wFlags
)
2194 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2196 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2198 if (menu
&& top_popup
)
2204 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2206 item
= &menu
->items
[menu
->FocusedItem
];
2207 if (!(item
->fType
& MF_POPUP
) ||
2208 !(item
->fState
& MF_MOUSESELECT
)) return;
2209 item
->fState
&= ~MF_MOUSESELECT
;
2210 hsubmenu
= item
->hSubMenu
;
2213 if (!(submenu
= MENU_GetMenu( hsubmenu
))) return;
2214 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
, wFlags
);
2215 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2216 NtUserDestroyWindow( submenu
->hWnd
);
2219 if (!(wFlags
& TPM_NONOTIFY
))
2220 SendMessageW( hwndOwner
, WM_UNINITMENUPOPUP
, (WPARAM
)hsubmenu
,
2221 MAKELPARAM(0, IS_SYSTEM_MENU(submenu
)) );
2226 /***********************************************************************
2229 * Display the sub-menu of the selected item of this menu.
2230 * Return the handle of the submenu, or hmenu if no submenu to display.
2232 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2233 BOOL selectFirst
, UINT wFlags
)
2240 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2242 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2244 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2246 item
= &menu
->items
[menu
->FocusedItem
];
2247 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2250 /* message must be sent before using item,
2251 because nearly everything may be changed by the application ! */
2253 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2254 if (!(wFlags
& TPM_NONOTIFY
))
2255 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2256 MAKELPARAM( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2258 item
= &menu
->items
[menu
->FocusedItem
];
2261 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2262 if (!(item
->fState
& MF_HILITE
))
2264 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2265 else hdc
= NtUserGetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2267 SelectObject( hdc
, get_menu_font(FALSE
));
2269 item
->fState
|= MF_HILITE
;
2270 MENU_DrawMenuItem( menu
->hWnd
, menu
, hwndOwner
, hdc
, item
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2271 NtUserReleaseDC( menu
->hWnd
, hdc
);
2273 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2276 item
->fState
|= MF_MOUSESELECT
;
2278 if (IS_SYSTEM_MENU(menu
))
2280 MENU_InitSysMenuPopup(item
->hSubMenu
,
2281 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2282 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2284 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2285 if (wFlags
& TPM_LAYOUTRTL
) rect
.left
= rect
.right
;
2286 rect
.top
= rect
.bottom
;
2287 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2288 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2292 RECT item_rect
= item
->rect
;
2294 MENU_AdjustMenuItemRect(menu
, &item_rect
);
2295 GetWindowRect( menu
->hWnd
, &rect
);
2297 if (menu
->wFlags
& MF_POPUP
)
2299 /* The first item in the popup menu has to be at the
2300 same y position as the focused menu item */
2301 if (wFlags
& TPM_LAYOUTRTL
)
2302 rect
.left
+= GetSystemMetrics(SM_CXBORDER
);
2304 rect
.left
+= item_rect
.right
- GetSystemMetrics(SM_CXBORDER
);
2305 rect
.top
+= item_rect
.top
- MENU_MARGIN
;
2306 rect
.right
= item_rect
.left
- item_rect
.right
+ GetSystemMetrics(SM_CXBORDER
);
2307 rect
.bottom
= item_rect
.top
- item_rect
.bottom
- 2 * MENU_MARGIN
;
2311 if (wFlags
& TPM_LAYOUTRTL
)
2312 rect
.left
= rect
.right
- item_rect
.left
;
2314 rect
.left
+= item_rect
.left
;
2315 rect
.top
+= item_rect
.bottom
;
2316 rect
.right
= item_rect
.right
- item_rect
.left
;
2317 rect
.bottom
= item_rect
.bottom
- item_rect
.top
;
2321 /* use default alignment for submenus */
2322 wFlags
&= ~(TPM_CENTERALIGN
| TPM_RIGHTALIGN
| TPM_VCENTERALIGN
| TPM_BOTTOMALIGN
);
2324 MENU_InitPopup( hwndOwner
, item
->hSubMenu
, wFlags
);
2326 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
, wFlags
,
2327 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2329 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2330 return item
->hSubMenu
;
2335 /**********************************************************************
2338 HWND
MENU_IsMenuActive(void)
2343 /**********************************************************************
2346 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2348 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2350 void MENU_EndMenu( HWND hwnd
)
2353 menu
= top_popup_hmenu
? MENU_GetMenu( top_popup_hmenu
) : NULL
;
2354 if (menu
&& (hwnd
== menu
->hWnd
|| hwnd
== menu
->hwndOwner
)) EndMenu();
2357 /***********************************************************************
2360 * Walks menu chain trying to find a menu pt maps to.
2362 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2364 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2365 UINT item
= menu
->FocusedItem
;
2368 /* try subpopup first (if any) */
2369 ret
= (item
!= NO_SELECTED_ITEM
&&
2370 (menu
->items
[item
].fType
& MF_POPUP
) &&
2371 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2372 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2374 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2376 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2377 if( menu
->wFlags
& MF_POPUP
)
2379 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2381 else if (ht
== HTSYSMENU
)
2382 ret
= get_win_sys_menu( menu
->hWnd
);
2383 else if (ht
== HTMENU
)
2384 ret
= GetMenu( menu
->hWnd
);
2389 /***********************************************************************
2390 * MENU_ExecFocusedItem
2392 * Execute a menu item (for instance when user pressed Enter).
2393 * Return the wID of the executed item. Otherwise, -1 indicating
2394 * that no menu item was executed, -2 if a popup is shown;
2395 * Have to receive the flags for the TrackPopupMenu options to avoid
2396 * sending unwanted message.
2399 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2402 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2404 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2406 if (!menu
|| !menu
->nItems
||
2407 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2409 item
= &menu
->items
[menu
->FocusedItem
];
2411 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu
, item
->wID
, item
->hSubMenu
, item
->fType
);
2413 if (!(item
->fType
& MF_POPUP
))
2415 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2417 /* If TPM_RETURNCMD is set you return the id, but
2418 do not send a message to the owner */
2419 if(!(wFlags
& TPM_RETURNCMD
))
2421 if( menu
->wFlags
& MF_SYSMENU
)
2422 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2423 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2426 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2427 DWORD dwStyle
= menu
->dwStyle
| (topmenu
? topmenu
->dwStyle
: 0);
2429 if (dwStyle
& MNS_NOTIFYBYPOS
)
2430 PostMessageW( pmt
->hOwnerWnd
, WM_MENUCOMMAND
, menu
->FocusedItem
,
2433 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2441 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2448 /***********************************************************************
2449 * MENU_SwitchTracking
2451 * Helper function for menu navigation routines.
2453 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
, UINT wFlags
)
2455 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2456 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2458 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2460 if( pmt
->hTopMenu
!= hPtMenu
&&
2461 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2463 /* both are top level menus (system and menu-bar) */
2464 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2465 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2466 pmt
->hTopMenu
= hPtMenu
;
2468 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2469 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2473 /***********************************************************************
2476 * Return TRUE if we can go on with menu tracking.
2478 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, UINT message
, HMENU hPtMenu
, UINT wFlags
)
2480 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2485 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2486 enum hittest ht
= ht_item
;
2488 if( IS_SYSTEM_MENU(ptmenu
) )
2490 if (message
== WM_LBUTTONDBLCLK
) return FALSE
;
2494 ht
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &pos
);
2496 if (pos
!= NO_SELECTED_ITEM
)
2498 if (ptmenu
->FocusedItem
!= pos
)
2499 MENU_SwitchTracking( pmt
, hPtMenu
, pos
, wFlags
);
2501 /* If the popup menu is not already "popped" */
2502 if (!(ptmenu
->items
[pos
].fState
& MF_MOUSESELECT
))
2503 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2506 /* A click on an item or anywhere on a popup keeps tracking going */
2507 if (ht
== ht_item
|| ((ptmenu
->wFlags
& MF_POPUP
) && ht
!= ht_nowhere
))
2513 /***********************************************************************
2516 * Return the value of MENU_ExecFocusedItem if
2517 * the selected item was not a popup. Else open the popup.
2518 * A -1 return value indicates that we go on with menu tracking.
2521 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2523 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2528 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2530 if( IS_SYSTEM_MENU(ptmenu
) )
2532 else if (MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &pos
) != ht_item
)
2533 pos
= NO_SELECTED_ITEM
;
2535 if (pos
!= NO_SELECTED_ITEM
&& (ptmenu
->FocusedItem
== pos
))
2537 debug_print_menuitem ("FocusedItem: ", &ptmenu
->items
[pos
], "");
2539 if (!(ptmenu
->items
[pos
].fType
& MF_POPUP
))
2541 INT executedMenuId
= MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2542 if (executedMenuId
== -1 || executedMenuId
== -2) return -1;
2543 return executedMenuId
;
2546 /* If we are dealing with the menu bar */
2547 /* and this is a click on an already "popped" item: */
2548 /* Stop the menu tracking and close the opened submenus */
2549 if(((pmt
->hTopMenu
== hPtMenu
) || IS_SYSTEM_MENU(ptmenu
)) && (pmt
->trackFlags
& TF_RCVD_BTN_UP
))
2552 if( GetMenu(ptmenu
->hWnd
) == hPtMenu
|| IS_SYSTEM_MENU(ptmenu
) )
2554 if (pos
== NO_SELECTED_ITEM
) return 0;
2555 pmt
->trackFlags
|= TF_RCVD_BTN_UP
;
2562 /***********************************************************************
2565 * Return TRUE if we can go on with menu tracking.
2567 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2569 UINT id
= NO_SELECTED_ITEM
;
2570 POPUPMENU
*ptmenu
= NULL
;
2574 ptmenu
= MENU_GetMenu( hPtMenu
);
2575 if( IS_SYSTEM_MENU(ptmenu
) )
2577 else if (MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
) != ht_item
)
2578 id
= NO_SELECTED_ITEM
;
2581 if( id
== NO_SELECTED_ITEM
)
2583 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2584 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2587 else if( ptmenu
->FocusedItem
!= id
)
2589 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2590 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2596 /***********************************************************************
2599 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2601 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
, UINT wFlags
)
2603 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2606 /* When skipping left, we need to do something special after the
2608 if (vk
== VK_LEFT
&& menu
->FocusedItem
== 0)
2612 /* When skipping right, for the non-system menu, we need to
2613 handle the last non-special menu item (ie skip any window
2614 icons such as MDI maximize, restore or close) */
2615 else if ((vk
== VK_RIGHT
) && !IS_SYSTEM_MENU(menu
))
2617 UINT i
= menu
->FocusedItem
+ 1;
2618 while (i
< menu
->nItems
) {
2619 if ((menu
->items
[i
].wID
>= SC_SIZE
&&
2620 menu
->items
[i
].wID
<= SC_RESTORE
)) {
2624 if (i
== menu
->nItems
) {
2628 /* When skipping right, we need to cater for the system menu */
2629 else if ((vk
== VK_RIGHT
) && IS_SYSTEM_MENU(menu
))
2631 if (menu
->FocusedItem
== (menu
->nItems
- 1)) {
2638 MDINEXTMENU next_menu
;
2643 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2644 next_menu
.hmenuNext
= 0;
2645 next_menu
.hwndNext
= 0;
2646 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2648 TRACE("%p [%p] -> %p [%p]\n",
2649 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2651 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2653 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2654 hNewWnd
= pmt
->hOwnerWnd
;
2655 if( IS_SYSTEM_MENU(menu
) )
2657 /* switch to the menu bar */
2659 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2663 menu
= MENU_GetMenu( hNewMenu
);
2664 id
= menu
->nItems
- 1;
2666 /* Skip backwards over any system predefined icons,
2667 eg. MDI close, restore etc icons */
2669 (menu
->items
[id
].wID
>= SC_SIZE
&&
2670 menu
->items
[id
].wID
<= SC_RESTORE
)) id
--;
2673 else if (style
& WS_SYSMENU
)
2675 /* switch to the system menu */
2676 hNewMenu
= get_win_sys_menu( hNewWnd
);
2680 else /* application returned a new menu to switch to */
2682 hNewMenu
= next_menu
.hmenuNext
;
2683 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2685 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2687 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2689 if (style
& WS_SYSMENU
&&
2690 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2692 /* get the real system menu */
2693 hNewMenu
= get_win_sys_menu(hNewWnd
);
2695 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2697 /* FIXME: Not sure what to do here;
2698 * perhaps try to track hNewMenu as a popup? */
2700 TRACE(" -- got confused.\n");
2707 if( hNewMenu
!= pmt
->hTopMenu
)
2709 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2711 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2712 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2715 if( hNewWnd
!= pmt
->hOwnerWnd
)
2717 pmt
->hOwnerWnd
= hNewWnd
;
2718 set_capture_window( pmt
->hOwnerWnd
, GUI_INMENUMODE
, NULL
);
2721 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2722 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2729 /***********************************************************************
2732 * The idea is not to show the popup if the next input message is
2733 * going to hide it anyway.
2735 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT uMsg
)
2739 msg
.hwnd
= pmt
->hOwnerWnd
;
2741 PeekMessageW( &msg
, 0, uMsg
, uMsg
, PM_NOYIELD
| PM_REMOVE
);
2742 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2747 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2748 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2750 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2751 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2752 if( msg
.message
== WM_KEYDOWN
&&
2753 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2755 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2762 /* failures go through this */
2763 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2767 /***********************************************************************
2770 * Handle a VK_ESCAPE key event in a menu.
2772 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2774 BOOL bEndMenu
= TRUE
;
2776 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2778 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2780 if (menu
->wFlags
& MF_POPUP
)
2782 HMENU hmenutmp
, hmenuprev
;
2784 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2786 /* close topmost popup */
2787 while (hmenutmp
!= pmt
->hCurrentMenu
)
2789 hmenuprev
= hmenutmp
;
2790 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2793 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2794 pmt
->hCurrentMenu
= hmenuprev
;
2802 /***********************************************************************
2805 * Handle a VK_LEFT key event in a menu.
2807 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
, UINT msg
)
2810 HMENU hmenutmp
, hmenuprev
;
2813 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2814 menu
= MENU_GetMenu( hmenutmp
);
2816 /* Try to move 1 column left (if possible) */
2817 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2818 NO_SELECTED_ITEM
) {
2820 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2825 /* close topmost popup */
2826 while (hmenutmp
!= pmt
->hCurrentMenu
)
2828 hmenuprev
= hmenutmp
;
2829 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2832 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2833 pmt
->hCurrentMenu
= hmenuprev
;
2835 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2837 /* move menu bar selection if no more popups are left */
2839 if( !MENU_DoNextMenu( pmt
, VK_LEFT
, wFlags
) )
2840 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2842 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2844 /* A sublevel menu was displayed - display the next one
2845 * unless there is another displacement coming up */
2847 if( !MENU_SuspendPopup( pmt
, msg
) )
2848 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2849 pmt
->hTopMenu
, TRUE
, wFlags
);
2855 /***********************************************************************
2858 * Handle a VK_RIGHT key event in a menu.
2860 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
, UINT msg
)
2863 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2866 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2868 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2869 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2871 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2873 /* If already displaying a popup, try to display sub-popup */
2875 hmenutmp
= pmt
->hCurrentMenu
;
2876 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2878 /* if subpopup was displayed then we are done */
2879 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2882 /* Check to see if there's another column */
2883 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2884 NO_SELECTED_ITEM
) {
2885 TRACE("Going to %d.\n", nextcol
);
2886 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2891 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2893 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2895 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2896 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
2897 } else hmenutmp
= 0;
2899 /* try to move to the next item */
2900 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
, wFlags
) )
2901 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
2903 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2904 if( !MENU_SuspendPopup( pmt
, msg
) )
2905 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2906 pmt
->hTopMenu
, TRUE
, wFlags
);
2910 static void CALLBACK
release_capture( BOOL __normal
)
2912 set_capture_window( 0, GUI_INMENUMODE
, NULL
);
2915 /***********************************************************************
2918 * Menu tracking code.
2920 static BOOL
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
2921 HWND hwnd
, const RECT
*lprect
)
2926 INT executedMenuId
= -1;
2928 BOOL enterIdleSent
= FALSE
;
2932 mt
.hCurrentMenu
= hmenu
;
2933 mt
.hTopMenu
= hmenu
;
2934 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
2938 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2939 hmenu
, wFlags
, x
, y
, hwnd
, wine_dbgstr_rect( lprect
));
2941 if (!(menu
= MENU_GetMenu( hmenu
)))
2943 WARN("Invalid menu handle %p\n", hmenu
);
2944 SetLastError(ERROR_INVALID_MENU_HANDLE
);
2948 if (wFlags
& TPM_BUTTONDOWN
)
2950 /* Get the result in order to start the tracking or not */
2951 fRemove
= MENU_ButtonDown( &mt
, WM_LBUTTONDOWN
, hmenu
, wFlags
);
2952 fEndMenu
= !fRemove
;
2955 if (wFlags
& TF_ENDMENU
) fEndMenu
= TRUE
;
2957 /* owner may not be visible when tracking a popup, so use the menu itself */
2958 capture_win
= (wFlags
& TPM_POPUPMENU
) ? menu
->hWnd
: mt
.hOwnerWnd
;
2959 set_capture_window( capture_win
, GUI_INMENUMODE
, NULL
);
2961 if ((wFlags
& TPM_POPUPMENU
) && menu
->nItems
== 0)
2964 __TRY
while (!fEndMenu
)
2966 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
2967 if (!menu
) /* sometimes happens if I do a window manager close */
2970 /* we have to keep the message in the queue until it's
2971 * clear that menu loop is not over yet. */
2975 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
2977 if (!NtUserCallMsgFilter( &msg
, MSGF_MENU
)) break;
2978 /* remove the message from the queue */
2979 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
2985 HWND win
= menu
->wFlags
& MF_POPUP
? menu
->hWnd
: 0;
2986 enterIdleSent
= TRUE
;
2987 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
2993 /* check if EndMenu() tried to cancel us, by posting this message */
2994 if(msg
.message
== WM_CANCELMODE
)
2996 /* we are now out of the loop */
2999 /* remove the message from the queue */
3000 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3002 /* break out of internal loop, ala ESCAPE */
3008 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
3009 enterIdleSent
=FALSE
;
3012 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
3015 * Use the mouse coordinates in lParam instead of those in the MSG
3016 * struct to properly handle synthetic messages. They are already
3017 * in screen coordinates.
3019 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
3020 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
3022 /* Find a menu for this mouse event */
3023 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
3027 /* no WM_NC... messages in captured state */
3029 case WM_RBUTTONDBLCLK
:
3030 case WM_RBUTTONDOWN
:
3031 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3033 case WM_LBUTTONDBLCLK
:
3034 case WM_LBUTTONDOWN
:
3035 /* If the message belongs to the menu, removes it from the queue */
3036 /* Else, end menu tracking */
3037 fRemove
= MENU_ButtonDown( &mt
, msg
.message
, hmenu
, wFlags
);
3038 fEndMenu
= !fRemove
;
3042 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3045 /* Check if a menu was selected by the mouse */
3048 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
3049 TRACE("executedMenuId %d\n", executedMenuId
);
3051 /* End the loop if executedMenuId is an item ID */
3052 /* or if the job was done (executedMenuId = 0). */
3053 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3055 /* No menu was selected by the mouse */
3056 /* if the function was called by TrackPopupMenu, continue
3057 with the menu tracking. If not, stop it */
3059 fEndMenu
= !(wFlags
& TPM_POPUPMENU
);
3064 /* the selected menu item must be changed every time */
3065 /* the mouse moves. */
3068 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
3070 } /* switch(msg.message) - mouse */
3072 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3074 fRemove
= TRUE
; /* Keyboard messages are always removed */
3088 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3089 NO_SELECTED_ITEM
, FALSE
, 0 );
3090 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3091 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
3095 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3097 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3098 if (!(menu
->wFlags
& MF_POPUP
))
3099 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
3100 else /* otherwise try to move selection */
3101 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3102 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3106 MENU_KeyLeft( &mt
, wFlags
, msg
.message
);
3110 MENU_KeyRight( &mt
, wFlags
, msg
.message
);
3114 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
3120 hi
.cbSize
= sizeof(HELPINFO
);
3121 hi
.iContextType
= HELPINFO_MENUITEM
;
3122 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
3125 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
3126 hi
.hItemHandle
= hmenu
;
3127 hi
.dwContextId
= menu
->dwContextHelpID
;
3128 hi
.MousePos
= msg
.pt
;
3129 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3134 TranslateMessage( &msg
);
3137 break; /* WM_KEYDOWN */
3144 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
3146 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3147 fEndMenu
= (executedMenuId
!= -2);
3152 /* Hack to avoid control chars. */
3153 /* We will find a better way real soon... */
3154 if (msg
.wParam
< 32) break;
3156 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3157 LOWORD(msg
.wParam
), FALSE
);
3158 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3159 else if (pos
== (UINT
)-1) MessageBeep(0);
3162 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
3164 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3165 fEndMenu
= (executedMenuId
!= -2);
3169 } /* switch(msg.message) - kbd */
3173 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3174 DispatchMessageW( &msg
);
3178 if (!fEndMenu
) fRemove
= TRUE
;
3180 /* finally remove message from the queue */
3182 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
3183 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3184 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
3186 __FINALLY( release_capture
)
3188 /* If dropdown is still painted and the close box is clicked on
3189 then the menu will be destroyed as part of the DispatchMessage above.
3190 This will then invalidate the menu handle in mt.hTopMenu. We should
3191 check for this first. */
3192 if( IsMenu( mt
.hTopMenu
) )
3194 menu
= MENU_GetMenu( mt
.hTopMenu
);
3196 if( IsWindow( mt
.hOwnerWnd
) )
3198 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
, wFlags
);
3200 if (menu
&& (menu
->wFlags
& MF_POPUP
))
3202 NtUserDestroyWindow( menu
->hWnd
);
3205 if (!(wFlags
& TPM_NONOTIFY
))
3206 SendMessageW( mt
.hOwnerWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)mt
.hTopMenu
,
3207 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3209 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
3210 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKEWPARAM(0,0xffff), 0 );
3214 SetLastError( ERROR_SUCCESS
);
3215 /* The return value is only used by TrackPopupMenu */
3216 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3217 if (executedMenuId
== -1) executedMenuId
= 0;
3218 return executedMenuId
;
3221 /***********************************************************************
3224 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3228 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3230 NtUserHideCaret( 0 );
3232 if (!(menu
= MENU_GetMenu( hMenu
))) return FALSE
;
3234 /* This makes the menus of applications built with Delphi work.
3235 * It also enables menus to be displayed in more than one window,
3236 * but there are some bugs left that need to be fixed in this case.
3238 if (!bPopup
) menu
->hWnd
= hWnd
;
3241 top_popup
= menu
->hWnd
;
3242 top_popup_hmenu
= hMenu
;
3247 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3248 if (!(wFlags
& TPM_NONOTIFY
))
3249 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3251 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3253 if (!(wFlags
& TPM_NONOTIFY
))
3255 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3256 /* If an app changed/recreated menu bar entries in WM_INITMENU
3257 * menu sizes will be recalculated once the menu created/shown.
3264 /***********************************************************************
3267 static BOOL
MENU_ExitTracking(HWND hWnd
, BOOL bPopup
)
3269 TRACE("hwnd=%p\n", hWnd
);
3271 SendMessageW( hWnd
, WM_EXITMENULOOP
, bPopup
, 0 );
3272 NtUserShowCaret( 0 );
3274 top_popup_hmenu
= NULL
;
3278 /***********************************************************************
3279 * MENU_TrackMouseMenuBar
3281 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3283 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3285 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3286 UINT wFlags
= TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3288 TRACE("wnd=%p ht=0x%04x %s\n", hWnd
, ht
, wine_dbgstr_point( &pt
));
3290 if (GetWindowLongW( hWnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) wFlags
|= TPM_LAYOUTRTL
;
3293 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3295 /* fetch the window menu again, it may have changed */
3296 hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3297 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3298 MENU_ExitTracking(hWnd
, FALSE
);
3303 /***********************************************************************
3304 * MENU_TrackKbdMenuBar
3306 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3308 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3310 UINT uItem
= NO_SELECTED_ITEM
;
3312 UINT wFlags
= TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3314 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3316 /* find window that has a menu */
3318 while (is_win_menu_disallowed(hwnd
))
3319 if (!(hwnd
= NtUserGetAncestor( hwnd
, GA_PARENT
))) return;
3321 /* check if we have to track a system menu */
3323 hTrackMenu
= GetMenu( hwnd
);
3324 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3326 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3327 hTrackMenu
= get_win_sys_menu( hwnd
);
3329 wParam
|= HTSYSMENU
; /* prevent item lookup */
3331 if (GetWindowLongW( hwnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) wFlags
|= TPM_LAYOUTRTL
;
3333 if (!IsMenu( hTrackMenu
)) return;
3335 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3337 /* fetch the window menu again, it may have changed */
3338 hTrackMenu
= (wParam
& HTSYSMENU
) ? get_win_sys_menu( hwnd
) : GetMenu( hwnd
);
3340 if( wChar
&& wChar
!= ' ' )
3342 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3343 if ( uItem
>= (UINT
)(-2) )
3345 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3346 /* schedule end of menu tracking */
3347 wFlags
|= TF_ENDMENU
;
3352 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3354 if (!(wParam
& HTSYSMENU
) || wChar
== ' ')
3356 if( uItem
== NO_SELECTED_ITEM
)
3357 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3359 PostMessageW( hwnd
, WM_KEYDOWN
, VK_RETURN
, 0 );
3363 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3364 MENU_ExitTracking( hwnd
, FALSE
);
3367 /**********************************************************************
3368 * TrackPopupMenuEx (USER32.@)
3370 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3371 HWND hWnd
, LPTPMPARAMS lpTpm
)
3376 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3377 hMenu
, wFlags
, x
, y
, hWnd
, lpTpm
,
3378 lpTpm
? wine_dbgstr_rect( &lpTpm
->rcExclude
) : "-" );
3380 /* Parameter check */
3381 /* FIXME: this check is performed several times, here and in the called
3382 functions. That could be optimized */
3383 if (!(menu
= MENU_GetMenu( hMenu
)))
3385 SetLastError( ERROR_INVALID_MENU_HANDLE
);
3389 if (IsWindow(menu
->hWnd
))
3391 SetLastError( ERROR_POPUP_ALREADY_ACTIVE
);
3395 if (MENU_InitPopup( hWnd
, hMenu
, wFlags
))
3397 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3399 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3400 if (!(wFlags
& TPM_NONOTIFY
))
3401 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3403 if (menu
->wFlags
& MF_SYSMENU
)
3404 MENU_InitSysMenuPopup( hMenu
, GetWindowLongW( hWnd
, GWL_STYLE
),
3405 GetClassLongW( hWnd
, GCL_STYLE
));
3407 if (MENU_ShowPopup( hWnd
, hMenu
, 0, wFlags
, x
, y
, 0, 0 ))
3408 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
,
3409 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3410 MENU_ExitTracking(hWnd
, TRUE
);
3414 NtUserDestroyWindow( menu
->hWnd
);
3417 if (!(wFlags
& TPM_NONOTIFY
))
3418 SendMessageW( hWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)hMenu
,
3419 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3427 /**********************************************************************
3428 * TrackPopupMenu (USER32.@)
3430 * Like the win32 API, the function return the command ID only if the
3431 * flag TPM_RETURNCMD is on.
3434 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3435 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3437 return TrackPopupMenuEx( hMenu
, wFlags
, x
, y
, hWnd
, NULL
);
3440 /***********************************************************************
3443 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3445 LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3447 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3453 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3454 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3458 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3459 return MA_NOACTIVATE
;
3464 NtUserBeginPaint( hwnd
, &ps
);
3465 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3466 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3467 NtUserEndPaint( hwnd
, &ps
);
3471 case WM_PRINTCLIENT
:
3473 MENU_DrawPopupMenu( hwnd
, (HDC
)wParam
,
3474 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3482 /* zero out global pointer in case resident popup window was destroyed. */
3483 if (hwnd
== top_popup
) {
3485 top_popup_hmenu
= NULL
;
3493 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3496 SetWindowLongPtrW( hwnd
, 0, 0 );
3500 return GetWindowLongPtrW( hwnd
, 0 );
3503 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3509 /***********************************************************************
3510 * MENU_GetMenuBarHeight
3512 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3514 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3515 INT orgX
, INT orgY
)
3521 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3523 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3525 hdc
= NtUserGetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3526 SelectObject( hdc
, get_menu_font(FALSE
));
3527 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3528 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3529 NtUserReleaseDC( hwnd
, hdc
);
3530 return lppop
->Height
;
3534 /*******************************************************************
3535 * ChangeMenuA (USER32.@)
3537 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3538 UINT id
, UINT flags
)
3540 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3541 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3543 if (flags
& MF_DELETE
) return NtUserDeleteMenu( hMenu
, pos
, flags
& ~MF_DELETE
);
3544 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3546 if (flags
& MF_REMOVE
) return NtUserRemoveMenu( hMenu
,
3547 flags
& MF_BYPOSITION
? pos
: id
,
3548 flags
& ~MF_REMOVE
);
3549 /* Default: MF_INSERT */
3550 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3554 /*******************************************************************
3555 * ChangeMenuW (USER32.@)
3557 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3558 UINT id
, UINT flags
)
3560 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3561 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3563 if (flags
& MF_DELETE
) return NtUserDeleteMenu( hMenu
, pos
, flags
& ~MF_DELETE
);
3564 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3566 if (flags
& MF_REMOVE
) return NtUserRemoveMenu( hMenu
,
3567 flags
& MF_BYPOSITION
? pos
: id
,
3568 flags
& ~MF_REMOVE
);
3569 /* Default: MF_INSERT */
3570 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3574 /*******************************************************************
3575 * GetMenuStringA (USER32.@)
3577 INT WINAPI
GetMenuStringA(
3578 HMENU hMenu
, /* [in] menuhandle */
3579 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3580 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3581 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3582 UINT wFlags
/* [in] MF_ flags */
3590 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3591 if (str
&& nMaxSiz
) str
[0] = '\0';
3593 if (!(menu
= find_menu_item(hMenu
, wItemID
, wFlags
, &pos
)))
3595 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3598 item
= &menu
->items
[pos
];
3602 else if (!str
|| !nMaxSiz
)
3603 ret
= WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, NULL
, 0, NULL
, NULL
);
3606 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3610 release_menu_ptr(menu
);
3612 TRACE("returning %s\n", debugstr_a(str
));
3617 /*******************************************************************
3618 * GetMenuStringW (USER32.@)
3620 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3621 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3628 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3629 if (str
&& nMaxSiz
) str
[0] = '\0';
3631 if (!(menu
= find_menu_item(hMenu
, wItemID
, wFlags
, &pos
)))
3633 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3636 item
= &menu
->items
[pos
];
3638 if (!str
|| !nMaxSiz
)
3639 ret
= item
->text
? lstrlenW(item
->text
) : 0;
3640 else if (!item
->text
)
3647 lstrcpynW( str
, item
->text
, nMaxSiz
);
3648 ret
= lstrlenW(str
);
3650 release_menu_ptr(menu
);
3652 TRACE("returning %s\n", debugstr_w(str
));
3657 /**********************************************************************
3658 * HiliteMenuItem (USER32.@)
3660 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3668 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3670 if (!(menu
= find_menu_item(hMenu
, wItemID
, wHilite
, &pos
))) return FALSE
;
3672 handle_menu
= menu
->obj
.handle
;
3673 focused_item
= menu
->FocusedItem
;
3674 release_menu_ptr(menu
);
3676 if (focused_item
!= pos
)
3678 MENU_HideSubPopups( hWnd
, handle_menu
, FALSE
, 0 );
3679 MENU_SelectItem( hWnd
, handle_menu
, pos
, TRUE
, 0 );
3686 /**********************************************************************
3687 * GetMenuState (USER32.@)
3689 UINT WINAPI
GetMenuState( HMENU menu
, UINT item
, UINT flags
)
3691 return NtUserThunkedMenuItemInfo( menu
, item
, flags
, NtUserGetMenuState
, NULL
, NULL
);
3695 /**********************************************************************
3696 * GetMenuItemCount (USER32.@)
3698 INT WINAPI
GetMenuItemCount( HMENU menu
)
3700 return NtUserGetMenuItemCount( menu
);
3704 /**********************************************************************
3705 * GetMenuItemID (USER32.@)
3707 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3712 if (!(menu
= find_menu_item(hMenu
, nPos
, MF_BYPOSITION
, &pos
)))
3715 id
= menu
->items
[pos
].fType
& MF_POPUP
? -1 : menu
->items
[pos
].wID
;
3716 release_menu_ptr(menu
);
3721 /**********************************************************************
3724 * Uses flags, id and text ptr, passed by InsertMenu() and
3725 * ModifyMenu() to setup a MenuItemInfo structure.
3727 static void MENU_mnu2mnuii( UINT flags
, UINT_PTR id
, LPCWSTR str
,
3728 LPMENUITEMINFOW pmii
)
3730 ZeroMemory( pmii
, sizeof( MENUITEMINFOW
));
3731 pmii
->cbSize
= sizeof( MENUITEMINFOW
);
3732 pmii
->fMask
= MIIM_STATE
| MIIM_ID
| MIIM_FTYPE
;
3733 /* setting bitmap clears text and vice versa */
3734 if( IS_STRING_ITEM(flags
)) {
3735 pmii
->fMask
|= MIIM_STRING
| MIIM_BITMAP
;
3737 flags
|= MF_SEPARATOR
;
3738 /* Item beginning with a backspace is a help item */
3739 /* FIXME: wrong place, this is only true in win16 */
3740 else if( *str
== '\b') {
3744 pmii
->dwTypeData
= (LPWSTR
)str
;
3745 } else if( flags
& MFT_BITMAP
){
3746 pmii
->fMask
|= MIIM_BITMAP
| MIIM_STRING
;
3747 pmii
->hbmpItem
= (HBITMAP
)str
;
3749 if( flags
& MF_OWNERDRAW
){
3750 pmii
->fMask
|= MIIM_DATA
;
3751 pmii
->dwItemData
= (ULONG_PTR
) str
;
3753 if( flags
& MF_POPUP
&& MENU_GetMenu((HMENU
)id
)) {
3754 pmii
->fMask
|= MIIM_SUBMENU
;
3755 pmii
->hSubMenu
= (HMENU
)id
;
3757 if( flags
& MF_SEPARATOR
) flags
|= MF_GRAYED
| MF_DISABLED
;
3758 pmii
->fState
= flags
& MENUITEMINFO_STATE_MASK
& ~MFS_DEFAULT
;
3759 pmii
->fType
= flags
& MENUITEMINFO_TYPE_MASK
;
3760 pmii
->wID
= (UINT
)id
;
3764 /*******************************************************************
3765 * InsertMenuW (USER32.@)
3767 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3768 UINT_PTR id
, LPCWSTR str
)
3772 if (IS_STRING_ITEM(flags
) && str
)
3773 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3774 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3775 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3776 hMenu
, pos
, flags
, id
, str
);
3778 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
3779 mii
.fMask
|= MIIM_CHECKMARKS
;
3780 return NtUserThunkedMenuItemInfo( hMenu
, pos
, flags
, NtUserInsertMenuItem
, &mii
, NULL
);
3784 /*******************************************************************
3785 * InsertMenuA (USER32.@)
3787 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3788 UINT_PTR id
, LPCSTR str
)
3792 if (IS_STRING_ITEM(flags
) && str
)
3794 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3795 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3798 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3799 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3800 HeapFree( GetProcessHeap(), 0, newstr
);
3804 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3808 /*******************************************************************
3809 * AppendMenuA (USER32.@)
3811 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3812 UINT_PTR id
, LPCSTR data
)
3814 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3818 /*******************************************************************
3819 * AppendMenuW (USER32.@)
3821 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3822 UINT_PTR id
, LPCWSTR data
)
3824 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3828 /*******************************************************************
3829 * ModifyMenuW (USER32.@)
3831 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3832 UINT_PTR id
, LPCWSTR str
)
3836 if (IS_STRING_ITEM(flags
))
3837 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3839 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
3841 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
3842 return NtUserThunkedMenuItemInfo( hMenu
, pos
, flags
, NtUserSetMenuItemInfo
, &mii
, NULL
);
3846 /*******************************************************************
3847 * ModifyMenuA (USER32.@)
3849 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3850 UINT_PTR id
, LPCSTR str
)
3854 if (IS_STRING_ITEM(flags
) && str
)
3856 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3857 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3860 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3861 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
3862 HeapFree( GetProcessHeap(), 0, newstr
);
3866 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3870 /**********************************************************************
3871 * CreatePopupMenu (USER32.@)
3873 HMENU WINAPI
CreatePopupMenu(void)
3875 return NtUserCreateMenu( TRUE
);
3879 /**********************************************************************
3880 * GetMenuCheckMarkDimensions (USER.417)
3881 * GetMenuCheckMarkDimensions (USER32.@)
3883 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
3885 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
3889 /**********************************************************************
3890 * SetMenuItemBitmaps (USER32.@)
3892 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
3893 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
3899 if (!(menu
= find_menu_item(hMenu
, nPos
, wFlags
, &pos
)))
3902 item
= &menu
->items
[pos
];
3903 if (!hNewCheck
&& !hNewUnCheck
)
3905 item
->fState
&= ~MF_USECHECKBITMAPS
;
3907 else /* Install new bitmaps */
3909 item
->hCheckBit
= hNewCheck
;
3910 item
->hUnCheckBit
= hNewUnCheck
;
3911 item
->fState
|= MF_USECHECKBITMAPS
;
3913 release_menu_ptr(menu
);
3919 /**********************************************************************
3920 * CreateMenu (USER32.@)
3922 HMENU WINAPI
CreateMenu(void)
3924 return NtUserCreateMenu( FALSE
);
3928 /**********************************************************************
3929 * GetMenu (USER32.@)
3931 HMENU WINAPI
GetMenu( HWND hWnd
)
3933 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
3934 TRACE("for %p returning %p\n", hWnd
, retvalue
);
3938 /**********************************************************************
3939 * GetMenuBarInfo (USER32.@)
3941 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
3947 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
3952 class_atom
= GetClassLongW(hwnd
, GCW_ATOM
);
3955 if (class_atom
!= POPUPMENU_CLASS_ATOM
)
3957 WARN("called on invalid window: %d\n", class_atom
);
3958 SetLastError(ERROR_INVALID_MENU_HANDLE
);
3962 hmenu
= (HMENU
)GetWindowLongPtrW(hwnd
, 0);
3965 hmenu
= GetMenu(hwnd
);
3968 hmenu
= NtUserGetSystemMenu( hwnd
, FALSE
);
3977 if (pmbi
->cbSize
!= sizeof(MENUBARINFO
))
3979 SetLastError(ERROR_INVALID_PARAMETER
);
3983 menu
= MENU_GetMenu(hmenu
);
3986 if (idItem
< 0 || idItem
> menu
->nItems
)
3991 SetRectEmpty(&pmbi
->rcBar
);
3993 else if (idItem
== 0)
3995 NtUserGetMenuItemRect( hwnd
, hmenu
, 0, &pmbi
->rcBar
);
3996 pmbi
->rcBar
.right
= pmbi
->rcBar
.left
+ menu
->Width
;
3997 pmbi
->rcBar
.bottom
= pmbi
->rcBar
.top
+ menu
->Height
;
4001 NtUserGetMenuItemRect( hwnd
, hmenu
, idItem
- 1, &pmbi
->rcBar
);
4004 pmbi
->hMenu
= hmenu
;
4005 pmbi
->hwndMenu
= NULL
;
4006 pmbi
->fBarFocused
= top_popup_hmenu
== hmenu
;
4009 pmbi
->fFocused
= menu
->FocusedItem
== idItem
- 1;
4010 if (pmbi
->fFocused
&& (menu
->items
[idItem
- 1].fType
& MF_POPUP
))
4012 menu
= MENU_GetMenu(menu
->items
[idItem
- 1].hSubMenu
);
4014 pmbi
->hwndMenu
= menu
->hWnd
;
4019 pmbi
->fFocused
= pmbi
->fBarFocused
;
4026 /**********************************************************************
4027 * GetSubMenu (USER32.@)
4029 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4035 if (!(menu
= find_menu_item(hMenu
, nPos
, MF_BYPOSITION
, &pos
)))
4038 if (menu
->items
[pos
].fType
& MF_POPUP
)
4039 submenu
= menu
->items
[pos
].hSubMenu
;
4043 release_menu_ptr(menu
);
4048 /**********************************************************************
4049 * DrawMenuBar (USER32.@)
4051 BOOL WINAPI
DrawMenuBar( HWND hwnd
)
4053 return NtUserDrawMenuBar( hwnd
);
4056 /***********************************************************************
4057 * DrawMenuBarTemp (USER32.@)
4061 * called by W98SE desk.cpl Control Panel Applet
4063 * Not 100% sure about the param names, but close.
4065 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4070 BOOL flat_menu
= FALSE
;
4072 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4075 hMenu
= GetMenu(hwnd
);
4078 hFont
= get_menu_font(FALSE
);
4080 lppop
= MENU_GetMenu( hMenu
);
4081 if (lppop
== NULL
|| lprect
== NULL
)
4083 retvalue
= GetSystemMetrics(SM_CYMENU
);
4087 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4089 hfontOld
= SelectObject( hDC
, hFont
);
4091 if (lppop
->Height
== 0)
4092 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4094 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4096 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4098 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4099 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4100 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4102 if (lppop
->nItems
== 0)
4104 retvalue
= GetSystemMetrics(SM_CYMENU
);
4108 for (i
= 0; i
< lppop
->nItems
; i
++)
4109 MENU_DrawMenuItem( hwnd
, lppop
, hwnd
, hDC
, &lppop
->items
[i
], TRUE
, ODA_DRAWENTIRE
);
4111 retvalue
= lppop
->Height
;
4114 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4118 /***********************************************************************
4119 * EndMenu (USER.187)
4120 * EndMenu (USER32.@)
4122 BOOL WINAPI
EndMenu(void)
4124 /* if we are in the menu code, and it is active */
4125 if (!fEndMenu
&& top_popup
)
4127 /* terminate the menu handling code */
4130 /* needs to be posted to wakeup the internal menu handler */
4131 /* which will now terminate the menu, in the event that */
4132 /* the main window was minimized, or lost focus, so we */
4133 /* don't end up with an orphaned menu */
4134 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4140 /*****************************************************************
4141 * LoadMenuA (USER32.@)
4143 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4145 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4146 if (!hrsrc
) return 0;
4147 return LoadMenuIndirectA( LoadResource( instance
, hrsrc
));
4151 /*****************************************************************
4152 * LoadMenuW (USER32.@)
4154 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4156 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4157 if (!hrsrc
) return 0;
4158 return LoadMenuIndirectW( LoadResource( instance
, hrsrc
));
4162 /**********************************************************************
4163 * LoadMenuIndirectW (USER32.@)
4165 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4168 WORD version
, offset
;
4169 LPCSTR p
= template;
4171 version
= GET_WORD(p
);
4173 TRACE("%p, ver %d\n", template, version
);
4176 case 0: /* standard format is version of 0 */
4177 offset
= GET_WORD(p
);
4178 p
+= sizeof(WORD
) + offset
;
4179 if (!(hMenu
= CreateMenu())) return 0;
4180 if (!MENU_ParseResource( p
, hMenu
))
4182 NtUserDestroyMenu( hMenu
);
4186 case 1: /* extended format is version of 1 */
4187 offset
= GET_WORD(p
);
4188 p
+= sizeof(WORD
) + offset
;
4189 if (!(hMenu
= CreateMenu())) return 0;
4190 if (!MENUEX_ParseResource( p
, hMenu
))
4192 NtUserDestroyMenu( hMenu
);
4197 ERR("version %d not supported.\n", version
);
4203 /**********************************************************************
4204 * LoadMenuIndirectA (USER32.@)
4206 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4208 return LoadMenuIndirectW( template );
4212 /**********************************************************************
4215 BOOL WINAPI
IsMenu( HMENU menu
)
4219 info
.cbSize
= sizeof(info
);
4221 if (GetMenuInfo( menu
, &info
)) return TRUE
;
4223 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4227 /**********************************************************************
4228 * GetMenuItemInfo_common
4231 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT id
, BOOL bypos
,
4232 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4238 menu
= find_menu_item(hmenu
, id
, bypos
? MF_BYPOSITION
: 0, &pos
);
4240 item
= menu
? &menu
->items
[pos
] : NULL
;
4242 debug_print_menuitem("GetMenuItemInfo_common: ", item
, "");
4246 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
4250 if( lpmii
->fMask
& MIIM_TYPE
) {
4251 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4252 release_menu_ptr(menu
);
4253 WARN("invalid combination of fMask bits used\n");
4254 /* this does not happen on Win9x/ME */
4255 SetLastError( ERROR_INVALID_PARAMETER
);
4258 lpmii
->fType
= item
->fType
& MENUITEMINFO_TYPE_MASK
;
4259 if (item
->hbmpItem
&& !IS_MAGIC_BITMAP(item
->hbmpItem
))
4260 lpmii
->fType
|= MFT_BITMAP
;
4261 lpmii
->hbmpItem
= item
->hbmpItem
; /* not on Win9x/ME */
4262 if( lpmii
->fType
& MFT_BITMAP
) {
4263 lpmii
->dwTypeData
= (LPWSTR
) item
->hbmpItem
;
4265 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4266 /* this does not happen on Win9x/ME */
4267 lpmii
->dwTypeData
= 0;
4272 /* copy the text string */
4273 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4275 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4277 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4279 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4286 len
= lstrlenW(item
->text
);
4287 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4288 lstrcpynW(lpmii
->dwTypeData
, item
->text
, lpmii
->cch
);
4292 len
= WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, NULL
,
4293 0, NULL
, NULL
) - 1;
4294 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4295 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1,
4296 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4297 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4299 /* if we've copied a substring we return its length */
4300 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4301 if (lpmii
->cch
<= len
+ 1)
4306 /* return length of string */
4307 /* not on Win9x/ME if fType & MFT_BITMAP */
4313 if (lpmii
->fMask
& MIIM_FTYPE
)
4314 lpmii
->fType
= item
->fType
& MENUITEMINFO_TYPE_MASK
;
4316 if (lpmii
->fMask
& MIIM_BITMAP
)
4317 lpmii
->hbmpItem
= item
->hbmpItem
;
4319 if (lpmii
->fMask
& MIIM_STATE
)
4320 lpmii
->fState
= item
->fState
& MENUITEMINFO_STATE_MASK
;
4322 if (lpmii
->fMask
& MIIM_ID
)
4323 lpmii
->wID
= item
->wID
;
4325 if (lpmii
->fMask
& MIIM_SUBMENU
)
4326 lpmii
->hSubMenu
= item
->hSubMenu
;
4328 /* hSubMenu is always cleared
4329 * (not on Win9x/ME ) */
4330 lpmii
->hSubMenu
= 0;
4333 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4334 lpmii
->hbmpChecked
= item
->hCheckBit
;
4335 lpmii
->hbmpUnchecked
= item
->hUnCheckBit
;
4337 if (lpmii
->fMask
& MIIM_DATA
)
4338 lpmii
->dwItemData
= item
->dwItemData
;
4340 release_menu_ptr(menu
);
4344 /**********************************************************************
4345 * GetMenuItemInfoA (USER32.@)
4347 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4348 LPMENUITEMINFOA lpmii
)
4352 if( lpmii
->cbSize
!= sizeof( mii
) &&
4353 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4354 SetLastError( ERROR_INVALID_PARAMETER
);
4357 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4358 mii
.cbSize
= sizeof( mii
);
4359 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4360 (LPMENUITEMINFOW
)&mii
, FALSE
);
4361 mii
.cbSize
= lpmii
->cbSize
;
4362 memcpy( lpmii
, &mii
, mii
.cbSize
);
4366 /**********************************************************************
4367 * GetMenuItemInfoW (USER32.@)
4369 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4370 LPMENUITEMINFOW lpmii
)
4374 if( lpmii
->cbSize
!= sizeof( mii
) &&
4375 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4376 SetLastError( ERROR_INVALID_PARAMETER
);
4379 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4380 mii
.cbSize
= sizeof( mii
);
4381 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4382 mii
.cbSize
= lpmii
->cbSize
;
4383 memcpy( lpmii
, &mii
, mii
.cbSize
);
4388 /**********************************************************************
4389 * MENU_NormalizeMenuItemInfoStruct
4391 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4392 * check, copy and extend the MENUITEMINFO struct from the version that the application
4393 * supplied to the version used by wine source. */
4394 static BOOL
MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW
*pmii_in
,
4395 MENUITEMINFOW
*pmii_out
)
4397 /* do we recognize the size? */
4398 if( !pmii_in
|| (pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) &&
4399 pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) - sizeof( pmii_in
->hbmpItem
)) ) {
4400 SetLastError( ERROR_INVALID_PARAMETER
);
4403 /* copy the fields that we have */
4404 memcpy( pmii_out
, pmii_in
, pmii_in
->cbSize
);
4405 /* if the hbmpItem member is missing then extend */
4406 if( pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
)) {
4407 pmii_out
->cbSize
= sizeof( MENUITEMINFOW
);
4408 pmii_out
->hbmpItem
= NULL
;
4410 /* test for invalid bit combinations */
4411 if( (pmii_out
->fMask
& MIIM_TYPE
&&
4412 pmii_out
->fMask
& (MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) ||
4413 (pmii_out
->fMask
& MIIM_FTYPE
&& pmii_out
->fType
& MFT_BITMAP
)) {
4414 WARN("invalid combination of fMask bits used\n");
4415 /* this does not happen on Win9x/ME */
4416 SetLastError( ERROR_INVALID_PARAMETER
);
4419 /* convert old style (MIIM_TYPE) to the new */
4420 if( pmii_out
->fMask
& MIIM_TYPE
){
4421 pmii_out
->fMask
|= MIIM_FTYPE
;
4422 if( IS_STRING_ITEM(pmii_out
->fType
)){
4423 pmii_out
->fMask
|= MIIM_STRING
;
4424 } else if( (pmii_out
->fType
) & MFT_BITMAP
){
4425 pmii_out
->fMask
|= MIIM_BITMAP
;
4426 pmii_out
->hbmpItem
= UlongToHandle(LOWORD(pmii_out
->dwTypeData
));
4432 /**********************************************************************
4433 * SetMenuItemInfoA (USER32.@)
4435 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4436 const MENUITEMINFOA
*lpmii
)
4442 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4444 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
4446 if ((mii
.fMask
& MIIM_STRING
) && mii
.dwTypeData
)
4448 const char *str
= (const char *)mii
.dwTypeData
;
4449 UINT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4450 if (!(strW
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) ))) return FALSE
;
4451 MultiByteToWideChar( CP_ACP
, 0, str
, -1, strW
, len
);
4452 mii
.dwTypeData
= strW
;
4455 ret
= NtUserThunkedMenuItemInfo( hmenu
, item
, bypos
? MF_BYPOSITION
: 0,
4456 NtUserSetMenuItemInfo
, &mii
, NULL
);
4458 HeapFree( GetProcessHeap(), 0, strW
);
4462 /**********************************************************************
4463 * SetMenuItemInfoW (USER32.@)
4465 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4466 const MENUITEMINFOW
*lpmii
)
4470 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4472 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
4474 return NtUserThunkedMenuItemInfo( hmenu
, item
, bypos
? MF_BYPOSITION
: 0,
4475 NtUserSetMenuItemInfo
, &mii
, NULL
);
4478 /**********************************************************************
4479 * GetMenuDefaultItem (USER32.@)
4481 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4487 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4489 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4491 /* find default item */
4495 if (! item
) return -1;
4497 while ( !( item
->fState
& MFS_DEFAULT
) )
4500 if (i
>= menu
->nItems
) return -1;
4503 /* default: don't return disabled items */
4504 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4506 /* search rekursiv when needed */
4507 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4510 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4511 if ( -1 != ret
) return ret
;
4513 /* when item not found in submenu, return the popup item */
4515 return ( bypos
) ? i
: item
->wID
;
4520 /**********************************************************************
4521 * InsertMenuItemA (USER32.@)
4523 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4524 const MENUITEMINFOA
*lpmii
)
4530 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4532 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
4534 if ((mii
.fMask
& MIIM_STRING
) && mii
.dwTypeData
)
4536 const char *str
= (const char *)mii
.dwTypeData
;
4537 UINT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4538 if (!(strW
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) ))) return FALSE
;
4539 MultiByteToWideChar( CP_ACP
, 0, str
, -1, strW
, len
);
4540 mii
.dwTypeData
= strW
;
4543 ret
= NtUserThunkedMenuItemInfo( hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0,
4544 NtUserInsertMenuItem
, &mii
, NULL
);
4546 HeapFree( GetProcessHeap(), 0, strW
);
4551 /**********************************************************************
4552 * InsertMenuItemW (USER32.@)
4554 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4555 const MENUITEMINFOW
*lpmii
)
4559 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4561 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
4563 return NtUserThunkedMenuItemInfo( hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0,
4564 NtUserInsertMenuItem
, &mii
, NULL
);
4567 /**********************************************************************
4568 * CheckMenuRadioItem (USER32.@)
4571 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
, UINT first
, UINT last
,
4572 UINT check
, UINT flags
)
4574 POPUPMENU
*first_menu
= NULL
, *check_menu
;
4578 for (i
= first
; i
<= last
; i
++)
4582 if (!(check_menu
= find_menu_item(hMenu
, i
, flags
, &check_pos
)))
4586 first_menu
= grab_menu_ptr(check_menu
->obj
.handle
);
4588 if (first_menu
!= check_menu
)
4590 release_menu_ptr(check_menu
);
4594 item
= &check_menu
->items
[check_pos
];
4595 if (item
->fType
!= MFT_SEPARATOR
)
4599 item
->fType
|= MFT_RADIOCHECK
;
4600 item
->fState
|= MFS_CHECKED
;
4605 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4606 item
->fState
&= ~MFS_CHECKED
;
4610 release_menu_ptr(check_menu
);
4612 release_menu_ptr(first_menu
);
4618 /**********************************************************************
4619 * SetMenuInfo (USER32.@)
4621 BOOL WINAPI
SetMenuInfo( HMENU menu
, const MENUINFO
*info
)
4623 TRACE( "(%p %p)\n", menu
, info
);
4625 if (!info
|| info
->cbSize
!= sizeof(*info
))
4627 SetLastError( ERROR_INVALID_PARAMETER
);
4631 return NtUserThunkedMenuInfo( menu
, info
);
4634 /**********************************************************************
4635 * GetMenuInfo (USER32.@)
4637 BOOL WINAPI
GetMenuInfo( HMENU menu
, MENUINFO
*info
)
4639 return NtUserGetMenuInfo( menu
, info
);
4643 /**********************************************************************
4644 * GetMenuContextHelpId (USER32.@)
4646 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
4651 TRACE("(%p)\n", hMenu
);
4653 if ((menu
= grab_menu_ptr(hMenu
)))
4655 help_id
= menu
->dwContextHelpID
;
4656 release_menu_ptr(menu
);
4662 /**********************************************************************
4663 * MenuItemFromPoint (USER32.@)
4665 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
4667 POPUPMENU
*menu
= grab_menu_ptr(hMenu
);
4670 /*FIXME: Do we have to handle hWnd here? */
4671 if (!menu
) return -1;
4673 if (MENU_FindItemByCoords( menu
, ptScreen
, &pos
) != ht_item
)
4676 release_menu_ptr(menu
);
4681 /**********************************************************************
4682 * CalcMenuBar (USER32.@)
4684 DWORD WINAPI
CalcMenuBar(HWND hwnd
, DWORD left
, DWORD right
, DWORD top
, RECT
*rect
)
4686 FIXME("(%p, %d, %d, %d, %p): stub\n", hwnd
, left
, right
, top
, rect
);
4691 /**********************************************************************
4692 * TranslateAcceleratorA (USER32.@)
4693 * TranslateAccelerator (USER32.@)
4695 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
4697 switch (msg
->message
)
4701 return NtUserTranslateAccelerator( hWnd
, hAccel
, msg
);
4707 char ch
= LOWORD(msg
->wParam
);
4709 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
4710 msgW
.wParam
= MAKEWPARAM(wch
, HIWORD(msg
->wParam
));
4711 return NtUserTranslateAccelerator( hWnd
, hAccel
, &msgW
);