4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
43 #include "wine/port.h"
54 #include "wine/server.h"
55 #include "wine/unicode.h"
56 #include "wine/exception.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
63 WINE_DECLARE_DEBUG_CHANNEL(accel
);
65 /* Menu item structure */
67 /* ----------- MENUITEMINFO Stuff ----------- */
68 UINT fType
; /* Item type. */
69 UINT fState
; /* Item state. */
70 UINT_PTR wID
; /* Item id. */
71 HMENU hSubMenu
; /* Pop-up menu. */
72 HBITMAP hCheckBit
; /* Bitmap when checked. */
73 HBITMAP hUnCheckBit
; /* Bitmap when unchecked. */
74 LPWSTR text
; /* Item text. */
75 ULONG_PTR dwItemData
; /* Application defined. */
76 LPWSTR dwTypeData
; /* depends on fMask */
77 HBITMAP hbmpItem
; /* bitmap */
78 /* ----------- Wine stuff ----------- */
79 RECT rect
; /* Item area (relative to menu window) */
80 UINT xTab
; /* X position of text after Tab */
81 SIZE bmpsize
; /* size needed for the HBMMENU_CALLBACK
85 /* Popup menu structure */
87 struct user_object obj
;
88 WORD wFlags
; /* Menu flags (MF_POPUP, MF_SYSMENU) */
89 WORD Width
; /* Width of the whole menu */
90 WORD Height
; /* Height of the whole menu */
91 UINT nItems
; /* Number of items in the menu */
92 HWND hWnd
; /* Window containing the menu */
93 MENUITEM
*items
; /* Array of menu items */
94 UINT FocusedItem
; /* Currently focused item */
95 HWND hwndOwner
; /* window receiving the messages for ownerdraw */
96 BOOL bTimeToHide
; /* Request hiding when receiving a second click in the top-level menu item */
97 BOOL bScrolling
; /* Scroll arrows are active */
98 UINT nScrollPos
; /* Current scroll position */
99 UINT nTotalHeight
; /* Total height of menu items inside menu */
100 /* ------------ MENUINFO members ------ */
101 DWORD dwStyle
; /* Extended menu style */
102 UINT cyMax
; /* max height of the whole menu, 0 is screen height */
103 HBRUSH hbrBack
; /* brush for menu background */
104 DWORD dwContextHelpID
;
105 DWORD dwMenuData
; /* application defined value */
106 HMENU hSysMenuOwner
; /* Handle to the dummy sys menu holder */
107 WORD textOffset
; /* Offset of text when items have both bitmaps and text */
108 } POPUPMENU
, *LPPOPUPMENU
;
110 /* internal flags for menu tracking */
112 #define TF_ENDMENU 0x10000
113 #define TF_SUSPENDPOPUP 0x20000
114 #define TF_SKIPREMOVE 0x40000
119 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
120 HMENU hTopMenu
; /* initial menu */
121 HWND hOwnerWnd
; /* where notifications are sent */
128 /* Internal MENU_TrackMenu() flags */
129 #define TPM_INTERNAL 0xF0000000
130 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
131 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
133 /* Space between 2 columns */
134 #define MENU_COL_SPACE 4
136 /* top and bottom margins for popup menus */
137 #define MENU_TOP_MARGIN 3
138 #define MENU_BOTTOM_MARGIN 2
140 /* maximum allowed depth of any branch in the menu tree.
141 * This value is slightly larger than in windows (25) to
142 * stay on the safe side. */
143 #define MAXMENUDEPTH 30
145 /* (other menu->FocusedItem values give the position of the focused item) */
146 #define NO_SELECTED_ITEM 0xffff
148 #define MENU_ITEM_TYPE(flags) \
149 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
151 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
152 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
153 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
155 #define IS_SYSTEM_MENU(menu) \
156 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
158 #define MENUITEMINFO_TYPE_MASK \
159 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
160 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
161 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
162 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
163 #define STATE_MASK (~TYPE_MASK)
164 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
166 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
168 static SIZE menucharsize
;
169 static UINT ODitemheight
; /* default owner drawn item height */
171 /* Use global popup window because there's no way 2 menus can
172 * be tracked at the same time. */
173 static HWND top_popup
;
174 static HMENU top_popup_hmenu
;
176 /* Flag set by EndMenu() to force an exit from menu tracking */
177 static BOOL fEndMenu
= FALSE
;
179 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
);
181 static BOOL
SetMenuItemInfo_common( MENUITEM
*, const MENUITEMINFOW
*, BOOL
);
183 /*********************************************************************
184 * menu class descriptor
186 const struct builtin_class_descr MENU_builtin_class
=
188 (LPCWSTR
)POPUPMENU_CLASS_ATOM
, /* name */
189 CS_DROPSHADOW
| CS_SAVEBITS
| CS_DBLCLKS
, /* style */
190 WINPROC_MENU
, /* proc */
191 sizeof(HMENU
), /* extra */
192 IDC_ARROW
, /* cursor */
193 (HBRUSH
)(COLOR_MENU
+1) /* brush */
197 /***********************************************************************
198 * debug_print_menuitem
200 * Print a menuitem in readable form.
203 #define debug_print_menuitem(pre, mp, post) \
204 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
206 #define MENUOUT(text) \
207 TRACE("%s%s", (count++ ? "," : ""), (text))
209 #define MENUFLAG(bit,text) \
211 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
214 static void do_debug_print_menuitem(const char *prefix
, const MENUITEM
*mp
,
217 static const char * const hbmmenus
[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
218 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
219 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
220 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
221 TRACE("%s ", prefix
);
223 UINT flags
= mp
->fType
;
224 TRACE( "{ ID=0x%lx", mp
->wID
);
226 TRACE( ", Sub=%p", mp
->hSubMenu
);
230 MENUFLAG( MFT_SEPARATOR
, "sep");
231 MENUFLAG( MFT_OWNERDRAW
, "own");
232 MENUFLAG( MFT_BITMAP
, "bit");
233 MENUFLAG(MF_POPUP
, "pop");
234 MENUFLAG(MFT_MENUBARBREAK
, "barbrk");
235 MENUFLAG(MFT_MENUBREAK
, "brk");
236 MENUFLAG(MFT_RADIOCHECK
, "radio");
237 MENUFLAG(MFT_RIGHTORDER
, "rorder");
238 MENUFLAG(MF_SYSMENU
, "sys");
239 MENUFLAG(MFT_RIGHTJUSTIFY
, "right"); /* same as MF_HELP */
241 TRACE( "+0x%x", flags
);
247 MENUFLAG(MFS_GRAYED
, "grey");
248 MENUFLAG(MFS_DEFAULT
, "default");
249 MENUFLAG(MFS_DISABLED
, "dis");
250 MENUFLAG(MFS_CHECKED
, "check");
251 MENUFLAG(MFS_HILITE
, "hi");
252 MENUFLAG(MF_USECHECKBITMAPS
, "usebit");
253 MENUFLAG(MF_MOUSESELECT
, "mouse");
255 TRACE( "+0x%x", flags
);
258 TRACE( ", Chk=%p", mp
->hCheckBit
);
260 TRACE( ", Unc=%p", mp
->hUnCheckBit
);
262 TRACE( ", Text=%s", debugstr_w(mp
->text
));
264 TRACE( ", ItemData=0x%08lx", mp
->dwItemData
);
267 if( IS_MAGIC_BITMAP(mp
->hbmpItem
))
268 TRACE( ", hbitmap=%s", hbmmenus
[ (INT_PTR
)mp
->hbmpItem
+ 1]);
270 TRACE( ", hbitmap=%p", mp
->hbmpItem
);
275 TRACE(" %s\n", postfix
);
282 /***********************************************************************
285 * Validate the given menu handle and returns the menu structure pointer.
287 static POPUPMENU
*MENU_GetMenu(HMENU hMenu
)
289 POPUPMENU
*menu
= get_user_handle_ptr( hMenu
, USER_MENU
);
291 if (menu
== OBJ_OTHER_PROCESS
)
293 WARN( "other process menu %p?\n", hMenu
);
296 if (menu
) release_user_handle_ptr( menu
); /* FIXME! */
297 else WARN("invalid menu handle=%p\n", hMenu
);
301 /***********************************************************************
304 * Get the system menu of a window
306 static HMENU
get_win_sys_menu( HWND hwnd
)
309 WND
*win
= WIN_GetPtr( hwnd
);
310 if (win
&& win
!= WND_OTHER_PROCESS
&& win
!= WND_DESKTOP
)
313 WIN_ReleasePtr( win
);
318 /***********************************************************************
321 static HFONT
get_menu_font( BOOL bold
)
323 static HFONT hMenuFont
, hMenuFontBold
;
325 HFONT ret
= bold
? hMenuFontBold
: hMenuFont
;
329 NONCLIENTMETRICSW ncm
;
332 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
333 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSW
), &ncm
, 0);
337 ncm
.lfMenuFont
.lfWeight
+= 300;
338 if (ncm
.lfMenuFont
.lfWeight
> 1000) ncm
.lfMenuFont
.lfWeight
= 1000;
340 if (!(ret
= CreateFontIndirectW( &ncm
.lfMenuFont
))) return 0;
341 prev
= InterlockedCompareExchangePointer( (void **)(bold
? &hMenuFontBold
: &hMenuFont
),
345 /* another thread beat us to it */
353 /***********************************************************************
356 static HBITMAP
get_arrow_bitmap(void)
358 static HBITMAP arrow_bitmap
;
360 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
364 /***********************************************************************
365 * get_down_arrow_bitmap
367 static HBITMAP
get_down_arrow_bitmap(void)
369 static HBITMAP arrow_bitmap
;
371 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW
));
375 /***********************************************************************
376 * get_down_arrow_inactive_bitmap
378 static HBITMAP
get_down_arrow_inactive_bitmap(void)
380 static HBITMAP arrow_bitmap
;
382 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI
));
386 /***********************************************************************
387 * get_up_arrow_bitmap
389 static HBITMAP
get_up_arrow_bitmap(void)
391 static HBITMAP arrow_bitmap
;
393 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW
));
397 /***********************************************************************
398 * get_up_arrow_inactive_bitmap
400 static HBITMAP
get_up_arrow_inactive_bitmap(void)
402 static HBITMAP arrow_bitmap
;
404 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI
));
408 /***********************************************************************
411 * Return the default system menu.
413 static HMENU
MENU_CopySysPopup(BOOL mdi
)
415 static const WCHAR sysmenuW
[] = {'S','Y','S','M','E','N','U',0};
416 static const WCHAR sysmenumdiW
[] = {'S','Y','S','M','E','N','U','M','D','I',0};
417 HMENU hMenu
= LoadMenuW(user32_module
, (mdi
? sysmenumdiW
: sysmenuW
));
421 MENUITEMINFOW miteminfo
;
422 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
423 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
424 /* decorate the menu with bitmaps */
425 minfo
.cbSize
= sizeof( MENUINFO
);
426 minfo
.dwStyle
= MNS_CHECKORBMP
;
427 minfo
.fMask
= MIM_STYLE
;
428 SetMenuInfo( hMenu
, &minfo
);
429 miteminfo
.cbSize
= sizeof( MENUITEMINFOW
);
430 miteminfo
.fMask
= MIIM_BITMAP
;
431 miteminfo
.hbmpItem
= HBMMENU_POPUP_CLOSE
;
432 SetMenuItemInfoW( hMenu
, SC_CLOSE
, FALSE
, &miteminfo
);
433 miteminfo
.hbmpItem
= HBMMENU_POPUP_RESTORE
;
434 SetMenuItemInfoW( hMenu
, SC_RESTORE
, FALSE
, &miteminfo
);
435 miteminfo
.hbmpItem
= HBMMENU_POPUP_MAXIMIZE
;
436 SetMenuItemInfoW( hMenu
, SC_MAXIMIZE
, FALSE
, &miteminfo
);
437 miteminfo
.hbmpItem
= HBMMENU_POPUP_MINIMIZE
;
438 SetMenuItemInfoW( hMenu
, SC_MINIMIZE
, FALSE
, &miteminfo
);
439 SetMenuDefaultItem(hMenu
, SC_CLOSE
, FALSE
);
442 ERR("Unable to load default system menu\n" );
444 TRACE("returning %p (mdi=%d).\n", hMenu
, mdi
);
450 /**********************************************************************
453 * Create a copy of the system menu. System menu in Windows is
454 * a special menu bar with the single entry - system menu popup.
455 * This popup is presented to the outside world as a "system menu".
456 * However, the real system menu handle is sometimes seen in the
457 * WM_MENUSELECT parameters (and Word 6 likes it this way).
459 static HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
463 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
464 if ((hMenu
= CreateMenu()))
466 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
467 menu
->wFlags
= MF_SYSMENU
;
468 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
469 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
473 if (GetWindowLongW(hWnd
, GWL_EXSTYLE
) & WS_EX_MDICHILD
)
474 hPopupMenu
= MENU_CopySysPopup(TRUE
);
476 hPopupMenu
= MENU_CopySysPopup(FALSE
);
481 if (GetClassLongW(hWnd
, GCL_STYLE
) & CS_NOCLOSE
)
482 DeleteMenu(hPopupMenu
, SC_CLOSE
, MF_BYCOMMAND
);
484 InsertMenuW( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
485 (UINT_PTR
)hPopupMenu
, NULL
);
487 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
488 menu
->items
[0].fState
= 0;
489 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
491 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
494 DestroyMenu( hMenu
);
496 ERR("failed to load system menu!\n");
501 /***********************************************************************
502 * MENU_InitSysMenuPopup
504 * Grey the appropriate items in System menu.
506 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
510 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
511 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
512 gray
= ((style
& WS_MAXIMIZE
) != 0);
513 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
514 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
515 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
516 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
517 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
518 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
519 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
520 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
522 /* The menu item must keep its state if it's disabled */
524 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
528 /******************************************************************************
530 * UINT MENU_GetStartOfNextColumn(
533 *****************************************************************************/
535 static UINT
MENU_GetStartOfNextColumn(
538 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
542 return NO_SELECTED_ITEM
;
544 i
= menu
->FocusedItem
+ 1;
545 if( i
== NO_SELECTED_ITEM
)
548 for( ; i
< menu
->nItems
; ++i
) {
549 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
553 return NO_SELECTED_ITEM
;
557 /******************************************************************************
559 * UINT MENU_GetStartOfPrevColumn(
562 *****************************************************************************/
564 static UINT
MENU_GetStartOfPrevColumn(
567 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
571 return NO_SELECTED_ITEM
;
573 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
574 return NO_SELECTED_ITEM
;
576 /* Find the start of the column */
578 for(i
= menu
->FocusedItem
; i
!= 0 &&
579 !(menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
583 return NO_SELECTED_ITEM
;
585 for(--i
; i
!= 0; --i
) {
586 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
590 TRACE("ret %d.\n", i
);
597 /***********************************************************************
600 * Find a menu item. Return a pointer on the item, and modifies *hmenu
601 * in case the item was in a sub-menu.
603 static MENUITEM
*MENU_FindItem( HMENU
*hmenu
, UINT
*nPos
, UINT wFlags
)
606 MENUITEM
*fallback
= NULL
;
607 UINT fallback_pos
= 0;
610 if ((*hmenu
== (HMENU
)0xffff) || (!(menu
= MENU_GetMenu(*hmenu
)))) return NULL
;
611 if (wFlags
& MF_BYPOSITION
)
613 if (*nPos
>= menu
->nItems
) return NULL
;
614 return &menu
->items
[*nPos
];
618 MENUITEM
*item
= menu
->items
;
619 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
621 if (item
->fType
& MF_POPUP
)
623 HMENU hsubmenu
= item
->hSubMenu
;
624 MENUITEM
*subitem
= MENU_FindItem( &hsubmenu
, nPos
, wFlags
);
630 else if (item
->wID
== *nPos
)
632 /* fallback to this item if nothing else found */
637 else if (item
->wID
== *nPos
)
646 *nPos
= fallback_pos
;
651 /***********************************************************************
654 * Find a Sub menu. Return the position of the submenu, and modifies
655 * *hmenu in case it is found in another sub-menu.
656 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
658 static UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
663 if (((*hmenu
)==(HMENU
)0xffff) ||
664 (!(menu
= MENU_GetMenu(*hmenu
))))
665 return NO_SELECTED_ITEM
;
667 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
668 if(!(item
->fType
& MF_POPUP
)) continue;
669 if (item
->hSubMenu
== hSubTarget
) {
673 HMENU hsubmenu
= item
->hSubMenu
;
674 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
675 if (pos
!= NO_SELECTED_ITEM
) {
681 return NO_SELECTED_ITEM
;
684 /***********************************************************************
687 static void MENU_FreeItemData( MENUITEM
* item
)
690 HeapFree( GetProcessHeap(), 0, item
->text
);
693 /***********************************************************************
694 * MENU_AdjustMenuItemRect
696 * Adjust menu item rectangle according to scrolling state.
699 MENU_AdjustMenuItemRect(const POPUPMENU
*menu
, LPRECT rect
)
701 if (menu
->bScrolling
)
703 UINT arrow_bitmap_height
;
706 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp
), &bmp
);
707 arrow_bitmap_height
= bmp
.bmHeight
;
708 rect
->top
+= arrow_bitmap_height
- menu
->nScrollPos
;
709 rect
->bottom
+= arrow_bitmap_height
- menu
->nScrollPos
;
714 /***********************************************************************
715 * MENU_FindItemByCoords
717 * Find the item at the specified coordinates (screen coords). Does
718 * not work for child windows and therefore should not be called for
719 * an arbitrary system menu.
721 static MENUITEM
*MENU_FindItemByCoords( const POPUPMENU
*menu
,
722 POINT pt
, UINT
*pos
)
728 if (!GetWindowRect(menu
->hWnd
, &rect
)) return NULL
;
729 if (GetWindowLongW( menu
->hWnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) pt
.x
= rect
.right
- 1 - pt
.x
;
730 else pt
.x
-= rect
.left
;
733 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
736 MENU_AdjustMenuItemRect(menu
, &rect
);
737 if (PtInRect(&rect
, pt
))
747 /***********************************************************************
750 * Find the menu item selected by a key press.
751 * Return item id, -1 if none, -2 if we should close the menu.
753 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
754 WCHAR key
, BOOL forceMenuChar
)
756 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key
, key
, hmenu
);
758 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
762 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
763 MENUITEM
*item
= menu
->items
;
769 BOOL cjk
= GetSystemMetrics( SM_DBCSENABLED
);
771 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
775 const WCHAR
*p
= item
->text
- 2;
778 const WCHAR
*q
= p
+ 2;
779 p
= strchrW (q
, '&');
780 if (!p
&& cjk
) p
= strchrW (q
, '\036'); /* Japanese Win16 */
782 while (p
!= NULL
&& p
[1] == '&');
783 if (p
&& (toupperW(p
[1]) == toupperW(key
))) return i
;
787 menuchar
= SendMessageW( hwndOwner
, WM_MENUCHAR
,
788 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
789 if (HIWORD(menuchar
) == MNC_EXECUTE
) return LOWORD(menuchar
);
790 if (HIWORD(menuchar
) == MNC_CLOSE
) return (UINT
)(-2);
796 /***********************************************************************
797 * MENU_GetBitmapItemSize
799 * Get the size of a bitmap item.
801 static void MENU_GetBitmapItemSize( MENUITEM
*lpitem
, SIZE
*size
,
805 HBITMAP bmp
= lpitem
->hbmpItem
;
807 size
->cx
= size
->cy
= 0;
809 /* check if there is a magic menu item associated with this item */
810 switch( (INT_PTR
) bmp
)
812 case (INT_PTR
)HBMMENU_CALLBACK
:
814 MEASUREITEMSTRUCT measItem
;
815 measItem
.CtlType
= ODT_MENU
;
817 measItem
.itemID
= lpitem
->wID
;
818 measItem
.itemWidth
= lpitem
->rect
.right
- lpitem
->rect
.left
;
819 measItem
.itemHeight
= lpitem
->rect
.bottom
- lpitem
->rect
.top
;
820 measItem
.itemData
= lpitem
->dwItemData
;
821 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&measItem
);
822 size
->cx
= measItem
.itemWidth
;
823 size
->cy
= measItem
.itemHeight
;
827 case (INT_PTR
)HBMMENU_SYSTEM
:
828 if (lpitem
->dwItemData
)
830 bmp
= (HBITMAP
)lpitem
->dwItemData
;
834 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
835 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
836 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
837 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
838 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
839 size
->cx
= GetSystemMetrics( SM_CYMENU
) - 4;
842 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
843 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
844 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
845 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
846 size
->cx
= GetSystemMetrics( SM_CXMENUSIZE
);
847 size
->cy
= GetSystemMetrics( SM_CYMENUSIZE
);
850 if (GetObjectW(bmp
, sizeof(bm
), &bm
))
852 size
->cx
= bm
.bmWidth
;
853 size
->cy
= bm
.bmHeight
;
857 /***********************************************************************
858 * MENU_DrawBitmapItem
860 * Draw a bitmap item.
862 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
,
863 HMENU hmenu
, HWND hwndOwner
, UINT odaction
, BOOL menuBar
)
869 int w
= rect
->right
- rect
->left
;
870 int h
= rect
->bottom
- rect
->top
;
873 HBITMAP hbmToDraw
= lpitem
->hbmpItem
;
876 /* Check if there is a magic menu item associated with this item */
877 if (IS_MAGIC_BITMAP(hbmToDraw
))
883 switch((INT_PTR
)hbmToDraw
)
885 case (INT_PTR
)HBMMENU_SYSTEM
:
886 if (lpitem
->dwItemData
)
888 bmp
= (HBITMAP
)lpitem
->dwItemData
;
889 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
893 static HBITMAP hBmpSysMenu
;
895 if (!hBmpSysMenu
) hBmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
897 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
898 /* only use right half of the bitmap */
899 bmp_xoffset
= bm
.bmWidth
/ 2;
900 bm
.bmWidth
-= bmp_xoffset
;
903 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
904 flags
= DFCS_CAPTIONRESTORE
;
906 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
907 flags
= DFCS_CAPTIONMIN
;
909 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
910 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
912 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
913 flags
= DFCS_CAPTIONCLOSE
;
915 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
916 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
918 case (INT_PTR
)HBMMENU_CALLBACK
:
920 DRAWITEMSTRUCT drawItem
;
921 drawItem
.CtlType
= ODT_MENU
;
923 drawItem
.itemID
= lpitem
->wID
;
924 drawItem
.itemAction
= odaction
;
925 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
926 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
927 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
928 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
929 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
930 drawItem
.hwndItem
= (HWND
)hmenu
;
932 drawItem
.itemData
= lpitem
->dwItemData
;
933 drawItem
.rcItem
= *rect
;
934 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
938 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
941 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
944 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
947 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
951 FIXME("Magic %p not implemented\n", hbmToDraw
);
956 /* draw the magic bitmaps using marlett font characters */
957 /* FIXME: fontsize and the position (x,y) could probably be better */
958 HFONT hfont
, hfontsav
;
959 LOGFONTW logfont
= { 0, 0, 0, 0, FW_NORMAL
,
960 0, 0, 0, SYMBOL_CHARSET
, 0, 0, 0, 0,
961 { 'M','a','r','l','e','t','t',0 } };
962 logfont
.lfHeight
= min( h
, w
) - 5 ;
963 TRACE(" height %d rect %s\n", logfont
.lfHeight
, wine_dbgstr_rect( rect
));
964 hfont
= CreateFontIndirectW( &logfont
);
965 hfontsav
= SelectObject(hdc
, hfont
);
966 TextOutW( hdc
, rect
->left
, rect
->top
+ 2, &bmchr
, 1);
967 SelectObject(hdc
, hfontsav
);
968 DeleteObject( hfont
);
973 InflateRect( &r
, -1, -1 );
974 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
975 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
980 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
983 hdcMem
= CreateCompatibleDC( hdc
);
984 SelectObject( hdcMem
, bmp
);
986 /* handle fontsize > bitmap_height */
987 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
989 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
990 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmpItem
)
991 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
992 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
997 /***********************************************************************
1000 * Calculate the size of the menu item and store it in lpitem->rect.
1002 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
1003 INT orgX
, INT orgY
, BOOL menuBar
, POPUPMENU
* lppop
)
1006 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1007 UINT arrow_bitmap_width
;
1011 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
1012 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
1013 (menuBar
? " (MenuBar)" : ""));
1015 GetObjectW( get_arrow_bitmap(), sizeof(bm
), &bm
);
1016 arrow_bitmap_width
= bm
.bmWidth
;
1018 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1019 if( !menucharsize
.cx
) {
1020 menucharsize
.cx
= GdiGetCharDimensions( hdc
, NULL
, &menucharsize
.cy
);
1021 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1022 * but it is unlikely an application will depend on that */
1023 ODitemheight
= HIWORD( GetDialogBaseUnits());
1026 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
1028 if (lpitem
->fType
& MF_OWNERDRAW
)
1030 MEASUREITEMSTRUCT mis
;
1031 mis
.CtlType
= ODT_MENU
;
1033 mis
.itemID
= lpitem
->wID
;
1034 mis
.itemData
= lpitem
->dwItemData
;
1035 mis
.itemHeight
= ODitemheight
;
1037 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
1038 /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
1039 * width of a menufont character to the width of an owner-drawn menu.
1041 lpitem
->rect
.right
+= mis
.itemWidth
+ 2 * menucharsize
.cx
;
1043 /* under at least win95 you seem to be given a standard
1044 height for the menu and the height value is ignored */
1045 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
1047 lpitem
->rect
.bottom
+= mis
.itemHeight
;
1049 TRACE("id=%04lx size=%dx%d\n",
1050 lpitem
->wID
, lpitem
->rect
.right
-lpitem
->rect
.left
,
1051 lpitem
->rect
.bottom
-lpitem
->rect
.top
);
1055 if (lpitem
->fType
& MF_SEPARATOR
)
1057 lpitem
->rect
.bottom
+= GetSystemMetrics( SM_CYMENUSIZE
)/2;
1059 lpitem
->rect
.right
+= arrow_bitmap_width
+ menucharsize
.cx
;
1067 if (lpitem
->hbmpItem
) {
1070 MENU_GetBitmapItemSize(lpitem
, &size
, hwndOwner
);
1071 /* Keep the size of the bitmap in callback mode to be able
1072 * to draw it correctly */
1073 lpitem
->bmpsize
= size
;
1074 lppop
->textOffset
= max( lppop
->textOffset
, size
.cx
);
1075 lpitem
->rect
.right
+= size
.cx
+ 2;
1076 itemheight
= size
.cy
+ 2;
1078 if( !(lppop
->dwStyle
& MNS_NOCHECK
))
1079 lpitem
->rect
.right
+= check_bitmap_width
;
1080 lpitem
->rect
.right
+= 4 + menucharsize
.cx
;
1081 lpitem
->xTab
= lpitem
->rect
.right
;
1082 lpitem
->rect
.right
+= arrow_bitmap_width
;
1083 } else if (lpitem
->hbmpItem
) { /* menuBar */
1086 MENU_GetBitmapItemSize( lpitem
, &size
, hwndOwner
);
1087 lpitem
->bmpsize
= size
;
1088 lpitem
->rect
.right
+= size
.cx
;
1089 if( lpitem
->text
) lpitem
->rect
.right
+= 2;
1090 itemheight
= size
.cy
;
1093 /* it must be a text item - unless it's the system menu */
1094 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->text
) {
1095 HFONT hfontOld
= NULL
;
1096 RECT rc
= lpitem
->rect
;
1097 LONG txtheight
, txtwidth
;
1099 if ( lpitem
->fState
& MFS_DEFAULT
) {
1100 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1103 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1104 DT_SINGLELINE
|DT_CALCRECT
);
1105 lpitem
->rect
.right
+= rc
.right
- rc
.left
;
1106 itemheight
= max( max( itemheight
, txtheight
),
1107 GetSystemMetrics( SM_CYMENU
) - 1);
1108 lpitem
->rect
.right
+= 2 * menucharsize
.cx
;
1110 if ((p
= strchrW( lpitem
->text
, '\t' )) != NULL
) {
1113 int n
= (int)( p
- lpitem
->text
);
1114 /* Item contains a tab (only meaningful in popup menus) */
1115 /* get text size before the tab */
1116 txtheight
= DrawTextW( hdc
, lpitem
->text
, n
, &rc
,
1117 DT_SINGLELINE
|DT_CALCRECT
);
1118 txtwidth
= rc
.right
- rc
.left
;
1119 p
+= 1; /* advance past the Tab */
1120 /* get text size after the tab */
1121 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1122 DT_SINGLELINE
|DT_CALCRECT
);
1123 lpitem
->xTab
+= txtwidth
;
1124 txtheight
= max( txtheight
, tmpheight
);
1125 txtwidth
+= menucharsize
.cx
+ /* space for the tab */
1126 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1128 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1129 DT_SINGLELINE
|DT_CALCRECT
);
1130 txtwidth
= rc
.right
- rc
.left
;
1131 lpitem
->xTab
+= txtwidth
;
1133 lpitem
->rect
.right
+= 2 + txtwidth
;
1134 itemheight
= max( itemheight
,
1135 max( txtheight
+ 2, menucharsize
.cy
+ 4));
1137 if (hfontOld
) SelectObject (hdc
, hfontOld
);
1138 } else if( menuBar
) {
1139 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
1141 lpitem
->rect
.bottom
+= itemheight
;
1142 TRACE("%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1146 /***********************************************************************
1147 * MENU_GetMaxPopupHeight
1150 MENU_GetMaxPopupHeight(const POPUPMENU
*lppop
)
1153 return lppop
->cyMax
;
1154 return GetSystemMetrics(SM_CYSCREEN
) - GetSystemMetrics(SM_CYBORDER
);
1158 /***********************************************************************
1159 * MENU_PopupMenuCalcSize
1161 * Calculate the size of a popup menu.
1163 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
)
1168 BOOL textandbmp
= FALSE
;
1169 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
, maxHeight
;
1171 lppop
->Width
= lppop
->Height
= 0;
1172 if (lppop
->nItems
== 0) return;
1175 SelectObject( hdc
, get_menu_font(FALSE
));
1180 lppop
->textOffset
= 0;
1182 while (start
< lppop
->nItems
)
1184 lpitem
= &lppop
->items
[start
];
1186 if( lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1187 orgX
+= MENU_COL_SPACE
;
1188 orgY
= MENU_TOP_MARGIN
;
1190 maxTab
= maxTabWidth
= 0;
1191 /* Parse items until column break or end of menu */
1192 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1195 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1197 MENU_CalcItemSize( hdc
, lpitem
, lppop
->hwndOwner
, orgX
, orgY
, FALSE
, lppop
);
1198 maxX
= max( maxX
, lpitem
->rect
.right
);
1199 orgY
= lpitem
->rect
.bottom
;
1200 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1202 maxTab
= max( maxTab
, lpitem
->xTab
);
1203 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1205 if( lpitem
->text
&& lpitem
->hbmpItem
) textandbmp
= TRUE
;
1208 /* Finish the column (set all items to the largest width found) */
1209 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
1210 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1212 lpitem
->rect
.right
= maxX
;
1213 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1214 lpitem
->xTab
= maxTab
;
1217 lppop
->Height
= max( lppop
->Height
, orgY
);
1220 lppop
->Width
= maxX
;
1221 /* if none of the items have both text and bitmap then
1222 * the text and bitmaps are all aligned on the left. If there is at
1223 * least one item with both text and bitmap then bitmaps are
1224 * on the left and texts left aligned with the right hand side
1226 if( !textandbmp
) lppop
->textOffset
= 0;
1228 /* space for 3d border */
1229 lppop
->Height
+= MENU_BOTTOM_MARGIN
;
1232 /* Adjust popup height if it exceeds maximum */
1233 maxHeight
= MENU_GetMaxPopupHeight(lppop
);
1234 lppop
->nTotalHeight
= lppop
->Height
- MENU_TOP_MARGIN
;
1235 if (lppop
->Height
>= maxHeight
)
1237 lppop
->Height
= maxHeight
;
1238 lppop
->bScrolling
= TRUE
;
1242 lppop
->bScrolling
= FALSE
;
1245 ReleaseDC( 0, hdc
);
1249 /***********************************************************************
1250 * MENU_MenuBarCalcSize
1252 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1253 * height is off by 1 pixel which causes lengthy window relocations when
1254 * active document window is maximized/restored.
1256 * Calculate the size of the menu bar.
1258 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1259 LPPOPUPMENU lppop
, HWND hwndOwner
)
1262 UINT start
, i
, helpPos
;
1263 int orgX
, orgY
, maxY
;
1265 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1266 if (lppop
->nItems
== 0) return;
1267 TRACE("lprect %p %s\n", lprect
, wine_dbgstr_rect( lprect
));
1268 lppop
->Width
= lprect
->right
- lprect
->left
;
1270 maxY
= lprect
->top
+1;
1273 lppop
->textOffset
= 0;
1274 while (start
< lppop
->nItems
)
1276 lpitem
= &lppop
->items
[start
];
1277 orgX
= lprect
->left
;
1280 /* Parse items until line break or end of menu */
1281 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1283 if ((helpPos
== ~0U) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1285 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1287 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
1288 debug_print_menuitem (" item: ", lpitem
, "");
1289 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
, lppop
);
1291 if (lpitem
->rect
.right
> lprect
->right
)
1293 if (i
!= start
) break;
1294 else lpitem
->rect
.right
= lprect
->right
;
1296 maxY
= max( maxY
, lpitem
->rect
.bottom
);
1297 orgX
= lpitem
->rect
.right
;
1300 /* Finish the line (set all items to the largest height found) */
1301 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= maxY
;
1304 lprect
->bottom
= maxY
;
1305 lppop
->Height
= lprect
->bottom
- lprect
->top
;
1307 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1308 /* the last item (if several lines, only move the last line) */
1309 if (helpPos
== ~0U) return;
1310 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1311 orgY
= lpitem
->rect
.top
;
1312 orgX
= lprect
->right
;
1313 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1314 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1315 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1316 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1317 lpitem
->rect
.right
= orgX
;
1318 orgX
= lpitem
->rect
.left
;
1323 /***********************************************************************
1324 * MENU_DrawScrollArrows
1326 * Draw scroll arrows.
1329 MENU_DrawScrollArrows(const POPUPMENU
*lppop
, HDC hdc
)
1331 HDC hdcMem
= CreateCompatibleDC(hdc
);
1332 HBITMAP hOrigBitmap
;
1333 UINT arrow_bitmap_width
, arrow_bitmap_height
;
1337 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1338 arrow_bitmap_width
= bmp
.bmWidth
;
1339 arrow_bitmap_height
= bmp
.bmHeight
;
1342 if (lppop
->nScrollPos
)
1343 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_bitmap());
1345 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_inactive_bitmap());
1348 rect
.right
= lppop
->Width
;
1349 rect
.bottom
= arrow_bitmap_height
;
1350 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1351 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2, 0,
1352 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1353 rect
.top
= lppop
->Height
- arrow_bitmap_height
;
1354 rect
.bottom
= lppop
->Height
;
1355 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1356 if (lppop
->nScrollPos
< lppop
->nTotalHeight
- (MENU_GetMaxPopupHeight(lppop
) - 2 * arrow_bitmap_height
))
1357 SelectObject(hdcMem
, get_down_arrow_bitmap());
1359 SelectObject(hdcMem
, get_down_arrow_inactive_bitmap());
1360 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2,
1361 lppop
->Height
- arrow_bitmap_height
,
1362 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1363 SelectObject(hdcMem
, hOrigBitmap
);
1368 /***********************************************************************
1371 * Draws the popup-menu arrow.
1373 static void draw_popup_arrow( HDC hdc
, RECT rect
, UINT arrow_bitmap_width
,
1374 UINT arrow_bitmap_height
)
1376 HDC hdcMem
= CreateCompatibleDC( hdc
);
1377 HBITMAP hOrigBitmap
;
1379 hOrigBitmap
= SelectObject( hdcMem
, get_arrow_bitmap() );
1380 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1381 (rect
.top
+ rect
.bottom
- arrow_bitmap_height
) / 2,
1382 arrow_bitmap_width
, arrow_bitmap_height
,
1383 hdcMem
, 0, 0, SRCCOPY
);
1384 SelectObject( hdcMem
, hOrigBitmap
);
1387 /***********************************************************************
1390 * Draw a single menu item.
1392 static void MENU_DrawMenuItem( HWND hwnd
, HMENU hmenu
, HWND hwndOwner
, HDC hdc
, MENUITEM
*lpitem
,
1393 UINT height
, BOOL menuBar
, UINT odaction
)
1396 BOOL flat_menu
= FALSE
;
1398 UINT arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
1399 POPUPMENU
*menu
= MENU_GetMenu(hmenu
);
1402 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1406 GetObjectW( get_arrow_bitmap(), sizeof(bmp
), &bmp
);
1407 arrow_bitmap_width
= bmp
.bmWidth
;
1408 arrow_bitmap_height
= bmp
.bmHeight
;
1411 if (lpitem
->fType
& MF_SYSMENU
)
1413 if( !IsIconic(hwnd
) )
1414 NC_DrawSysButton( hwnd
, hdc
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
) );
1418 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1419 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
1423 if (lpitem
->fState
& MF_HILITE
)
1425 if(menuBar
&& !flat_menu
) {
1426 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1427 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1429 if(lpitem
->fState
& MF_GRAYED
)
1430 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1432 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1433 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1438 if (lpitem
->fState
& MF_GRAYED
)
1439 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1441 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1442 SetBkColor( hdc
, GetSysColor( bkgnd
) );
1445 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1446 rect
= lpitem
->rect
;
1447 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu
), &rect
);
1449 if (lpitem
->fType
& MF_OWNERDRAW
)
1452 ** Experimentation under Windows reveals that an owner-drawn
1453 ** menu is given the rectangle which includes the space it requested
1454 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1455 ** and a popup-menu arrow. This is the value of lpitem->rect.
1456 ** Windows will leave all drawing to the application except for
1457 ** the popup-menu arrow. Windows always draws that itself, after
1458 ** the menu owner has finished drawing.
1461 COLORREF old_bk
, old_text
;
1463 dis
.CtlType
= ODT_MENU
;
1465 dis
.itemID
= lpitem
->wID
;
1466 dis
.itemData
= lpitem
->dwItemData
;
1468 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1469 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1470 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1471 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1472 dis
.hwndItem
= (HWND
)hmenu
;
1475 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1476 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner
,
1477 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1478 dis
.hDC
, wine_dbgstr_rect( &dis
.rcItem
));
1479 old_bk
= GetBkColor( hdc
);
1480 old_text
= GetTextColor( hdc
);
1481 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1482 /* Draw the popup-menu arrow */
1483 SetBkColor( hdc
, old_bk
);
1484 SetTextColor( hdc
, old_text
);
1485 if (lpitem
->fType
& MF_POPUP
)
1486 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1487 arrow_bitmap_height
);
1491 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
1493 if (lpitem
->fState
& MF_HILITE
)
1497 InflateRect (&rect
, -1, -1);
1498 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
1499 InflateRect (&rect
, 1, 1);
1500 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1505 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1507 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1511 FillRect( hdc
, &rect
, GetSysColorBrush(bkgnd
) );
1513 SetBkMode( hdc
, TRANSPARENT
);
1515 /* vertical separator */
1516 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1521 rc
.left
-= MENU_COL_SPACE
/ 2 + 1;
1523 rc
.bottom
= height
- 3;
1526 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1527 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1528 LineTo( hdc
, rc
.left
, rc
.bottom
);
1529 SelectObject( hdc
, oldPen
);
1532 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1535 /* horizontal separator */
1536 if (lpitem
->fType
& MF_SEPARATOR
)
1543 rc
.top
= ( rc
.top
+ rc
.bottom
) / 2;
1546 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1547 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1548 LineTo( hdc
, rc
.right
, rc
.top
);
1549 SelectObject( hdc
, oldPen
);
1552 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1556 /* helper lines for debugging */
1557 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1558 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1559 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1560 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1563 if (lpitem
->hbmpItem
) {
1564 /* calculate the bitmap rectangle in coordinates relative
1565 * to the item rectangle */
1567 if( lpitem
->hbmpItem
== HBMMENU_CALLBACK
)
1570 bmprc
.left
= lpitem
->text
? menucharsize
.cx
: 0;
1572 else if (menu
->dwStyle
& MNS_NOCHECK
)
1574 else if (menu
->dwStyle
& MNS_CHECKORBMP
)
1577 bmprc
.left
= 4 + GetSystemMetrics(SM_CXMENUCHECK
);
1578 bmprc
.right
= bmprc
.left
+ lpitem
->bmpsize
.cx
;
1579 if( menuBar
&& !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1582 bmprc
.top
= (rect
.bottom
- rect
.top
-
1583 lpitem
->bmpsize
.cy
) / 2;
1584 bmprc
.bottom
= bmprc
.top
+ lpitem
->bmpsize
.cy
;
1590 INT y
= rect
.top
+ rect
.bottom
;
1592 BOOL checked
= FALSE
;
1593 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1594 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1595 /* Draw the check mark
1598 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1600 if( !(menu
->dwStyle
& MNS_NOCHECK
)) {
1601 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
:
1602 lpitem
->hUnCheckBit
;
1603 if (bm
) /* we have a custom bitmap */
1605 HDC hdcMem
= CreateCompatibleDC( hdc
);
1607 SelectObject( hdcMem
, bm
);
1608 BitBlt( hdc
, rc
.left
, (y
- check_bitmap_height
) / 2,
1609 check_bitmap_width
, check_bitmap_height
,
1610 hdcMem
, 0, 0, SRCCOPY
);
1614 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1617 HBITMAP bm
= CreateBitmap( check_bitmap_width
,
1618 check_bitmap_height
, 1, 1, NULL
);
1619 HDC hdcMem
= CreateCompatibleDC( hdc
);
1621 SelectObject( hdcMem
, bm
);
1622 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1623 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1624 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1625 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1626 BitBlt( hdc
, rc
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1627 hdcMem
, 0, 0, SRCCOPY
);
1633 if( lpitem
->hbmpItem
&&
1634 !( checked
&& (menu
->dwStyle
& MNS_CHECKORBMP
))) {
1636 /* some applications make this assumption on the DC's origin */
1637 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1638 MENU_DrawBitmapItem(hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1640 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1642 /* Draw the popup-menu arrow */
1643 if (lpitem
->fType
& MF_POPUP
)
1644 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1645 arrow_bitmap_height
);
1647 if( !(menu
->dwStyle
& MNS_NOCHECK
))
1648 rect
.left
+= check_bitmap_width
;
1649 rect
.right
-= arrow_bitmap_width
;
1651 else if( lpitem
->hbmpItem
)
1652 { /* Draw the bitmap */
1655 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1656 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1658 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1660 /* process text if present */
1666 UINT uFormat
= (menuBar
) ?
1667 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1668 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1670 if( !(menu
->dwStyle
& MNS_CHECKORBMP
))
1671 rect
.left
+= menu
->textOffset
;
1673 if ( lpitem
->fState
& MFS_DEFAULT
)
1675 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1679 if( lpitem
->hbmpItem
)
1680 rect
.left
+= lpitem
->bmpsize
.cx
;
1681 if( !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1682 rect
.left
+= menucharsize
.cx
;
1683 rect
.right
-= menucharsize
.cx
;
1686 for (i
= 0; lpitem
->text
[i
]; i
++)
1687 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
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
, &rect
, uFormat
);
1697 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1699 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1702 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1704 /* paint the shortcut text */
1705 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1707 if (lpitem
->text
[i
] == '\t')
1709 rect
.left
= lpitem
->xTab
;
1710 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1714 rect
.right
= lpitem
->xTab
;
1715 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1718 if(lpitem
->fState
& MF_GRAYED
)
1720 if (!(lpitem
->fState
& MF_HILITE
) )
1722 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1723 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1724 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1725 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1727 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1729 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1733 SelectObject (hdc
, hfontOld
);
1738 /***********************************************************************
1739 * MENU_DrawPopupMenu
1741 * Paint a popup menu.
1743 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1745 HBRUSH hPrevBrush
, brush
= GetSysColorBrush( COLOR_MENU
);
1747 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
1749 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1751 GetClientRect( hwnd
, &rect
);
1753 if (menu
&& menu
->hbrBack
) brush
= menu
->hbrBack
;
1754 if ((hPrevBrush
= SelectObject( hdc
, brush
))
1755 && SelectObject( hdc
, get_menu_font(FALSE
) ))
1759 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1761 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1764 BOOL flat_menu
= FALSE
;
1766 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1768 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1770 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1774 TRACE("hmenu %p Style %08x\n", hmenu
, menu
->dwStyle
);
1775 /* draw menu items */
1782 for( u
= menu
->nItems
; u
> 0; u
--, item
++)
1783 MENU_DrawMenuItem( hwnd
, hmenu
, menu
->hwndOwner
, hdc
,
1784 item
, menu
->Height
, FALSE
, ODA_DRAWENTIRE
);
1786 /* draw scroll arrows */
1787 if (menu
->bScrolling
)
1788 MENU_DrawScrollArrows(menu
, hdc
);
1792 SelectObject( hdc
, hPrevBrush
);
1797 /***********************************************************************
1800 * Paint a menu bar. Returns the height of the menu bar.
1801 * called from [windows/nonclient.c]
1803 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
)
1806 HMENU hMenu
= GetMenu(hwnd
);
1808 lppop
= MENU_GetMenu( hMenu
);
1809 if (lppop
== NULL
|| lprect
== NULL
)
1811 return GetSystemMetrics(SM_CYMENU
);
1814 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1818 /***********************************************************************
1821 * Popup menu initialization before WM_ENTERMENULOOP.
1823 static BOOL
MENU_InitPopup( HWND hwndOwner
, HMENU hmenu
, UINT flags
)
1828 TRACE("owner=%p hmenu=%p\n", hwndOwner
, hmenu
);
1830 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1832 /* store the owner for DrawItem */
1833 if (!IsWindow( hwndOwner
))
1835 SetLastError( ERROR_INVALID_WINDOW_HANDLE
);
1838 menu
->hwndOwner
= hwndOwner
;
1840 if (flags
& TPM_LAYOUTRTL
)
1841 ex_style
= WS_EX_LAYOUTRTL
;
1843 /* NOTE: In Windows, top menu popup is not owned. */
1844 menu
->hWnd
= CreateWindowExW( ex_style
, (LPCWSTR
)POPUPMENU_CLASS_ATOM
, NULL
,
1845 WS_POPUP
, 0, 0, 0, 0,
1846 hwndOwner
, 0, (HINSTANCE
)GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1848 if( !menu
->hWnd
) return FALSE
;
1853 /***********************************************************************
1856 * Display a popup menu.
1858 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
, UINT flags
,
1859 INT x
, INT y
, INT xanchor
, INT yanchor
)
1867 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1868 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1870 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1871 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1873 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1874 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1877 menu
->nScrollPos
= 0;
1878 MENU_PopupMenuCalcSize( menu
);
1880 /* adjust popup menu pos so that it fits within the desktop */
1882 width
= menu
->Width
+ GetSystemMetrics(SM_CXBORDER
);
1883 height
= menu
->Height
+ GetSystemMetrics(SM_CYBORDER
);
1885 /* FIXME: should use item rect */
1888 monitor
= MonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
1889 info
.cbSize
= sizeof(info
);
1890 GetMonitorInfoW( monitor
, &info
);
1892 if (flags
& TPM_LAYOUTRTL
)
1893 flags
^= TPM_RIGHTALIGN
;
1895 if( flags
& TPM_RIGHTALIGN
) x
-= width
;
1896 if( flags
& TPM_CENTERALIGN
) x
-= width
/ 2;
1898 if( flags
& TPM_BOTTOMALIGN
) y
-= height
;
1899 if( flags
& TPM_VCENTERALIGN
) y
-= height
/ 2;
1901 if( x
+ width
> info
.rcWork
.right
)
1903 if( xanchor
&& x
>= width
- xanchor
)
1904 x
-= width
- xanchor
;
1906 if( x
+ width
> info
.rcWork
.right
)
1907 x
= info
.rcWork
.right
- width
;
1909 if( x
< info
.rcWork
.left
) x
= info
.rcWork
.left
;
1911 if( y
+ height
> info
.rcWork
.bottom
)
1913 if( yanchor
&& y
>= height
+ yanchor
)
1914 y
-= height
+ yanchor
;
1916 if( y
+ height
> info
.rcWork
.bottom
)
1917 y
= info
.rcWork
.bottom
- height
;
1919 if( y
< info
.rcWork
.top
) y
= info
.rcWork
.top
;
1922 top_popup
= menu
->hWnd
;
1923 top_popup_hmenu
= hmenu
;
1925 /* Display the window */
1927 SetWindowPos( menu
->hWnd
, HWND_TOPMOST
, x
, y
, width
, height
,
1928 SWP_SHOWWINDOW
| SWP_NOACTIVATE
);
1929 UpdateWindow( menu
->hWnd
);
1934 /***********************************************************************
1935 * MENU_EnsureMenuItemVisible
1938 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop
, UINT wIndex
, HDC hdc
)
1940 if (lppop
->bScrolling
)
1942 MENUITEM
*item
= &lppop
->items
[wIndex
];
1943 UINT nMaxHeight
= MENU_GetMaxPopupHeight(lppop
);
1944 UINT nOldPos
= lppop
->nScrollPos
;
1946 UINT arrow_bitmap_height
;
1949 GetClientRect(lppop
->hWnd
, &rc
);
1951 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1952 arrow_bitmap_height
= bmp
.bmHeight
;
1954 rc
.top
+= arrow_bitmap_height
;
1955 rc
.bottom
-= arrow_bitmap_height
+ MENU_BOTTOM_MARGIN
;
1957 nMaxHeight
-= GetSystemMetrics(SM_CYBORDER
) + 2 * arrow_bitmap_height
;
1958 if (item
->rect
.bottom
> lppop
->nScrollPos
+ nMaxHeight
)
1961 lppop
->nScrollPos
= item
->rect
.bottom
- nMaxHeight
;
1962 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1963 MENU_DrawScrollArrows(lppop
, hdc
);
1965 else if (item
->rect
.top
- MENU_TOP_MARGIN
< lppop
->nScrollPos
)
1967 lppop
->nScrollPos
= item
->rect
.top
- MENU_TOP_MARGIN
;
1968 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1969 MENU_DrawScrollArrows(lppop
, hdc
);
1975 /***********************************************************************
1978 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1979 BOOL sendMenuSelect
, HMENU topmenu
)
1984 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1986 lppop
= MENU_GetMenu( hmenu
);
1987 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1989 if (lppop
->FocusedItem
== wIndex
) return;
1990 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1991 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1993 top_popup
= lppop
->hWnd
;
1994 top_popup_hmenu
= hmenu
;
1997 SelectObject( hdc
, get_menu_font(FALSE
));
1999 /* Clear previous highlighted item */
2000 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
2002 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
2003 MENU_DrawMenuItem(lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,&lppop
->items
[lppop
->FocusedItem
],
2004 lppop
->Height
, !(lppop
->wFlags
& MF_POPUP
),
2008 /* Highlight new item (if any) */
2009 lppop
->FocusedItem
= wIndex
;
2010 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
2012 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
2013 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
2014 MENU_EnsureMenuItemVisible(lppop
, wIndex
, hdc
);
2015 MENU_DrawMenuItem( lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,
2016 &lppop
->items
[wIndex
], lppop
->Height
,
2017 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
2021 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
2022 SendMessageW( hwndOwner
, WM_MENUSELECT
,
2023 MAKEWPARAM(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
2024 ip
->fType
| ip
->fState
|
2025 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
2028 else if (sendMenuSelect
) {
2031 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
2032 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
2033 MENUITEM
*ip
= &ptm
->items
[pos
];
2034 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKEWPARAM(pos
,
2035 ip
->fType
| ip
->fState
|
2036 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
2040 ReleaseDC( lppop
->hWnd
, hdc
);
2044 /***********************************************************************
2045 * MENU_MoveSelection
2047 * Moves currently selected item according to the offset parameter.
2048 * If there is no selection then it should select the last item if
2049 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2051 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
2056 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
2058 menu
= MENU_GetMenu( hmenu
);
2059 if ((!menu
) || (!menu
->items
)) return;
2061 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2063 if( menu
->nItems
== 1 ) return; else
2064 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
2066 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2068 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2073 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
2074 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
2075 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2077 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2083 /**********************************************************************
2086 * Insert (allocate) a new item into a menu.
2088 static MENUITEM
*MENU_InsertItem( HMENU hMenu
, UINT pos
, UINT flags
)
2093 if (!(menu
= MENU_GetMenu(hMenu
)))
2096 /* Find where to insert new item */
2098 if (flags
& MF_BYPOSITION
) {
2099 if (pos
> menu
->nItems
)
2102 if (!MENU_FindItem( &hMenu
, &pos
, flags
))
2105 if (!(menu
= MENU_GetMenu( hMenu
)))
2110 /* Make sure that MDI system buttons stay on the right side.
2111 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2112 * regardless of their id.
2114 while (pos
> 0 && (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
>= (INT_PTR
)HBMMENU_SYSTEM
&&
2115 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
<= (INT_PTR
)HBMMENU_MBAR_CLOSE_D
)
2118 TRACE("inserting at %u flags %x\n", pos
, flags
);
2120 /* Create new items array */
2122 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
2125 WARN("allocation failed\n" );
2128 if (menu
->nItems
> 0)
2130 /* Copy the old array into the new one */
2131 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
2132 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
2133 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
2134 HeapFree( GetProcessHeap(), 0, menu
->items
);
2136 menu
->items
= newItems
;
2138 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
2139 menu
->Height
= 0; /* force size recalculate */
2140 return &newItems
[pos
];
2144 /**********************************************************************
2145 * MENU_ParseResource
2147 * Parse a standard menu resource and add items to the menu.
2148 * Return a pointer to the end of the resource.
2150 * NOTE: flags is equivalent to the mtOption field
2152 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
)
2160 flags
= GET_WORD(res
);
2161 end_flag
= flags
& MF_END
;
2162 /* Remove MF_END because it has the same value as MF_HILITE */
2164 res
+= sizeof(WORD
);
2165 if (!(flags
& MF_POPUP
))
2168 res
+= sizeof(WORD
);
2171 res
+= (strlenW(str
) + 1) * sizeof(WCHAR
);
2172 if (flags
& MF_POPUP
)
2174 HMENU hSubMenu
= CreatePopupMenu();
2175 if (!hSubMenu
) return NULL
;
2176 if (!(res
= MENU_ParseResource( res
, hSubMenu
))) return NULL
;
2177 AppendMenuW( hMenu
, flags
, (UINT_PTR
)hSubMenu
, str
);
2179 else /* Not a popup */
2181 AppendMenuW( hMenu
, flags
, id
, *str
? str
: NULL
);
2183 } while (!end_flag
);
2188 /**********************************************************************
2189 * MENUEX_ParseResource
2191 * Parse an extended menu resource and add items to the menu.
2192 * Return a pointer to the end of the resource.
2194 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2200 mii
.cbSize
= sizeof(mii
);
2201 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
2202 mii
.fType
= GET_DWORD(res
);
2203 res
+= sizeof(DWORD
);
2204 mii
.fState
= GET_DWORD(res
);
2205 res
+= sizeof(DWORD
);
2206 mii
.wID
= GET_DWORD(res
);
2207 res
+= sizeof(DWORD
);
2208 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
2209 res
+= sizeof(WORD
);
2210 /* Align the text on a word boundary. */
2211 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2212 mii
.dwTypeData
= (LPWSTR
) res
;
2213 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2214 /* Align the following fields on a dword boundary. */
2215 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2217 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2218 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
2220 if (resinfo
& 1) { /* Pop-up? */
2221 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2222 res
+= sizeof(DWORD
);
2223 mii
.hSubMenu
= CreatePopupMenu();
2226 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2227 DestroyMenu(mii
.hSubMenu
);
2230 mii
.fMask
|= MIIM_SUBMENU
;
2231 mii
.fType
|= MF_POPUP
;
2233 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2235 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2236 mii
.wID
, mii
.fType
);
2237 mii
.fType
|= MF_SEPARATOR
;
2239 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2240 } while (!(resinfo
& MF_END
));
2245 /***********************************************************************
2248 * Return the handle of the selected sub-popup menu (if any).
2250 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2255 menu
= MENU_GetMenu( hmenu
);
2257 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2259 item
= &menu
->items
[menu
->FocusedItem
];
2260 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2261 return item
->hSubMenu
;
2266 /***********************************************************************
2267 * MENU_HideSubPopups
2269 * Hide the sub-popup menus of this menu.
2271 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2272 BOOL sendMenuSelect
, UINT wFlags
)
2274 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2276 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2278 if (menu
&& top_popup
)
2284 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2286 item
= &menu
->items
[menu
->FocusedItem
];
2287 if (!(item
->fType
& MF_POPUP
) ||
2288 !(item
->fState
& MF_MOUSESELECT
)) return;
2289 item
->fState
&= ~MF_MOUSESELECT
;
2290 hsubmenu
= item
->hSubMenu
;
2293 if (!(submenu
= MENU_GetMenu( hsubmenu
))) return;
2294 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
, wFlags
);
2295 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2296 DestroyWindow( submenu
->hWnd
);
2299 if (!(wFlags
& TPM_NONOTIFY
))
2300 SendMessageW( hwndOwner
, WM_UNINITMENUPOPUP
, (WPARAM
)hsubmenu
,
2301 MAKELPARAM(0, IS_SYSTEM_MENU(submenu
)) );
2306 /***********************************************************************
2309 * Display the sub-menu of the selected item of this menu.
2310 * Return the handle of the submenu, or hmenu if no submenu to display.
2312 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2313 BOOL selectFirst
, UINT wFlags
)
2320 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2322 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2324 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2326 item
= &menu
->items
[menu
->FocusedItem
];
2327 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2330 /* message must be sent before using item,
2331 because nearly everything may be changed by the application ! */
2333 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2334 if (!(wFlags
& TPM_NONOTIFY
))
2335 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2336 MAKELPARAM( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2338 item
= &menu
->items
[menu
->FocusedItem
];
2341 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2342 if (!(item
->fState
& MF_HILITE
))
2344 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2345 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2347 SelectObject( hdc
, get_menu_font(FALSE
));
2349 item
->fState
|= MF_HILITE
;
2350 MENU_DrawMenuItem( menu
->hWnd
, hmenu
, hwndOwner
, hdc
, item
, menu
->Height
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2351 ReleaseDC( menu
->hWnd
, hdc
);
2353 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2356 item
->fState
|= MF_MOUSESELECT
;
2358 if (IS_SYSTEM_MENU(menu
))
2360 MENU_InitSysMenuPopup(item
->hSubMenu
,
2361 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2362 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2364 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2365 if (wFlags
& TPM_LAYOUTRTL
) rect
.left
= rect
.right
;
2366 rect
.top
= rect
.bottom
;
2367 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2368 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2372 GetWindowRect( menu
->hWnd
, &rect
);
2373 if (menu
->wFlags
& MF_POPUP
)
2375 RECT rc
= item
->rect
;
2377 MENU_AdjustMenuItemRect(menu
, &rc
);
2379 /* The first item in the popup menu has to be at the
2380 same y position as the focused menu item */
2381 if (wFlags
& TPM_LAYOUTRTL
)
2382 rect
.left
+= GetSystemMetrics(SM_CXBORDER
);
2384 rect
.left
+= rc
.right
- GetSystemMetrics(SM_CXBORDER
);
2385 rect
.top
+= rc
.top
- MENU_TOP_MARGIN
;
2386 rect
.right
= rc
.left
- rc
.right
+ GetSystemMetrics(SM_CXBORDER
);
2387 rect
.bottom
= rc
.top
- rc
.bottom
- MENU_TOP_MARGIN
2388 - MENU_BOTTOM_MARGIN
- GetSystemMetrics(SM_CYBORDER
);
2392 if (wFlags
& TPM_LAYOUTRTL
)
2393 rect
.left
= rect
.right
- item
->rect
.left
;
2395 rect
.left
+= item
->rect
.left
;
2396 rect
.top
+= item
->rect
.bottom
;
2397 rect
.right
= item
->rect
.right
- item
->rect
.left
;
2398 rect
.bottom
= item
->rect
.bottom
- item
->rect
.top
;
2402 /* use default alignment for submenus */
2403 wFlags
&= ~(TPM_CENTERALIGN
| TPM_RIGHTALIGN
| TPM_VCENTERALIGN
| TPM_BOTTOMALIGN
);
2405 MENU_InitPopup( hwndOwner
, item
->hSubMenu
, wFlags
);
2407 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
, wFlags
,
2408 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2410 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2411 return item
->hSubMenu
;
2416 /**********************************************************************
2419 HWND
MENU_IsMenuActive(void)
2424 /**********************************************************************
2427 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2429 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2431 void MENU_EndMenu( HWND hwnd
)
2434 menu
= top_popup_hmenu
? MENU_GetMenu( top_popup_hmenu
) : NULL
;
2435 if (menu
&& (hwnd
== menu
->hWnd
|| hwnd
== menu
->hwndOwner
)) EndMenu();
2438 /***********************************************************************
2441 * Walks menu chain trying to find a menu pt maps to.
2443 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2445 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2446 UINT item
= menu
->FocusedItem
;
2449 /* try subpopup first (if any) */
2450 ret
= (item
!= NO_SELECTED_ITEM
&&
2451 (menu
->items
[item
].fType
& MF_POPUP
) &&
2452 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2453 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2455 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2457 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2458 if( menu
->wFlags
& MF_POPUP
)
2460 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2462 else if (ht
== HTSYSMENU
)
2463 ret
= get_win_sys_menu( menu
->hWnd
);
2464 else if (ht
== HTMENU
)
2465 ret
= GetMenu( menu
->hWnd
);
2470 /***********************************************************************
2471 * MENU_ExecFocusedItem
2473 * Execute a menu item (for instance when user pressed Enter).
2474 * Return the wID of the executed item. Otherwise, -1 indicating
2475 * that no menu item was executed, -2 if a popup is shown;
2476 * Have to receive the flags for the TrackPopupMenu options to avoid
2477 * sending unwanted message.
2480 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2483 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2485 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2487 if (!menu
|| !menu
->nItems
||
2488 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2490 item
= &menu
->items
[menu
->FocusedItem
];
2492 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu
, item
->wID
, item
->hSubMenu
, item
->fType
);
2494 if (!(item
->fType
& MF_POPUP
))
2496 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2498 /* If TPM_RETURNCMD is set you return the id, but
2499 do not send a message to the owner */
2500 if(!(wFlags
& TPM_RETURNCMD
))
2502 if( menu
->wFlags
& MF_SYSMENU
)
2503 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2504 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2507 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2508 DWORD dwStyle
= menu
->dwStyle
| (topmenu
? topmenu
->dwStyle
: 0);
2510 if (dwStyle
& MNS_NOTIFYBYPOS
)
2511 PostMessageW( pmt
->hOwnerWnd
, WM_MENUCOMMAND
, menu
->FocusedItem
,
2514 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2522 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2529 /***********************************************************************
2530 * MENU_SwitchTracking
2532 * Helper function for menu navigation routines.
2534 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
, UINT wFlags
)
2536 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2537 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2539 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2541 if( pmt
->hTopMenu
!= hPtMenu
&&
2542 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2544 /* both are top level menus (system and menu-bar) */
2545 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2546 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2547 pmt
->hTopMenu
= hPtMenu
;
2549 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2550 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2554 /***********************************************************************
2557 * Return TRUE if we can go on with menu tracking.
2559 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2561 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2566 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2569 if( IS_SYSTEM_MENU(ptmenu
) )
2570 item
= ptmenu
->items
;
2572 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2576 if( ptmenu
->FocusedItem
!= id
)
2577 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2579 /* If the popup menu is not already "popped" */
2580 if(!(item
->fState
& MF_MOUSESELECT
))
2582 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2587 /* Else the click was on the menu bar, finish the tracking */
2592 /***********************************************************************
2595 * Return the value of MENU_ExecFocusedItem if
2596 * the selected item was not a popup. Else open the popup.
2597 * A -1 return value indicates that we go on with menu tracking.
2600 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2602 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2607 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2610 if( IS_SYSTEM_MENU(ptmenu
) )
2611 item
= ptmenu
->items
;
2613 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2615 if( item
&& (ptmenu
->FocusedItem
== id
))
2617 debug_print_menuitem ("FocusedItem: ", item
, "");
2619 if( !(item
->fType
& MF_POPUP
) )
2621 INT executedMenuId
= MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2622 if (executedMenuId
== -1 || executedMenuId
== -2) return -1;
2623 return executedMenuId
;
2626 /* If we are dealing with the menu bar */
2627 /* and this is a click on an already "popped" item: */
2628 /* Stop the menu tracking and close the opened submenus */
2629 if((pmt
->hTopMenu
== hPtMenu
) && ptmenu
->bTimeToHide
)
2632 if( GetMenu(ptmenu
->hWnd
) == hPtMenu
)
2633 ptmenu
->bTimeToHide
= TRUE
;
2639 /***********************************************************************
2642 * Return TRUE if we can go on with menu tracking.
2644 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2646 UINT id
= NO_SELECTED_ITEM
;
2647 POPUPMENU
*ptmenu
= NULL
;
2651 ptmenu
= MENU_GetMenu( hPtMenu
);
2652 if( IS_SYSTEM_MENU(ptmenu
) )
2655 MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2658 if( id
== NO_SELECTED_ITEM
)
2660 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2661 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2664 else if( ptmenu
->FocusedItem
!= id
)
2666 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2667 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2673 /***********************************************************************
2676 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2678 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
, UINT wFlags
)
2680 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2683 /* When skipping left, we need to do something special after the
2685 if (vk
== VK_LEFT
&& menu
->FocusedItem
== 0)
2689 /* When skipping right, for the non-system menu, we need to
2690 handle the last non-special menu item (ie skip any window
2691 icons such as MDI maximize, restore or close) */
2692 else if ((vk
== VK_RIGHT
) && !IS_SYSTEM_MENU(menu
))
2694 UINT i
= menu
->FocusedItem
+ 1;
2695 while (i
< menu
->nItems
) {
2696 if ((menu
->items
[i
].wID
>= SC_SIZE
&&
2697 menu
->items
[i
].wID
<= SC_RESTORE
)) {
2701 if (i
== menu
->nItems
) {
2705 /* When skipping right, we need to cater for the system menu */
2706 else if ((vk
== VK_RIGHT
) && IS_SYSTEM_MENU(menu
))
2708 if (menu
->FocusedItem
== (menu
->nItems
- 1)) {
2715 MDINEXTMENU next_menu
;
2720 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2721 next_menu
.hmenuNext
= 0;
2722 next_menu
.hwndNext
= 0;
2723 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2725 TRACE("%p [%p] -> %p [%p]\n",
2726 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2728 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2730 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2731 hNewWnd
= pmt
->hOwnerWnd
;
2732 if( IS_SYSTEM_MENU(menu
) )
2734 /* switch to the menu bar */
2736 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2740 menu
= MENU_GetMenu( hNewMenu
);
2741 id
= menu
->nItems
- 1;
2743 /* Skip backwards over any system predefined icons,
2744 eg. MDI close, restore etc icons */
2746 (menu
->items
[id
].wID
>= SC_SIZE
&&
2747 menu
->items
[id
].wID
<= SC_RESTORE
)) id
--;
2750 else if (style
& WS_SYSMENU
)
2752 /* switch to the system menu */
2753 hNewMenu
= get_win_sys_menu( hNewWnd
);
2757 else /* application returned a new menu to switch to */
2759 hNewMenu
= next_menu
.hmenuNext
;
2760 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2762 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2764 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2766 if (style
& WS_SYSMENU
&&
2767 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2769 /* get the real system menu */
2770 hNewMenu
= get_win_sys_menu(hNewWnd
);
2772 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2774 /* FIXME: Not sure what to do here;
2775 * perhaps try to track hNewMenu as a popup? */
2777 TRACE(" -- got confused.\n");
2784 if( hNewMenu
!= pmt
->hTopMenu
)
2786 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2788 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2789 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2792 if( hNewWnd
!= pmt
->hOwnerWnd
)
2794 pmt
->hOwnerWnd
= hNewWnd
;
2795 set_capture_window( pmt
->hOwnerWnd
, GUI_INMENUMODE
, NULL
);
2798 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2799 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2806 /***********************************************************************
2809 * The idea is not to show the popup if the next input message is
2810 * going to hide it anyway.
2812 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT16 uMsg
)
2816 msg
.hwnd
= pmt
->hOwnerWnd
;
2818 PeekMessageW( &msg
, 0, uMsg
, uMsg
, PM_NOYIELD
| PM_REMOVE
);
2819 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2824 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2825 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2827 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2828 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2829 if( msg
.message
== WM_KEYDOWN
&&
2830 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2832 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2839 /* failures go through this */
2840 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2844 /***********************************************************************
2847 * Handle a VK_ESCAPE key event in a menu.
2849 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2851 BOOL bEndMenu
= TRUE
;
2853 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2855 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2857 if (menu
->wFlags
& MF_POPUP
)
2859 HMENU hmenutmp
, hmenuprev
;
2861 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2863 /* close topmost popup */
2864 while (hmenutmp
!= pmt
->hCurrentMenu
)
2866 hmenuprev
= hmenutmp
;
2867 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2870 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2871 pmt
->hCurrentMenu
= hmenuprev
;
2879 /***********************************************************************
2882 * Handle a VK_LEFT key event in a menu.
2884 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
)
2887 HMENU hmenutmp
, hmenuprev
;
2890 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2891 menu
= MENU_GetMenu( hmenutmp
);
2893 /* Try to move 1 column left (if possible) */
2894 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2895 NO_SELECTED_ITEM
) {
2897 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2902 /* close topmost popup */
2903 while (hmenutmp
!= pmt
->hCurrentMenu
)
2905 hmenuprev
= hmenutmp
;
2906 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2909 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2910 pmt
->hCurrentMenu
= hmenuprev
;
2912 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2914 /* move menu bar selection if no more popups are left */
2916 if( !MENU_DoNextMenu( pmt
, VK_LEFT
, wFlags
) )
2917 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2919 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2921 /* A sublevel menu was displayed - display the next one
2922 * unless there is another displacement coming up */
2924 if( !MENU_SuspendPopup( pmt
, WM_KEYDOWN
) )
2925 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2926 pmt
->hTopMenu
, TRUE
, wFlags
);
2932 /***********************************************************************
2935 * Handle a VK_RIGHT key event in a menu.
2937 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
)
2940 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2943 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2945 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2946 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2948 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2950 /* If already displaying a popup, try to display sub-popup */
2952 hmenutmp
= pmt
->hCurrentMenu
;
2953 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2955 /* if subpopup was displayed then we are done */
2956 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2959 /* Check to see if there's another column */
2960 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2961 NO_SELECTED_ITEM
) {
2962 TRACE("Going to %d.\n", nextcol
);
2963 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2968 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2970 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2972 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2973 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
2974 } else hmenutmp
= 0;
2976 /* try to move to the next item */
2977 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
, wFlags
) )
2978 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
2980 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2981 if( !MENU_SuspendPopup(pmt
, WM_KEYDOWN
) )
2982 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2983 pmt
->hTopMenu
, TRUE
, wFlags
);
2987 static void CALLBACK
release_capture( BOOL __normal
)
2989 set_capture_window( 0, GUI_INMENUMODE
, NULL
);
2992 /***********************************************************************
2995 * Menu tracking code.
2997 static BOOL
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
2998 HWND hwnd
, const RECT
*lprect
)
3003 INT executedMenuId
= -1;
3005 BOOL enterIdleSent
= FALSE
;
3008 mt
.hCurrentMenu
= hmenu
;
3009 mt
.hTopMenu
= hmenu
;
3010 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
3014 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3015 hmenu
, wFlags
, x
, y
, hwnd
, wine_dbgstr_rect( lprect
));
3017 if (!(menu
= MENU_GetMenu( hmenu
)))
3019 WARN("Invalid menu handle %p\n", hmenu
);
3020 SetLastError(ERROR_INVALID_MENU_HANDLE
);
3024 if (wFlags
& TPM_BUTTONDOWN
)
3026 /* Get the result in order to start the tracking or not */
3027 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3028 fEndMenu
= !fRemove
;
3031 if (wFlags
& TF_ENDMENU
) fEndMenu
= TRUE
;
3033 if (!(wFlags
& TPM_POPUPMENU
))
3034 set_capture_window( mt
.hOwnerWnd
, GUI_INMENUMODE
, NULL
);
3036 if ((wFlags
& TPM_POPUPMENU
) && menu
->nItems
== 0)
3039 __TRY
while (!fEndMenu
)
3041 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3042 if (!menu
) /* sometimes happens if I do a window manager close */
3045 /* we have to keep the message in the queue until it's
3046 * clear that menu loop is not over yet. */
3050 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
3052 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
3053 /* remove the message from the queue */
3054 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3060 HWND win
= menu
->wFlags
& MF_POPUP
? menu
->hWnd
: 0;
3061 enterIdleSent
= TRUE
;
3062 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
3068 /* check if EndMenu() tried to cancel us, by posting this message */
3069 if(msg
.message
== WM_CANCELMODE
)
3071 /* we are now out of the loop */
3074 /* remove the message from the queue */
3075 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3077 /* break out of internal loop, ala ESCAPE */
3081 TranslateMessage( &msg
);
3084 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
3085 enterIdleSent
=FALSE
;
3088 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
3091 * Use the mouse coordinates in lParam instead of those in the MSG
3092 * struct to properly handle synthetic messages. They are already
3093 * in screen coordinates.
3095 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
3096 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
3098 /* Find a menu for this mouse event */
3099 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
3103 /* no WM_NC... messages in captured state */
3105 case WM_RBUTTONDBLCLK
:
3106 case WM_RBUTTONDOWN
:
3107 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3109 case WM_LBUTTONDBLCLK
:
3110 case WM_LBUTTONDOWN
:
3111 /* If the message belongs to the menu, removes it from the queue */
3112 /* Else, end menu tracking */
3113 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3114 fEndMenu
= !fRemove
;
3118 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3121 /* Check if a menu was selected by the mouse */
3124 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
3125 TRACE("executedMenuId %d\n", executedMenuId
);
3127 /* End the loop if executedMenuId is an item ID */
3128 /* or if the job was done (executedMenuId = 0). */
3129 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3131 /* No menu was selected by the mouse */
3132 /* if the function was called by TrackPopupMenu, continue
3133 with the menu tracking. If not, stop it */
3135 fEndMenu
= !(wFlags
& TPM_POPUPMENU
);
3140 /* the selected menu item must be changed every time */
3141 /* the mouse moves. */
3144 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
3146 } /* switch(msg.message) - mouse */
3148 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3150 fRemove
= TRUE
; /* Keyboard messages are always removed */
3164 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3165 NO_SELECTED_ITEM
, FALSE
, 0 );
3166 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3167 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
3171 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3173 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3174 if (!(menu
->wFlags
& MF_POPUP
))
3175 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
3176 else /* otherwise try to move selection */
3177 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3178 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3182 MENU_KeyLeft( &mt
, wFlags
);
3186 MENU_KeyRight( &mt
, wFlags
);
3190 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
3196 hi
.cbSize
= sizeof(HELPINFO
);
3197 hi
.iContextType
= HELPINFO_MENUITEM
;
3198 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
3201 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
3202 hi
.hItemHandle
= hmenu
;
3203 hi
.dwContextId
= menu
->dwContextHelpID
;
3204 hi
.MousePos
= msg
.pt
;
3205 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3212 break; /* WM_KEYDOWN */
3219 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
3221 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3222 fEndMenu
= (executedMenuId
!= -2);
3227 /* Hack to avoid control chars. */
3228 /* We will find a better way real soon... */
3229 if (msg
.wParam
< 32) break;
3231 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3232 LOWORD(msg
.wParam
), FALSE
);
3233 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3234 else if (pos
== (UINT
)-1) MessageBeep(0);
3237 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
3239 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3240 fEndMenu
= (executedMenuId
!= -2);
3244 } /* switch(msg.message) - kbd */
3248 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3249 DispatchMessageW( &msg
);
3253 if (!fEndMenu
) fRemove
= TRUE
;
3255 /* finally remove message from the queue */
3257 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
3258 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3259 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
3261 __FINALLY( release_capture
)
3263 /* If dropdown is still painted and the close box is clicked on
3264 then the menu will be destroyed as part of the DispatchMessage above.
3265 This will then invalidate the menu handle in mt.hTopMenu. We should
3266 check for this first. */
3267 if( IsMenu( mt
.hTopMenu
) )
3269 menu
= MENU_GetMenu( mt
.hTopMenu
);
3271 if( IsWindow( mt
.hOwnerWnd
) )
3273 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
, wFlags
);
3275 if (menu
&& (menu
->wFlags
& MF_POPUP
))
3277 DestroyWindow( menu
->hWnd
);
3280 if (!(wFlags
& TPM_NONOTIFY
))
3281 SendMessageW( mt
.hOwnerWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)mt
.hTopMenu
,
3282 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3284 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
3285 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKEWPARAM(0,0xffff), 0 );
3288 /* Reset the variable for hiding menu */
3289 if( menu
) menu
->bTimeToHide
= FALSE
;
3292 /* The return value is only used by TrackPopupMenu */
3293 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3294 if (executedMenuId
== -1) executedMenuId
= 0;
3295 return executedMenuId
;
3298 /***********************************************************************
3301 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3305 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3309 if (!(menu
= MENU_GetMenu( hMenu
))) return FALSE
;
3311 /* This makes the menus of applications built with Delphi work.
3312 * It also enables menus to be displayed in more than one window,
3313 * but there are some bugs left that need to be fixed in this case.
3315 if (!bPopup
) menu
->hWnd
= hWnd
;
3318 top_popup
= menu
->hWnd
;
3319 top_popup_hmenu
= hMenu
;
3324 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3325 if (!(wFlags
& TPM_NONOTIFY
))
3326 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3328 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3331 /* owner may not be visible when tracking a popup, so use the menu itself */
3332 set_capture_window( menu
->hWnd
, GUI_INMENUMODE
, NULL
);
3334 if (!(wFlags
& TPM_NONOTIFY
))
3336 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3337 /* If an app changed/recreated menu bar entries in WM_INITMENU
3338 * menu sizes will be recalculated once the menu created/shown.
3345 /***********************************************************************
3348 static BOOL
MENU_ExitTracking(HWND hWnd
, BOOL bPopup
)
3350 TRACE("hwnd=%p\n", hWnd
);
3352 SendMessageW( hWnd
, WM_EXITMENULOOP
, bPopup
, 0 );
3355 top_popup_hmenu
= NULL
;
3359 /***********************************************************************
3360 * MENU_TrackMouseMenuBar
3362 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3364 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3366 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3367 UINT wFlags
= TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3369 TRACE("wnd=%p ht=0x%04x %s\n", hWnd
, ht
, wine_dbgstr_point( &pt
));
3371 if (GetWindowLongW( hWnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) wFlags
|= TPM_LAYOUTRTL
;
3374 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3376 /* fetch the window menu again, it may have changed */
3377 hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3378 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3379 MENU_ExitTracking(hWnd
, FALSE
);
3384 /***********************************************************************
3385 * MENU_TrackKbdMenuBar
3387 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3389 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3391 UINT uItem
= NO_SELECTED_ITEM
;
3393 UINT wFlags
= TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3395 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3397 /* find window that has a menu */
3399 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd
, GWL_STYLE
)))
3400 if (!(hwnd
= GetAncestor( hwnd
, GA_PARENT
))) return;
3402 /* check if we have to track a system menu */
3404 hTrackMenu
= GetMenu( hwnd
);
3405 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3407 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3408 hTrackMenu
= get_win_sys_menu( hwnd
);
3410 wParam
|= HTSYSMENU
; /* prevent item lookup */
3412 if (GetWindowLongW( hwnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) wFlags
|= TPM_LAYOUTRTL
;
3414 if (!IsMenu( hTrackMenu
)) return;
3416 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3418 /* fetch the window menu again, it may have changed */
3419 hTrackMenu
= (wParam
& HTSYSMENU
) ? get_win_sys_menu( hwnd
) : GetMenu( hwnd
);
3421 if( wChar
&& wChar
!= ' ' )
3423 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3424 if ( uItem
>= (UINT
)(-2) )
3426 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3427 /* schedule end of menu tracking */
3428 wFlags
|= TF_ENDMENU
;
3433 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3435 if (!(wParam
& HTSYSMENU
) || wChar
== ' ')
3437 if( uItem
== NO_SELECTED_ITEM
)
3438 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3440 PostMessageW( hwnd
, WM_KEYDOWN
, VK_RETURN
, 0 );
3444 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3445 MENU_ExitTracking( hwnd
, FALSE
);
3448 /**********************************************************************
3449 * TrackPopupMenuEx (USER32.@)
3451 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3452 HWND hWnd
, LPTPMPARAMS lpTpm
)
3457 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3458 hMenu
, wFlags
, x
, y
, hWnd
, lpTpm
,
3459 lpTpm
? wine_dbgstr_rect( &lpTpm
->rcExclude
) : "-" );
3461 /* Parameter check */
3462 /* FIXME: this check is performed several times, here and in the called
3463 functions. That could be optimized */
3464 if (!(menu
= MENU_GetMenu( hMenu
)))
3466 SetLastError( ERROR_INVALID_MENU_HANDLE
);
3470 if (IsWindow(menu
->hWnd
))
3472 SetLastError( ERROR_POPUP_ALREADY_ACTIVE
);
3476 if (MENU_InitPopup( hWnd
, hMenu
, wFlags
))
3478 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3480 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3481 if (!(wFlags
& TPM_NONOTIFY
))
3482 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3484 if (MENU_ShowPopup( hWnd
, hMenu
, 0, wFlags
, x
, y
, 0, 0 ))
3485 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
,
3486 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3488 set_capture_window( 0, GUI_INMENUMODE
, NULL
);
3490 MENU_ExitTracking(hWnd
, TRUE
);
3494 DestroyWindow( menu
->hWnd
);
3497 if (!(wFlags
& TPM_NONOTIFY
))
3498 SendMessageW( hWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)hMenu
,
3499 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3506 /**********************************************************************
3507 * TrackPopupMenu (USER32.@)
3509 * Like the win32 API, the function return the command ID only if the
3510 * flag TPM_RETURNCMD is on.
3513 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3514 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3516 return TrackPopupMenuEx( hMenu
, wFlags
, x
, y
, hWnd
, NULL
);
3519 /***********************************************************************
3522 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3524 LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3526 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3532 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3533 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3537 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3538 return MA_NOACTIVATE
;
3543 BeginPaint( hwnd
, &ps
);
3544 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3545 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3546 EndPaint( hwnd
, &ps
);
3550 case WM_PRINTCLIENT
:
3552 MENU_DrawPopupMenu( hwnd
, (HDC
)wParam
,
3553 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3561 /* zero out global pointer in case resident popup window was destroyed. */
3562 if (hwnd
== top_popup
) {
3564 top_popup_hmenu
= NULL
;
3572 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3575 SetWindowLongPtrW( hwnd
, 0, 0 );
3579 return GetWindowLongPtrW( hwnd
, 0 );
3582 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3588 /***********************************************************************
3589 * MENU_GetMenuBarHeight
3591 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3593 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3594 INT orgX
, INT orgY
)
3600 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3602 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3604 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3605 SelectObject( hdc
, get_menu_font(FALSE
));
3606 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3607 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3608 ReleaseDC( hwnd
, hdc
);
3609 return lppop
->Height
;
3613 /*******************************************************************
3614 * ChangeMenuA (USER32.@)
3616 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3617 UINT id
, UINT flags
)
3619 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3620 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3622 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3623 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3625 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3626 flags
& MF_BYPOSITION
? pos
: id
,
3627 flags
& ~MF_REMOVE
);
3628 /* Default: MF_INSERT */
3629 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3633 /*******************************************************************
3634 * ChangeMenuW (USER32.@)
3636 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3637 UINT id
, UINT flags
)
3639 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3640 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3642 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3643 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3645 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3646 flags
& MF_BYPOSITION
? pos
: id
,
3647 flags
& ~MF_REMOVE
);
3648 /* Default: MF_INSERT */
3649 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3653 /*******************************************************************
3654 * CheckMenuItem (USER32.@)
3656 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3661 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3662 ret
= item
->fState
& MF_CHECKED
;
3663 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3664 else item
->fState
&= ~MF_CHECKED
;
3669 /**********************************************************************
3670 * EnableMenuItem (USER32.@)
3672 BOOL WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3678 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3680 /* Get the Popupmenu to access the owner menu */
3681 if (!(menu
= MENU_GetMenu(hMenu
)))
3684 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3687 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3688 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3690 /* If the close item in the system menu change update the close button */
3691 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3693 if (menu
->hSysMenuOwner
!= 0)
3696 POPUPMENU
* parentMenu
;
3698 /* Get the parent menu to access*/
3699 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3702 /* Refresh the frame to reflect the change */
3703 WIN_GetRectangles( parentMenu
->hWnd
, COORDS_CLIENT
, &rc
, NULL
);
3705 RedrawWindow(parentMenu
->hWnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3713 /*******************************************************************
3714 * GetMenuStringA (USER32.@)
3716 INT WINAPI
GetMenuStringA(
3717 HMENU hMenu
, /* [in] menuhandle */
3718 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3719 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3720 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3721 UINT wFlags
/* [in] MF_ flags */
3725 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3726 if (str
&& nMaxSiz
) str
[0] = '\0';
3727 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3728 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3731 if (!item
->text
) return 0;
3732 if (!str
|| !nMaxSiz
) return WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, NULL
, 0, NULL
, NULL
);
3733 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3735 TRACE("returning %s\n", debugstr_a(str
));
3740 /*******************************************************************
3741 * GetMenuStringW (USER32.@)
3743 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3744 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3748 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3749 if (str
&& nMaxSiz
) str
[0] = '\0';
3750 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3751 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3754 if (!str
|| !nMaxSiz
) return item
->text
? strlenW(item
->text
) : 0;
3755 if( !(item
->text
)) {
3759 lstrcpynW( str
, item
->text
, nMaxSiz
);
3760 TRACE("returning %s\n", debugstr_w(str
));
3761 return strlenW(str
);
3765 /**********************************************************************
3766 * HiliteMenuItem (USER32.@)
3768 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3772 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3773 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3774 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3775 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3776 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
, 0 );
3777 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3782 /**********************************************************************
3783 * GetMenuState (USER32.@)
3785 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3788 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3789 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3790 debug_print_menuitem (" item: ", item
, "");
3791 if (item
->fType
& MF_POPUP
)
3793 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3794 if (!menu
) return -1;
3795 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3799 /* We used to (from way back then) mask the result to 0xff. */
3800 /* I don't know why and it seems wrong as the documented */
3801 /* return flag MF_SEPARATOR is outside that mask. */
3802 return (item
->fType
| item
->fState
);
3807 /**********************************************************************
3808 * GetMenuItemCount (USER32.@)
3810 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3812 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3813 if (!menu
) return -1;
3814 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3815 return menu
->nItems
;
3819 /**********************************************************************
3820 * GetMenuItemID (USER32.@)
3822 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3826 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return -1;
3827 if (lpmi
->fType
& MF_POPUP
) return -1;
3833 /**********************************************************************
3836 * Uses flags, id and text ptr, passed by InsertMenu() and
3837 * ModifyMenu() to setup a MenuItemInfo structure.
3839 static void MENU_mnu2mnuii( UINT flags
, UINT_PTR id
, LPCWSTR str
,
3840 LPMENUITEMINFOW pmii
)
3842 ZeroMemory( pmii
, sizeof( MENUITEMINFOW
));
3843 pmii
->cbSize
= sizeof( MENUITEMINFOW
);
3844 pmii
->fMask
= MIIM_STATE
| MIIM_ID
| MIIM_FTYPE
;
3845 /* setting bitmap clears text and vice versa */
3846 if( IS_STRING_ITEM(flags
)) {
3847 pmii
->fMask
|= MIIM_STRING
| MIIM_BITMAP
;
3849 flags
|= MF_SEPARATOR
;
3850 /* Item beginning with a backspace is a help item */
3851 /* FIXME: wrong place, this is only true in win16 */
3852 else if( *str
== '\b') {
3856 pmii
->dwTypeData
= (LPWSTR
)str
;
3857 } else if( flags
& MFT_BITMAP
){
3858 pmii
->fMask
|= MIIM_BITMAP
| MIIM_STRING
;
3859 pmii
->hbmpItem
= (HBITMAP
)str
;
3861 if( flags
& MF_OWNERDRAW
){
3862 pmii
->fMask
|= MIIM_DATA
;
3863 pmii
->dwItemData
= (ULONG_PTR
) str
;
3865 if( flags
& MF_POPUP
&& MENU_GetMenu((HMENU
)id
)) {
3866 pmii
->fMask
|= MIIM_SUBMENU
;
3867 pmii
->hSubMenu
= (HMENU
)id
;
3869 if( flags
& MF_SEPARATOR
) flags
|= MF_GRAYED
| MF_DISABLED
;
3870 pmii
->fState
= flags
& MENUITEMINFO_STATE_MASK
& ~MFS_DEFAULT
;
3871 pmii
->fType
= flags
& MENUITEMINFO_TYPE_MASK
;
3872 pmii
->wID
= (UINT
)id
;
3876 /*******************************************************************
3877 * InsertMenuW (USER32.@)
3879 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3880 UINT_PTR id
, LPCWSTR str
)
3885 if (IS_STRING_ITEM(flags
) && str
)
3886 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3887 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3888 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3889 hMenu
, pos
, flags
, id
, str
);
3891 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3892 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
3893 if (!(SetMenuItemInfo_common( item
, &mii
, TRUE
)))
3895 RemoveMenu( hMenu
, pos
, flags
);
3899 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3904 /*******************************************************************
3905 * InsertMenuA (USER32.@)
3907 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3908 UINT_PTR id
, LPCSTR str
)
3912 if (IS_STRING_ITEM(flags
) && str
)
3914 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3915 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3918 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3919 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3920 HeapFree( GetProcessHeap(), 0, newstr
);
3924 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3928 /*******************************************************************
3929 * AppendMenuA (USER32.@)
3931 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3932 UINT_PTR id
, LPCSTR data
)
3934 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3938 /*******************************************************************
3939 * AppendMenuW (USER32.@)
3941 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3942 UINT_PTR id
, LPCWSTR data
)
3944 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3948 /**********************************************************************
3949 * RemoveMenu (USER32.@)
3951 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3956 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3957 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3958 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3962 MENU_FreeItemData( item
);
3964 if (--menu
->nItems
== 0)
3966 HeapFree( GetProcessHeap(), 0, menu
->items
);
3971 while(nPos
< menu
->nItems
)
3977 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3978 menu
->nItems
* sizeof(MENUITEM
) );
3984 /**********************************************************************
3985 * DeleteMenu (USER32.@)
3987 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3989 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3990 if (!item
) return FALSE
;
3991 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3992 /* nPos is now the position of the item */
3993 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3998 /*******************************************************************
3999 * ModifyMenuW (USER32.@)
4001 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
4002 UINT_PTR id
, LPCWSTR str
)
4007 if (IS_STRING_ITEM(flags
))
4008 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
4010 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
4012 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
)))
4014 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
4015 if (pos
== SC_TASKLIST
&& !(flags
& MF_BYPOSITION
)) return TRUE
;
4018 MENU_GetMenu(hMenu
)->Height
= 0; /* force size recalculate */
4019 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
4020 return SetMenuItemInfo_common( item
, &mii
, TRUE
);
4024 /*******************************************************************
4025 * ModifyMenuA (USER32.@)
4027 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
4028 UINT_PTR id
, LPCSTR str
)
4032 if (IS_STRING_ITEM(flags
) && str
)
4034 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4035 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
4038 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
4039 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
4040 HeapFree( GetProcessHeap(), 0, newstr
);
4044 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
4048 /**********************************************************************
4049 * CreatePopupMenu (USER32.@)
4051 HMENU WINAPI
CreatePopupMenu(void)
4056 if (!(hmenu
= CreateMenu())) return 0;
4057 menu
= MENU_GetMenu( hmenu
);
4058 menu
->wFlags
|= MF_POPUP
;
4059 menu
->bTimeToHide
= FALSE
;
4064 /**********************************************************************
4065 * GetMenuCheckMarkDimensions (USER.417)
4066 * GetMenuCheckMarkDimensions (USER32.@)
4068 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
4070 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
4074 /**********************************************************************
4075 * SetMenuItemBitmaps (USER32.@)
4077 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
4078 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
4082 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
4084 if (!hNewCheck
&& !hNewUnCheck
)
4086 item
->fState
&= ~MF_USECHECKBITMAPS
;
4088 else /* Install new bitmaps */
4090 item
->hCheckBit
= hNewCheck
;
4091 item
->hUnCheckBit
= hNewUnCheck
;
4092 item
->fState
|= MF_USECHECKBITMAPS
;
4098 /**********************************************************************
4099 * CreateMenu (USER32.@)
4101 HMENU WINAPI
CreateMenu(void)
4106 if (!(menu
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*menu
) ))) return 0;
4107 menu
->FocusedItem
= NO_SELECTED_ITEM
;
4108 menu
->bTimeToHide
= FALSE
;
4110 if (!(hMenu
= alloc_user_handle( &menu
->obj
, USER_MENU
))) HeapFree( GetProcessHeap(), 0, menu
);
4112 TRACE("return %p\n", hMenu
);
4118 /**********************************************************************
4119 * DestroyMenu (USER32.@)
4121 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
4125 TRACE("(%p)\n", hMenu
);
4127 if (!(lppop
= free_user_handle( hMenu
, USER_MENU
))) return FALSE
;
4128 if (lppop
== OBJ_OTHER_PROCESS
) return FALSE
;
4130 /* DestroyMenu should not destroy system menu popup owner */
4131 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
4133 DestroyWindow( lppop
->hWnd
);
4137 if (lppop
->items
) /* recursively destroy submenus */
4140 MENUITEM
*item
= lppop
->items
;
4141 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
4143 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
4144 MENU_FreeItemData( item
);
4146 HeapFree( GetProcessHeap(), 0, lppop
->items
);
4148 HeapFree( GetProcessHeap(), 0, lppop
);
4153 /**********************************************************************
4154 * GetSystemMenu (USER32.@)
4156 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
4158 WND
*wndPtr
= WIN_GetPtr( hWnd
);
4161 if (wndPtr
== WND_DESKTOP
) return 0;
4162 if (wndPtr
== WND_OTHER_PROCESS
)
4164 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
4168 if (wndPtr
->hSysMenu
&& bRevert
)
4170 DestroyMenu(wndPtr
->hSysMenu
);
4171 wndPtr
->hSysMenu
= 0;
4174 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
4175 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
4177 if( wndPtr
->hSysMenu
)
4180 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
4182 /* Store the dummy sysmenu handle to facilitate the refresh */
4183 /* of the close button if the SC_CLOSE item change */
4184 menu
= MENU_GetMenu(retvalue
);
4186 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
4188 WIN_ReleasePtr( wndPtr
);
4190 return bRevert
? 0 : retvalue
;
4194 /*******************************************************************
4195 * SetSystemMenu (USER32.@)
4197 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
4199 WND
*wndPtr
= WIN_GetPtr( hwnd
);
4201 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
4203 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
4204 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
4205 WIN_ReleasePtr( wndPtr
);
4212 /**********************************************************************
4213 * GetMenu (USER32.@)
4215 HMENU WINAPI
GetMenu( HWND hWnd
)
4217 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
4218 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4222 /**********************************************************************
4223 * GetMenuBarInfo (USER32.@)
4225 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4231 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4236 class_atom
= GetClassLongW(hwnd
, GCW_ATOM
);
4239 if (class_atom
!= POPUPMENU_CLASS_ATOM
)
4241 WARN("called on invalid window: %d\n", class_atom
);
4242 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4246 hmenu
= (HMENU
)GetWindowLongPtrW(hwnd
, 0);
4249 hmenu
= GetMenu(hwnd
);
4252 hmenu
= GetSystemMenu(hwnd
, FALSE
);
4261 if (pmbi
->cbSize
!= sizeof(MENUBARINFO
))
4263 SetLastError(ERROR_INVALID_PARAMETER
);
4267 menu
= MENU_GetMenu(hmenu
);
4270 if (idItem
< 0 || idItem
> menu
->nItems
)
4275 SetRectEmpty(&pmbi
->rcBar
);
4277 else if (idItem
== 0)
4279 GetMenuItemRect(hwnd
, hmenu
, 0, &pmbi
->rcBar
);
4280 pmbi
->rcBar
.right
= pmbi
->rcBar
.left
+ menu
->Width
;
4281 pmbi
->rcBar
.bottom
= pmbi
->rcBar
.top
+ menu
->Height
;
4285 GetMenuItemRect(hwnd
, hmenu
, idItem
- 1, &pmbi
->rcBar
);
4288 pmbi
->hMenu
= hmenu
;
4289 pmbi
->hwndMenu
= NULL
;
4290 pmbi
->fBarFocused
= top_popup_hmenu
== hmenu
;
4293 pmbi
->fFocused
= menu
->FocusedItem
== idItem
- 1;
4294 if (pmbi
->fFocused
&& (menu
->items
[idItem
- 1].fType
& MF_POPUP
))
4296 menu
= MENU_GetMenu(menu
->items
[idItem
- 1].hSubMenu
);
4298 pmbi
->hwndMenu
= menu
->hWnd
;
4303 pmbi
->fFocused
= pmbi
->fBarFocused
;
4309 /**********************************************************************
4312 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4313 * SetWindowPos call that would result if SetMenu were called directly.
4315 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4317 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4319 if (hMenu
&& !IsMenu(hMenu
))
4321 WARN("hMenu %p is not a menu handle\n", hMenu
);
4324 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4327 hWnd
= WIN_GetFullHandle( hWnd
);
4328 if (GetCapture() == hWnd
)
4329 set_capture_window( 0, GUI_INMENUMODE
, NULL
); /* release the capture */
4335 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
4337 lpmenu
->hWnd
= hWnd
;
4338 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
4340 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4345 /**********************************************************************
4346 * SetMenu (USER32.@)
4348 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4350 if(!MENU_SetMenu(hWnd
, hMenu
))
4353 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4354 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4359 /**********************************************************************
4360 * GetSubMenu (USER32.@)
4362 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4366 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return 0;
4367 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
4368 return lpmi
->hSubMenu
;
4372 /**********************************************************************
4373 * DrawMenuBar (USER32.@)
4375 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4380 if (!IsWindow( hWnd
))
4382 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4385 if ((hMenu
= GetMenu( hWnd
)) && (lppop
= MENU_GetMenu( hMenu
))) {
4386 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4387 lppop
->hwndOwner
= hWnd
;
4390 return SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4391 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4394 /***********************************************************************
4395 * DrawMenuBarTemp (USER32.@)
4399 * called by W98SE desk.cpl Control Panel Applet
4401 * Not 100% sure about the param names, but close.
4403 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4408 BOOL flat_menu
= FALSE
;
4410 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4413 hMenu
= GetMenu(hwnd
);
4416 hFont
= get_menu_font(FALSE
);
4418 lppop
= MENU_GetMenu( hMenu
);
4419 if (lppop
== NULL
|| lprect
== NULL
)
4421 retvalue
= GetSystemMetrics(SM_CYMENU
);
4425 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4427 hfontOld
= SelectObject( hDC
, hFont
);
4429 if (lppop
->Height
== 0)
4430 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4432 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4434 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4436 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4437 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4438 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4440 if (lppop
->nItems
== 0)
4442 retvalue
= GetSystemMetrics(SM_CYMENU
);
4446 for (i
= 0; i
< lppop
->nItems
; i
++)
4448 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
4449 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
4451 retvalue
= lppop
->Height
;
4454 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4458 /***********************************************************************
4459 * EndMenu (USER.187)
4460 * EndMenu (USER32.@)
4462 BOOL WINAPI
EndMenu(void)
4464 /* if we are in the menu code, and it is active */
4465 if (!fEndMenu
&& top_popup
)
4467 /* terminate the menu handling code */
4470 /* needs to be posted to wakeup the internal menu handler */
4471 /* which will now terminate the menu, in the event that */
4472 /* the main window was minimized, or lost focus, so we */
4473 /* don't end up with an orphaned menu */
4474 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4480 /*****************************************************************
4481 * LoadMenuA (USER32.@)
4483 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4485 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4486 if (!hrsrc
) return 0;
4487 return LoadMenuIndirectA( LoadResource( instance
, hrsrc
));
4491 /*****************************************************************
4492 * LoadMenuW (USER32.@)
4494 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4496 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4497 if (!hrsrc
) return 0;
4498 return LoadMenuIndirectW( LoadResource( instance
, hrsrc
));
4502 /**********************************************************************
4503 * LoadMenuIndirectW (USER32.@)
4505 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4508 WORD version
, offset
;
4509 LPCSTR p
= template;
4511 version
= GET_WORD(p
);
4513 TRACE("%p, ver %d\n", template, version
);
4516 case 0: /* standard format is version of 0 */
4517 offset
= GET_WORD(p
);
4518 p
+= sizeof(WORD
) + offset
;
4519 if (!(hMenu
= CreateMenu())) return 0;
4520 if (!MENU_ParseResource( p
, hMenu
))
4522 DestroyMenu( hMenu
);
4526 case 1: /* extended format is version of 1 */
4527 offset
= GET_WORD(p
);
4528 p
+= sizeof(WORD
) + offset
;
4529 if (!(hMenu
= CreateMenu())) return 0;
4530 if (!MENUEX_ParseResource( p
, hMenu
))
4532 DestroyMenu( hMenu
);
4537 ERR("version %d not supported.\n", version
);
4543 /**********************************************************************
4544 * LoadMenuIndirectA (USER32.@)
4546 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4548 return LoadMenuIndirectW( template );
4552 /**********************************************************************
4555 BOOL WINAPI
IsMenu(HMENU hmenu
)
4557 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4561 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4567 /**********************************************************************
4568 * GetMenuItemInfo_common
4571 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4572 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4574 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4576 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4579 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
4583 if( lpmii
->fMask
& MIIM_TYPE
) {
4584 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4585 WARN("invalid combination of fMask bits used\n");
4586 /* this does not happen on Win9x/ME */
4587 SetLastError( ERROR_INVALID_PARAMETER
);
4590 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4591 if (menu
->hbmpItem
&& !IS_MAGIC_BITMAP(menu
->hbmpItem
))
4592 lpmii
->fType
|= MFT_BITMAP
;
4593 lpmii
->hbmpItem
= menu
->hbmpItem
; /* not on Win9x/ME */
4594 if( lpmii
->fType
& MFT_BITMAP
) {
4595 lpmii
->dwTypeData
= (LPWSTR
) menu
->hbmpItem
;
4597 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4598 /* this does not happen on Win9x/ME */
4599 lpmii
->dwTypeData
= 0;
4604 /* copy the text string */
4605 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4607 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4609 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4611 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4618 len
= strlenW(menu
->text
);
4619 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4620 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4624 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
,
4625 0, NULL
, NULL
) - 1;
4626 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4627 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4628 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4629 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4631 /* if we've copied a substring we return its length */
4632 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4633 if (lpmii
->cch
<= len
+ 1)
4638 /* return length of string */
4639 /* not on Win9x/ME if fType & MFT_BITMAP */
4645 if (lpmii
->fMask
& MIIM_FTYPE
)
4646 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4648 if (lpmii
->fMask
& MIIM_BITMAP
)
4649 lpmii
->hbmpItem
= menu
->hbmpItem
;
4651 if (lpmii
->fMask
& MIIM_STATE
)
4652 lpmii
->fState
= menu
->fState
& MENUITEMINFO_STATE_MASK
;
4654 if (lpmii
->fMask
& MIIM_ID
)
4655 lpmii
->wID
= menu
->wID
;
4657 if (lpmii
->fMask
& MIIM_SUBMENU
)
4658 lpmii
->hSubMenu
= menu
->hSubMenu
;
4660 /* hSubMenu is always cleared
4661 * (not on Win9x/ME ) */
4662 lpmii
->hSubMenu
= 0;
4665 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4666 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4667 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4669 if (lpmii
->fMask
& MIIM_DATA
)
4670 lpmii
->dwItemData
= menu
->dwItemData
;
4675 /**********************************************************************
4676 * GetMenuItemInfoA (USER32.@)
4678 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4679 LPMENUITEMINFOA lpmii
)
4683 if( lpmii
->cbSize
!= sizeof( mii
) &&
4684 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4685 SetLastError( ERROR_INVALID_PARAMETER
);
4688 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4689 mii
.cbSize
= sizeof( mii
);
4690 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4691 (LPMENUITEMINFOW
)&mii
, FALSE
);
4692 mii
.cbSize
= lpmii
->cbSize
;
4693 memcpy( lpmii
, &mii
, mii
.cbSize
);
4697 /**********************************************************************
4698 * GetMenuItemInfoW (USER32.@)
4700 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4701 LPMENUITEMINFOW lpmii
)
4705 if( lpmii
->cbSize
!= sizeof( mii
) &&
4706 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4707 SetLastError( ERROR_INVALID_PARAMETER
);
4710 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4711 mii
.cbSize
= sizeof( mii
);
4712 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4713 mii
.cbSize
= lpmii
->cbSize
;
4714 memcpy( lpmii
, &mii
, mii
.cbSize
);
4719 /* set a menu item text from a ASCII or Unicode string */
4720 static inline void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4726 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4727 strcpyW( menu
->text
, text
);
4731 LPCSTR str
= (LPCSTR
)text
;
4732 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4733 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4734 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4739 /**********************************************************************
4742 * detect if there are loops in the menu tree (or the depth is too large)
4744 static int MENU_depth( POPUPMENU
*pmenu
, int depth
)
4751 if( depth
> MAXMENUDEPTH
) return depth
;
4752 item
= pmenu
->items
;
4754 for( i
= 0; i
< pmenu
->nItems
&& subdepth
<= MAXMENUDEPTH
; i
++, item
++){
4755 POPUPMENU
*psubmenu
= item
->hSubMenu
? MENU_GetMenu( item
->hSubMenu
) : NULL
;
4757 int bdepth
= MENU_depth( psubmenu
, depth
);
4758 if( bdepth
> subdepth
) subdepth
= bdepth
;
4760 if( subdepth
> MAXMENUDEPTH
)
4761 TRACE("<- hmenu %p\n", item
->hSubMenu
);
4767 /**********************************************************************
4768 * SetMenuItemInfo_common
4770 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4771 * MIIM_BITMAP and MIIM_STRING flags instead.
4774 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4775 const MENUITEMINFOW
*lpmii
,
4778 if (!menu
) return FALSE
;
4780 debug_print_menuitem("SetMenuItemInfo_common from: ", menu
, "");
4782 if (lpmii
->fMask
& MIIM_FTYPE
) {
4783 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4784 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4786 if (lpmii
->fMask
& MIIM_STRING
) {
4787 /* free the string when used */
4788 HeapFree(GetProcessHeap(), 0, menu
->text
);
4789 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4792 if (lpmii
->fMask
& MIIM_STATE
)
4793 /* Other menu items having MFS_DEFAULT are not converted
4795 menu
->fState
= lpmii
->fState
& MENUITEMINFO_STATE_MASK
;
4797 if (lpmii
->fMask
& MIIM_ID
)
4798 menu
->wID
= lpmii
->wID
;
4800 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4801 menu
->hSubMenu
= lpmii
->hSubMenu
;
4802 if (menu
->hSubMenu
) {
4803 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4805 if( MENU_depth( subMenu
, 0) > MAXMENUDEPTH
) {
4806 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4810 subMenu
->wFlags
|= MF_POPUP
;
4811 menu
->fType
|= MF_POPUP
;
4813 SetLastError( ERROR_INVALID_PARAMETER
);
4818 menu
->fType
&= ~MF_POPUP
;
4821 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4823 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4824 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4826 if (lpmii
->fMask
& MIIM_DATA
)
4827 menu
->dwItemData
= lpmii
->dwItemData
;
4829 if (lpmii
->fMask
& MIIM_BITMAP
)
4830 menu
->hbmpItem
= lpmii
->hbmpItem
;
4832 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
4833 menu
->fType
|= MFT_SEPARATOR
;
4835 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4839 /**********************************************************************
4840 * MENU_NormalizeMenuItemInfoStruct
4842 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4843 * check, copy and extend the MENUITEMINFO struct from the version that the application
4844 * supplied to the version used by wine source. */
4845 static BOOL
MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW
*pmii_in
,
4846 MENUITEMINFOW
*pmii_out
)
4848 /* do we recognize the size? */
4849 if( !pmii_in
|| (pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) &&
4850 pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) - sizeof( pmii_in
->hbmpItem
)) ) {
4851 SetLastError( ERROR_INVALID_PARAMETER
);
4854 /* copy the fields that we have */
4855 memcpy( pmii_out
, pmii_in
, pmii_in
->cbSize
);
4856 /* if the hbmpItem member is missing then extend */
4857 if( pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
)) {
4858 pmii_out
->cbSize
= sizeof( MENUITEMINFOW
);
4859 pmii_out
->hbmpItem
= NULL
;
4861 /* test for invalid bit combinations */
4862 if( (pmii_out
->fMask
& MIIM_TYPE
&&
4863 pmii_out
->fMask
& (MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) ||
4864 (pmii_out
->fMask
& MIIM_FTYPE
&& pmii_out
->fType
& MFT_BITMAP
)) {
4865 WARN("invalid combination of fMask bits used\n");
4866 /* this does not happen on Win9x/ME */
4867 SetLastError( ERROR_INVALID_PARAMETER
);
4870 /* convert old style (MIIM_TYPE) to the new */
4871 if( pmii_out
->fMask
& MIIM_TYPE
){
4872 pmii_out
->fMask
|= MIIM_FTYPE
;
4873 if( IS_STRING_ITEM(pmii_out
->fType
)){
4874 pmii_out
->fMask
|= MIIM_STRING
;
4875 } else if( (pmii_out
->fType
) & MFT_BITMAP
){
4876 pmii_out
->fMask
|= MIIM_BITMAP
;
4877 pmii_out
->hbmpItem
= UlongToHandle(LOWORD(pmii_out
->dwTypeData
));
4883 /**********************************************************************
4884 * SetMenuItemInfoA (USER32.@)
4886 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4887 const MENUITEMINFOA
*lpmii
)
4892 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4894 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
4896 if (!(menuitem
= MENU_FindItem( &hmenu
, &item
, bypos
? MF_BYPOSITION
: 0 )))
4898 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
4899 if (item
== SC_TASKLIST
&& !bypos
) return TRUE
;
4902 return SetMenuItemInfo_common( menuitem
, &mii
, FALSE
);
4905 /**********************************************************************
4906 * SetMenuItemInfoW (USER32.@)
4908 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4909 const MENUITEMINFOW
*lpmii
)
4914 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4916 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
4917 if (!(menuitem
= MENU_FindItem( &hmenu
, &item
, bypos
? MF_BYPOSITION
: 0 )))
4919 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
4920 if (item
== SC_TASKLIST
&& !bypos
) return TRUE
;
4923 return SetMenuItemInfo_common( menuitem
, &mii
, TRUE
);
4926 /**********************************************************************
4927 * SetMenuDefaultItem (USER32.@)
4930 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4936 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4938 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4940 /* reset all default-item flags */
4942 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4944 item
->fState
&= ~MFS_DEFAULT
;
4947 /* no default item */
4956 if ( uItem
>= menu
->nItems
) return FALSE
;
4957 item
[uItem
].fState
|= MFS_DEFAULT
;
4962 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4964 if (item
->wID
== uItem
)
4966 item
->fState
|= MFS_DEFAULT
;
4975 /**********************************************************************
4976 * GetMenuDefaultItem (USER32.@)
4978 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4984 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4986 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4988 /* find default item */
4992 if (! item
) return -1;
4994 while ( !( item
->fState
& MFS_DEFAULT
) )
4997 if (i
>= menu
->nItems
) return -1;
5000 /* default: don't return disabled items */
5001 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
5003 /* search rekursiv when needed */
5004 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
5007 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
5008 if ( -1 != ret
) return ret
;
5010 /* when item not found in submenu, return the popup item */
5012 return ( bypos
) ? i
: item
->wID
;
5017 /**********************************************************************
5018 * InsertMenuItemA (USER32.@)
5020 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
5021 const MENUITEMINFOA
*lpmii
)
5026 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
5028 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
5030 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
5031 return SetMenuItemInfo_common(item
, &mii
, FALSE
);
5035 /**********************************************************************
5036 * InsertMenuItemW (USER32.@)
5038 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
5039 const MENUITEMINFOW
*lpmii
)
5044 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
5046 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
5048 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
5049 return SetMenuItemInfo_common(item
, &mii
, TRUE
);
5052 /**********************************************************************
5053 * CheckMenuRadioItem (USER32.@)
5056 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
5057 UINT first
, UINT last
, UINT check
,
5062 MENUITEM
*mi_first
= NULL
, *mi_check
;
5063 HMENU m_first
, m_check
;
5065 for (i
= first
; i
<= last
; i
++)
5072 mi_first
= MENU_FindItem(&m_first
, &pos
, bypos
);
5073 if (!mi_first
) continue;
5074 mi_check
= mi_first
;
5080 mi_check
= MENU_FindItem(&m_check
, &pos
, bypos
);
5081 if (!mi_check
) continue;
5084 if (m_first
!= m_check
) continue;
5085 if (mi_check
->fType
== MFT_SEPARATOR
) continue;
5089 mi_check
->fType
|= MFT_RADIOCHECK
;
5090 mi_check
->fState
|= MFS_CHECKED
;
5095 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5096 mi_check
->fState
&= ~MFS_CHECKED
;
5104 /**********************************************************************
5105 * GetMenuItemRect (USER32.@)
5107 * ATTENTION: Here, the returned values in rect are the screen
5108 * coordinates of the item just like if the menu was
5109 * always on the upper left side of the application.
5112 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
5115 POPUPMENU
*itemMenu
;
5119 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
5121 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
5122 referenceHwnd
= hwnd
;
5126 itemMenu
= MENU_GetMenu(hMenu
);
5127 if (itemMenu
== NULL
)
5130 if(itemMenu
->hWnd
== 0)
5132 referenceHwnd
= itemMenu
->hWnd
;
5135 if ((rect
== NULL
) || (item
== NULL
))
5140 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
5145 /**********************************************************************
5146 * SetMenuInfo (USER32.@)
5149 * actually use the items to draw the menu
5150 * (recalculate and/or redraw)
5152 static BOOL
menu_SetMenuInfo( HMENU hMenu
, LPCMENUINFO lpmi
)
5155 if( !(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
5157 if (lpmi
->fMask
& MIM_BACKGROUND
)
5158 menu
->hbrBack
= lpmi
->hbrBack
;
5160 if (lpmi
->fMask
& MIM_HELPID
)
5161 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
5163 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5164 menu
->cyMax
= lpmi
->cyMax
;
5166 if (lpmi
->fMask
& MIM_MENUDATA
)
5167 menu
->dwMenuData
= lpmi
->dwMenuData
;
5169 if (lpmi
->fMask
& MIM_STYLE
)
5170 menu
->dwStyle
= lpmi
->dwStyle
;
5172 if( lpmi
->fMask
& MIM_APPLYTOSUBMENUS
) {
5174 MENUITEM
*item
= menu
->items
;
5175 for( i
= menu
->nItems
; i
; i
--, item
++)
5176 if( item
->fType
& MF_POPUP
)
5177 menu_SetMenuInfo( item
->hSubMenu
, lpmi
);
5182 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
5184 TRACE("(%p %p)\n", hMenu
, lpmi
);
5185 if( lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu_SetMenuInfo( hMenu
, lpmi
))) {
5186 if( lpmi
->fMask
& MIM_STYLE
) {
5187 if (lpmi
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
5188 if (lpmi
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
5189 if (lpmi
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
5193 SetLastError( ERROR_INVALID_PARAMETER
);
5197 /**********************************************************************
5198 * GetMenuInfo (USER32.@)
5204 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
5207 TRACE("(%p %p)\n", hMenu
, lpmi
);
5209 if (lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
5212 if (lpmi
->fMask
& MIM_BACKGROUND
)
5213 lpmi
->hbrBack
= menu
->hbrBack
;
5215 if (lpmi
->fMask
& MIM_HELPID
)
5216 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
5218 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5219 lpmi
->cyMax
= menu
->cyMax
;
5221 if (lpmi
->fMask
& MIM_MENUDATA
)
5222 lpmi
->dwMenuData
= menu
->dwMenuData
;
5224 if (lpmi
->fMask
& MIM_STYLE
)
5225 lpmi
->dwStyle
= menu
->dwStyle
;
5229 SetLastError( ERROR_INVALID_PARAMETER
);
5234 /**********************************************************************
5235 * SetMenuContextHelpId (USER32.@)
5237 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
5241 TRACE("(%p 0x%08x)\n", hMenu
, dwContextHelpID
);
5243 if ((menu
= MENU_GetMenu(hMenu
)))
5245 menu
->dwContextHelpID
= dwContextHelpID
;
5252 /**********************************************************************
5253 * GetMenuContextHelpId (USER32.@)
5255 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
5259 TRACE("(%p)\n", hMenu
);
5261 if ((menu
= MENU_GetMenu(hMenu
)))
5263 return menu
->dwContextHelpID
;
5268 /**********************************************************************
5269 * MenuItemFromPoint (USER32.@)
5271 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
5273 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
5276 /*FIXME: Do we have to handle hWnd here? */
5277 if (!menu
) return -1;
5278 if (!MENU_FindItemByCoords(menu
, ptScreen
, &pos
)) return -1;
5283 /**********************************************************************
5284 * translate_accelerator
5286 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
5287 BYTE fVirt
, WORD key
, WORD cmd
)
5292 if (wParam
!= key
) return FALSE
;
5294 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
5295 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
5296 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
5298 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
5300 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
5302 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
5308 if(fVirt
& FVIRTKEY
)
5310 TRACE_(accel
)("found accel for virt_key %04lx (scan %04x)\n",
5311 wParam
, 0xff & HIWORD(lParam
));
5313 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
5314 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5318 if (!(lParam
& 0x01000000)) /* no special_key */
5320 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5321 { /* ^^ ALT pressed */
5322 TRACE_(accel
)("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
5331 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5335 HMENU hMenu
, hSubMenu
, hSysMenu
;
5336 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5338 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5339 hSysMenu
= get_win_sys_menu( hWnd
);
5341 /* find menu item and ask application to initialize it */
5342 /* 1. in the system menu */
5343 hSubMenu
= hSysMenu
;
5345 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5349 if (!IsWindowEnabled(hWnd
))
5353 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5354 if(hSubMenu
!= hSysMenu
)
5356 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5357 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5358 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5360 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5363 else /* 2. in the window's menu */
5367 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5371 if (!IsWindowEnabled(hWnd
))
5375 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5376 if(hSubMenu
!= hMenu
)
5378 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5379 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5380 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5382 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5389 if (uSysStat
!= (UINT
)-1)
5391 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5398 if (uStat
!= (UINT
)-1)
5404 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5416 if( mesg
==WM_COMMAND
)
5418 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5419 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5421 else if( mesg
==WM_SYSCOMMAND
)
5423 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5424 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5428 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5429 * #0: unknown (please report!)
5430 * #1: for WM_KEYUP,WM_SYSKEYUP
5431 * #2: mouse is captured
5432 * #3: window is disabled
5433 * #4: it's a disabled system menu option
5434 * #5: it's a menu option, but window is iconic
5435 * #6: it's a menu option, but disabled
5437 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5439 ERR_(accel
)(" unknown reason - please report!\n");
5444 /**********************************************************************
5445 * TranslateAcceleratorA (USER32.@)
5446 * TranslateAccelerator (USER32.@)
5448 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5450 switch (msg
->message
)
5454 return TranslateAcceleratorW( hWnd
, hAccel
, msg
);
5460 char ch
= LOWORD(msg
->wParam
);
5462 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5463 msgW
.wParam
= MAKEWPARAM(wch
, HIWORD(msg
->wParam
));
5464 return TranslateAcceleratorW( hWnd
, hAccel
, &msgW
);
5472 /**********************************************************************
5473 * TranslateAcceleratorW (USER32.@)
5475 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5477 ACCEL data
[32], *ptr
= data
;
5480 if (!hWnd
) return 0;
5482 if (msg
->message
!= WM_KEYDOWN
&&
5483 msg
->message
!= WM_SYSKEYDOWN
&&
5484 msg
->message
!= WM_CHAR
&&
5485 msg
->message
!= WM_SYSCHAR
)
5488 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5489 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5491 if (!(count
= CopyAcceleratorTableW( hAccel
, NULL
, 0 ))) return 0;
5492 if (count
> sizeof(data
)/sizeof(data
[0]))
5494 if (!(ptr
= HeapAlloc( GetProcessHeap(), 0, count
* sizeof(*ptr
) ))) return 0;
5496 count
= CopyAcceleratorTableW( hAccel
, ptr
, count
);
5497 for (i
= 0; i
< count
; i
++)
5499 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5500 ptr
[i
].fVirt
, ptr
[i
].key
, ptr
[i
].cmd
))
5503 if (ptr
!= data
) HeapFree( GetProcessHeap(), 0, ptr
);