Added YUV routines needed for v4l driver, and in the future possibly
[wine/gsoc-2012-control.git] / dlls / user / menu.c
blobeb1a089b5d862d1b67e7620cacb91d4867da39d4
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * Note: the style MF_MOUSESELECT is used to mark popup items that
26 * have been selected, i.e. their popup menu is currently displayed.
27 * This is probably not the meaning this style has in MS-Windows.
29 * TODO:
30 * implements styles :
31 * - MNS_AUTODISMISS
32 * - MNS_DRAGDROP
33 * - MNS_MODELESS
34 * - MNS_NOCHECK
35 * - MNS_NOTIFYBYPOS
38 #include "config.h"
39 #include "wine/port.h"
41 #include <stdarg.h>
42 #include <string.h>
44 #include "windef.h"
45 #include "winbase.h"
46 #include "wingdi.h"
47 #include "winnls.h"
48 #include "wine/winbase16.h"
49 #include "wine/winuser16.h"
50 #include "wownt32.h"
51 #include "wine/server.h"
52 #include "wine/unicode.h"
53 #include "win.h"
54 #include "controls.h"
55 #include "user_private.h"
56 #include "wine/debug.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(menu);
59 WINE_DECLARE_DEBUG_CHANNEL(accel);
61 /* internal popup menu window messages */
63 #define MM_SETMENUHANDLE (WM_USER + 0)
64 #define MM_GETMENUHANDLE (WM_USER + 1)
66 /* Menu item structure */
67 typedef struct {
68 /* ----------- MENUITEMINFO Stuff ----------- */
69 UINT fType; /* Item type. */
70 UINT fState; /* Item state. */
71 UINT_PTR wID; /* Item id. */
72 HMENU hSubMenu; /* Pop-up menu. */
73 HBITMAP hCheckBit; /* Bitmap when checked. */
74 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
75 LPWSTR text; /* Item text or bitmap handle. */
76 DWORD dwItemData; /* Application defined. */
77 DWORD dwTypeData; /* depends on fMask */
78 HBITMAP hbmpItem; /* bitmap in win98 style menus */
79 /* ----------- Wine stuff ----------- */
80 RECT rect; /* Item area (relative to menu window) */
81 UINT xTab; /* X position of text after Tab */
82 } MENUITEM;
84 /* Popup menu structure */
85 typedef struct {
86 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
87 WORD wMagic; /* Magic number */
88 WORD Width; /* Width of the whole menu */
89 WORD Height; /* Height of the whole menu */
90 UINT nItems; /* Number of items in the menu */
91 HWND hWnd; /* Window containing the menu */
92 MENUITEM *items; /* Array of menu items */
93 UINT FocusedItem; /* Currently focused item */
94 HWND hwndOwner; /* window receiving the messages for ownerdraw */
95 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
96 /* ------------ MENUINFO members ------ */
97 DWORD dwStyle; /* Extended mennu style */
98 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
99 HBRUSH hbrBack; /* brush for menu background */
100 DWORD dwContextHelpID;
101 DWORD dwMenuData; /* application defined value */
102 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
103 SIZE maxBmpSize; /* Maximum size of the bitmap items in MIIM_BITMAP state */
104 } POPUPMENU, *LPPOPUPMENU;
106 /* internal flags for menu tracking */
108 #define TF_ENDMENU 0x0001
109 #define TF_SUSPENDPOPUP 0x0002
110 #define TF_SKIPREMOVE 0x0004
112 typedef struct
114 UINT trackFlags;
115 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
116 HMENU hTopMenu; /* initial menu */
117 HWND hOwnerWnd; /* where notifications are sent */
118 POINT pt;
119 } MTRACKER;
121 #define MENU_MAGIC 0x554d /* 'MU' */
123 #define ITEM_PREV -1
124 #define ITEM_NEXT 1
126 /* Internal MENU_TrackMenu() flags */
127 #define TPM_INTERNAL 0xF0000000
128 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
129 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
130 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
132 /* popup menu shade thickness */
133 #define POPUP_XSHADE 4
134 #define POPUP_YSHADE 4
136 /* Space between 2 menu bar items */
137 #define MENU_BAR_ITEMS_SPACE 12
139 /* Minimum width of a tab character */
140 #define MENU_TAB_SPACE 8
142 /* Height of a separator item */
143 #define SEPARATOR_HEIGHT 5
145 /* Space between 2 columns */
146 #define MENU_COL_SPACE 4
148 /* (other menu->FocusedItem values give the position of the focused item) */
149 #define NO_SELECTED_ITEM 0xffff
151 #define MENU_ITEM_TYPE(flags) \
152 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
154 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
155 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
156 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
158 #define IS_SYSTEM_MENU(menu) \
159 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
161 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
162 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
163 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
164 MF_POPUP | MF_SYSMENU | MF_HELP)
165 #define STATE_MASK (~TYPE_MASK)
167 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
169 /* Dimension of the menu bitmaps */
170 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
172 static HBITMAP hStdMnArrow = 0;
173 static HBITMAP hBmpSysMenu = 0;
175 static HBRUSH hShadeBrush = 0;
176 static HFONT hMenuFont = 0;
177 static HFONT hMenuFontBold = 0;
179 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
181 /* Use global popup window because there's no way 2 menus can
182 * be tracked at the same time. */
183 static HWND top_popup;
185 /* Flag set by EndMenu() to force an exit from menu tracking */
186 static BOOL fEndMenu = FALSE;
188 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
190 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
192 /*********************************************************************
193 * menu class descriptor
195 const struct builtin_class_descr MENU_builtin_class =
197 POPUPMENU_CLASS_ATOMA, /* name */
198 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
199 NULL, /* procA (winproc is Unicode only) */
200 PopupMenuWndProc, /* procW */
201 sizeof(HMENU), /* extra */
202 IDC_ARROW, /* cursor */
203 (HBRUSH)(COLOR_MENU+1) /* brush */
207 /***********************************************************************
208 * debug_print_menuitem
210 * Print a menuitem in readable form.
213 #define debug_print_menuitem(pre, mp, post) \
214 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
216 #define MENUOUT(text) \
217 DPRINTF("%s%s", (count++ ? "," : ""), (text))
219 #define MENUFLAG(bit,text) \
220 do { \
221 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
222 } while (0)
224 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
225 const char *postfix)
227 TRACE("%s ", prefix);
228 if (mp) {
229 UINT flags = mp->fType;
230 int type = MENU_ITEM_TYPE(flags);
231 DPRINTF( "{ ID=0x%x", mp->wID);
232 if (flags & MF_POPUP)
233 DPRINTF( ", Sub=%p", mp->hSubMenu);
234 if (flags) {
235 int count = 0;
236 DPRINTF( ", Type=");
237 if (type == MFT_STRING)
238 /* Nothing */ ;
239 else if (type == MFT_SEPARATOR)
240 MENUOUT("sep");
241 else if (type == MFT_OWNERDRAW)
242 MENUOUT("own");
243 else if (type == MFT_BITMAP)
244 MENUOUT("bit");
245 else
246 MENUOUT("???");
247 flags -= type;
249 MENUFLAG(MF_POPUP, "pop");
250 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
251 MENUFLAG(MFT_MENUBREAK, "brk");
252 MENUFLAG(MFT_RADIOCHECK, "radio");
253 MENUFLAG(MFT_RIGHTORDER, "rorder");
254 MENUFLAG(MF_SYSMENU, "sys");
255 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
257 if (flags)
258 DPRINTF( "+0x%x", flags);
260 flags = mp->fState;
261 if (flags) {
262 int count = 0;
263 DPRINTF( ", State=");
264 MENUFLAG(MFS_GRAYED, "grey");
265 MENUFLAG(MFS_DEFAULT, "default");
266 MENUFLAG(MFS_DISABLED, "dis");
267 MENUFLAG(MFS_CHECKED, "check");
268 MENUFLAG(MFS_HILITE, "hi");
269 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
270 MENUFLAG(MF_MOUSESELECT, "mouse");
271 if (flags)
272 DPRINTF( "+0x%x", flags);
274 if (mp->hCheckBit)
275 DPRINTF( ", Chk=%p", mp->hCheckBit);
276 if (mp->hUnCheckBit)
277 DPRINTF( ", Unc=%p", mp->hUnCheckBit);
279 if (type == MFT_STRING) {
280 if (mp->text)
281 DPRINTF( ", Text=%s", debugstr_w(mp->text));
282 else
283 DPRINTF( ", Text=Null");
284 } else if (mp->text == NULL)
285 /* Nothing */ ;
286 else
287 DPRINTF( ", Text=%p", mp->text);
288 if (mp->dwItemData)
289 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
290 DPRINTF( " }");
291 } else {
292 DPRINTF( "NULL");
295 DPRINTF(" %s\n", postfix);
298 #undef MENUOUT
299 #undef MENUFLAG
302 /***********************************************************************
303 * MENU_GetMenu
305 * Validate the given menu handle and returns the menu structure pointer.
307 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
309 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
310 if (!menu || menu->wMagic != MENU_MAGIC)
312 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
313 menu = NULL;
315 return menu;
318 /***********************************************************************
319 * get_win_sys_menu
321 * Get the system menu of a window
323 static HMENU get_win_sys_menu( HWND hwnd )
325 HMENU ret = 0;
326 WND *win = WIN_GetPtr( hwnd );
327 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
329 ret = win->hSysMenu;
330 WIN_ReleasePtr( win );
332 return ret;
335 /***********************************************************************
336 * MENU_CopySysPopup
338 * Return the default system menu.
340 static HMENU MENU_CopySysPopup(void)
342 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
343 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
345 if( hMenu ) {
346 POPUPMENU* menu = MENU_GetMenu(hMenu);
347 menu->wFlags |= MF_SYSMENU | MF_POPUP;
348 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
350 else
351 ERR("Unable to load default system menu\n" );
353 TRACE("returning %p.\n", hMenu );
355 return hMenu;
359 /**********************************************************************
360 * MENU_GetSysMenu
362 * Create a copy of the system menu. System menu in Windows is
363 * a special menu bar with the single entry - system menu popup.
364 * This popup is presented to the outside world as a "system menu".
365 * However, the real system menu handle is sometimes seen in the
366 * WM_MENUSELECT parameters (and Word 6 likes it this way).
368 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
370 HMENU hMenu;
372 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
373 if ((hMenu = CreateMenu()))
375 POPUPMENU *menu = MENU_GetMenu(hMenu);
376 menu->wFlags = MF_SYSMENU;
377 menu->hWnd = WIN_GetFullHandle( hWnd );
378 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
380 if (hPopupMenu == (HMENU)(-1))
381 hPopupMenu = MENU_CopySysPopup();
382 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
384 if (hPopupMenu)
386 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
387 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
389 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
390 (UINT_PTR)hPopupMenu, NULL );
392 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
393 menu->items[0].fState = 0;
394 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
396 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
397 return hMenu;
399 DestroyMenu( hMenu );
401 ERR("failed to load system menu!\n");
402 return 0;
406 /***********************************************************************
407 * MENU_Init
409 * Menus initialisation.
411 BOOL MENU_Init(void)
413 HBITMAP hBitmap;
414 NONCLIENTMETRICSW ncm;
416 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
417 0x55, 0, 0xAA, 0,
418 0x55, 0, 0xAA, 0,
419 0x55, 0, 0xAA, 0 };
421 /* Load menu bitmaps */
422 hStdMnArrow = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
423 /* Load system buttons bitmaps */
424 hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
426 if (hStdMnArrow)
428 BITMAP bm;
429 GetObjectW( hStdMnArrow, sizeof(bm), &bm );
430 arrow_bitmap_width = bm.bmWidth;
431 arrow_bitmap_height = bm.bmHeight;
432 } else
433 return FALSE;
435 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
436 return FALSE;
438 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
439 return FALSE;
441 DeleteObject( hBitmap );
442 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
443 return FALSE;
445 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
446 if (!(SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0)))
447 return FALSE;
449 if (!(hMenuFont = CreateFontIndirectW( &ncm.lfMenuFont )))
450 return FALSE;
452 ncm.lfMenuFont.lfWeight += 300;
453 if ( ncm.lfMenuFont.lfWeight > 1000)
454 ncm.lfMenuFont.lfWeight = 1000;
456 if (!(hMenuFontBold = CreateFontIndirectW( &ncm.lfMenuFont )))
457 return FALSE;
459 return TRUE;
462 /***********************************************************************
463 * MENU_InitSysMenuPopup
465 * Grey the appropriate items in System menu.
467 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
469 BOOL gray;
471 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
472 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
473 gray = ((style & WS_MAXIMIZE) != 0);
474 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
475 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
476 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
477 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
478 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
479 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
480 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
481 gray = (clsStyle & CS_NOCLOSE) != 0;
483 /* The menu item must keep its state if it's disabled */
484 if(gray)
485 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
489 /******************************************************************************
491 * UINT MENU_GetStartOfNextColumn(
492 * HMENU hMenu )
494 *****************************************************************************/
496 static UINT MENU_GetStartOfNextColumn(
497 HMENU hMenu )
499 POPUPMENU *menu = MENU_GetMenu(hMenu);
500 UINT i;
502 if(!menu)
503 return NO_SELECTED_ITEM;
505 i = menu->FocusedItem + 1;
506 if( i == NO_SELECTED_ITEM )
507 return i;
509 for( ; i < menu->nItems; ++i ) {
510 if (menu->items[i].fType & MF_MENUBARBREAK)
511 return i;
514 return NO_SELECTED_ITEM;
518 /******************************************************************************
520 * UINT MENU_GetStartOfPrevColumn(
521 * HMENU hMenu )
523 *****************************************************************************/
525 static UINT MENU_GetStartOfPrevColumn(
526 HMENU hMenu )
528 POPUPMENU *menu = MENU_GetMenu(hMenu);
529 UINT i;
531 if( !menu )
532 return NO_SELECTED_ITEM;
534 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
535 return NO_SELECTED_ITEM;
537 /* Find the start of the column */
539 for(i = menu->FocusedItem; i != 0 &&
540 !(menu->items[i].fType & MF_MENUBARBREAK);
541 --i); /* empty */
543 if(i == 0)
544 return NO_SELECTED_ITEM;
546 for(--i; i != 0; --i) {
547 if (menu->items[i].fType & MF_MENUBARBREAK)
548 break;
551 TRACE("ret %d.\n", i );
553 return i;
558 /***********************************************************************
559 * MENU_FindItem
561 * Find a menu item. Return a pointer on the item, and modifies *hmenu
562 * in case the item was in a sub-menu.
564 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
566 POPUPMENU *menu;
567 UINT i;
569 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
570 if (wFlags & MF_BYPOSITION)
572 if (*nPos >= menu->nItems) return NULL;
573 return &menu->items[*nPos];
575 else
577 MENUITEM *item = menu->items;
578 for (i = 0; i < menu->nItems; i++, item++)
580 if (item->wID == *nPos)
582 *nPos = i;
583 return item;
585 else if (item->fType & MF_POPUP)
587 HMENU hsubmenu = item->hSubMenu;
588 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
589 if (subitem)
591 *hmenu = hsubmenu;
592 return subitem;
597 return NULL;
600 /***********************************************************************
601 * MENU_FindSubMenu
603 * Find a Sub menu. Return the position of the submenu, and modifies
604 * *hmenu in case it is found in another sub-menu.
605 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
607 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
609 POPUPMENU *menu;
610 UINT i;
611 MENUITEM *item;
612 if (((*hmenu)==(HMENU)0xffff) ||
613 (!(menu = MENU_GetMenu(*hmenu))))
614 return NO_SELECTED_ITEM;
615 item = menu->items;
616 for (i = 0; i < menu->nItems; i++, item++) {
617 if(!(item->fType & MF_POPUP)) continue;
618 if (item->hSubMenu == hSubTarget) {
619 return i;
621 else {
622 HMENU hsubmenu = item->hSubMenu;
623 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
624 if (pos != NO_SELECTED_ITEM) {
625 *hmenu = hsubmenu;
626 return pos;
630 return NO_SELECTED_ITEM;
633 /***********************************************************************
634 * MENU_FreeItemData
636 static void MENU_FreeItemData( MENUITEM* item )
638 /* delete text */
639 if (IS_STRING_ITEM(item->fType) && item->text)
640 HeapFree( GetProcessHeap(), 0, item->text );
643 /***********************************************************************
644 * MENU_FindItemByCoords
646 * Find the item at the specified coordinates (screen coords). Does
647 * not work for child windows and therefore should not be called for
648 * an arbitrary system menu.
650 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
651 POINT pt, UINT *pos )
653 MENUITEM *item;
654 UINT i;
655 RECT wrect;
657 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
658 pt.x -= wrect.left;pt.y -= wrect.top;
659 item = menu->items;
660 for (i = 0; i < menu->nItems; i++, item++)
662 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
663 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
665 if (pos) *pos = i;
666 return item;
669 return NULL;
673 /***********************************************************************
674 * MENU_FindItemByKey
676 * Find the menu item selected by a key press.
677 * Return item id, -1 if none, -2 if we should close the menu.
679 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
680 WCHAR key, BOOL forceMenuChar )
682 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
684 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
686 if (hmenu)
688 POPUPMENU *menu = MENU_GetMenu( hmenu );
689 MENUITEM *item = menu->items;
690 LRESULT menuchar;
692 if( !forceMenuChar )
694 UINT i;
696 for (i = 0; i < menu->nItems; i++, item++)
698 if (IS_STRING_ITEM(item->fType) && item->text)
700 WCHAR *p = item->text - 2;
703 p = strchrW (p + 2, '&');
705 while (p != NULL && p [1] == '&');
706 if (p && (toupperW(p[1]) == toupperW(key))) return i;
710 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
711 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
712 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
713 if (HIWORD(menuchar) == 1) return (UINT)(-2);
715 return (UINT)(-1);
719 /***********************************************************************
720 * MENU_GetBitmapItemSize
722 * Get the size of a bitmap item.
724 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
726 BITMAP bm;
727 HBITMAP bmp = (HBITMAP)id;
729 size->cx = size->cy = 0;
731 /* check if there is a magic menu item associated with this item */
732 if (id && IS_MAGIC_ITEM( id ))
734 switch(LOWORD(id))
736 case (INT_PTR)HBMMENU_SYSTEM:
737 if (data)
739 bmp = (HBITMAP)data;
740 break;
742 /* fall through */
743 case (INT_PTR)HBMMENU_MBAR_RESTORE:
744 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
745 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
746 case (INT_PTR)HBMMENU_MBAR_CLOSE:
747 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
748 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
749 size->cy = size->cx;
750 return;
751 case (INT_PTR)HBMMENU_CALLBACK:
752 case (INT_PTR)HBMMENU_POPUP_CLOSE:
753 case (INT_PTR)HBMMENU_POPUP_RESTORE:
754 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
755 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
756 default:
757 FIXME("Magic 0x%08x not implemented\n", id);
758 return;
761 if (GetObjectW(bmp, sizeof(bm), &bm ))
763 size->cx = bm.bmWidth;
764 size->cy = bm.bmHeight;
768 /***********************************************************************
769 * MENU_DrawBitmapItem
771 * Draw a bitmap item.
772 * drawhbmbitmap : True to draw the hbmbitmap(MIIM_BITMAP)/False to draw the MF_BITMAP
774 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar, BOOL drawhbmbitmap )
776 BITMAP bm;
777 DWORD rop;
778 HDC hdcMem;
779 HBITMAP bmp = (HBITMAP)lpitem->text;
780 int w = rect->right - rect->left;
781 int h = rect->bottom - rect->top;
782 int bmp_xoffset = 0;
783 int left, top;
784 HBITMAP hbmToDraw = (drawhbmbitmap)?lpitem->hbmpItem:(HBITMAP)lpitem->text;
786 /* Check if there is a magic menu item associated with this item */
787 if (hbmToDraw && IS_MAGIC_ITEM(hbmToDraw))
789 UINT flags = 0;
790 RECT r;
792 switch(LOWORD(hbmToDraw))
794 case (INT_PTR)HBMMENU_SYSTEM:
795 if (lpitem->dwItemData)
797 bmp = (HBITMAP)lpitem->dwItemData;
798 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
800 else
802 bmp = hBmpSysMenu;
803 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
804 /* only use right half of the bitmap */
805 bmp_xoffset = bm.bmWidth / 2;
806 bm.bmWidth -= bmp_xoffset;
808 goto got_bitmap;
809 case (INT_PTR)HBMMENU_MBAR_RESTORE:
810 flags = DFCS_CAPTIONRESTORE;
811 break;
812 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
813 flags = DFCS_CAPTIONMIN;
814 break;
815 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
816 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
817 break;
818 case (INT_PTR)HBMMENU_MBAR_CLOSE:
819 flags = DFCS_CAPTIONCLOSE;
820 break;
821 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
822 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
823 break;
824 case (INT_PTR)HBMMENU_CALLBACK:
825 case (INT_PTR)HBMMENU_POPUP_CLOSE:
826 case (INT_PTR)HBMMENU_POPUP_RESTORE:
827 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
828 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
829 default:
830 FIXME("Magic 0x%08x not implemented\n", LOWORD(hbmToDraw));
831 return;
833 r = *rect;
834 InflateRect( &r, -1, -1 );
835 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
836 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
837 return;
840 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
842 got_bitmap:
843 hdcMem = CreateCompatibleDC( hdc );
844 SelectObject( hdcMem, bmp );
846 /* handle fontsize > bitmap_height */
847 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
848 left=rect->left;
849 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
850 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
851 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
852 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
853 DeleteDC( hdcMem );
857 /***********************************************************************
858 * MENU_CalcItemSize
860 * Calculate the size of the menu item and store it in lpitem->rect.
862 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
863 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
865 WCHAR *p;
866 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
868 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
869 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
870 (menuBar ? " (MenuBar)" : ""));
872 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
874 if (lpitem->fType & MF_OWNERDRAW)
877 ** Experimentation under Windows reveals that an owner-drawn
878 ** menu is expected to return the size of the content part of
879 ** the menu item, not including the checkmark nor the submenu
880 ** arrow. Windows adds those values itself and returns the
881 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
883 MEASUREITEMSTRUCT mis;
884 mis.CtlType = ODT_MENU;
885 mis.CtlID = 0;
886 mis.itemID = lpitem->wID;
887 mis.itemData = (DWORD)lpitem->dwItemData;
888 mis.itemHeight = 0;
889 mis.itemWidth = 0;
890 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
891 lpitem->rect.right += mis.itemWidth;
893 if (menuBar)
895 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
898 /* under at least win95 you seem to be given a standard
899 height for the menu and the height value is ignored */
900 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
902 else
903 lpitem->rect.bottom += mis.itemHeight;
905 TRACE("id=%04x size=%dx%d\n",
906 lpitem->wID, mis.itemWidth, mis.itemHeight);
907 /* Fall through to get check/arrow width calculation. */
910 if (lpitem->fType & MF_SEPARATOR)
912 lpitem->rect.bottom += SEPARATOR_HEIGHT;
913 return;
916 if (!menuBar)
918 /* New style MIIM_BITMAP */
919 if (lpitem->hbmpItem)
921 if (lpitem->hbmpItem == HBMMENU_CALLBACK)
923 MEASUREITEMSTRUCT measItem;
924 measItem.CtlType = ODT_MENU;
925 measItem.CtlID = 0;
926 measItem.itemID = lpitem->wID;
927 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
928 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
929 measItem.itemData = lpitem->dwItemData;
931 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
933 /* Keep the size of the bitmap in callback mode to be able to draw it correctly */
934 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, measItem.itemWidth - (lpitem->rect.right - lpitem->rect.left));
935 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, measItem.itemHeight - (lpitem->rect.bottom - lpitem->rect.top));
936 lpitem->rect.right = lpitem->rect.left + measItem.itemWidth;
937 } else {
938 SIZE size;
939 MENU_GetBitmapItemSize((UINT)lpitem->hbmpItem, lpitem->dwItemData, &size);
940 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, size.cx);
941 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, size.cy);
942 lpitem->rect.right += size.cx;
943 lpitem->rect.bottom += size.cy;
945 if (lppop->dwStyle & MNS_CHECKORBMP)
946 lpitem->rect.right += check_bitmap_width;
947 else
948 lpitem->rect.right += 2 * check_bitmap_width;
949 } else
950 lpitem->rect.right += 2 * check_bitmap_width;
951 if (lpitem->fType & MF_POPUP)
952 lpitem->rect.right += arrow_bitmap_width;
955 if (lpitem->fType & MF_OWNERDRAW)
956 return;
958 if (IS_BITMAP_ITEM(lpitem->fType))
960 SIZE size;
962 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
963 lpitem->rect.right += size.cx;
964 lpitem->rect.bottom += size.cy;
965 /* Leave space for the sunken border */
966 lpitem->rect.right += 2;
967 lpitem->rect.bottom += 2;
971 /* it must be a text item - unless it's the system menu */
972 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
973 { SIZE size;
975 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
977 lpitem->rect.right += size.cx;
978 lpitem->rect.bottom += max(max(size.cy, GetSystemMetrics(SM_CYMENU)-1), lppop->maxBmpSize.cy);
979 lpitem->xTab = 0;
981 if (menuBar)
983 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
985 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
987 /* Item contains a tab (only meaningful in popup menus) */
988 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
989 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
990 lpitem->rect.right += MENU_TAB_SPACE;
992 else
994 if (strchrW( lpitem->text, '\b' ))
995 lpitem->rect.right += MENU_TAB_SPACE;
996 lpitem->xTab = lpitem->rect.right - check_bitmap_width
997 - arrow_bitmap_width;
1000 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
1004 /***********************************************************************
1005 * MENU_PopupMenuCalcSize
1007 * Calculate the size of a popup menu.
1009 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1011 MENUITEM *lpitem;
1012 HDC hdc;
1013 int start, i;
1014 int orgX, orgY, maxX, maxTab, maxTabWidth;
1016 lppop->Width = lppop->Height = 0;
1017 if (lppop->nItems == 0) return;
1018 hdc = GetDC( 0 );
1020 SelectObject( hdc, hMenuFont);
1022 start = 0;
1023 maxX = 2 + 1;
1025 lppop->maxBmpSize.cx = 0;
1026 lppop->maxBmpSize.cy = 0;
1028 while (start < lppop->nItems)
1030 lpitem = &lppop->items[start];
1031 orgX = maxX;
1032 if( lpitem->fType & MF_MENUBREAK)
1033 orgX += MENU_COL_SPACE;
1034 orgY = 3;
1036 maxTab = maxTabWidth = 0;
1037 /* Parse items until column break or end of menu */
1038 for (i = start; i < lppop->nItems; i++, lpitem++)
1040 if ((i != start) &&
1041 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1043 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1045 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1046 maxX = max( maxX, lpitem->rect.right );
1047 orgY = lpitem->rect.bottom;
1048 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1050 maxTab = max( maxTab, lpitem->xTab );
1051 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1055 /* Finish the column (set all items to the largest width found) */
1056 maxX = max( maxX, maxTab + maxTabWidth );
1057 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1059 lpitem->rect.right = maxX;
1060 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1061 lpitem->xTab = maxTab;
1064 lppop->Height = max( lppop->Height, orgY );
1067 lppop->Width = maxX;
1069 /* space for 3d border */
1070 lppop->Height += 2;
1071 lppop->Width += 2;
1073 ReleaseDC( 0, hdc );
1077 /***********************************************************************
1078 * MENU_MenuBarCalcSize
1080 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1081 * height is off by 1 pixel which causes lengthy window relocations when
1082 * active document window is maximized/restored.
1084 * Calculate the size of the menu bar.
1086 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1087 LPPOPUPMENU lppop, HWND hwndOwner )
1089 MENUITEM *lpitem;
1090 int start, i, orgX, orgY, maxY, helpPos;
1092 if ((lprect == NULL) || (lppop == NULL)) return;
1093 if (lppop->nItems == 0) return;
1094 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1095 lprect->left, lprect->top, lprect->right, lprect->bottom);
1096 lppop->Width = lprect->right - lprect->left;
1097 lppop->Height = 0;
1098 maxY = lprect->top+1;
1099 start = 0;
1100 helpPos = -1;
1101 lppop->maxBmpSize.cx = 0;
1102 lppop->maxBmpSize.cy = 0;
1103 while (start < lppop->nItems)
1105 lpitem = &lppop->items[start];
1106 orgX = lprect->left;
1107 orgY = maxY;
1109 /* Parse items until line break or end of menu */
1110 for (i = start; i < lppop->nItems; i++, lpitem++)
1112 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1113 if ((i != start) &&
1114 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1116 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1117 orgX, orgY );
1118 debug_print_menuitem (" item: ", lpitem, "");
1119 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1121 if (lpitem->rect.right > lprect->right)
1123 if (i != start) break;
1124 else lpitem->rect.right = lprect->right;
1126 maxY = max( maxY, lpitem->rect.bottom );
1127 orgX = lpitem->rect.right;
1130 /* Finish the line (set all items to the largest height found) */
1131 while (start < i) lppop->items[start++].rect.bottom = maxY;
1134 lprect->bottom = maxY;
1135 lppop->Height = lprect->bottom - lprect->top;
1137 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1138 /* the last item (if several lines, only move the last line) */
1139 lpitem = &lppop->items[lppop->nItems-1];
1140 orgY = lpitem->rect.top;
1141 orgX = lprect->right;
1142 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1143 if ( (helpPos==-1) || (helpPos>i) )
1144 break; /* done */
1145 if (lpitem->rect.top != orgY) break; /* Other line */
1146 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1147 lpitem->rect.left += orgX - lpitem->rect.right;
1148 lpitem->rect.right = orgX;
1149 orgX = lpitem->rect.left;
1153 /***********************************************************************
1154 * MENU_DrawMenuItem
1156 * Draw a single menu item.
1158 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1159 UINT height, BOOL menuBar, UINT odaction )
1161 RECT rect;
1163 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1165 if (lpitem->fType & MF_SYSMENU)
1167 if( !IsIconic(hwnd) )
1168 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1169 return;
1172 /* Setup colors */
1174 if (lpitem->fState & MF_HILITE)
1176 if(menuBar) {
1177 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1178 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1179 } else {
1180 if(lpitem->fState & MF_GRAYED)
1181 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1182 else
1183 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1184 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1187 else
1189 if (lpitem->fState & MF_GRAYED)
1190 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1191 else
1192 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1193 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1196 if (lpitem->fType & MF_OWNERDRAW)
1199 ** Experimentation under Windows reveals that an owner-drawn
1200 ** menu is given the rectangle which includes the space it requested
1201 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1202 ** and a popup-menu arrow. This is the value of lpitem->rect.
1203 ** Windows will leave all drawing to the application except for
1204 ** the popup-menu arrow. Windows always draws that itself, after
1205 ** the menu owner has finished drawing.
1207 DRAWITEMSTRUCT dis;
1209 dis.CtlType = ODT_MENU;
1210 dis.CtlID = 0;
1211 dis.itemID = lpitem->wID;
1212 dis.itemData = (DWORD)lpitem->dwItemData;
1213 dis.itemState = 0;
1214 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1215 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1216 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1217 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1218 dis.hwndItem = (HWND)hmenu;
1219 dis.hDC = hdc;
1220 dis.rcItem = lpitem->rect;
1221 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1222 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner,
1223 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1224 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1225 dis.rcItem.bottom);
1226 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1227 /* Fall through to draw popup-menu arrow */
1230 TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem->rect.left, lpitem->rect.top,
1231 lpitem->rect.right,lpitem->rect.bottom);
1233 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1235 rect = lpitem->rect;
1237 if (!(lpitem->fType & MF_OWNERDRAW))
1239 if (lpitem->fState & MF_HILITE)
1241 if(menuBar)
1242 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1243 else
1244 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1246 else
1247 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1250 SetBkMode( hdc, TRANSPARENT );
1252 if (!(lpitem->fType & MF_OWNERDRAW))
1254 /* vertical separator */
1255 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1257 RECT rc = rect;
1258 rc.top = 3;
1259 rc.bottom = height - 3;
1260 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1263 /* horizontal separator */
1264 if (lpitem->fType & MF_SEPARATOR)
1266 RECT rc = rect;
1267 rc.left++;
1268 rc.right--;
1269 rc.top += SEPARATOR_HEIGHT / 2;
1270 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1271 return;
1275 /* helper lines for debugging */
1276 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1277 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1278 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1279 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1282 if (!menuBar)
1284 HBITMAP bm;
1285 INT y = rect.top + rect.bottom;
1286 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1287 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1289 if (!(lpitem->fType & MF_OWNERDRAW))
1291 /* New style MIIM_BITMAP */
1292 if (lpitem->hbmpItem)
1294 POPUPMENU *menu = MENU_GetMenu(hmenu);
1295 HBITMAP hbm = lpitem->hbmpItem;
1297 if (hbm == HBMMENU_CALLBACK)
1299 DRAWITEMSTRUCT drawItem;
1300 drawItem.CtlType = ODT_MENU;
1301 drawItem.CtlID = 0;
1302 drawItem.itemID = lpitem->wID;
1303 drawItem.itemAction = odaction;
1304 drawItem.itemState |= (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1305 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1306 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1307 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1308 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1309 drawItem.hwndItem = (HWND)hmenu;
1310 drawItem.hDC = hdc;
1311 drawItem.rcItem = lpitem->rect;
1312 drawItem.itemData = lpitem->dwItemData;
1314 if (!(lpitem->fState & MF_CHECKED))
1315 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
1317 } else {
1318 MENU_DrawBitmapItem(hdc, lpitem, &rect, FALSE, TRUE);
1320 if (menu->dwStyle & MNS_CHECKORBMP)
1321 rect.left += menu->maxBmpSize.cx - check_bitmap_width;
1322 else
1323 rect.left += menu->maxBmpSize.cx;
1325 /* Draw the check mark
1327 * FIXME:
1328 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1330 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1331 if (bm) /* we have a custom bitmap */
1333 HDC hdcMem = CreateCompatibleDC( hdc );
1334 SelectObject( hdcMem, bm );
1335 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1336 check_bitmap_width, check_bitmap_height,
1337 hdcMem, 0, 0, SRCCOPY );
1338 DeleteDC( hdcMem );
1340 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1342 RECT r;
1343 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1344 HDC hdcMem = CreateCompatibleDC( hdc );
1345 SelectObject( hdcMem, bm );
1346 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1347 DrawFrameControl( hdcMem, &r, DFC_MENU,
1348 (lpitem->fType & MFT_RADIOCHECK) ?
1349 DFCS_MENUBULLET : DFCS_MENUCHECK );
1350 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1351 hdcMem, 0, 0, SRCCOPY );
1352 DeleteDC( hdcMem );
1353 DeleteObject( bm );
1357 /* Draw the popup-menu arrow */
1358 if (lpitem->fType & MF_POPUP)
1360 HDC hdcMem = CreateCompatibleDC( hdc );
1361 HBITMAP hOrigBitmap;
1363 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1364 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1365 (y - arrow_bitmap_height) / 2,
1366 arrow_bitmap_width, arrow_bitmap_height,
1367 hdcMem, 0, 0, SRCCOPY );
1368 SelectObject( hdcMem, hOrigBitmap );
1369 DeleteDC( hdcMem );
1372 rect.left += check_bitmap_width;
1373 rect.right -= arrow_bitmap_width;
1376 /* Done for owner-drawn */
1377 if (lpitem->fType & MF_OWNERDRAW)
1378 return;
1380 /* Draw the item text or bitmap */
1381 if (IS_BITMAP_ITEM(lpitem->fType))
1383 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar, FALSE);
1384 return;
1386 /* No bitmap - process text if present */
1387 else if (IS_STRING_ITEM(lpitem->fType))
1389 register int i;
1390 HFONT hfontOld = 0;
1392 UINT uFormat = (menuBar) ?
1393 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1394 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1396 if ( lpitem->fState & MFS_DEFAULT )
1398 hfontOld = SelectObject( hdc, hMenuFontBold);
1401 if (menuBar)
1403 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1404 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1407 for (i = 0; lpitem->text[i]; i++)
1408 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1409 break;
1411 if(lpitem->fState & MF_GRAYED)
1413 if (!(lpitem->fState & MF_HILITE) )
1415 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1416 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1417 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1418 --rect.left; --rect.top; --rect.right; --rect.bottom;
1420 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1423 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1425 /* paint the shortcut text */
1426 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1428 if (lpitem->text[i] == '\t')
1430 rect.left = lpitem->xTab;
1431 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1433 else
1435 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1438 if(lpitem->fState & MF_GRAYED)
1440 if (!(lpitem->fState & MF_HILITE) )
1442 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1443 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1444 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1445 --rect.left; --rect.top; --rect.right; --rect.bottom;
1447 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1449 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1452 if (hfontOld)
1453 SelectObject (hdc, hfontOld);
1458 /***********************************************************************
1459 * MENU_DrawPopupMenu
1461 * Paint a popup menu.
1463 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1465 HBRUSH hPrevBrush = 0;
1466 RECT rect;
1468 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1470 GetClientRect( hwnd, &rect );
1472 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1473 && (SelectObject( hdc, hMenuFont)))
1475 HPEN hPrevPen;
1477 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1479 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1480 if( hPrevPen )
1482 POPUPMENU *menu;
1484 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1486 /* draw menu items */
1488 menu = MENU_GetMenu( hmenu );
1489 if (menu && menu->nItems)
1491 MENUITEM *item;
1492 UINT u;
1494 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1495 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1496 menu->Height, FALSE, ODA_DRAWENTIRE );
1499 } else
1501 SelectObject( hdc, hPrevBrush );
1506 /***********************************************************************
1507 * MENU_DrawMenuBar
1509 * Paint a menu bar. Returns the height of the menu bar.
1510 * called from [windows/nonclient.c]
1512 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1513 BOOL suppress_draw)
1515 LPPOPUPMENU lppop;
1516 HFONT hfontOld = 0;
1517 HMENU hMenu = GetMenu(hwnd);
1519 lppop = MENU_GetMenu( hMenu );
1520 if (lppop == NULL || lprect == NULL)
1522 return GetSystemMetrics(SM_CYMENU);
1525 if (suppress_draw)
1527 hfontOld = SelectObject( hDC, hMenuFont);
1529 if (lppop->Height == 0)
1530 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1532 lprect->bottom = lprect->top + lppop->Height;
1534 if (hfontOld) SelectObject( hDC, hfontOld);
1535 return lppop->Height;
1537 else
1538 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1542 /***********************************************************************
1543 * MENU_ShowPopup
1545 * Display a popup menu.
1547 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1548 INT x, INT y, INT xanchor, INT yanchor )
1550 POPUPMENU *menu;
1551 UINT width, height;
1553 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1554 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1556 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1557 if (menu->FocusedItem != NO_SELECTED_ITEM)
1559 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1560 menu->FocusedItem = NO_SELECTED_ITEM;
1563 /* store the owner for DrawItem */
1564 menu->hwndOwner = hwndOwner;
1566 MENU_PopupMenuCalcSize( menu, hwndOwner );
1568 /* adjust popup menu pos so that it fits within the desktop */
1570 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1571 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1573 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1575 if( xanchor )
1576 x -= width - xanchor;
1577 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1578 x = GetSystemMetrics(SM_CXSCREEN) - width;
1580 if( x < 0 ) x = 0;
1582 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1584 if( yanchor )
1585 y -= height + yanchor;
1586 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1587 y = GetSystemMetrics(SM_CYSCREEN) - height;
1589 if( y < 0 ) y = 0;
1591 /* NOTE: In Windows, top menu popup is not owned. */
1592 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1593 WS_POPUP, x, y, width, height,
1594 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1595 (LPVOID)hmenu );
1596 if( !menu->hWnd ) return FALSE;
1597 if (!top_popup) top_popup = menu->hWnd;
1599 /* Display the window */
1601 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1602 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1603 UpdateWindow( menu->hWnd );
1604 return TRUE;
1608 /***********************************************************************
1609 * MENU_SelectItem
1611 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1612 BOOL sendMenuSelect, HMENU topmenu )
1614 LPPOPUPMENU lppop;
1615 HDC hdc;
1617 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1619 lppop = MENU_GetMenu( hmenu );
1620 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1622 if (lppop->FocusedItem == wIndex) return;
1623 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1624 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1625 if (!top_popup) top_popup = lppop->hWnd;
1627 SelectObject( hdc, hMenuFont);
1629 /* Clear previous highlighted item */
1630 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1632 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1633 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1634 lppop->Height, !(lppop->wFlags & MF_POPUP),
1635 ODA_SELECT );
1638 /* Highlight new item (if any) */
1639 lppop->FocusedItem = wIndex;
1640 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1642 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1643 lppop->items[wIndex].fState |= MF_HILITE;
1644 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1645 &lppop->items[wIndex], lppop->Height,
1646 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1648 if (sendMenuSelect)
1650 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1651 SendMessageW( hwndOwner, WM_MENUSELECT,
1652 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1653 ip->fType | ip->fState |
1654 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1657 else if (sendMenuSelect) {
1658 if(topmenu){
1659 int pos;
1660 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1661 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1662 MENUITEM *ip = &ptm->items[pos];
1663 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1664 ip->fType | ip->fState |
1665 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1669 ReleaseDC( lppop->hWnd, hdc );
1673 /***********************************************************************
1674 * MENU_MoveSelection
1676 * Moves currently selected item according to the offset parameter.
1677 * If there is no selection then it should select the last item if
1678 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1680 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1682 INT i;
1683 POPUPMENU *menu;
1685 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1687 menu = MENU_GetMenu( hmenu );
1688 if ((!menu) || (!menu->items)) return;
1690 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1692 if( menu->nItems == 1 ) return; else
1693 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1694 ; i += offset)
1695 if (!(menu->items[i].fType & MF_SEPARATOR))
1697 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1698 return;
1702 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1703 i >= 0 && i < menu->nItems ; i += offset)
1704 if (!(menu->items[i].fType & MF_SEPARATOR))
1706 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1707 return;
1712 /**********************************************************************
1713 * MENU_SetItemData
1715 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1716 * ModifyMenu().
1718 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1719 LPCWSTR str )
1721 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1723 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1724 TRACE("flags=%x str=%p\n", flags, str);
1726 if (IS_STRING_ITEM(flags))
1728 if (!str)
1730 flags |= MF_SEPARATOR;
1731 item->text = NULL;
1733 else
1735 LPWSTR text;
1736 /* Item beginning with a backspace is a help item */
1737 if (*str == '\b')
1739 flags |= MF_HELP;
1740 str++;
1742 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1743 return FALSE;
1744 strcpyW( text, str );
1745 item->text = text;
1748 else if (IS_BITMAP_ITEM(flags))
1749 item->text = (LPWSTR)HBITMAP_32(LOWORD(str));
1750 else item->text = NULL;
1752 if (flags & MF_OWNERDRAW)
1753 item->dwItemData = (DWORD)str;
1754 else
1755 item->dwItemData = 0;
1757 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1758 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1760 if (flags & MF_POPUP)
1762 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1763 if (menu) menu->wFlags |= MF_POPUP;
1764 else
1766 item->wID = 0;
1767 item->hSubMenu = 0;
1768 item->fType = 0;
1769 item->fState = 0;
1770 return FALSE;
1774 item->wID = id;
1775 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
1777 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1778 flags |= MF_POPUP; /* keep popup */
1780 item->fType = flags & TYPE_MASK;
1781 item->fState = (flags & STATE_MASK) &
1782 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1785 /* Don't call SetRectEmpty here! */
1788 HeapFree( GetProcessHeap(), 0, prevText );
1790 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1791 return TRUE;
1795 /**********************************************************************
1796 * MENU_InsertItem
1798 * Insert (allocate) a new item into a menu.
1800 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1802 MENUITEM *newItems;
1803 POPUPMENU *menu;
1805 if (!(menu = MENU_GetMenu(hMenu)))
1806 return NULL;
1808 /* Find where to insert new item */
1810 if (flags & MF_BYPOSITION) {
1811 if (pos > menu->nItems)
1812 pos = menu->nItems;
1813 } else {
1814 if (!MENU_FindItem( &hMenu, &pos, flags ))
1815 pos = menu->nItems;
1816 else {
1817 if (!(menu = MENU_GetMenu( hMenu )))
1818 return NULL;
1822 /* Create new items array */
1824 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1825 if (!newItems)
1827 WARN("allocation failed\n" );
1828 return NULL;
1830 if (menu->nItems > 0)
1832 /* Copy the old array into the new one */
1833 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1834 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1835 (menu->nItems-pos)*sizeof(MENUITEM) );
1836 HeapFree( GetProcessHeap(), 0, menu->items );
1838 menu->items = newItems;
1839 menu->nItems++;
1840 memset( &newItems[pos], 0, sizeof(*newItems) );
1841 menu->Height = 0; /* force size recalculate */
1842 return &newItems[pos];
1846 /**********************************************************************
1847 * MENU_ParseResource
1849 * Parse a standard menu resource and add items to the menu.
1850 * Return a pointer to the end of the resource.
1852 * NOTE: flags is equivalent to the mtOption field
1854 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1856 WORD flags, id = 0;
1857 LPCSTR str;
1861 flags = GET_WORD(res);
1862 res += sizeof(WORD);
1863 if (!(flags & MF_POPUP))
1865 id = GET_WORD(res);
1866 res += sizeof(WORD);
1868 str = res;
1869 if (!unicode) res += strlen(str) + 1;
1870 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1871 if (flags & MF_POPUP)
1873 HMENU hSubMenu = CreatePopupMenu();
1874 if (!hSubMenu) return NULL;
1875 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1876 return NULL;
1877 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1878 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1880 else /* Not a popup */
1882 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1883 else AppendMenuW( hMenu, flags, id,
1884 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1886 } while (!(flags & MF_END));
1887 return res;
1891 /**********************************************************************
1892 * MENUEX_ParseResource
1894 * Parse an extended menu resource and add items to the menu.
1895 * Return a pointer to the end of the resource.
1897 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1899 WORD resinfo;
1900 do {
1901 MENUITEMINFOW mii;
1903 mii.cbSize = sizeof(mii);
1904 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1905 mii.fType = GET_DWORD(res);
1906 res += sizeof(DWORD);
1907 mii.fState = GET_DWORD(res);
1908 res += sizeof(DWORD);
1909 mii.wID = GET_DWORD(res);
1910 res += sizeof(DWORD);
1911 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1912 res += sizeof(WORD);
1913 /* Align the text on a word boundary. */
1914 res += (~((int)res - 1)) & 1;
1915 mii.dwTypeData = (LPWSTR) res;
1916 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1917 /* Align the following fields on a dword boundary. */
1918 res += (~((int)res - 1)) & 3;
1920 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1921 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1923 if (resinfo & 1) { /* Pop-up? */
1924 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1925 res += sizeof(DWORD);
1926 mii.hSubMenu = CreatePopupMenu();
1927 if (!mii.hSubMenu)
1928 return NULL;
1929 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1930 DestroyMenu(mii.hSubMenu);
1931 return NULL;
1933 mii.fMask |= MIIM_SUBMENU;
1934 mii.fType |= MF_POPUP;
1936 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1938 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1939 mii.wID, mii.fType);
1940 mii.fType |= MF_SEPARATOR;
1942 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1943 } while (!(resinfo & MF_END));
1944 return res;
1948 /***********************************************************************
1949 * MENU_GetSubPopup
1951 * Return the handle of the selected sub-popup menu (if any).
1953 static HMENU MENU_GetSubPopup( HMENU hmenu )
1955 POPUPMENU *menu;
1956 MENUITEM *item;
1958 menu = MENU_GetMenu( hmenu );
1960 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1962 item = &menu->items[menu->FocusedItem];
1963 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1964 return item->hSubMenu;
1965 return 0;
1969 /***********************************************************************
1970 * MENU_HideSubPopups
1972 * Hide the sub-popup menus of this menu.
1974 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1975 BOOL sendMenuSelect )
1977 POPUPMENU *menu = MENU_GetMenu( hmenu );
1979 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1981 if (menu && top_popup)
1983 HMENU hsubmenu;
1984 POPUPMENU *submenu;
1985 MENUITEM *item;
1987 if (menu->FocusedItem != NO_SELECTED_ITEM)
1989 item = &menu->items[menu->FocusedItem];
1990 if (!(item->fType & MF_POPUP) ||
1991 !(item->fState & MF_MOUSESELECT)) return;
1992 item->fState &= ~MF_MOUSESELECT;
1993 hsubmenu = item->hSubMenu;
1994 } else return;
1996 submenu = MENU_GetMenu( hsubmenu );
1997 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1998 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
1999 DestroyWindow( submenu->hWnd );
2000 submenu->hWnd = 0;
2005 /***********************************************************************
2006 * MENU_ShowSubPopup
2008 * Display the sub-menu of the selected item of this menu.
2009 * Return the handle of the submenu, or hmenu if no submenu to display.
2011 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2012 BOOL selectFirst, UINT wFlags )
2014 RECT rect;
2015 POPUPMENU *menu;
2016 MENUITEM *item;
2017 HDC hdc;
2019 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2021 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2023 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2025 item = &menu->items[menu->FocusedItem];
2026 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2027 return hmenu;
2029 /* message must be sent before using item,
2030 because nearly everything may be changed by the application ! */
2032 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2033 if (!(wFlags & TPM_NONOTIFY))
2034 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2035 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2037 item = &menu->items[menu->FocusedItem];
2038 rect = item->rect;
2040 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2041 if (!(item->fState & MF_HILITE))
2043 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2044 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2046 SelectObject( hdc, hMenuFont);
2048 item->fState |= MF_HILITE;
2049 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2050 ReleaseDC( menu->hWnd, hdc );
2052 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2053 item->rect = rect;
2055 item->fState |= MF_MOUSESELECT;
2057 if (IS_SYSTEM_MENU(menu))
2059 MENU_InitSysMenuPopup(item->hSubMenu,
2060 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2061 GetClassLongW( menu->hWnd, GCL_STYLE));
2063 NC_GetSysPopupPos( menu->hWnd, &rect );
2064 rect.top = rect.bottom;
2065 rect.right = GetSystemMetrics(SM_CXSIZE);
2066 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2068 else
2070 GetWindowRect( menu->hWnd, &rect );
2071 if (menu->wFlags & MF_POPUP)
2073 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2074 rect.top += item->rect.top;
2075 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2076 rect.bottom = item->rect.top - item->rect.bottom;
2078 else
2080 rect.left += item->rect.left;
2081 rect.top += item->rect.bottom;
2082 rect.right = item->rect.right - item->rect.left;
2083 rect.bottom = item->rect.bottom - item->rect.top;
2087 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2088 rect.left, rect.top, rect.right, rect.bottom );
2089 if (selectFirst)
2090 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2091 return item->hSubMenu;
2096 /**********************************************************************
2097 * MENU_IsMenuActive
2099 HWND MENU_IsMenuActive(void)
2101 return top_popup;
2104 /***********************************************************************
2105 * MENU_PtMenu
2107 * Walks menu chain trying to find a menu pt maps to.
2109 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2111 POPUPMENU *menu = MENU_GetMenu( hMenu );
2112 UINT item = menu->FocusedItem;
2113 HMENU ret;
2115 /* try subpopup first (if any) */
2116 ret = (item != NO_SELECTED_ITEM &&
2117 (menu->items[item].fType & MF_POPUP) &&
2118 (menu->items[item].fState & MF_MOUSESELECT))
2119 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2121 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2123 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2124 if( menu->wFlags & MF_POPUP )
2126 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2128 else if (ht == HTSYSMENU)
2129 ret = get_win_sys_menu( menu->hWnd );
2130 else if (ht == HTMENU)
2131 ret = GetMenu( menu->hWnd );
2133 return ret;
2136 /***********************************************************************
2137 * MENU_ExecFocusedItem
2139 * Execute a menu item (for instance when user pressed Enter).
2140 * Return the wID of the executed item. Otherwise, -1 indicating
2141 * that no menu item was executed;
2142 * Have to receive the flags for the TrackPopupMenu options to avoid
2143 * sending unwanted message.
2146 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2148 MENUITEM *item;
2149 POPUPMENU *menu = MENU_GetMenu( hMenu );
2151 TRACE("%p hmenu=%p\n", pmt, hMenu);
2153 if (!menu || !menu->nItems ||
2154 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2156 item = &menu->items[menu->FocusedItem];
2158 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2160 if (!(item->fType & MF_POPUP))
2162 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2164 /* If TPM_RETURNCMD is set you return the id, but
2165 do not send a message to the owner */
2166 if(!(wFlags & TPM_RETURNCMD))
2168 if( menu->wFlags & MF_SYSMENU )
2169 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2170 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2171 else
2172 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2174 return item->wID;
2177 else
2178 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2180 return -1;
2183 /***********************************************************************
2184 * MENU_SwitchTracking
2186 * Helper function for menu navigation routines.
2188 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2190 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2191 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2193 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2195 if( pmt->hTopMenu != hPtMenu &&
2196 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2198 /* both are top level menus (system and menu-bar) */
2199 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2200 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2201 pmt->hTopMenu = hPtMenu;
2203 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2204 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2208 /***********************************************************************
2209 * MENU_ButtonDown
2211 * Return TRUE if we can go on with menu tracking.
2213 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2215 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2217 if (hPtMenu)
2219 UINT id = 0;
2220 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2221 MENUITEM *item;
2223 if( IS_SYSTEM_MENU(ptmenu) )
2224 item = ptmenu->items;
2225 else
2226 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2228 if( item )
2230 if( ptmenu->FocusedItem != id )
2231 MENU_SwitchTracking( pmt, hPtMenu, id );
2233 /* If the popup menu is not already "popped" */
2234 if(!(item->fState & MF_MOUSESELECT ))
2236 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2239 return TRUE;
2241 /* Else the click was on the menu bar, finish the tracking */
2243 return FALSE;
2246 /***********************************************************************
2247 * MENU_ButtonUp
2249 * Return the value of MENU_ExecFocusedItem if
2250 * the selected item was not a popup. Else open the popup.
2251 * A -1 return value indicates that we go on with menu tracking.
2254 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2256 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2258 if (hPtMenu)
2260 UINT id = 0;
2261 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2262 MENUITEM *item;
2264 if( IS_SYSTEM_MENU(ptmenu) )
2265 item = ptmenu->items;
2266 else
2267 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2269 if( item && (ptmenu->FocusedItem == id ))
2271 if( !(item->fType & MF_POPUP) )
2272 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2274 /* If we are dealing with the top-level menu */
2275 /* and this is a click on an already "popped" item: */
2276 /* Stop the menu tracking and close the opened submenus */
2277 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2278 return 0;
2280 ptmenu->bTimeToHide = TRUE;
2282 return -1;
2286 /***********************************************************************
2287 * MENU_MouseMove
2289 * Return TRUE if we can go on with menu tracking.
2291 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2293 UINT id = NO_SELECTED_ITEM;
2294 POPUPMENU *ptmenu = NULL;
2296 if( hPtMenu )
2298 ptmenu = MENU_GetMenu( hPtMenu );
2299 if( IS_SYSTEM_MENU(ptmenu) )
2300 id = 0;
2301 else
2302 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2305 if( id == NO_SELECTED_ITEM )
2307 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2308 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2311 else if( ptmenu->FocusedItem != id )
2313 MENU_SwitchTracking( pmt, hPtMenu, id );
2314 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2316 return TRUE;
2320 /***********************************************************************
2321 * MENU_SetCapture
2323 static void MENU_SetCapture( HWND hwnd )
2325 HWND previous = 0;
2327 SERVER_START_REQ( set_capture_window )
2329 req->handle = hwnd;
2330 req->flags = CAPTURE_MENU;
2331 if (!wine_server_call_err( req ))
2333 previous = reply->previous;
2334 hwnd = reply->full_handle;
2337 SERVER_END_REQ;
2339 if (previous && previous != hwnd)
2340 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2344 /***********************************************************************
2345 * MENU_DoNextMenu
2347 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2349 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2351 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2353 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2354 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2356 MDINEXTMENU next_menu;
2357 HMENU hNewMenu;
2358 HWND hNewWnd;
2359 UINT id = 0;
2361 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2362 next_menu.hmenuNext = 0;
2363 next_menu.hwndNext = 0;
2364 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2366 TRACE("%p [%p] -> %p [%p]\n",
2367 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2369 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2371 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2372 hNewWnd = pmt->hOwnerWnd;
2373 if( IS_SYSTEM_MENU(menu) )
2375 /* switch to the menu bar */
2377 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2379 if( vk == VK_LEFT )
2381 menu = MENU_GetMenu( hNewMenu );
2382 id = menu->nItems - 1;
2385 else if (style & WS_SYSMENU )
2387 /* switch to the system menu */
2388 hNewMenu = get_win_sys_menu( hNewWnd );
2390 else return FALSE;
2392 else /* application returned a new menu to switch to */
2394 hNewMenu = next_menu.hmenuNext;
2395 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2397 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2399 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2401 if (style & WS_SYSMENU &&
2402 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2404 /* get the real system menu */
2405 hNewMenu = get_win_sys_menu(hNewWnd);
2407 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2409 /* FIXME: Not sure what to do here;
2410 * perhaps try to track hNewMenu as a popup? */
2412 TRACE(" -- got confused.\n");
2413 return FALSE;
2416 else return FALSE;
2419 if( hNewMenu != pmt->hTopMenu )
2421 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2422 FALSE, 0 );
2423 if( pmt->hCurrentMenu != pmt->hTopMenu )
2424 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2427 if( hNewWnd != pmt->hOwnerWnd )
2429 pmt->hOwnerWnd = hNewWnd;
2430 MENU_SetCapture( pmt->hOwnerWnd );
2433 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2434 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2436 return TRUE;
2438 return FALSE;
2441 /***********************************************************************
2442 * MENU_SuspendPopup
2444 * The idea is not to show the popup if the next input message is
2445 * going to hide it anyway.
2447 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2449 MSG msg;
2451 msg.hwnd = pmt->hOwnerWnd;
2453 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2454 pmt->trackFlags |= TF_SKIPREMOVE;
2456 switch( uMsg )
2458 case WM_KEYDOWN:
2459 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2460 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2462 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2463 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2464 if( msg.message == WM_KEYDOWN &&
2465 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2467 pmt->trackFlags |= TF_SUSPENDPOPUP;
2468 return TRUE;
2471 break;
2474 /* failures go through this */
2475 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2476 return FALSE;
2479 /***********************************************************************
2480 * MENU_KeyEscape
2482 * Handle a VK_ESCAPE key event in a menu.
2484 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2486 BOOL bEndMenu = TRUE;
2488 if (pmt->hCurrentMenu != pmt->hTopMenu)
2490 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2492 if (menu->wFlags & MF_POPUP)
2494 HMENU hmenutmp, hmenuprev;
2496 hmenuprev = hmenutmp = pmt->hTopMenu;
2498 /* close topmost popup */
2499 while (hmenutmp != pmt->hCurrentMenu)
2501 hmenuprev = hmenutmp;
2502 hmenutmp = MENU_GetSubPopup( hmenuprev );
2505 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2506 pmt->hCurrentMenu = hmenuprev;
2507 bEndMenu = FALSE;
2511 return bEndMenu;
2514 /***********************************************************************
2515 * MENU_KeyLeft
2517 * Handle a VK_LEFT key event in a menu.
2519 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2521 POPUPMENU *menu;
2522 HMENU hmenutmp, hmenuprev;
2523 UINT prevcol;
2525 hmenuprev = hmenutmp = pmt->hTopMenu;
2526 menu = MENU_GetMenu( hmenutmp );
2528 /* Try to move 1 column left (if possible) */
2529 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2530 NO_SELECTED_ITEM ) {
2532 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2533 prevcol, TRUE, 0 );
2534 return;
2537 /* close topmost popup */
2538 while (hmenutmp != pmt->hCurrentMenu)
2540 hmenuprev = hmenutmp;
2541 hmenutmp = MENU_GetSubPopup( hmenuprev );
2544 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2545 pmt->hCurrentMenu = hmenuprev;
2547 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2549 /* move menu bar selection if no more popups are left */
2551 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2552 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2554 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2556 /* A sublevel menu was displayed - display the next one
2557 * unless there is another displacement coming up */
2559 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2560 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2561 pmt->hTopMenu, TRUE, wFlags);
2567 /***********************************************************************
2568 * MENU_KeyRight
2570 * Handle a VK_RIGHT key event in a menu.
2572 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2574 HMENU hmenutmp;
2575 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2576 UINT nextcol;
2578 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2579 pmt->hCurrentMenu,
2580 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2581 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2583 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2585 /* If already displaying a popup, try to display sub-popup */
2587 hmenutmp = pmt->hCurrentMenu;
2588 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2590 /* if subpopup was displayed then we are done */
2591 if (hmenutmp != pmt->hCurrentMenu) return;
2594 /* Check to see if there's another column */
2595 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2596 NO_SELECTED_ITEM ) {
2597 TRACE("Going to %d.\n", nextcol );
2598 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2599 nextcol, TRUE, 0 );
2600 return;
2603 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2605 if( pmt->hCurrentMenu != pmt->hTopMenu )
2607 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2608 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2609 } else hmenutmp = 0;
2611 /* try to move to the next item */
2612 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2613 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2615 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2616 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2617 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2618 pmt->hTopMenu, TRUE, wFlags);
2622 /***********************************************************************
2623 * MENU_TrackMenu
2625 * Menu tracking code.
2627 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2628 HWND hwnd, const RECT *lprect )
2630 MSG msg;
2631 POPUPMENU *menu;
2632 BOOL fRemove;
2633 INT executedMenuId = -1;
2634 MTRACKER mt;
2635 BOOL enterIdleSent = FALSE;
2637 mt.trackFlags = 0;
2638 mt.hCurrentMenu = hmenu;
2639 mt.hTopMenu = hmenu;
2640 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2641 mt.pt.x = x;
2642 mt.pt.y = y;
2644 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2645 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2646 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2648 fEndMenu = FALSE;
2649 if (!(menu = MENU_GetMenu( hmenu )))
2651 WARN("Invalid menu handle %p\n", hmenu);
2652 SetLastError(ERROR_INVALID_MENU_HANDLE);
2653 return FALSE;
2656 if (wFlags & TPM_BUTTONDOWN)
2658 /* Get the result in order to start the tracking or not */
2659 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2660 fEndMenu = !fRemove;
2663 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2665 MENU_SetCapture( mt.hOwnerWnd );
2667 while (!fEndMenu)
2669 menu = MENU_GetMenu( mt.hCurrentMenu );
2670 if (!menu) /* sometimes happens if I do a window manager close */
2671 break;
2673 /* we have to keep the message in the queue until it's
2674 * clear that menu loop is not over yet. */
2676 for (;;)
2678 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2680 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2681 /* remove the message from the queue */
2682 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2684 else
2686 if (!enterIdleSent)
2688 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2689 enterIdleSent = TRUE;
2690 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2692 WaitMessage();
2696 /* check if EndMenu() tried to cancel us, by posting this message */
2697 if(msg.message == WM_CANCELMODE)
2699 /* we are now out of the loop */
2700 fEndMenu = TRUE;
2702 /* remove the message from the queue */
2703 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2705 /* break out of internal loop, ala ESCAPE */
2706 break;
2709 TranslateMessage( &msg );
2710 mt.pt = msg.pt;
2712 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2713 enterIdleSent=FALSE;
2715 fRemove = FALSE;
2716 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2719 * Use the mouse coordinates in lParam instead of those in the MSG
2720 * struct to properly handle synthetic messages. They are already
2721 * in screen coordinates.
2723 mt.pt.x = (short)LOWORD(msg.lParam);
2724 mt.pt.y = (short)HIWORD(msg.lParam);
2726 /* Find a menu for this mouse event */
2727 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2729 switch(msg.message)
2731 /* no WM_NC... messages in captured state */
2733 case WM_RBUTTONDBLCLK:
2734 case WM_RBUTTONDOWN:
2735 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2736 /* fall through */
2737 case WM_LBUTTONDBLCLK:
2738 case WM_LBUTTONDOWN:
2739 /* If the message belongs to the menu, removes it from the queue */
2740 /* Else, end menu tracking */
2741 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2742 fEndMenu = !fRemove;
2743 break;
2745 case WM_RBUTTONUP:
2746 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2747 /* fall through */
2748 case WM_LBUTTONUP:
2749 /* Check if a menu was selected by the mouse */
2750 if (hmenu)
2752 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2754 /* End the loop if executedMenuId is an item ID */
2755 /* or if the job was done (executedMenuId = 0). */
2756 fEndMenu = fRemove = (executedMenuId != -1);
2758 /* No menu was selected by the mouse */
2759 /* if the function was called by TrackPopupMenu, continue
2760 with the menu tracking. If not, stop it */
2761 else
2762 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2764 break;
2766 case WM_MOUSEMOVE:
2767 /* the selected menu item must be changed every time */
2768 /* the mouse moves. */
2770 if (hmenu)
2771 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2773 } /* switch(msg.message) - mouse */
2775 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2777 fRemove = TRUE; /* Keyboard messages are always removed */
2778 switch(msg.message)
2780 case WM_KEYDOWN:
2781 case WM_SYSKEYDOWN:
2782 switch(msg.wParam)
2784 case VK_MENU:
2785 fEndMenu = TRUE;
2786 break;
2788 case VK_HOME:
2789 case VK_END:
2790 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2791 NO_SELECTED_ITEM, FALSE, 0 );
2792 /* fall through */
2793 case VK_UP:
2794 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2795 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2796 break;
2798 case VK_DOWN: /* If on menu bar, pull-down the menu */
2800 menu = MENU_GetMenu( mt.hCurrentMenu );
2801 if (!(menu->wFlags & MF_POPUP))
2802 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2803 else /* otherwise try to move selection */
2804 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2805 break;
2807 case VK_LEFT:
2808 MENU_KeyLeft( &mt, wFlags );
2809 break;
2811 case VK_RIGHT:
2812 MENU_KeyRight( &mt, wFlags );
2813 break;
2815 case VK_ESCAPE:
2816 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2817 break;
2819 case VK_F1:
2821 HELPINFO hi;
2822 hi.cbSize = sizeof(HELPINFO);
2823 hi.iContextType = HELPINFO_MENUITEM;
2824 if (menu->FocusedItem == NO_SELECTED_ITEM)
2825 hi.iCtrlId = 0;
2826 else
2827 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2828 hi.hItemHandle = hmenu;
2829 hi.dwContextId = menu->dwContextHelpID;
2830 hi.MousePos = msg.pt;
2831 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
2832 break;
2835 default:
2836 break;
2838 break; /* WM_KEYDOWN */
2840 case WM_CHAR:
2841 case WM_SYSCHAR:
2843 UINT pos;
2845 if (msg.wParam == '\r' || msg.wParam == ' ')
2847 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2848 fEndMenu = (executedMenuId != -1);
2850 break;
2853 /* Hack to avoid control chars. */
2854 /* We will find a better way real soon... */
2855 if (msg.wParam < 32) break;
2857 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2858 LOWORD(msg.wParam), FALSE );
2859 if (pos == (UINT)-2) fEndMenu = TRUE;
2860 else if (pos == (UINT)-1) MessageBeep(0);
2861 else
2863 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2864 TRUE, 0 );
2865 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2866 fEndMenu = (executedMenuId != -1);
2869 break;
2870 } /* switch(msg.message) - kbd */
2872 else
2874 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2875 DispatchMessageW( &msg );
2876 continue;
2879 if (!fEndMenu) fRemove = TRUE;
2881 /* finally remove message from the queue */
2883 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2884 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2885 else mt.trackFlags &= ~TF_SKIPREMOVE;
2888 MENU_SetCapture(0); /* release the capture */
2890 /* If dropdown is still painted and the close box is clicked on
2891 then the menu will be destroyed as part of the DispatchMessage above.
2892 This will then invalidate the menu handle in mt.hTopMenu. We should
2893 check for this first. */
2894 if( IsMenu( mt.hTopMenu ) )
2896 menu = MENU_GetMenu( mt.hTopMenu );
2898 if( IsWindow( mt.hOwnerWnd ) )
2900 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2902 if (menu && (menu->wFlags & MF_POPUP))
2904 DestroyWindow( menu->hWnd );
2905 menu->hWnd = 0;
2907 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2908 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2911 /* Reset the variable for hiding menu */
2912 if( menu ) menu->bTimeToHide = FALSE;
2915 /* The return value is only used by TrackPopupMenu */
2916 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
2917 if (executedMenuId == -1) executedMenuId = 0;
2918 return executedMenuId;
2921 /***********************************************************************
2922 * MENU_InitTracking
2924 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2926 POPUPMENU *menu;
2928 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2930 HideCaret(0);
2932 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2933 if (!(wFlags & TPM_NONOTIFY))
2934 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2936 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2938 if (!(wFlags & TPM_NONOTIFY))
2940 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2941 /* If an app changed/recreated menu bar entries in WM_INITMENU
2942 * menu sizes will be recalculated once the menu created/shown.
2946 /* This makes the menus of applications built with Delphi work.
2947 * It also enables menus to be displayed in more than one window,
2948 * but there are some bugs left that need to be fixed in this case.
2950 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
2952 return TRUE;
2954 /***********************************************************************
2955 * MENU_ExitTracking
2957 static BOOL MENU_ExitTracking(HWND hWnd)
2959 TRACE("hwnd=%p\n", hWnd);
2961 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2962 ShowCaret(0);
2963 top_popup = 0;
2964 return TRUE;
2967 /***********************************************************************
2968 * MENU_TrackMouseMenuBar
2970 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2972 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2974 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2975 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2977 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2979 if (IsMenu(hMenu))
2981 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2982 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2983 MENU_ExitTracking(hWnd);
2988 /***********************************************************************
2989 * MENU_TrackKbdMenuBar
2991 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2993 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
2995 UINT uItem = NO_SELECTED_ITEM;
2996 HMENU hTrackMenu;
2997 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2999 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3001 /* find window that has a menu */
3003 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3004 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3006 /* check if we have to track a system menu */
3008 hTrackMenu = GetMenu( hwnd );
3009 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3011 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3012 hTrackMenu = get_win_sys_menu( hwnd );
3013 uItem = 0;
3014 wParam |= HTSYSMENU; /* prevent item lookup */
3017 if (!IsMenu( hTrackMenu )) return;
3019 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3021 if( wChar && wChar != ' ' )
3023 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3024 if ( uItem >= (UINT)(-2) )
3026 if( uItem == (UINT)(-1) ) MessageBeep(0);
3027 /* schedule end of menu tracking */
3028 wFlags |= TF_ENDMENU;
3029 goto track_menu;
3033 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3035 if (wParam & HTSYSMENU)
3037 /* prevent sysmenu activation for managed windows on Alt down/up */
3038 if (GetPropA( hwnd, "__wine_x11_managed" ))
3039 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3041 else
3043 if( uItem == NO_SELECTED_ITEM )
3044 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3045 else
3046 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3049 track_menu:
3050 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3051 MENU_ExitTracking( hwnd );
3055 /**********************************************************************
3056 * TrackPopupMenu (USER32.@)
3058 * Like the win32 API, the function return the command ID only if the
3059 * flag TPM_RETURNCMD is on.
3062 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3063 INT nReserved, HWND hWnd, const RECT *lpRect )
3065 BOOL ret = FALSE;
3067 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3069 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3070 if (!(wFlags & TPM_NONOTIFY))
3071 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3073 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3074 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3075 MENU_ExitTracking(hWnd);
3077 return ret;
3080 /**********************************************************************
3081 * TrackPopupMenuEx (USER32.@)
3083 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3084 HWND hWnd, LPTPMPARAMS lpTpm )
3086 FIXME("not fully implemented\n" );
3087 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3088 lpTpm ? &lpTpm->rcExclude : NULL );
3091 /***********************************************************************
3092 * PopupMenuWndProc
3094 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3096 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3098 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3100 switch(message)
3102 case WM_CREATE:
3104 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3105 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3106 return 0;
3109 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3110 return MA_NOACTIVATE;
3112 case WM_PAINT:
3114 PAINTSTRUCT ps;
3115 BeginPaint( hwnd, &ps );
3116 MENU_DrawPopupMenu( hwnd, ps.hdc,
3117 (HMENU)GetWindowLongW( hwnd, 0 ) );
3118 EndPaint( hwnd, &ps );
3119 return 0;
3121 case WM_ERASEBKGND:
3122 return 1;
3124 case WM_DESTROY:
3125 /* zero out global pointer in case resident popup window was destroyed. */
3126 if (hwnd == top_popup) top_popup = 0;
3127 break;
3129 case WM_SHOWWINDOW:
3131 if( wParam )
3133 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3135 else
3136 SetWindowLongW( hwnd, 0, 0 );
3137 break;
3139 case MM_SETMENUHANDLE:
3140 SetWindowLongW( hwnd, 0, wParam );
3141 break;
3143 case MM_GETMENUHANDLE:
3144 return GetWindowLongW( hwnd, 0 );
3146 default:
3147 return DefWindowProcW( hwnd, message, wParam, lParam );
3149 return 0;
3153 /***********************************************************************
3154 * MENU_GetMenuBarHeight
3156 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3158 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3159 INT orgX, INT orgY )
3161 HDC hdc;
3162 RECT rectBar;
3163 LPPOPUPMENU lppop;
3165 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3167 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3169 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3170 SelectObject( hdc, hMenuFont);
3171 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3172 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3173 ReleaseDC( hwnd, hdc );
3174 return lppop->Height;
3178 /*******************************************************************
3179 * ChangeMenuA (USER32.@)
3181 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3182 UINT id, UINT flags )
3184 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3185 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3186 id, data );
3187 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3188 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3189 id, data );
3190 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3191 flags & MF_BYPOSITION ? pos : id,
3192 flags & ~MF_REMOVE );
3193 /* Default: MF_INSERT */
3194 return InsertMenuA( hMenu, pos, flags, id, data );
3198 /*******************************************************************
3199 * ChangeMenuW (USER32.@)
3201 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3202 UINT id, UINT flags )
3204 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3205 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3206 id, data );
3207 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3208 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3209 id, data );
3210 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3211 flags & MF_BYPOSITION ? pos : id,
3212 flags & ~MF_REMOVE );
3213 /* Default: MF_INSERT */
3214 return InsertMenuW( hMenu, pos, flags, id, data );
3218 /*******************************************************************
3219 * CheckMenuItem (USER32.@)
3221 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3223 MENUITEM *item;
3224 DWORD ret;
3226 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3227 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3228 ret = item->fState & MF_CHECKED;
3229 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3230 else item->fState &= ~MF_CHECKED;
3231 return ret;
3235 /**********************************************************************
3236 * EnableMenuItem (USER32.@)
3238 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3240 UINT oldflags;
3241 MENUITEM *item;
3242 POPUPMENU *menu;
3244 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3246 /* Get the Popupmenu to access the owner menu */
3247 if (!(menu = MENU_GetMenu(hMenu)))
3248 return (UINT)-1;
3250 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3251 return (UINT)-1;
3253 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3254 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3256 /* If the close item in the system menu change update the close button */
3257 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3259 if (menu->hSysMenuOwner != 0)
3261 RECT rc;
3262 POPUPMENU* parentMenu;
3264 /* Get the parent menu to access*/
3265 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3266 return (UINT)-1;
3268 /* Refresh the frame to reflect the change */
3269 GetWindowRect(parentMenu->hWnd, &rc);
3270 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3271 rc.bottom = 0;
3272 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3276 return oldflags;
3280 /*******************************************************************
3281 * GetMenuStringA (USER32.@)
3283 INT WINAPI GetMenuStringA(
3284 HMENU hMenu, /* [in] menuhandle */
3285 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3286 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3287 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3288 UINT wFlags /* [in] MF_ flags */
3290 MENUITEM *item;
3292 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3293 if (str && nMaxSiz) str[0] = '\0';
3294 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3295 if (!IS_STRING_ITEM(item->fType)) return 0;
3296 if (!str || !nMaxSiz) return strlenW(item->text);
3297 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3298 str[nMaxSiz-1] = 0;
3299 TRACE("returning '%s'\n", str );
3300 return strlen(str);
3304 /*******************************************************************
3305 * GetMenuStringW (USER32.@)
3307 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3308 LPWSTR str, INT nMaxSiz, UINT wFlags )
3310 MENUITEM *item;
3312 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3313 if (str && nMaxSiz) str[0] = '\0';
3314 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3315 if (!IS_STRING_ITEM(item->fType)) return 0;
3316 if (!str || !nMaxSiz) return strlenW(item->text);
3317 lstrcpynW( str, item->text, nMaxSiz );
3318 return strlenW(str);
3322 /**********************************************************************
3323 * HiliteMenuItem (USER32.@)
3325 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3326 UINT wHilite )
3328 LPPOPUPMENU menu;
3329 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3330 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3331 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3332 if (menu->FocusedItem == wItemID) return TRUE;
3333 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3334 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3335 return TRUE;
3339 /**********************************************************************
3340 * GetMenuState (USER32.@)
3342 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3344 MENUITEM *item;
3345 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3346 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3347 debug_print_menuitem (" item: ", item, "");
3348 if (item->fType & MF_POPUP)
3350 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3351 if (!menu) return -1;
3352 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3354 else
3356 /* We used to (from way back then) mask the result to 0xff. */
3357 /* I don't know why and it seems wrong as the documented */
3358 /* return flag MF_SEPARATOR is outside that mask. */
3359 return (item->fType | item->fState);
3364 /**********************************************************************
3365 * GetMenuItemCount (USER32.@)
3367 INT WINAPI GetMenuItemCount( HMENU hMenu )
3369 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3370 if (!menu) return -1;
3371 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3372 return menu->nItems;
3376 /**********************************************************************
3377 * GetMenuItemID (USER32.@)
3379 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3381 MENUITEM * lpmi;
3383 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3384 if (lpmi->fType & MF_POPUP) return -1;
3385 return lpmi->wID;
3390 /*******************************************************************
3391 * InsertMenuW (USER32.@)
3393 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3394 UINT_PTR id, LPCWSTR str )
3396 MENUITEM *item;
3398 if (IS_STRING_ITEM(flags) && str)
3399 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3400 hMenu, pos, flags, id, debugstr_w(str) );
3401 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3402 hMenu, pos, flags, id, (DWORD)str );
3404 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3406 if (!(MENU_SetItemData( item, flags, id, str )))
3408 RemoveMenu( hMenu, pos, flags );
3409 return FALSE;
3412 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3413 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3415 item->hCheckBit = item->hUnCheckBit = 0;
3416 return TRUE;
3420 /*******************************************************************
3421 * InsertMenuA (USER32.@)
3423 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3424 UINT_PTR id, LPCSTR str )
3426 BOOL ret = FALSE;
3428 if (IS_STRING_ITEM(flags) && str)
3430 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3431 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3432 if (newstr)
3434 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3435 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3436 HeapFree( GetProcessHeap(), 0, newstr );
3438 return ret;
3440 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3444 /*******************************************************************
3445 * AppendMenuA (USER32.@)
3447 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3448 UINT_PTR id, LPCSTR data )
3450 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3454 /*******************************************************************
3455 * AppendMenuW (USER32.@)
3457 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3458 UINT_PTR id, LPCWSTR data )
3460 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3464 /**********************************************************************
3465 * RemoveMenu (USER32.@)
3467 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3469 LPPOPUPMENU menu;
3470 MENUITEM *item;
3472 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3473 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3474 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3476 /* Remove item */
3478 MENU_FreeItemData( item );
3480 if (--menu->nItems == 0)
3482 HeapFree( GetProcessHeap(), 0, menu->items );
3483 menu->items = NULL;
3485 else
3487 while(nPos < menu->nItems)
3489 *item = *(item+1);
3490 item++;
3491 nPos++;
3493 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3494 menu->nItems * sizeof(MENUITEM) );
3496 return TRUE;
3500 /**********************************************************************
3501 * DeleteMenu (USER32.@)
3503 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3505 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3506 if (!item) return FALSE;
3507 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3508 /* nPos is now the position of the item */
3509 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3510 return TRUE;
3514 /*******************************************************************
3515 * ModifyMenuW (USER32.@)
3517 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3518 UINT_PTR id, LPCWSTR str )
3520 MENUITEM *item;
3522 if (IS_STRING_ITEM(flags))
3524 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3526 else
3528 TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3531 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3532 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3533 return MENU_SetItemData( item, flags, id, str );
3537 /*******************************************************************
3538 * ModifyMenuA (USER32.@)
3540 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3541 UINT_PTR id, LPCSTR str )
3543 BOOL ret = FALSE;
3545 if (IS_STRING_ITEM(flags) && str)
3547 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3548 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3549 if (newstr)
3551 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3552 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3553 HeapFree( GetProcessHeap(), 0, newstr );
3555 return ret;
3557 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3561 /**********************************************************************
3562 * CreatePopupMenu (USER32.@)
3564 HMENU WINAPI CreatePopupMenu(void)
3566 HMENU hmenu;
3567 POPUPMENU *menu;
3569 if (!(hmenu = CreateMenu())) return 0;
3570 menu = MENU_GetMenu( hmenu );
3571 menu->wFlags |= MF_POPUP;
3572 menu->bTimeToHide = FALSE;
3573 return hmenu;
3577 /**********************************************************************
3578 * GetMenuCheckMarkDimensions (USER.417)
3579 * GetMenuCheckMarkDimensions (USER32.@)
3581 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3583 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3587 /**********************************************************************
3588 * SetMenuItemBitmaps (USER32.@)
3590 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3591 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3593 MENUITEM *item;
3594 TRACE("(%p, %04x, %04x, %p, %p)\n",
3595 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3596 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3598 if (!hNewCheck && !hNewUnCheck)
3600 item->fState &= ~MF_USECHECKBITMAPS;
3602 else /* Install new bitmaps */
3604 item->hCheckBit = hNewCheck;
3605 item->hUnCheckBit = hNewUnCheck;
3606 item->fState |= MF_USECHECKBITMAPS;
3608 return TRUE;
3612 /**********************************************************************
3613 * CreateMenu (USER32.@)
3615 HMENU WINAPI CreateMenu(void)
3617 HMENU hMenu;
3618 LPPOPUPMENU menu;
3619 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3620 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3622 ZeroMemory(menu, sizeof(POPUPMENU));
3623 menu->wMagic = MENU_MAGIC;
3624 menu->FocusedItem = NO_SELECTED_ITEM;
3625 menu->bTimeToHide = FALSE;
3627 TRACE("return %p\n", hMenu );
3629 return hMenu;
3633 /**********************************************************************
3634 * DestroyMenu (USER32.@)
3636 BOOL WINAPI DestroyMenu( HMENU hMenu )
3638 TRACE("(%p)\n", hMenu);
3640 /* Silently ignore attempts to destroy default system popup */
3642 if (hMenu && hMenu != MENU_DefSysPopup)
3644 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3646 if (!lppop) return FALSE;
3648 lppop->wMagic = 0; /* Mark it as destroyed */
3650 /* DestroyMenu should not destroy system menu popup owner */
3651 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3653 DestroyWindow( lppop->hWnd );
3654 lppop->hWnd = 0;
3657 if (lppop->items) /* recursively destroy submenus */
3659 int i;
3660 MENUITEM *item = lppop->items;
3661 for (i = lppop->nItems; i > 0; i--, item++)
3663 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3664 MENU_FreeItemData( item );
3666 HeapFree( GetProcessHeap(), 0, lppop->items );
3668 USER_HEAP_FREE( hMenu );
3670 return (hMenu != MENU_DefSysPopup);
3674 /**********************************************************************
3675 * GetSystemMenu (USER32.@)
3677 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3679 WND *wndPtr = WIN_GetPtr( hWnd );
3680 HMENU retvalue = 0;
3682 if (wndPtr == WND_DESKTOP) return 0;
3683 if (wndPtr == WND_OTHER_PROCESS)
3685 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3687 else if (wndPtr)
3689 if( wndPtr->hSysMenu )
3691 if( bRevert )
3693 DestroyMenu(wndPtr->hSysMenu);
3694 wndPtr->hSysMenu = 0;
3696 else
3698 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3699 if( menu )
3701 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3702 menu->items[0].hSubMenu = MENU_CopySysPopup();
3704 else
3706 WARN("Current sys-menu (%p) of wnd %p is broken\n",
3707 wndPtr->hSysMenu, hWnd);
3708 wndPtr->hSysMenu = 0;
3713 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3714 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3716 if( wndPtr->hSysMenu )
3718 POPUPMENU *menu;
3719 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3721 /* Store the dummy sysmenu handle to facilitate the refresh */
3722 /* of the close button if the SC_CLOSE item change */
3723 menu = MENU_GetMenu(retvalue);
3724 if ( menu )
3725 menu->hSysMenuOwner = wndPtr->hSysMenu;
3727 WIN_ReleasePtr( wndPtr );
3729 return bRevert ? 0 : retvalue;
3733 /*******************************************************************
3734 * SetSystemMenu (USER32.@)
3736 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3738 WND *wndPtr = WIN_GetPtr( hwnd );
3740 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3742 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3743 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3744 WIN_ReleasePtr( wndPtr );
3745 return TRUE;
3747 return FALSE;
3751 /**********************************************************************
3752 * GetMenu (USER32.@)
3754 HMENU WINAPI GetMenu( HWND hWnd )
3756 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3757 TRACE("for %p returning %p\n", hWnd, retvalue);
3758 return retvalue;
3762 /**********************************************************************
3763 * MENU_SetMenu
3765 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
3766 * SetWindowPos call that would result if SetMenu were called directly.
3768 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
3770 TRACE("(%p, %p);\n", hWnd, hMenu);
3772 if (hMenu && !IsMenu(hMenu))
3774 WARN("hMenu %p is not a menu handle\n", hMenu);
3775 return FALSE;
3777 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3778 return FALSE;
3780 hWnd = WIN_GetFullHandle( hWnd );
3781 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
3783 if (hMenu != 0)
3785 LPPOPUPMENU lpmenu;
3787 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3789 lpmenu->hWnd = hWnd;
3790 lpmenu->Height = 0; /* Make sure we recalculate the size */
3792 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
3793 return TRUE;
3797 /**********************************************************************
3798 * SetMenu (USER32.@)
3800 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3802 if(!MENU_SetMenu(hWnd, hMenu))
3803 return FALSE;
3805 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3806 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3807 return TRUE;
3811 /**********************************************************************
3812 * GetSubMenu (USER32.@)
3814 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3816 MENUITEM * lpmi;
3818 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3819 if (!(lpmi->fType & MF_POPUP)) return 0;
3820 return lpmi->hSubMenu;
3824 /**********************************************************************
3825 * DrawMenuBar (USER32.@)
3827 BOOL WINAPI DrawMenuBar( HWND hWnd )
3829 LPPOPUPMENU lppop;
3830 HMENU hMenu = GetMenu(hWnd);
3832 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3833 return FALSE;
3834 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3836 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3837 lppop->hwndOwner = hWnd;
3838 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3839 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3840 return TRUE;
3843 /***********************************************************************
3844 * DrawMenuBarTemp (USER32.@)
3846 * UNDOCUMENTED !!
3848 * called by W98SE desk.cpl Control Panel Applet
3850 * Not 100% sure about the param names, but close.
3852 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3854 LPPOPUPMENU lppop;
3855 UINT i,retvalue;
3856 HFONT hfontOld = 0;
3858 if (!hMenu)
3859 hMenu = GetMenu(hwnd);
3861 if (!hFont)
3862 hFont = hMenuFont;
3864 lppop = MENU_GetMenu( hMenu );
3865 if (lppop == NULL || lprect == NULL)
3867 retvalue = GetSystemMetrics(SM_CYMENU);
3868 goto END;
3871 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3873 hfontOld = SelectObject( hDC, hFont);
3875 if (lppop->Height == 0)
3876 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3878 lprect->bottom = lprect->top + lppop->Height;
3880 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
3882 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3883 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3884 LineTo( hDC, lprect->right, lprect->bottom );
3886 if (lppop->nItems == 0)
3888 retvalue = GetSystemMetrics(SM_CYMENU);
3889 goto END;
3892 for (i = 0; i < lppop->nItems; i++)
3894 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3895 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3897 retvalue = lppop->Height;
3899 END:
3900 if (hfontOld) SelectObject (hDC, hfontOld);
3901 return retvalue;
3904 /***********************************************************************
3905 * EndMenu (USER.187)
3906 * EndMenu (USER32.@)
3908 void WINAPI EndMenu(void)
3910 /* if we are in the menu code, and it is active */
3911 if (!fEndMenu && top_popup)
3913 /* terminate the menu handling code */
3914 fEndMenu = TRUE;
3916 /* needs to be posted to wakeup the internal menu handler */
3917 /* which will now terminate the menu, in the event that */
3918 /* the main window was minimized, or lost focus, so we */
3919 /* don't end up with an orphaned menu */
3920 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3925 /***********************************************************************
3926 * LookupMenuHandle (USER.217)
3928 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3930 HMENU hmenu32 = HMENU_32(hmenu);
3931 UINT id32 = id;
3932 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3933 else return HMENU_16(hmenu32);
3937 /**********************************************************************
3938 * LoadMenu (USER.150)
3940 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3942 HRSRC16 hRsrc;
3943 HGLOBAL16 handle;
3944 HMENU16 hMenu;
3946 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3947 if (!name) return 0;
3949 instance = GetExePtr( instance );
3950 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3951 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3952 hMenu = LoadMenuIndirect16(LockResource16(handle));
3953 FreeResource16( handle );
3954 return hMenu;
3958 /*****************************************************************
3959 * LoadMenuA (USER32.@)
3961 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3963 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
3964 if (!hrsrc) return 0;
3965 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3969 /*****************************************************************
3970 * LoadMenuW (USER32.@)
3972 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3974 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
3975 if (!hrsrc) return 0;
3976 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
3980 /**********************************************************************
3981 * LoadMenuIndirect (USER.220)
3983 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3985 HMENU hMenu;
3986 WORD version, offset;
3987 LPCSTR p = (LPCSTR)template;
3989 TRACE("(%p)\n", template );
3990 version = GET_WORD(p);
3991 p += sizeof(WORD);
3992 if (version)
3994 WARN("version must be 0 for Win16\n" );
3995 return 0;
3997 offset = GET_WORD(p);
3998 p += sizeof(WORD) + offset;
3999 if (!(hMenu = CreateMenu())) return 0;
4000 if (!MENU_ParseResource( p, hMenu, FALSE ))
4002 DestroyMenu( hMenu );
4003 return 0;
4005 return HMENU_16(hMenu);
4009 /**********************************************************************
4010 * LoadMenuIndirectW (USER32.@)
4012 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4014 HMENU hMenu;
4015 WORD version, offset;
4016 LPCSTR p = (LPCSTR)template;
4018 version = GET_WORD(p);
4019 p += sizeof(WORD);
4020 TRACE("%p, ver %d\n", template, version );
4021 switch (version)
4023 case 0: /* standard format is version of 0 */
4024 offset = GET_WORD(p);
4025 p += sizeof(WORD) + offset;
4026 if (!(hMenu = CreateMenu())) return 0;
4027 if (!MENU_ParseResource( p, hMenu, TRUE ))
4029 DestroyMenu( hMenu );
4030 return 0;
4032 return hMenu;
4033 case 1: /* extended format is version of 1 */
4034 offset = GET_WORD(p);
4035 p += sizeof(WORD) + offset;
4036 if (!(hMenu = CreateMenu())) return 0;
4037 if (!MENUEX_ParseResource( p, hMenu))
4039 DestroyMenu( hMenu );
4040 return 0;
4042 return hMenu;
4043 default:
4044 ERR("version %d not supported.\n", version);
4045 return 0;
4050 /**********************************************************************
4051 * LoadMenuIndirectA (USER32.@)
4053 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4055 return LoadMenuIndirectW( template );
4059 /**********************************************************************
4060 * IsMenu (USER32.@)
4062 BOOL WINAPI IsMenu(HMENU hmenu)
4064 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4065 return menu != NULL;
4068 /**********************************************************************
4069 * GetMenuItemInfo_common
4072 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4073 LPMENUITEMINFOW lpmii, BOOL unicode)
4075 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4077 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4079 if (!menu)
4080 return FALSE;
4082 if (lpmii->fMask & MIIM_TYPE) {
4083 lpmii->fType = menu->fType;
4084 switch (MENU_ITEM_TYPE(menu->fType)) {
4085 case MF_STRING:
4086 break; /* will be done below */
4087 case MF_OWNERDRAW:
4088 case MF_BITMAP:
4089 lpmii->dwTypeData = menu->text;
4090 /* fall through */
4091 default:
4092 lpmii->cch = 0;
4096 /* copy the text string */
4097 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4098 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4100 int len;
4101 if (unicode)
4103 len = strlenW(menu->text);
4104 if(lpmii->dwTypeData && lpmii->cch)
4105 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4107 else
4109 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4110 if(lpmii->dwTypeData && lpmii->cch)
4111 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4112 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4113 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4115 /* if we've copied a substring we return its length */
4116 if(lpmii->dwTypeData && lpmii->cch)
4118 if (lpmii->cch <= len) lpmii->cch--;
4120 else /* return length of string */
4121 lpmii->cch = len;
4124 if (lpmii->fMask & MIIM_FTYPE)
4125 lpmii->fType = menu->fType;
4127 if (lpmii->fMask & MIIM_BITMAP)
4128 lpmii->hbmpItem = menu->hbmpItem;
4130 if (lpmii->fMask & MIIM_STATE)
4131 lpmii->fState = menu->fState;
4133 if (lpmii->fMask & MIIM_ID)
4134 lpmii->wID = menu->wID;
4136 if (lpmii->fMask & MIIM_SUBMENU)
4137 lpmii->hSubMenu = menu->hSubMenu;
4139 if (lpmii->fMask & MIIM_CHECKMARKS) {
4140 lpmii->hbmpChecked = menu->hCheckBit;
4141 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4143 if (lpmii->fMask & MIIM_DATA)
4144 lpmii->dwItemData = menu->dwItemData;
4146 return TRUE;
4149 /**********************************************************************
4150 * GetMenuItemInfoA (USER32.@)
4152 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4153 LPMENUITEMINFOA lpmii)
4155 return GetMenuItemInfo_common (hmenu, item, bypos,
4156 (LPMENUITEMINFOW)lpmii, FALSE);
4159 /**********************************************************************
4160 * GetMenuItemInfoW (USER32.@)
4162 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4163 LPMENUITEMINFOW lpmii)
4165 return GetMenuItemInfo_common (hmenu, item, bypos,
4166 lpmii, TRUE);
4170 /* set a menu item text from a ASCII or Unicode string */
4171 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4173 if (!text)
4175 menu->text = NULL;
4176 menu->fType |= MF_SEPARATOR;
4178 else if (unicode)
4180 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4181 strcpyW( menu->text, text );
4183 else
4185 LPCSTR str = (LPCSTR)text;
4186 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4187 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4188 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4193 /**********************************************************************
4194 * SetMenuItemInfo_common
4197 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4198 const MENUITEMINFOW *lpmii,
4199 BOOL unicode)
4201 if (!menu) return FALSE;
4203 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4205 if (lpmii->fMask & MIIM_TYPE ) {
4206 /* Get rid of old string. */
4207 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4208 HeapFree(GetProcessHeap(), 0, menu->text);
4209 menu->text = NULL;
4212 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4213 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4214 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4216 menu->text = lpmii->dwTypeData;
4218 if (IS_STRING_ITEM(menu->fType))
4219 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4222 if (lpmii->fMask & MIIM_FTYPE ) {
4223 /* free the string when the type is changing */
4224 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4225 HeapFree(GetProcessHeap(), 0, menu->text);
4226 menu->text = NULL;
4228 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4229 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4230 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4231 menu->fType |= MF_SEPARATOR;
4234 if (lpmii->fMask & MIIM_STRING ) {
4235 if (IS_STRING_ITEM(menu->fType)) {
4236 /* free the string when used */
4237 HeapFree(GetProcessHeap(), 0, menu->text);
4238 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4242 if (lpmii->fMask & MIIM_STATE)
4244 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4245 menu->fState = lpmii->fState;
4248 if (lpmii->fMask & MIIM_ID)
4249 menu->wID = lpmii->wID;
4251 if (lpmii->fMask & MIIM_SUBMENU) {
4252 menu->hSubMenu = lpmii->hSubMenu;
4253 if (menu->hSubMenu) {
4254 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4255 if (subMenu) {
4256 subMenu->wFlags |= MF_POPUP;
4257 menu->fType |= MF_POPUP;
4259 else
4260 /* FIXME: Return an error ? */
4261 menu->fType &= ~MF_POPUP;
4263 else
4264 menu->fType &= ~MF_POPUP;
4267 if (lpmii->fMask & MIIM_CHECKMARKS)
4269 if (lpmii->fType & MFT_RADIOCHECK)
4270 menu->fType |= MFT_RADIOCHECK;
4272 menu->hCheckBit = lpmii->hbmpChecked;
4273 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4275 if (lpmii->fMask & MIIM_DATA)
4276 menu->dwItemData = lpmii->dwItemData;
4278 if (lpmii->fMask & MIIM_BITMAP)
4279 menu->hbmpItem = lpmii->hbmpItem;
4281 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4282 return TRUE;
4285 /**********************************************************************
4286 * SetMenuItemInfoA (USER32.@)
4288 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4289 const MENUITEMINFOA *lpmii)
4291 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4292 (const MENUITEMINFOW *)lpmii, FALSE);
4295 /**********************************************************************
4296 * SetMenuItemInfoW (USER32.@)
4298 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4299 const MENUITEMINFOW *lpmii)
4301 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4302 lpmii, TRUE);
4305 /**********************************************************************
4306 * SetMenuDefaultItem (USER32.@)
4309 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4311 UINT i;
4312 POPUPMENU *menu;
4313 MENUITEM *item;
4315 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4317 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4319 /* reset all default-item flags */
4320 item = menu->items;
4321 for (i = 0; i < menu->nItems; i++, item++)
4323 item->fState &= ~MFS_DEFAULT;
4326 /* no default item */
4327 if ( -1 == uItem)
4329 return TRUE;
4332 item = menu->items;
4333 if ( bypos )
4335 if ( uItem >= menu->nItems ) return FALSE;
4336 item[uItem].fState |= MFS_DEFAULT;
4337 return TRUE;
4339 else
4341 for (i = 0; i < menu->nItems; i++, item++)
4343 if (item->wID == uItem)
4345 item->fState |= MFS_DEFAULT;
4346 return TRUE;
4351 return FALSE;
4354 /**********************************************************************
4355 * GetMenuDefaultItem (USER32.@)
4357 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4359 POPUPMENU *menu;
4360 MENUITEM * item;
4361 UINT i = 0;
4363 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4365 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4367 /* find default item */
4368 item = menu->items;
4370 /* empty menu */
4371 if (! item) return -1;
4373 while ( !( item->fState & MFS_DEFAULT ) )
4375 i++; item++;
4376 if (i >= menu->nItems ) return -1;
4379 /* default: don't return disabled items */
4380 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4382 /* search rekursiv when needed */
4383 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4385 UINT ret;
4386 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4387 if ( -1 != ret ) return ret;
4389 /* when item not found in submenu, return the popup item */
4391 return ( bypos ) ? i : item->wID;
4396 /**********************************************************************
4397 * InsertMenuItemA (USER32.@)
4399 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4400 const MENUITEMINFOA *lpmii)
4402 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4403 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4407 /**********************************************************************
4408 * InsertMenuItemW (USER32.@)
4410 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4411 const MENUITEMINFOW *lpmii)
4413 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4414 return SetMenuItemInfo_common(item, lpmii, TRUE);
4417 /**********************************************************************
4418 * CheckMenuRadioItem (USER32.@)
4421 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4422 UINT first, UINT last, UINT check,
4423 UINT bypos)
4425 MENUITEM *mifirst, *milast, *micheck;
4426 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4428 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4430 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4431 milast = MENU_FindItem (&mlast, &last, bypos);
4432 micheck = MENU_FindItem (&mcheck, &check, bypos);
4434 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4435 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4436 micheck > milast || micheck < mifirst)
4437 return FALSE;
4439 while (mifirst <= milast)
4441 if (mifirst == micheck)
4443 mifirst->fType |= MFT_RADIOCHECK;
4444 mifirst->fState |= MFS_CHECKED;
4445 } else {
4446 mifirst->fType &= ~MFT_RADIOCHECK;
4447 mifirst->fState &= ~MFS_CHECKED;
4449 mifirst++;
4452 return TRUE;
4456 /**********************************************************************
4457 * GetMenuItemRect (USER32.@)
4459 * ATTENTION: Here, the returned values in rect are the screen
4460 * coordinates of the item just like if the menu was
4461 * always on the upper left side of the application.
4464 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4465 LPRECT rect)
4467 POPUPMENU *itemMenu;
4468 MENUITEM *item;
4469 HWND referenceHwnd;
4471 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4473 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4474 referenceHwnd = hwnd;
4476 if(!hwnd)
4478 itemMenu = MENU_GetMenu(hMenu);
4479 if (itemMenu == NULL)
4480 return FALSE;
4482 if(itemMenu->hWnd == 0)
4483 return FALSE;
4484 referenceHwnd = itemMenu->hWnd;
4487 if ((rect == NULL) || (item == NULL))
4488 return FALSE;
4490 *rect = item->rect;
4492 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4494 return TRUE;
4498 /**********************************************************************
4499 * SetMenuInfo (USER32.@)
4501 * FIXME
4502 * MIM_APPLYTOSUBMENUS
4503 * actually use the items to draw the menu
4505 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4507 POPUPMENU *menu;
4509 TRACE("(%p %p)\n", hMenu, lpmi);
4511 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4514 if (lpmi->fMask & MIM_BACKGROUND)
4515 menu->hbrBack = lpmi->hbrBack;
4517 if (lpmi->fMask & MIM_HELPID)
4518 menu->dwContextHelpID = lpmi->dwContextHelpID;
4520 if (lpmi->fMask & MIM_MAXHEIGHT)
4521 menu->cyMax = lpmi->cyMax;
4523 if (lpmi->fMask & MIM_MENUDATA)
4524 menu->dwMenuData = lpmi->dwMenuData;
4526 if (lpmi->fMask & MIM_STYLE)
4528 menu->dwStyle = lpmi->dwStyle;
4529 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4530 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4531 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4532 if (menu->dwStyle & MNS_NOCHECK) FIXME("MNS_NOCHECK unimplemented\n");
4533 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4536 return TRUE;
4538 return FALSE;
4541 /**********************************************************************
4542 * GetMenuInfo (USER32.@)
4544 * NOTES
4545 * win98/NT5.0
4548 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4549 { POPUPMENU *menu;
4551 TRACE("(%p %p)\n", hMenu, lpmi);
4553 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4556 if (lpmi->fMask & MIM_BACKGROUND)
4557 lpmi->hbrBack = menu->hbrBack;
4559 if (lpmi->fMask & MIM_HELPID)
4560 lpmi->dwContextHelpID = menu->dwContextHelpID;
4562 if (lpmi->fMask & MIM_MAXHEIGHT)
4563 lpmi->cyMax = menu->cyMax;
4565 if (lpmi->fMask & MIM_MENUDATA)
4566 lpmi->dwMenuData = menu->dwMenuData;
4568 if (lpmi->fMask & MIM_STYLE)
4569 lpmi->dwStyle = menu->dwStyle;
4571 return TRUE;
4573 return FALSE;
4577 /**********************************************************************
4578 * SetMenuContextHelpId (USER32.@)
4580 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4582 LPPOPUPMENU menu;
4584 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4586 if ((menu = MENU_GetMenu(hMenu)))
4588 menu->dwContextHelpID = dwContextHelpID;
4589 return TRUE;
4591 return FALSE;
4595 /**********************************************************************
4596 * GetMenuContextHelpId (USER32.@)
4598 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4600 LPPOPUPMENU menu;
4602 TRACE("(%p)\n", hMenu);
4604 if ((menu = MENU_GetMenu(hMenu)))
4606 return menu->dwContextHelpID;
4608 return 0;
4611 /**********************************************************************
4612 * MenuItemFromPoint (USER32.@)
4614 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4616 POPUPMENU *menu = MENU_GetMenu(hMenu);
4617 UINT pos;
4619 /*FIXME: Do we have to handle hWnd here? */
4620 if (!menu) return -1;
4621 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4622 return pos;
4626 /**********************************************************************
4627 * translate_accelerator
4629 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4630 BYTE fVirt, WORD key, WORD cmd )
4632 INT mask = 0;
4633 UINT mesg = 0;
4635 if (wParam != key) return FALSE;
4637 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4638 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4639 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4641 if (message == WM_CHAR || message == WM_SYSCHAR)
4643 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4645 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4646 goto found;
4649 else
4651 if(fVirt & FVIRTKEY)
4653 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4654 wParam, 0xff & HIWORD(lParam));
4656 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4657 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4659 else
4661 if (!(lParam & 0x01000000)) /* no special_key */
4663 if ((fVirt & FALT) && (lParam & 0x20000000))
4664 { /* ^^ ALT pressed */
4665 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4666 goto found;
4671 return FALSE;
4673 found:
4674 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4675 mesg = 1;
4676 else
4678 HMENU hMenu, hSubMenu, hSysMenu;
4679 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4681 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4682 hSysMenu = get_win_sys_menu( hWnd );
4684 /* find menu item and ask application to initialize it */
4685 /* 1. in the system menu */
4686 hSubMenu = hSysMenu;
4687 nPos = cmd;
4688 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4690 if (GetCapture())
4691 mesg = 2;
4692 if (!IsWindowEnabled(hWnd))
4693 mesg = 3;
4694 else
4696 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4697 if(hSubMenu != hSysMenu)
4699 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4700 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4701 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4703 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4706 else /* 2. in the window's menu */
4708 hSubMenu = hMenu;
4709 nPos = cmd;
4710 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4712 if (GetCapture())
4713 mesg = 2;
4714 if (!IsWindowEnabled(hWnd))
4715 mesg = 3;
4716 else
4718 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4719 if(hSubMenu != hMenu)
4721 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4722 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4723 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4725 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4730 if (mesg == 0)
4732 if (uSysStat != (UINT)-1)
4734 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4735 mesg=4;
4736 else
4737 mesg=WM_SYSCOMMAND;
4739 else
4741 if (uStat != (UINT)-1)
4743 if (IsIconic(hWnd))
4744 mesg=5;
4745 else
4747 if (uStat & (MF_DISABLED|MF_GRAYED))
4748 mesg=6;
4749 else
4750 mesg=WM_COMMAND;
4753 else
4754 mesg=WM_COMMAND;
4759 if( mesg==WM_COMMAND )
4761 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4762 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4764 else if( mesg==WM_SYSCOMMAND )
4766 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4767 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4769 else
4771 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4772 * #0: unknown (please report!)
4773 * #1: for WM_KEYUP,WM_SYSKEYUP
4774 * #2: mouse is captured
4775 * #3: window is disabled
4776 * #4: it's a disabled system menu option
4777 * #5: it's a menu option, but window is iconic
4778 * #6: it's a menu option, but disabled
4780 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4781 if(mesg==0)
4782 ERR_(accel)(" unknown reason - please report!\n");
4784 return TRUE;
4787 /**********************************************************************
4788 * TranslateAccelerator (USER32.@)
4789 * TranslateAcceleratorA (USER32.@)
4791 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
4793 /* YES, Accel16! */
4794 LPACCEL16 lpAccelTbl;
4795 int i;
4796 WPARAM wParam;
4798 if (!hWnd || !msg) return 0;
4800 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4802 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4803 return 0;
4806 wParam = msg->wParam;
4808 switch (msg->message)
4810 case WM_KEYDOWN:
4811 case WM_SYSKEYDOWN:
4812 break;
4814 case WM_CHAR:
4815 case WM_SYSCHAR:
4817 char ch = LOWORD(wParam);
4818 WCHAR wch;
4819 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
4820 wParam = MAKEWPARAM(wch, HIWORD(wParam));
4822 break;
4824 default:
4825 return 0;
4828 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4829 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4830 i = 0;
4833 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
4834 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4835 return 1;
4836 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4838 return 0;
4841 /**********************************************************************
4842 * TranslateAcceleratorW (USER32.@)
4844 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4846 /* YES, Accel16! */
4847 LPACCEL16 lpAccelTbl;
4848 int i;
4850 if (!hWnd || !msg) return 0;
4852 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4854 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4855 return 0;
4858 switch (msg->message)
4860 case WM_KEYDOWN:
4861 case WM_SYSKEYDOWN:
4862 case WM_CHAR:
4863 case WM_SYSCHAR:
4864 break;
4866 default:
4867 return 0;
4870 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4871 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4872 i = 0;
4875 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4876 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4877 return 1;
4878 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4880 return 0;