Fix some DPA functions so they pass the new tests.
[wine/multimedia.git] / dlls / user / menu.c
blobb1f29987f3a20fd383a62c1d0973aaf656eb798a
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 /* Space between 2 menu bar items */
133 #define MENU_BAR_ITEMS_SPACE 12
135 /* Minimum width of a tab character */
136 #define MENU_TAB_SPACE 8
138 /* Height of a separator item */
139 #define SEPARATOR_HEIGHT 5
141 /* Space between 2 columns */
142 #define MENU_COL_SPACE 4
144 /* (other menu->FocusedItem values give the position of the focused item) */
145 #define NO_SELECTED_ITEM 0xffff
147 #define MENU_ITEM_TYPE(flags) \
148 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
150 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
151 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
152 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
154 #define IS_SYSTEM_MENU(menu) \
155 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
157 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
158 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
159 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
160 MF_POPUP | MF_SYSMENU | MF_HELP)
161 #define STATE_MASK (~TYPE_MASK)
163 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
165 /* Dimension of the menu bitmaps */
166 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
168 static HBITMAP hStdMnArrow = 0;
169 static HBITMAP hBmpSysMenu = 0;
171 static HFONT hMenuFont = 0;
172 static HFONT hMenuFontBold = 0;
173 static SIZE menucharsize;
174 static UINT ODitemheight; /* default owner drawn item height */
176 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
178 /* Use global popup window because there's no way 2 menus can
179 * be tracked at the same time. */
180 static HWND top_popup;
182 /* Flag set by EndMenu() to force an exit from menu tracking */
183 static BOOL fEndMenu = FALSE;
185 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
187 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
189 /*********************************************************************
190 * menu class descriptor
192 const struct builtin_class_descr MENU_builtin_class =
194 POPUPMENU_CLASS_ATOMA, /* name */
195 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
196 NULL, /* procA (winproc is Unicode only) */
197 PopupMenuWndProc, /* procW */
198 sizeof(HMENU), /* extra */
199 IDC_ARROW, /* cursor */
200 (HBRUSH)(COLOR_MENU+1) /* brush */
204 /***********************************************************************
205 * debug_print_menuitem
207 * Print a menuitem in readable form.
210 #define debug_print_menuitem(pre, mp, post) \
211 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
213 #define MENUOUT(text) \
214 DPRINTF("%s%s", (count++ ? "," : ""), (text))
216 #define MENUFLAG(bit,text) \
217 do { \
218 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
219 } while (0)
221 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
222 const char *postfix)
224 TRACE("%s ", prefix);
225 if (mp) {
226 UINT flags = mp->fType;
227 int type = MENU_ITEM_TYPE(flags);
228 DPRINTF( "{ ID=0x%x", mp->wID);
229 if (flags & MF_POPUP)
230 DPRINTF( ", Sub=%p", mp->hSubMenu);
231 if (flags) {
232 int count = 0;
233 DPRINTF( ", Type=");
234 if (type == MFT_STRING)
235 /* Nothing */ ;
236 else if (type == MFT_SEPARATOR)
237 MENUOUT("sep");
238 else if (type == MFT_OWNERDRAW)
239 MENUOUT("own");
240 else if (type == MFT_BITMAP)
241 MENUOUT("bit");
242 else
243 MENUOUT("???");
244 flags -= type;
246 MENUFLAG(MF_POPUP, "pop");
247 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
248 MENUFLAG(MFT_MENUBREAK, "brk");
249 MENUFLAG(MFT_RADIOCHECK, "radio");
250 MENUFLAG(MFT_RIGHTORDER, "rorder");
251 MENUFLAG(MF_SYSMENU, "sys");
252 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
254 if (flags)
255 DPRINTF( "+0x%x", flags);
257 flags = mp->fState;
258 if (flags) {
259 int count = 0;
260 DPRINTF( ", State=");
261 MENUFLAG(MFS_GRAYED, "grey");
262 MENUFLAG(MFS_DEFAULT, "default");
263 MENUFLAG(MFS_DISABLED, "dis");
264 MENUFLAG(MFS_CHECKED, "check");
265 MENUFLAG(MFS_HILITE, "hi");
266 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
267 MENUFLAG(MF_MOUSESELECT, "mouse");
268 if (flags)
269 DPRINTF( "+0x%x", flags);
271 if (mp->hCheckBit)
272 DPRINTF( ", Chk=%p", mp->hCheckBit);
273 if (mp->hUnCheckBit)
274 DPRINTF( ", Unc=%p", mp->hUnCheckBit);
276 if (type == MFT_STRING) {
277 if (mp->text)
278 DPRINTF( ", Text=%s", debugstr_w(mp->text));
279 else
280 DPRINTF( ", Text=Null");
281 } else if (mp->text == NULL)
282 /* Nothing */ ;
283 else
284 DPRINTF( ", Text=%p", mp->text);
285 if (mp->dwItemData)
286 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
287 DPRINTF( " }");
288 } else {
289 DPRINTF( "NULL");
292 DPRINTF(" %s\n", postfix);
295 #undef MENUOUT
296 #undef MENUFLAG
299 /***********************************************************************
300 * MENU_GetMenu
302 * Validate the given menu handle and returns the menu structure pointer.
304 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
306 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
307 if (!menu || menu->wMagic != MENU_MAGIC)
309 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
310 menu = NULL;
312 return menu;
315 /***********************************************************************
316 * get_win_sys_menu
318 * Get the system menu of a window
320 static HMENU get_win_sys_menu( HWND hwnd )
322 HMENU ret = 0;
323 WND *win = WIN_GetPtr( hwnd );
324 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
326 ret = win->hSysMenu;
327 WIN_ReleasePtr( win );
329 return ret;
332 /***********************************************************************
333 * MENU_CopySysPopup
335 * Return the default system menu.
337 static HMENU MENU_CopySysPopup(void)
339 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
340 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
342 if( hMenu ) {
343 POPUPMENU* menu = MENU_GetMenu(hMenu);
344 menu->wFlags |= MF_SYSMENU | MF_POPUP;
345 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
347 else
348 ERR("Unable to load default system menu\n" );
350 TRACE("returning %p.\n", hMenu );
352 return hMenu;
356 /**********************************************************************
357 * MENU_GetSysMenu
359 * Create a copy of the system menu. System menu in Windows is
360 * a special menu bar with the single entry - system menu popup.
361 * This popup is presented to the outside world as a "system menu".
362 * However, the real system menu handle is sometimes seen in the
363 * WM_MENUSELECT parameters (and Word 6 likes it this way).
365 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
367 HMENU hMenu;
369 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
370 if ((hMenu = CreateMenu()))
372 POPUPMENU *menu = MENU_GetMenu(hMenu);
373 menu->wFlags = MF_SYSMENU;
374 menu->hWnd = WIN_GetFullHandle( hWnd );
375 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
377 if (hPopupMenu == (HMENU)(-1))
378 hPopupMenu = MENU_CopySysPopup();
379 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
381 if (hPopupMenu)
383 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
384 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
386 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
387 (UINT_PTR)hPopupMenu, NULL );
389 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
390 menu->items[0].fState = 0;
391 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
393 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
394 return hMenu;
396 DestroyMenu( hMenu );
398 ERR("failed to load system menu!\n");
399 return 0;
403 /***********************************************************************
404 * MENU_Init
406 * Menus initialisation.
408 BOOL MENU_Init(void)
410 NONCLIENTMETRICSW ncm;
412 /* Load menu bitmaps */
413 hStdMnArrow = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
414 /* Load system buttons bitmaps */
415 hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
417 if (hStdMnArrow)
419 BITMAP bm;
420 GetObjectW( hStdMnArrow, sizeof(bm), &bm );
421 arrow_bitmap_width = bm.bmWidth;
422 arrow_bitmap_height = bm.bmHeight;
423 } else
424 return FALSE;
426 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
427 return FALSE;
429 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
430 if (!(SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0)))
431 return FALSE;
433 if (!(hMenuFont = CreateFontIndirectW( &ncm.lfMenuFont )))
434 return FALSE;
436 ncm.lfMenuFont.lfWeight += 300;
437 if ( ncm.lfMenuFont.lfWeight > 1000)
438 ncm.lfMenuFont.lfWeight = 1000;
440 if (!(hMenuFontBold = CreateFontIndirectW( &ncm.lfMenuFont )))
441 return FALSE;
443 return TRUE;
446 /***********************************************************************
447 * MENU_InitSysMenuPopup
449 * Grey the appropriate items in System menu.
451 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
453 BOOL gray;
455 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
456 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
457 gray = ((style & WS_MAXIMIZE) != 0);
458 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
459 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
460 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
461 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
462 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
463 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
464 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
465 gray = (clsStyle & CS_NOCLOSE) != 0;
467 /* The menu item must keep its state if it's disabled */
468 if(gray)
469 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
473 /******************************************************************************
475 * UINT MENU_GetStartOfNextColumn(
476 * HMENU hMenu )
478 *****************************************************************************/
480 static UINT MENU_GetStartOfNextColumn(
481 HMENU hMenu )
483 POPUPMENU *menu = MENU_GetMenu(hMenu);
484 UINT i;
486 if(!menu)
487 return NO_SELECTED_ITEM;
489 i = menu->FocusedItem + 1;
490 if( i == NO_SELECTED_ITEM )
491 return i;
493 for( ; i < menu->nItems; ++i ) {
494 if (menu->items[i].fType & MF_MENUBARBREAK)
495 return i;
498 return NO_SELECTED_ITEM;
502 /******************************************************************************
504 * UINT MENU_GetStartOfPrevColumn(
505 * HMENU hMenu )
507 *****************************************************************************/
509 static UINT MENU_GetStartOfPrevColumn(
510 HMENU hMenu )
512 POPUPMENU *menu = MENU_GetMenu(hMenu);
513 UINT i;
515 if( !menu )
516 return NO_SELECTED_ITEM;
518 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
519 return NO_SELECTED_ITEM;
521 /* Find the start of the column */
523 for(i = menu->FocusedItem; i != 0 &&
524 !(menu->items[i].fType & MF_MENUBARBREAK);
525 --i); /* empty */
527 if(i == 0)
528 return NO_SELECTED_ITEM;
530 for(--i; i != 0; --i) {
531 if (menu->items[i].fType & MF_MENUBARBREAK)
532 break;
535 TRACE("ret %d.\n", i );
537 return i;
542 /***********************************************************************
543 * MENU_FindItem
545 * Find a menu item. Return a pointer on the item, and modifies *hmenu
546 * in case the item was in a sub-menu.
548 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
550 POPUPMENU *menu;
551 UINT i;
553 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
554 if (wFlags & MF_BYPOSITION)
556 if (*nPos >= menu->nItems) return NULL;
557 return &menu->items[*nPos];
559 else
561 MENUITEM *item = menu->items;
562 for (i = 0; i < menu->nItems; i++, item++)
564 if (item->wID == *nPos)
566 *nPos = i;
567 return item;
569 else if (item->fType & MF_POPUP)
571 HMENU hsubmenu = item->hSubMenu;
572 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
573 if (subitem)
575 *hmenu = hsubmenu;
576 return subitem;
581 return NULL;
584 /***********************************************************************
585 * MENU_FindSubMenu
587 * Find a Sub menu. Return the position of the submenu, and modifies
588 * *hmenu in case it is found in another sub-menu.
589 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
591 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
593 POPUPMENU *menu;
594 UINT i;
595 MENUITEM *item;
596 if (((*hmenu)==(HMENU)0xffff) ||
597 (!(menu = MENU_GetMenu(*hmenu))))
598 return NO_SELECTED_ITEM;
599 item = menu->items;
600 for (i = 0; i < menu->nItems; i++, item++) {
601 if(!(item->fType & MF_POPUP)) continue;
602 if (item->hSubMenu == hSubTarget) {
603 return i;
605 else {
606 HMENU hsubmenu = item->hSubMenu;
607 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
608 if (pos != NO_SELECTED_ITEM) {
609 *hmenu = hsubmenu;
610 return pos;
614 return NO_SELECTED_ITEM;
617 /***********************************************************************
618 * MENU_FreeItemData
620 static void MENU_FreeItemData( MENUITEM* item )
622 /* delete text */
623 if (IS_STRING_ITEM(item->fType) && item->text)
624 HeapFree( GetProcessHeap(), 0, item->text );
627 /***********************************************************************
628 * MENU_FindItemByCoords
630 * Find the item at the specified coordinates (screen coords). Does
631 * not work for child windows and therefore should not be called for
632 * an arbitrary system menu.
634 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
635 POINT pt, UINT *pos )
637 MENUITEM *item;
638 UINT i;
639 RECT wrect;
641 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
642 pt.x -= wrect.left;pt.y -= wrect.top;
643 item = menu->items;
644 for (i = 0; i < menu->nItems; i++, item++)
646 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
647 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
649 if (pos) *pos = i;
650 return item;
653 return NULL;
657 /***********************************************************************
658 * MENU_FindItemByKey
660 * Find the menu item selected by a key press.
661 * Return item id, -1 if none, -2 if we should close the menu.
663 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
664 WCHAR key, BOOL forceMenuChar )
666 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
668 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
670 if (hmenu)
672 POPUPMENU *menu = MENU_GetMenu( hmenu );
673 MENUITEM *item = menu->items;
674 LRESULT menuchar;
676 if( !forceMenuChar )
678 UINT i;
680 for (i = 0; i < menu->nItems; i++, item++)
682 if (IS_STRING_ITEM(item->fType) && item->text)
684 WCHAR *p = item->text - 2;
687 p = strchrW (p + 2, '&');
689 while (p != NULL && p [1] == '&');
690 if (p && (toupperW(p[1]) == toupperW(key))) return i;
694 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
695 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
696 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
697 if (HIWORD(menuchar) == 1) return (UINT)(-2);
699 return (UINT)(-1);
703 /***********************************************************************
704 * MENU_GetBitmapItemSize
706 * Get the size of a bitmap item.
708 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
710 BITMAP bm;
711 HBITMAP bmp = (HBITMAP)id;
713 size->cx = size->cy = 0;
715 /* check if there is a magic menu item associated with this item */
716 if (id && IS_MAGIC_ITEM( id ))
718 switch(LOWORD(id))
720 case (INT_PTR)HBMMENU_SYSTEM:
721 if (data)
723 bmp = (HBITMAP)data;
724 break;
726 /* fall through */
727 case (INT_PTR)HBMMENU_MBAR_RESTORE:
728 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
729 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
730 case (INT_PTR)HBMMENU_MBAR_CLOSE:
731 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
732 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
733 size->cy = size->cx;
734 return;
735 case (INT_PTR)HBMMENU_CALLBACK:
736 case (INT_PTR)HBMMENU_POPUP_CLOSE:
737 case (INT_PTR)HBMMENU_POPUP_RESTORE:
738 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
739 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
740 default:
741 FIXME("Magic 0x%08x not implemented\n", id);
742 return;
745 if (GetObjectW(bmp, sizeof(bm), &bm ))
747 size->cx = bm.bmWidth;
748 size->cy = bm.bmHeight;
752 /***********************************************************************
753 * MENU_DrawBitmapItem
755 * Draw a bitmap item.
756 * drawhbmbitmap : True to draw the hbmbitmap(MIIM_BITMAP)/False to draw the MF_BITMAP
758 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar, BOOL drawhbmbitmap )
760 BITMAP bm;
761 DWORD rop;
762 HDC hdcMem;
763 HBITMAP bmp = (HBITMAP)lpitem->text;
764 int w = rect->right - rect->left;
765 int h = rect->bottom - rect->top;
766 int bmp_xoffset = 0;
767 int left, top;
768 HBITMAP hbmToDraw = (drawhbmbitmap)?lpitem->hbmpItem:(HBITMAP)lpitem->text;
770 /* Check if there is a magic menu item associated with this item */
771 if (hbmToDraw && IS_MAGIC_ITEM(hbmToDraw))
773 UINT flags = 0;
774 RECT r;
776 switch(LOWORD(hbmToDraw))
778 case (INT_PTR)HBMMENU_SYSTEM:
779 if (lpitem->dwItemData)
781 bmp = (HBITMAP)lpitem->dwItemData;
782 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
784 else
786 bmp = hBmpSysMenu;
787 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
788 /* only use right half of the bitmap */
789 bmp_xoffset = bm.bmWidth / 2;
790 bm.bmWidth -= bmp_xoffset;
792 goto got_bitmap;
793 case (INT_PTR)HBMMENU_MBAR_RESTORE:
794 flags = DFCS_CAPTIONRESTORE;
795 break;
796 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
797 flags = DFCS_CAPTIONMIN;
798 break;
799 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
800 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
801 break;
802 case (INT_PTR)HBMMENU_MBAR_CLOSE:
803 flags = DFCS_CAPTIONCLOSE;
804 break;
805 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
806 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
807 break;
808 case (INT_PTR)HBMMENU_CALLBACK:
809 case (INT_PTR)HBMMENU_POPUP_CLOSE:
810 case (INT_PTR)HBMMENU_POPUP_RESTORE:
811 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
812 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
813 default:
814 FIXME("Magic 0x%08x not implemented\n", LOWORD(hbmToDraw));
815 return;
817 r = *rect;
818 InflateRect( &r, -1, -1 );
819 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
820 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
821 return;
824 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
826 got_bitmap:
827 hdcMem = CreateCompatibleDC( hdc );
828 SelectObject( hdcMem, bmp );
830 /* handle fontsize > bitmap_height */
831 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
832 left=rect->left;
833 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
834 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
835 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
836 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
837 DeleteDC( hdcMem );
841 /***********************************************************************
842 * MENU_CalcItemSize
844 * Calculate the size of the menu item and store it in lpitem->rect.
846 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
847 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
849 WCHAR *p;
850 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
852 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
853 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
854 (menuBar ? " (MenuBar)" : ""));
856 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
858 if (lpitem->fType & MF_OWNERDRAW)
860 MEASUREITEMSTRUCT mis;
861 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
862 if( !menucharsize.cx ) {
863 DIALOG_GetCharSize( hdc, hMenuFont, &menucharsize );
864 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
865 * but it is unlikely an application will depend on that */
866 ODitemheight = HIWORD( GetDialogBaseUnits());
868 mis.CtlType = ODT_MENU;
869 mis.CtlID = 0;
870 mis.itemID = lpitem->wID;
871 mis.itemData = (DWORD)lpitem->dwItemData;
872 mis.itemHeight = ODitemheight;
873 mis.itemWidth = 0;
874 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
875 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
876 * width of a menufont character to the width of an owner-drawn menu.
878 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
879 if (menuBar) {
880 /* under at least win95 you seem to be given a standard
881 height for the menu and the height value is ignored */
882 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
883 } else
884 lpitem->rect.bottom += mis.itemHeight;
886 TRACE("id=%04x size=%ldx%ld\n",
887 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
888 lpitem->rect.bottom-lpitem->rect.top);
889 return;
892 if (lpitem->fType & MF_SEPARATOR)
894 lpitem->rect.bottom += SEPARATOR_HEIGHT;
895 return;
898 if (!menuBar)
900 /* New style MIIM_BITMAP */
901 if (lpitem->hbmpItem)
903 if (lpitem->hbmpItem == HBMMENU_CALLBACK)
905 MEASUREITEMSTRUCT measItem;
906 measItem.CtlType = ODT_MENU;
907 measItem.CtlID = 0;
908 measItem.itemID = lpitem->wID;
909 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
910 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
911 measItem.itemData = lpitem->dwItemData;
913 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
915 /* Keep the size of the bitmap in callback mode to be able to draw it correctly */
916 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, measItem.itemWidth - (lpitem->rect.right - lpitem->rect.left));
917 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, measItem.itemHeight - (lpitem->rect.bottom - lpitem->rect.top));
918 lpitem->rect.right = lpitem->rect.left + measItem.itemWidth;
919 } else {
920 SIZE size;
921 MENU_GetBitmapItemSize((UINT)lpitem->hbmpItem, lpitem->dwItemData, &size);
922 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, size.cx);
923 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, size.cy);
924 lpitem->rect.right += size.cx;
925 lpitem->rect.bottom += size.cy;
927 if (lppop->dwStyle & MNS_CHECKORBMP)
928 lpitem->rect.right += check_bitmap_width;
929 else
930 lpitem->rect.right += 2 * check_bitmap_width;
931 } else
932 lpitem->rect.right += 2 * check_bitmap_width;
933 if (lpitem->fType & MF_POPUP)
934 lpitem->rect.right += arrow_bitmap_width;
937 if (IS_BITMAP_ITEM(lpitem->fType))
939 SIZE size;
941 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
942 lpitem->rect.right += size.cx;
943 lpitem->rect.bottom += size.cy;
944 /* Leave space for the sunken border */
945 lpitem->rect.right += 2;
946 lpitem->rect.bottom += 2;
950 /* it must be a text item - unless it's the system menu */
951 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
952 { SIZE size;
954 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
956 lpitem->rect.right += size.cx;
957 lpitem->rect.bottom += max(max(size.cy, GetSystemMetrics(SM_CYMENU)-1), lppop->maxBmpSize.cy);
958 lpitem->xTab = 0;
960 if (menuBar)
962 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
964 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
966 /* Item contains a tab (only meaningful in popup menus) */
967 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
968 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
969 lpitem->rect.right += MENU_TAB_SPACE;
971 else
973 if (strchrW( lpitem->text, '\b' ))
974 lpitem->rect.right += MENU_TAB_SPACE;
975 lpitem->xTab = lpitem->rect.right - check_bitmap_width
976 - arrow_bitmap_width;
979 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
983 /***********************************************************************
984 * MENU_PopupMenuCalcSize
986 * Calculate the size of a popup menu.
988 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
990 MENUITEM *lpitem;
991 HDC hdc;
992 int start, i;
993 int orgX, orgY, maxX, maxTab, maxTabWidth;
995 lppop->Width = lppop->Height = 0;
996 if (lppop->nItems == 0) return;
997 hdc = GetDC( 0 );
999 SelectObject( hdc, hMenuFont);
1001 start = 0;
1002 maxX = 2 + 1;
1004 lppop->maxBmpSize.cx = 0;
1005 lppop->maxBmpSize.cy = 0;
1007 while (start < lppop->nItems)
1009 lpitem = &lppop->items[start];
1010 orgX = maxX;
1011 if( lpitem->fType & MF_MENUBREAK)
1012 orgX += MENU_COL_SPACE;
1013 orgY = 3;
1015 maxTab = maxTabWidth = 0;
1016 /* Parse items until column break or end of menu */
1017 for (i = start; i < lppop->nItems; i++, lpitem++)
1019 if ((i != start) &&
1020 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1022 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1024 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1025 maxX = max( maxX, lpitem->rect.right );
1026 orgY = lpitem->rect.bottom;
1027 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1029 maxTab = max( maxTab, lpitem->xTab );
1030 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1034 /* Finish the column (set all items to the largest width found) */
1035 maxX = max( maxX, maxTab + maxTabWidth );
1036 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1038 lpitem->rect.right = maxX;
1039 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1040 lpitem->xTab = maxTab;
1043 lppop->Height = max( lppop->Height, orgY );
1046 lppop->Width = maxX;
1048 /* space for 3d border */
1049 lppop->Height += 2;
1050 lppop->Width += 2;
1052 ReleaseDC( 0, hdc );
1056 /***********************************************************************
1057 * MENU_MenuBarCalcSize
1059 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1060 * height is off by 1 pixel which causes lengthy window relocations when
1061 * active document window is maximized/restored.
1063 * Calculate the size of the menu bar.
1065 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1066 LPPOPUPMENU lppop, HWND hwndOwner )
1068 MENUITEM *lpitem;
1069 int start, i, orgX, orgY, maxY, helpPos;
1071 if ((lprect == NULL) || (lppop == NULL)) return;
1072 if (lppop->nItems == 0) return;
1073 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1074 lprect->left, lprect->top, lprect->right, lprect->bottom);
1075 lppop->Width = lprect->right - lprect->left;
1076 lppop->Height = 0;
1077 maxY = lprect->top+1;
1078 start = 0;
1079 helpPos = -1;
1080 lppop->maxBmpSize.cx = 0;
1081 lppop->maxBmpSize.cy = 0;
1082 while (start < lppop->nItems)
1084 lpitem = &lppop->items[start];
1085 orgX = lprect->left;
1086 orgY = maxY;
1088 /* Parse items until line break or end of menu */
1089 for (i = start; i < lppop->nItems; i++, lpitem++)
1091 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1092 if ((i != start) &&
1093 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1095 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1096 orgX, orgY );
1097 debug_print_menuitem (" item: ", lpitem, "");
1098 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1100 if (lpitem->rect.right > lprect->right)
1102 if (i != start) break;
1103 else lpitem->rect.right = lprect->right;
1105 maxY = max( maxY, lpitem->rect.bottom );
1106 orgX = lpitem->rect.right;
1109 /* Finish the line (set all items to the largest height found) */
1110 while (start < i) lppop->items[start++].rect.bottom = maxY;
1113 lprect->bottom = maxY;
1114 lppop->Height = lprect->bottom - lprect->top;
1116 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1117 /* the last item (if several lines, only move the last line) */
1118 lpitem = &lppop->items[lppop->nItems-1];
1119 orgY = lpitem->rect.top;
1120 orgX = lprect->right;
1121 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1122 if ( (helpPos==-1) || (helpPos>i) )
1123 break; /* done */
1124 if (lpitem->rect.top != orgY) break; /* Other line */
1125 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1126 lpitem->rect.left += orgX - lpitem->rect.right;
1127 lpitem->rect.right = orgX;
1128 orgX = lpitem->rect.left;
1132 /***********************************************************************
1133 * MENU_DrawMenuItem
1135 * Draw a single menu item.
1137 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1138 UINT height, BOOL menuBar, UINT odaction )
1140 RECT rect;
1141 BOOL flat_menu = FALSE;
1142 int bkgnd;
1144 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1146 if (lpitem->fType & MF_SYSMENU)
1148 if( !IsIconic(hwnd) )
1149 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1150 return;
1153 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1154 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1156 /* Setup colors */
1158 if (lpitem->fState & MF_HILITE)
1160 if(menuBar && !flat_menu) {
1161 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1162 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1163 } else {
1164 if(lpitem->fState & MF_GRAYED)
1165 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1166 else
1167 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1168 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1171 else
1173 if (lpitem->fState & MF_GRAYED)
1174 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1175 else
1176 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1177 SetBkColor( hdc, GetSysColor( bkgnd ) );
1180 if (lpitem->fType & MF_OWNERDRAW)
1183 ** Experimentation under Windows reveals that an owner-drawn
1184 ** menu is given the rectangle which includes the space it requested
1185 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1186 ** and a popup-menu arrow. This is the value of lpitem->rect.
1187 ** Windows will leave all drawing to the application except for
1188 ** the popup-menu arrow. Windows always draws that itself, after
1189 ** the menu owner has finished drawing.
1191 DRAWITEMSTRUCT dis;
1193 dis.CtlType = ODT_MENU;
1194 dis.CtlID = 0;
1195 dis.itemID = lpitem->wID;
1196 dis.itemData = (DWORD)lpitem->dwItemData;
1197 dis.itemState = 0;
1198 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1199 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1200 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1201 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1202 dis.hwndItem = (HWND)hmenu;
1203 dis.hDC = hdc;
1204 dis.rcItem = lpitem->rect;
1205 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1206 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner,
1207 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1208 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1209 dis.rcItem.bottom);
1210 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1211 /* Fall through to draw popup-menu arrow */
1214 TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem->rect.left, lpitem->rect.top,
1215 lpitem->rect.right,lpitem->rect.bottom);
1217 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1219 rect = lpitem->rect;
1221 if (!(lpitem->fType & MF_OWNERDRAW))
1223 if (lpitem->fState & MF_HILITE)
1225 if (flat_menu)
1227 InflateRect (&rect, -1, -1);
1228 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1229 InflateRect (&rect, 1, 1);
1230 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1232 else
1234 if(menuBar)
1235 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1236 else
1237 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1240 else
1241 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1244 SetBkMode( hdc, TRANSPARENT );
1246 if (!(lpitem->fType & MF_OWNERDRAW))
1248 HPEN oldPen;
1250 /* vertical separator */
1251 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1253 RECT rc = rect;
1254 rc.top = 3;
1255 rc.bottom = height - 3;
1256 if (flat_menu)
1258 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1259 MoveToEx( hdc, rc.left, rc.top, NULL );
1260 LineTo( hdc, rc.left, rc.bottom );
1261 SelectObject( hdc, oldPen );
1263 else
1264 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1267 /* horizontal separator */
1268 if (lpitem->fType & MF_SEPARATOR)
1270 RECT rc = rect;
1271 rc.left++;
1272 rc.right--;
1273 rc.top += SEPARATOR_HEIGHT / 2;
1274 if (flat_menu)
1276 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1277 MoveToEx( hdc, rc.left, rc.top, NULL );
1278 LineTo( hdc, rc.right, rc.top );
1279 SelectObject( hdc, oldPen );
1281 else
1282 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1283 return;
1287 /* helper lines for debugging */
1288 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1289 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1290 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1291 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1294 if (!menuBar)
1296 HBITMAP bm;
1297 INT y = rect.top + rect.bottom;
1298 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1299 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1301 if (!(lpitem->fType & MF_OWNERDRAW))
1303 RECT rc;
1304 rc = rect;
1305 /* New style MIIM_BITMAP */
1306 if (lpitem->hbmpItem)
1308 POPUPMENU *menu = MENU_GetMenu(hmenu);
1309 if (menu->dwStyle & MNS_CHECKORBMP)
1310 rc.left += menu->maxBmpSize.cx - check_bitmap_width;
1311 else
1312 rc.left += menu->maxBmpSize.cx;
1314 /* Draw the check mark
1316 * FIXME:
1317 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1319 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1320 if (bm) /* we have a custom bitmap */
1322 HDC hdcMem = CreateCompatibleDC( hdc );
1323 SelectObject( hdcMem, bm );
1324 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1325 check_bitmap_width, check_bitmap_height,
1326 hdcMem, 0, 0, SRCCOPY );
1327 DeleteDC( hdcMem );
1329 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1331 RECT r;
1332 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1333 HDC hdcMem = CreateCompatibleDC( hdc );
1334 SelectObject( hdcMem, bm );
1335 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1336 DrawFrameControl( hdcMem, &r, DFC_MENU,
1337 (lpitem->fType & MFT_RADIOCHECK) ?
1338 DFCS_MENUBULLET : DFCS_MENUCHECK );
1339 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1340 hdcMem, 0, 0, SRCCOPY );
1341 DeleteDC( hdcMem );
1342 DeleteObject( bm );
1344 /* New style MIIM_BITMAP */
1345 if (lpitem->hbmpItem)
1347 HBITMAP hbm = lpitem->hbmpItem;
1349 if (hbm == HBMMENU_CALLBACK)
1351 DRAWITEMSTRUCT drawItem;
1352 POINT origorg;
1353 drawItem.CtlType = ODT_MENU;
1354 drawItem.CtlID = 0;
1355 drawItem.itemID = lpitem->wID;
1356 drawItem.itemAction = odaction;
1357 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1358 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1359 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1360 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1361 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1362 drawItem.hwndItem = (HWND)hmenu;
1363 drawItem.hDC = hdc;
1364 drawItem.rcItem = lpitem->rect;
1365 drawItem.itemData = lpitem->dwItemData;
1366 /* some applications make this assumption on the DC's origin */
1367 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1368 OffsetRect( &drawItem.rcItem, - lpitem->rect.left, - lpitem->rect.top);
1369 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
1370 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1372 } else {
1373 MENU_DrawBitmapItem(hdc, lpitem, &rect, FALSE, TRUE);
1378 /* Draw the popup-menu arrow */
1379 if (lpitem->fType & MF_POPUP)
1381 HDC hdcMem = CreateCompatibleDC( hdc );
1382 HBITMAP hOrigBitmap;
1384 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1385 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1386 (y - arrow_bitmap_height) / 2,
1387 arrow_bitmap_width, arrow_bitmap_height,
1388 hdcMem, 0, 0, SRCCOPY );
1389 SelectObject( hdcMem, hOrigBitmap );
1390 DeleteDC( hdcMem );
1393 rect.left += check_bitmap_width;
1394 rect.right -= arrow_bitmap_width;
1397 /* Done for owner-drawn */
1398 if (lpitem->fType & MF_OWNERDRAW)
1399 return;
1401 /* Draw the item text or bitmap */
1402 if (IS_BITMAP_ITEM(lpitem->fType))
1404 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar, FALSE);
1405 return;
1407 /* No bitmap - process text if present */
1408 else if (IS_STRING_ITEM(lpitem->fType))
1410 register int i;
1411 HFONT hfontOld = 0;
1413 UINT uFormat = (menuBar) ?
1414 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1415 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1417 if ( lpitem->fState & MFS_DEFAULT )
1419 hfontOld = SelectObject( hdc, hMenuFontBold);
1422 if (menuBar)
1424 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1425 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1428 for (i = 0; lpitem->text[i]; i++)
1429 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1430 break;
1432 if(lpitem->fState & MF_GRAYED)
1434 if (!(lpitem->fState & MF_HILITE) )
1436 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1437 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1438 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1439 --rect.left; --rect.top; --rect.right; --rect.bottom;
1441 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1444 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1446 /* paint the shortcut text */
1447 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1449 if (lpitem->text[i] == '\t')
1451 rect.left = lpitem->xTab;
1452 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1454 else
1456 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1459 if(lpitem->fState & MF_GRAYED)
1461 if (!(lpitem->fState & MF_HILITE) )
1463 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1464 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1465 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1466 --rect.left; --rect.top; --rect.right; --rect.bottom;
1468 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1470 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1473 if (hfontOld)
1474 SelectObject (hdc, hfontOld);
1479 /***********************************************************************
1480 * MENU_DrawPopupMenu
1482 * Paint a popup menu.
1484 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1486 HBRUSH hPrevBrush = 0;
1487 RECT rect;
1489 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1491 GetClientRect( hwnd, &rect );
1493 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1494 && (SelectObject( hdc, hMenuFont)))
1496 HPEN hPrevPen;
1498 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1500 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1501 if( hPrevPen )
1503 POPUPMENU *menu;
1504 BOOL flat_menu = FALSE;
1506 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1507 if (flat_menu)
1508 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1509 else
1510 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1512 /* draw menu items */
1514 menu = MENU_GetMenu( hmenu );
1515 if (menu && menu->nItems)
1517 MENUITEM *item;
1518 UINT u;
1520 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1521 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1522 menu->Height, FALSE, ODA_DRAWENTIRE );
1525 } else
1527 SelectObject( hdc, hPrevBrush );
1532 /***********************************************************************
1533 * MENU_DrawMenuBar
1535 * Paint a menu bar. Returns the height of the menu bar.
1536 * called from [windows/nonclient.c]
1538 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1539 BOOL suppress_draw)
1541 LPPOPUPMENU lppop;
1542 HFONT hfontOld = 0;
1543 HMENU hMenu = GetMenu(hwnd);
1545 lppop = MENU_GetMenu( hMenu );
1546 if (lppop == NULL || lprect == NULL)
1548 return GetSystemMetrics(SM_CYMENU);
1551 if (suppress_draw)
1553 hfontOld = SelectObject( hDC, hMenuFont);
1555 if (lppop->Height == 0)
1556 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1558 lprect->bottom = lprect->top + lppop->Height;
1560 if (hfontOld) SelectObject( hDC, hfontOld);
1561 return lppop->Height;
1563 else
1564 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1568 /***********************************************************************
1569 * MENU_ShowPopup
1571 * Display a popup menu.
1573 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1574 INT x, INT y, INT xanchor, INT yanchor )
1576 POPUPMENU *menu;
1577 UINT width, height;
1579 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1580 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1582 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1583 if (menu->FocusedItem != NO_SELECTED_ITEM)
1585 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1586 menu->FocusedItem = NO_SELECTED_ITEM;
1589 /* store the owner for DrawItem */
1590 menu->hwndOwner = hwndOwner;
1592 MENU_PopupMenuCalcSize( menu, hwndOwner );
1594 /* adjust popup menu pos so that it fits within the desktop */
1596 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1597 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1599 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1601 if( xanchor )
1602 x -= width - xanchor;
1603 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1604 x = GetSystemMetrics(SM_CXSCREEN) - width;
1606 if( x < 0 ) x = 0;
1608 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1610 if( yanchor )
1611 y -= height + yanchor;
1612 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1613 y = GetSystemMetrics(SM_CYSCREEN) - height;
1615 if( y < 0 ) y = 0;
1617 /* NOTE: In Windows, top menu popup is not owned. */
1618 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1619 WS_POPUP, x, y, width, height,
1620 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1621 (LPVOID)hmenu );
1622 if( !menu->hWnd ) return FALSE;
1623 if (!top_popup) top_popup = menu->hWnd;
1625 /* Display the window */
1627 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1628 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1629 UpdateWindow( menu->hWnd );
1630 return TRUE;
1634 /***********************************************************************
1635 * MENU_SelectItem
1637 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1638 BOOL sendMenuSelect, HMENU topmenu )
1640 LPPOPUPMENU lppop;
1641 HDC hdc;
1643 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1645 lppop = MENU_GetMenu( hmenu );
1646 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1648 if (lppop->FocusedItem == wIndex) return;
1649 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1650 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1651 if (!top_popup) top_popup = lppop->hWnd;
1653 SelectObject( hdc, hMenuFont);
1655 /* Clear previous highlighted item */
1656 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1658 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1659 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1660 lppop->Height, !(lppop->wFlags & MF_POPUP),
1661 ODA_SELECT );
1664 /* Highlight new item (if any) */
1665 lppop->FocusedItem = wIndex;
1666 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1668 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1669 lppop->items[wIndex].fState |= MF_HILITE;
1670 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1671 &lppop->items[wIndex], lppop->Height,
1672 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1674 if (sendMenuSelect)
1676 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1677 SendMessageW( hwndOwner, WM_MENUSELECT,
1678 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1679 ip->fType | ip->fState |
1680 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1683 else if (sendMenuSelect) {
1684 if(topmenu){
1685 int pos;
1686 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1687 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1688 MENUITEM *ip = &ptm->items[pos];
1689 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1690 ip->fType | ip->fState |
1691 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1695 ReleaseDC( lppop->hWnd, hdc );
1699 /***********************************************************************
1700 * MENU_MoveSelection
1702 * Moves currently selected item according to the offset parameter.
1703 * If there is no selection then it should select the last item if
1704 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1706 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1708 INT i;
1709 POPUPMENU *menu;
1711 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1713 menu = MENU_GetMenu( hmenu );
1714 if ((!menu) || (!menu->items)) return;
1716 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1718 if( menu->nItems == 1 ) return; else
1719 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1720 ; i += offset)
1721 if (!(menu->items[i].fType & MF_SEPARATOR))
1723 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1724 return;
1728 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1729 i >= 0 && i < menu->nItems ; i += offset)
1730 if (!(menu->items[i].fType & MF_SEPARATOR))
1732 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1733 return;
1738 /**********************************************************************
1739 * MENU_SetItemData
1741 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1742 * ModifyMenu().
1744 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1745 LPCWSTR str )
1747 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1749 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1750 TRACE("flags=%x str=%p\n", flags, str);
1752 if (IS_STRING_ITEM(flags))
1754 if (!str)
1756 flags |= MF_SEPARATOR;
1757 item->text = NULL;
1759 else
1761 LPWSTR text;
1762 /* Item beginning with a backspace is a help item */
1763 if (*str == '\b')
1765 flags |= MF_HELP;
1766 str++;
1768 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1769 return FALSE;
1770 strcpyW( text, str );
1771 item->text = text;
1774 else if (IS_BITMAP_ITEM(flags))
1775 item->text = (LPWSTR)HBITMAP_32(LOWORD(str));
1776 else item->text = NULL;
1778 if (flags & MF_OWNERDRAW)
1779 item->dwItemData = (DWORD)str;
1780 else
1781 item->dwItemData = 0;
1783 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1784 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1786 if (flags & MF_POPUP)
1788 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1789 if (menu) menu->wFlags |= MF_POPUP;
1790 else
1792 item->wID = 0;
1793 item->hSubMenu = 0;
1794 item->fType = 0;
1795 item->fState = 0;
1796 return FALSE;
1800 item->wID = id;
1801 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
1803 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1804 flags |= MF_POPUP; /* keep popup */
1806 item->fType = flags & TYPE_MASK;
1807 item->fState = (flags & STATE_MASK) &
1808 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1811 /* Don't call SetRectEmpty here! */
1814 HeapFree( GetProcessHeap(), 0, prevText );
1816 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1817 return TRUE;
1821 /**********************************************************************
1822 * MENU_InsertItem
1824 * Insert (allocate) a new item into a menu.
1826 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1828 MENUITEM *newItems;
1829 POPUPMENU *menu;
1831 if (!(menu = MENU_GetMenu(hMenu)))
1832 return NULL;
1834 /* Find where to insert new item */
1836 if (flags & MF_BYPOSITION) {
1837 if (pos > menu->nItems)
1838 pos = menu->nItems;
1839 } else {
1840 if (!MENU_FindItem( &hMenu, &pos, flags ))
1841 pos = menu->nItems;
1842 else {
1843 if (!(menu = MENU_GetMenu( hMenu )))
1844 return NULL;
1848 /* Create new items array */
1850 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1851 if (!newItems)
1853 WARN("allocation failed\n" );
1854 return NULL;
1856 if (menu->nItems > 0)
1858 /* Copy the old array into the new one */
1859 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1860 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1861 (menu->nItems-pos)*sizeof(MENUITEM) );
1862 HeapFree( GetProcessHeap(), 0, menu->items );
1864 menu->items = newItems;
1865 menu->nItems++;
1866 memset( &newItems[pos], 0, sizeof(*newItems) );
1867 menu->Height = 0; /* force size recalculate */
1868 return &newItems[pos];
1872 /**********************************************************************
1873 * MENU_ParseResource
1875 * Parse a standard menu resource and add items to the menu.
1876 * Return a pointer to the end of the resource.
1878 * NOTE: flags is equivalent to the mtOption field
1880 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1882 WORD flags, id = 0;
1883 LPCSTR str;
1887 flags = GET_WORD(res);
1888 res += sizeof(WORD);
1889 if (!(flags & MF_POPUP))
1891 id = GET_WORD(res);
1892 res += sizeof(WORD);
1894 str = res;
1895 if (!unicode) res += strlen(str) + 1;
1896 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1897 if (flags & MF_POPUP)
1899 HMENU hSubMenu = CreatePopupMenu();
1900 if (!hSubMenu) return NULL;
1901 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1902 return NULL;
1903 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1904 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1906 else /* Not a popup */
1908 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1909 else AppendMenuW( hMenu, flags, id,
1910 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1912 } while (!(flags & MF_END));
1913 return res;
1917 /**********************************************************************
1918 * MENUEX_ParseResource
1920 * Parse an extended menu resource and add items to the menu.
1921 * Return a pointer to the end of the resource.
1923 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1925 WORD resinfo;
1926 do {
1927 MENUITEMINFOW mii;
1929 mii.cbSize = sizeof(mii);
1930 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1931 mii.fType = GET_DWORD(res);
1932 res += sizeof(DWORD);
1933 mii.fState = GET_DWORD(res);
1934 res += sizeof(DWORD);
1935 mii.wID = GET_DWORD(res);
1936 res += sizeof(DWORD);
1937 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1938 res += sizeof(WORD);
1939 /* Align the text on a word boundary. */
1940 res += (~((int)res - 1)) & 1;
1941 mii.dwTypeData = (LPWSTR) res;
1942 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1943 /* Align the following fields on a dword boundary. */
1944 res += (~((int)res - 1)) & 3;
1946 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1947 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1949 if (resinfo & 1) { /* Pop-up? */
1950 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1951 res += sizeof(DWORD);
1952 mii.hSubMenu = CreatePopupMenu();
1953 if (!mii.hSubMenu)
1954 return NULL;
1955 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1956 DestroyMenu(mii.hSubMenu);
1957 return NULL;
1959 mii.fMask |= MIIM_SUBMENU;
1960 mii.fType |= MF_POPUP;
1962 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1964 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1965 mii.wID, mii.fType);
1966 mii.fType |= MF_SEPARATOR;
1968 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1969 } while (!(resinfo & MF_END));
1970 return res;
1974 /***********************************************************************
1975 * MENU_GetSubPopup
1977 * Return the handle of the selected sub-popup menu (if any).
1979 static HMENU MENU_GetSubPopup( HMENU hmenu )
1981 POPUPMENU *menu;
1982 MENUITEM *item;
1984 menu = MENU_GetMenu( hmenu );
1986 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1988 item = &menu->items[menu->FocusedItem];
1989 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1990 return item->hSubMenu;
1991 return 0;
1995 /***********************************************************************
1996 * MENU_HideSubPopups
1998 * Hide the sub-popup menus of this menu.
2000 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2001 BOOL sendMenuSelect )
2003 POPUPMENU *menu = MENU_GetMenu( hmenu );
2005 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2007 if (menu && top_popup)
2009 HMENU hsubmenu;
2010 POPUPMENU *submenu;
2011 MENUITEM *item;
2013 if (menu->FocusedItem != NO_SELECTED_ITEM)
2015 item = &menu->items[menu->FocusedItem];
2016 if (!(item->fType & MF_POPUP) ||
2017 !(item->fState & MF_MOUSESELECT)) return;
2018 item->fState &= ~MF_MOUSESELECT;
2019 hsubmenu = item->hSubMenu;
2020 } else return;
2022 submenu = MENU_GetMenu( hsubmenu );
2023 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2024 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2025 DestroyWindow( submenu->hWnd );
2026 submenu->hWnd = 0;
2031 /***********************************************************************
2032 * MENU_ShowSubPopup
2034 * Display the sub-menu of the selected item of this menu.
2035 * Return the handle of the submenu, or hmenu if no submenu to display.
2037 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2038 BOOL selectFirst, UINT wFlags )
2040 RECT rect;
2041 POPUPMENU *menu;
2042 MENUITEM *item;
2043 HDC hdc;
2045 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2047 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2049 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2051 item = &menu->items[menu->FocusedItem];
2052 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2053 return hmenu;
2055 /* message must be sent before using item,
2056 because nearly everything may be changed by the application ! */
2058 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2059 if (!(wFlags & TPM_NONOTIFY))
2060 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2061 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2063 item = &menu->items[menu->FocusedItem];
2064 rect = item->rect;
2066 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2067 if (!(item->fState & MF_HILITE))
2069 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2070 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2072 SelectObject( hdc, hMenuFont);
2074 item->fState |= MF_HILITE;
2075 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2076 ReleaseDC( menu->hWnd, hdc );
2078 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2079 item->rect = rect;
2081 item->fState |= MF_MOUSESELECT;
2083 if (IS_SYSTEM_MENU(menu))
2085 MENU_InitSysMenuPopup(item->hSubMenu,
2086 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2087 GetClassLongW( menu->hWnd, GCL_STYLE));
2089 NC_GetSysPopupPos( menu->hWnd, &rect );
2090 rect.top = rect.bottom;
2091 rect.right = GetSystemMetrics(SM_CXSIZE);
2092 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2094 else
2096 GetWindowRect( menu->hWnd, &rect );
2097 if (menu->wFlags & MF_POPUP)
2099 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2100 rect.top += item->rect.top;
2101 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2102 rect.bottom = item->rect.top - item->rect.bottom;
2104 else
2106 rect.left += item->rect.left;
2107 rect.top += item->rect.bottom;
2108 rect.right = item->rect.right - item->rect.left;
2109 rect.bottom = item->rect.bottom - item->rect.top;
2113 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2114 rect.left, rect.top, rect.right, rect.bottom );
2115 if (selectFirst)
2116 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2117 return item->hSubMenu;
2122 /**********************************************************************
2123 * MENU_IsMenuActive
2125 HWND MENU_IsMenuActive(void)
2127 return top_popup;
2130 /***********************************************************************
2131 * MENU_PtMenu
2133 * Walks menu chain trying to find a menu pt maps to.
2135 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2137 POPUPMENU *menu = MENU_GetMenu( hMenu );
2138 UINT item = menu->FocusedItem;
2139 HMENU ret;
2141 /* try subpopup first (if any) */
2142 ret = (item != NO_SELECTED_ITEM &&
2143 (menu->items[item].fType & MF_POPUP) &&
2144 (menu->items[item].fState & MF_MOUSESELECT))
2145 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2147 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2149 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2150 if( menu->wFlags & MF_POPUP )
2152 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2154 else if (ht == HTSYSMENU)
2155 ret = get_win_sys_menu( menu->hWnd );
2156 else if (ht == HTMENU)
2157 ret = GetMenu( menu->hWnd );
2159 return ret;
2162 /***********************************************************************
2163 * MENU_ExecFocusedItem
2165 * Execute a menu item (for instance when user pressed Enter).
2166 * Return the wID of the executed item. Otherwise, -1 indicating
2167 * that no menu item was executed;
2168 * Have to receive the flags for the TrackPopupMenu options to avoid
2169 * sending unwanted message.
2172 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2174 MENUITEM *item;
2175 POPUPMENU *menu = MENU_GetMenu( hMenu );
2177 TRACE("%p hmenu=%p\n", pmt, hMenu);
2179 if (!menu || !menu->nItems ||
2180 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2182 item = &menu->items[menu->FocusedItem];
2184 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2186 if (!(item->fType & MF_POPUP))
2188 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2190 /* If TPM_RETURNCMD is set you return the id, but
2191 do not send a message to the owner */
2192 if(!(wFlags & TPM_RETURNCMD))
2194 if( menu->wFlags & MF_SYSMENU )
2195 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2196 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2197 else
2198 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2200 return item->wID;
2203 else
2204 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2206 return -1;
2209 /***********************************************************************
2210 * MENU_SwitchTracking
2212 * Helper function for menu navigation routines.
2214 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2216 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2217 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2219 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2221 if( pmt->hTopMenu != hPtMenu &&
2222 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2224 /* both are top level menus (system and menu-bar) */
2225 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2226 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2227 pmt->hTopMenu = hPtMenu;
2229 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2230 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2234 /***********************************************************************
2235 * MENU_ButtonDown
2237 * Return TRUE if we can go on with menu tracking.
2239 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2241 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2243 if (hPtMenu)
2245 UINT id = 0;
2246 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2247 MENUITEM *item;
2249 if( IS_SYSTEM_MENU(ptmenu) )
2250 item = ptmenu->items;
2251 else
2252 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2254 if( item )
2256 if( ptmenu->FocusedItem != id )
2257 MENU_SwitchTracking( pmt, hPtMenu, id );
2259 /* If the popup menu is not already "popped" */
2260 if(!(item->fState & MF_MOUSESELECT ))
2262 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2265 return TRUE;
2267 /* Else the click was on the menu bar, finish the tracking */
2269 return FALSE;
2272 /***********************************************************************
2273 * MENU_ButtonUp
2275 * Return the value of MENU_ExecFocusedItem if
2276 * the selected item was not a popup. Else open the popup.
2277 * A -1 return value indicates that we go on with menu tracking.
2280 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2282 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2284 if (hPtMenu)
2286 UINT id = 0;
2287 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2288 MENUITEM *item;
2290 if( IS_SYSTEM_MENU(ptmenu) )
2291 item = ptmenu->items;
2292 else
2293 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2295 if( item && (ptmenu->FocusedItem == id ))
2297 if( !(item->fType & MF_POPUP) )
2298 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2300 /* If we are dealing with the top-level menu */
2301 /* and this is a click on an already "popped" item: */
2302 /* Stop the menu tracking and close the opened submenus */
2303 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2304 return 0;
2306 ptmenu->bTimeToHide = TRUE;
2308 return -1;
2312 /***********************************************************************
2313 * MENU_MouseMove
2315 * Return TRUE if we can go on with menu tracking.
2317 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2319 UINT id = NO_SELECTED_ITEM;
2320 POPUPMENU *ptmenu = NULL;
2322 if( hPtMenu )
2324 ptmenu = MENU_GetMenu( hPtMenu );
2325 if( IS_SYSTEM_MENU(ptmenu) )
2326 id = 0;
2327 else
2328 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2331 if( id == NO_SELECTED_ITEM )
2333 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2334 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2337 else if( ptmenu->FocusedItem != id )
2339 MENU_SwitchTracking( pmt, hPtMenu, id );
2340 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2342 return TRUE;
2346 /***********************************************************************
2347 * MENU_SetCapture
2349 static void MENU_SetCapture( HWND hwnd )
2351 HWND previous = 0;
2353 SERVER_START_REQ( set_capture_window )
2355 req->handle = hwnd;
2356 req->flags = CAPTURE_MENU;
2357 if (!wine_server_call_err( req ))
2359 previous = reply->previous;
2360 hwnd = reply->full_handle;
2363 SERVER_END_REQ;
2365 if (previous && previous != hwnd)
2366 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2370 /***********************************************************************
2371 * MENU_DoNextMenu
2373 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2375 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2377 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2379 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2380 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2382 MDINEXTMENU next_menu;
2383 HMENU hNewMenu;
2384 HWND hNewWnd;
2385 UINT id = 0;
2387 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2388 next_menu.hmenuNext = 0;
2389 next_menu.hwndNext = 0;
2390 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2392 TRACE("%p [%p] -> %p [%p]\n",
2393 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2395 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2397 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2398 hNewWnd = pmt->hOwnerWnd;
2399 if( IS_SYSTEM_MENU(menu) )
2401 /* switch to the menu bar */
2403 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2405 if( vk == VK_LEFT )
2407 menu = MENU_GetMenu( hNewMenu );
2408 id = menu->nItems - 1;
2411 else if (style & WS_SYSMENU )
2413 /* switch to the system menu */
2414 hNewMenu = get_win_sys_menu( hNewWnd );
2416 else return FALSE;
2418 else /* application returned a new menu to switch to */
2420 hNewMenu = next_menu.hmenuNext;
2421 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2423 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2425 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2427 if (style & WS_SYSMENU &&
2428 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2430 /* get the real system menu */
2431 hNewMenu = get_win_sys_menu(hNewWnd);
2433 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2435 /* FIXME: Not sure what to do here;
2436 * perhaps try to track hNewMenu as a popup? */
2438 TRACE(" -- got confused.\n");
2439 return FALSE;
2442 else return FALSE;
2445 if( hNewMenu != pmt->hTopMenu )
2447 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2448 FALSE, 0 );
2449 if( pmt->hCurrentMenu != pmt->hTopMenu )
2450 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2453 if( hNewWnd != pmt->hOwnerWnd )
2455 pmt->hOwnerWnd = hNewWnd;
2456 MENU_SetCapture( pmt->hOwnerWnd );
2459 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2460 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2462 return TRUE;
2464 return FALSE;
2467 /***********************************************************************
2468 * MENU_SuspendPopup
2470 * The idea is not to show the popup if the next input message is
2471 * going to hide it anyway.
2473 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2475 MSG msg;
2477 msg.hwnd = pmt->hOwnerWnd;
2479 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2480 pmt->trackFlags |= TF_SKIPREMOVE;
2482 switch( uMsg )
2484 case WM_KEYDOWN:
2485 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2486 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2488 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2489 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2490 if( msg.message == WM_KEYDOWN &&
2491 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2493 pmt->trackFlags |= TF_SUSPENDPOPUP;
2494 return TRUE;
2497 break;
2500 /* failures go through this */
2501 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2502 return FALSE;
2505 /***********************************************************************
2506 * MENU_KeyEscape
2508 * Handle a VK_ESCAPE key event in a menu.
2510 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2512 BOOL bEndMenu = TRUE;
2514 if (pmt->hCurrentMenu != pmt->hTopMenu)
2516 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2518 if (menu->wFlags & MF_POPUP)
2520 HMENU hmenutmp, hmenuprev;
2522 hmenuprev = hmenutmp = pmt->hTopMenu;
2524 /* close topmost popup */
2525 while (hmenutmp != pmt->hCurrentMenu)
2527 hmenuprev = hmenutmp;
2528 hmenutmp = MENU_GetSubPopup( hmenuprev );
2531 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2532 pmt->hCurrentMenu = hmenuprev;
2533 bEndMenu = FALSE;
2537 return bEndMenu;
2540 /***********************************************************************
2541 * MENU_KeyLeft
2543 * Handle a VK_LEFT key event in a menu.
2545 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2547 POPUPMENU *menu;
2548 HMENU hmenutmp, hmenuprev;
2549 UINT prevcol;
2551 hmenuprev = hmenutmp = pmt->hTopMenu;
2552 menu = MENU_GetMenu( hmenutmp );
2554 /* Try to move 1 column left (if possible) */
2555 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2556 NO_SELECTED_ITEM ) {
2558 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2559 prevcol, TRUE, 0 );
2560 return;
2563 /* close topmost popup */
2564 while (hmenutmp != pmt->hCurrentMenu)
2566 hmenuprev = hmenutmp;
2567 hmenutmp = MENU_GetSubPopup( hmenuprev );
2570 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2571 pmt->hCurrentMenu = hmenuprev;
2573 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2575 /* move menu bar selection if no more popups are left */
2577 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2578 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2580 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2582 /* A sublevel menu was displayed - display the next one
2583 * unless there is another displacement coming up */
2585 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2586 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2587 pmt->hTopMenu, TRUE, wFlags);
2593 /***********************************************************************
2594 * MENU_KeyRight
2596 * Handle a VK_RIGHT key event in a menu.
2598 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2600 HMENU hmenutmp;
2601 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2602 UINT nextcol;
2604 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2605 pmt->hCurrentMenu,
2606 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2607 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2609 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2611 /* If already displaying a popup, try to display sub-popup */
2613 hmenutmp = pmt->hCurrentMenu;
2614 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2616 /* if subpopup was displayed then we are done */
2617 if (hmenutmp != pmt->hCurrentMenu) return;
2620 /* Check to see if there's another column */
2621 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2622 NO_SELECTED_ITEM ) {
2623 TRACE("Going to %d.\n", nextcol );
2624 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2625 nextcol, TRUE, 0 );
2626 return;
2629 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2631 if( pmt->hCurrentMenu != pmt->hTopMenu )
2633 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2634 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2635 } else hmenutmp = 0;
2637 /* try to move to the next item */
2638 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2639 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2641 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2642 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2643 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2644 pmt->hTopMenu, TRUE, wFlags);
2648 /***********************************************************************
2649 * MENU_TrackMenu
2651 * Menu tracking code.
2653 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2654 HWND hwnd, const RECT *lprect )
2656 MSG msg;
2657 POPUPMENU *menu;
2658 BOOL fRemove;
2659 INT executedMenuId = -1;
2660 MTRACKER mt;
2661 BOOL enterIdleSent = FALSE;
2663 mt.trackFlags = 0;
2664 mt.hCurrentMenu = hmenu;
2665 mt.hTopMenu = hmenu;
2666 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2667 mt.pt.x = x;
2668 mt.pt.y = y;
2670 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2671 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2672 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2674 fEndMenu = FALSE;
2675 if (!(menu = MENU_GetMenu( hmenu )))
2677 WARN("Invalid menu handle %p\n", hmenu);
2678 SetLastError(ERROR_INVALID_MENU_HANDLE);
2679 return FALSE;
2682 if (wFlags & TPM_BUTTONDOWN)
2684 /* Get the result in order to start the tracking or not */
2685 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2686 fEndMenu = !fRemove;
2689 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2691 MENU_SetCapture( mt.hOwnerWnd );
2693 while (!fEndMenu)
2695 menu = MENU_GetMenu( mt.hCurrentMenu );
2696 if (!menu) /* sometimes happens if I do a window manager close */
2697 break;
2699 /* we have to keep the message in the queue until it's
2700 * clear that menu loop is not over yet. */
2702 for (;;)
2704 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2706 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2707 /* remove the message from the queue */
2708 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2710 else
2712 if (!enterIdleSent)
2714 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2715 enterIdleSent = TRUE;
2716 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2718 WaitMessage();
2722 /* check if EndMenu() tried to cancel us, by posting this message */
2723 if(msg.message == WM_CANCELMODE)
2725 /* we are now out of the loop */
2726 fEndMenu = TRUE;
2728 /* remove the message from the queue */
2729 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2731 /* break out of internal loop, ala ESCAPE */
2732 break;
2735 TranslateMessage( &msg );
2736 mt.pt = msg.pt;
2738 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2739 enterIdleSent=FALSE;
2741 fRemove = FALSE;
2742 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2745 * Use the mouse coordinates in lParam instead of those in the MSG
2746 * struct to properly handle synthetic messages. They are already
2747 * in screen coordinates.
2749 mt.pt.x = (short)LOWORD(msg.lParam);
2750 mt.pt.y = (short)HIWORD(msg.lParam);
2752 /* Find a menu for this mouse event */
2753 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2755 switch(msg.message)
2757 /* no WM_NC... messages in captured state */
2759 case WM_RBUTTONDBLCLK:
2760 case WM_RBUTTONDOWN:
2761 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2762 /* fall through */
2763 case WM_LBUTTONDBLCLK:
2764 case WM_LBUTTONDOWN:
2765 /* If the message belongs to the menu, removes it from the queue */
2766 /* Else, end menu tracking */
2767 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2768 fEndMenu = !fRemove;
2769 break;
2771 case WM_RBUTTONUP:
2772 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2773 /* fall through */
2774 case WM_LBUTTONUP:
2775 /* Check if a menu was selected by the mouse */
2776 if (hmenu)
2778 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2780 /* End the loop if executedMenuId is an item ID */
2781 /* or if the job was done (executedMenuId = 0). */
2782 fEndMenu = fRemove = (executedMenuId != -1);
2784 /* No menu was selected by the mouse */
2785 /* if the function was called by TrackPopupMenu, continue
2786 with the menu tracking. If not, stop it */
2787 else
2788 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2790 break;
2792 case WM_MOUSEMOVE:
2793 /* the selected menu item must be changed every time */
2794 /* the mouse moves. */
2796 if (hmenu)
2797 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2799 } /* switch(msg.message) - mouse */
2801 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2803 fRemove = TRUE; /* Keyboard messages are always removed */
2804 switch(msg.message)
2806 case WM_KEYDOWN:
2807 case WM_SYSKEYDOWN:
2808 switch(msg.wParam)
2810 case VK_MENU:
2811 fEndMenu = TRUE;
2812 break;
2814 case VK_HOME:
2815 case VK_END:
2816 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2817 NO_SELECTED_ITEM, FALSE, 0 );
2818 /* fall through */
2819 case VK_UP:
2820 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2821 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2822 break;
2824 case VK_DOWN: /* If on menu bar, pull-down the menu */
2826 menu = MENU_GetMenu( mt.hCurrentMenu );
2827 if (!(menu->wFlags & MF_POPUP))
2828 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2829 else /* otherwise try to move selection */
2830 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2831 break;
2833 case VK_LEFT:
2834 MENU_KeyLeft( &mt, wFlags );
2835 break;
2837 case VK_RIGHT:
2838 MENU_KeyRight( &mt, wFlags );
2839 break;
2841 case VK_ESCAPE:
2842 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2843 break;
2845 case VK_F1:
2847 HELPINFO hi;
2848 hi.cbSize = sizeof(HELPINFO);
2849 hi.iContextType = HELPINFO_MENUITEM;
2850 if (menu->FocusedItem == NO_SELECTED_ITEM)
2851 hi.iCtrlId = 0;
2852 else
2853 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2854 hi.hItemHandle = hmenu;
2855 hi.dwContextId = menu->dwContextHelpID;
2856 hi.MousePos = msg.pt;
2857 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
2858 break;
2861 default:
2862 break;
2864 break; /* WM_KEYDOWN */
2866 case WM_CHAR:
2867 case WM_SYSCHAR:
2869 UINT pos;
2871 if (msg.wParam == '\r' || msg.wParam == ' ')
2873 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2874 fEndMenu = (executedMenuId != -1);
2876 break;
2879 /* Hack to avoid control chars. */
2880 /* We will find a better way real soon... */
2881 if (msg.wParam < 32) break;
2883 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2884 LOWORD(msg.wParam), FALSE );
2885 if (pos == (UINT)-2) fEndMenu = TRUE;
2886 else if (pos == (UINT)-1) MessageBeep(0);
2887 else
2889 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2890 TRUE, 0 );
2891 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2892 fEndMenu = (executedMenuId != -1);
2895 break;
2896 } /* switch(msg.message) - kbd */
2898 else
2900 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2901 DispatchMessageW( &msg );
2902 continue;
2905 if (!fEndMenu) fRemove = TRUE;
2907 /* finally remove message from the queue */
2909 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2910 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2911 else mt.trackFlags &= ~TF_SKIPREMOVE;
2914 MENU_SetCapture(0); /* release the capture */
2916 /* If dropdown is still painted and the close box is clicked on
2917 then the menu will be destroyed as part of the DispatchMessage above.
2918 This will then invalidate the menu handle in mt.hTopMenu. We should
2919 check for this first. */
2920 if( IsMenu( mt.hTopMenu ) )
2922 menu = MENU_GetMenu( mt.hTopMenu );
2924 if( IsWindow( mt.hOwnerWnd ) )
2926 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2928 if (menu && (menu->wFlags & MF_POPUP))
2930 DestroyWindow( menu->hWnd );
2931 menu->hWnd = 0;
2933 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2934 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2937 /* Reset the variable for hiding menu */
2938 if( menu ) menu->bTimeToHide = FALSE;
2941 /* The return value is only used by TrackPopupMenu */
2942 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
2943 if (executedMenuId == -1) executedMenuId = 0;
2944 return executedMenuId;
2947 /***********************************************************************
2948 * MENU_InitTracking
2950 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2952 POPUPMENU *menu;
2954 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2956 HideCaret(0);
2958 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2959 if (!(wFlags & TPM_NONOTIFY))
2960 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2962 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2964 if (!(wFlags & TPM_NONOTIFY))
2966 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2967 /* If an app changed/recreated menu bar entries in WM_INITMENU
2968 * menu sizes will be recalculated once the menu created/shown.
2972 /* This makes the menus of applications built with Delphi work.
2973 * It also enables menus to be displayed in more than one window,
2974 * but there are some bugs left that need to be fixed in this case.
2976 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
2978 return TRUE;
2980 /***********************************************************************
2981 * MENU_ExitTracking
2983 static BOOL MENU_ExitTracking(HWND hWnd)
2985 TRACE("hwnd=%p\n", hWnd);
2987 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2988 ShowCaret(0);
2989 top_popup = 0;
2990 return TRUE;
2993 /***********************************************************************
2994 * MENU_TrackMouseMenuBar
2996 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2998 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3000 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3001 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3003 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
3005 if (IsMenu(hMenu))
3007 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3008 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3009 MENU_ExitTracking(hWnd);
3014 /***********************************************************************
3015 * MENU_TrackKbdMenuBar
3017 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3019 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3021 UINT uItem = NO_SELECTED_ITEM;
3022 HMENU hTrackMenu;
3023 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3025 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3027 /* find window that has a menu */
3029 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3030 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3032 /* check if we have to track a system menu */
3034 hTrackMenu = GetMenu( hwnd );
3035 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3037 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3038 hTrackMenu = get_win_sys_menu( hwnd );
3039 uItem = 0;
3040 wParam |= HTSYSMENU; /* prevent item lookup */
3043 if (!IsMenu( hTrackMenu )) return;
3045 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3047 if( wChar && wChar != ' ' )
3049 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3050 if ( uItem >= (UINT)(-2) )
3052 if( uItem == (UINT)(-1) ) MessageBeep(0);
3053 /* schedule end of menu tracking */
3054 wFlags |= TF_ENDMENU;
3055 goto track_menu;
3059 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3061 if (wParam & HTSYSMENU)
3063 /* prevent sysmenu activation for managed windows on Alt down/up */
3064 if (GetPropA( hwnd, "__wine_x11_managed" ))
3065 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3067 else
3069 if( uItem == NO_SELECTED_ITEM )
3070 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3071 else
3072 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3075 track_menu:
3076 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3077 MENU_ExitTracking( hwnd );
3081 /**********************************************************************
3082 * TrackPopupMenu (USER32.@)
3084 * Like the win32 API, the function return the command ID only if the
3085 * flag TPM_RETURNCMD is on.
3088 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3089 INT nReserved, HWND hWnd, const RECT *lpRect )
3091 BOOL ret = FALSE;
3093 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3095 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3096 if (!(wFlags & TPM_NONOTIFY))
3097 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3099 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3100 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3101 MENU_ExitTracking(hWnd);
3103 return ret;
3106 /**********************************************************************
3107 * TrackPopupMenuEx (USER32.@)
3109 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3110 HWND hWnd, LPTPMPARAMS lpTpm )
3112 FIXME("not fully implemented\n" );
3113 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3114 lpTpm ? &lpTpm->rcExclude : NULL );
3117 /***********************************************************************
3118 * PopupMenuWndProc
3120 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3122 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3124 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3126 switch(message)
3128 case WM_CREATE:
3130 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3131 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3132 return 0;
3135 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3136 return MA_NOACTIVATE;
3138 case WM_PAINT:
3140 PAINTSTRUCT ps;
3141 BeginPaint( hwnd, &ps );
3142 MENU_DrawPopupMenu( hwnd, ps.hdc,
3143 (HMENU)GetWindowLongW( hwnd, 0 ) );
3144 EndPaint( hwnd, &ps );
3145 return 0;
3147 case WM_ERASEBKGND:
3148 return 1;
3150 case WM_DESTROY:
3151 /* zero out global pointer in case resident popup window was destroyed. */
3152 if (hwnd == top_popup) top_popup = 0;
3153 break;
3155 case WM_SHOWWINDOW:
3157 if( wParam )
3159 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3161 else
3162 SetWindowLongW( hwnd, 0, 0 );
3163 break;
3165 case MM_SETMENUHANDLE:
3166 SetWindowLongW( hwnd, 0, wParam );
3167 break;
3169 case MM_GETMENUHANDLE:
3170 return GetWindowLongW( hwnd, 0 );
3172 default:
3173 return DefWindowProcW( hwnd, message, wParam, lParam );
3175 return 0;
3179 /***********************************************************************
3180 * MENU_GetMenuBarHeight
3182 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3184 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3185 INT orgX, INT orgY )
3187 HDC hdc;
3188 RECT rectBar;
3189 LPPOPUPMENU lppop;
3191 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3193 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3195 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3196 SelectObject( hdc, hMenuFont);
3197 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3198 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3199 ReleaseDC( hwnd, hdc );
3200 return lppop->Height;
3204 /*******************************************************************
3205 * ChangeMenuA (USER32.@)
3207 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3208 UINT id, UINT flags )
3210 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3211 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3212 id, data );
3213 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3214 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3215 id, data );
3216 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3217 flags & MF_BYPOSITION ? pos : id,
3218 flags & ~MF_REMOVE );
3219 /* Default: MF_INSERT */
3220 return InsertMenuA( hMenu, pos, flags, id, data );
3224 /*******************************************************************
3225 * ChangeMenuW (USER32.@)
3227 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3228 UINT id, UINT flags )
3230 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3231 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3232 id, data );
3233 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3234 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3235 id, data );
3236 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3237 flags & MF_BYPOSITION ? pos : id,
3238 flags & ~MF_REMOVE );
3239 /* Default: MF_INSERT */
3240 return InsertMenuW( hMenu, pos, flags, id, data );
3244 /*******************************************************************
3245 * CheckMenuItem (USER32.@)
3247 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3249 MENUITEM *item;
3250 DWORD ret;
3252 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3253 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3254 ret = item->fState & MF_CHECKED;
3255 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3256 else item->fState &= ~MF_CHECKED;
3257 return ret;
3261 /**********************************************************************
3262 * EnableMenuItem (USER32.@)
3264 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3266 UINT oldflags;
3267 MENUITEM *item;
3268 POPUPMENU *menu;
3270 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3272 /* Get the Popupmenu to access the owner menu */
3273 if (!(menu = MENU_GetMenu(hMenu)))
3274 return (UINT)-1;
3276 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3277 return (UINT)-1;
3279 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3280 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3282 /* If the close item in the system menu change update the close button */
3283 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3285 if (menu->hSysMenuOwner != 0)
3287 RECT rc;
3288 POPUPMENU* parentMenu;
3290 /* Get the parent menu to access*/
3291 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3292 return (UINT)-1;
3294 /* Refresh the frame to reflect the change */
3295 GetWindowRect(parentMenu->hWnd, &rc);
3296 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3297 rc.bottom = 0;
3298 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3302 return oldflags;
3306 /*******************************************************************
3307 * GetMenuStringA (USER32.@)
3309 INT WINAPI GetMenuStringA(
3310 HMENU hMenu, /* [in] menuhandle */
3311 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3312 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3313 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3314 UINT wFlags /* [in] MF_ flags */
3316 MENUITEM *item;
3318 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3319 if (str && nMaxSiz) str[0] = '\0';
3320 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3321 if (!IS_STRING_ITEM(item->fType)) return 0;
3322 if (!str || !nMaxSiz) return strlenW(item->text);
3323 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3324 str[nMaxSiz-1] = 0;
3325 TRACE("returning '%s'\n", str );
3326 return strlen(str);
3330 /*******************************************************************
3331 * GetMenuStringW (USER32.@)
3333 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3334 LPWSTR str, INT nMaxSiz, UINT wFlags )
3336 MENUITEM *item;
3338 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3339 if (str && nMaxSiz) str[0] = '\0';
3340 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3341 if (!IS_STRING_ITEM(item->fType)) return 0;
3342 if (!str || !nMaxSiz) return strlenW(item->text);
3343 lstrcpynW( str, item->text, nMaxSiz );
3344 return strlenW(str);
3348 /**********************************************************************
3349 * HiliteMenuItem (USER32.@)
3351 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3352 UINT wHilite )
3354 LPPOPUPMENU menu;
3355 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3356 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3357 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3358 if (menu->FocusedItem == wItemID) return TRUE;
3359 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3360 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3361 return TRUE;
3365 /**********************************************************************
3366 * GetMenuState (USER32.@)
3368 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3370 MENUITEM *item;
3371 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3372 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3373 debug_print_menuitem (" item: ", item, "");
3374 if (item->fType & MF_POPUP)
3376 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3377 if (!menu) return -1;
3378 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3380 else
3382 /* We used to (from way back then) mask the result to 0xff. */
3383 /* I don't know why and it seems wrong as the documented */
3384 /* return flag MF_SEPARATOR is outside that mask. */
3385 return (item->fType | item->fState);
3390 /**********************************************************************
3391 * GetMenuItemCount (USER32.@)
3393 INT WINAPI GetMenuItemCount( HMENU hMenu )
3395 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3396 if (!menu) return -1;
3397 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3398 return menu->nItems;
3402 /**********************************************************************
3403 * GetMenuItemID (USER32.@)
3405 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3407 MENUITEM * lpmi;
3409 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3410 if (lpmi->fType & MF_POPUP) return -1;
3411 return lpmi->wID;
3416 /*******************************************************************
3417 * InsertMenuW (USER32.@)
3419 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3420 UINT_PTR id, LPCWSTR str )
3422 MENUITEM *item;
3424 if (IS_STRING_ITEM(flags) && str)
3425 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3426 hMenu, pos, flags, id, debugstr_w(str) );
3427 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3428 hMenu, pos, flags, id, (DWORD)str );
3430 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3432 if (!(MENU_SetItemData( item, flags, id, str )))
3434 RemoveMenu( hMenu, pos, flags );
3435 return FALSE;
3438 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3439 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3441 item->hCheckBit = item->hUnCheckBit = 0;
3442 return TRUE;
3446 /*******************************************************************
3447 * InsertMenuA (USER32.@)
3449 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3450 UINT_PTR id, LPCSTR str )
3452 BOOL ret = FALSE;
3454 if (IS_STRING_ITEM(flags) && str)
3456 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3457 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3458 if (newstr)
3460 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3461 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3462 HeapFree( GetProcessHeap(), 0, newstr );
3464 return ret;
3466 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3470 /*******************************************************************
3471 * AppendMenuA (USER32.@)
3473 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3474 UINT_PTR id, LPCSTR data )
3476 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3480 /*******************************************************************
3481 * AppendMenuW (USER32.@)
3483 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3484 UINT_PTR id, LPCWSTR data )
3486 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3490 /**********************************************************************
3491 * RemoveMenu (USER32.@)
3493 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3495 LPPOPUPMENU menu;
3496 MENUITEM *item;
3498 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3499 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3500 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3502 /* Remove item */
3504 MENU_FreeItemData( item );
3506 if (--menu->nItems == 0)
3508 HeapFree( GetProcessHeap(), 0, menu->items );
3509 menu->items = NULL;
3511 else
3513 while(nPos < menu->nItems)
3515 *item = *(item+1);
3516 item++;
3517 nPos++;
3519 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3520 menu->nItems * sizeof(MENUITEM) );
3522 return TRUE;
3526 /**********************************************************************
3527 * DeleteMenu (USER32.@)
3529 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3531 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3532 if (!item) return FALSE;
3533 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3534 /* nPos is now the position of the item */
3535 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3536 return TRUE;
3540 /*******************************************************************
3541 * ModifyMenuW (USER32.@)
3543 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3544 UINT_PTR id, LPCWSTR str )
3546 MENUITEM *item;
3548 if (IS_STRING_ITEM(flags))
3550 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3552 else
3554 TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3557 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3558 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3559 return MENU_SetItemData( item, flags, id, str );
3563 /*******************************************************************
3564 * ModifyMenuA (USER32.@)
3566 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3567 UINT_PTR id, LPCSTR str )
3569 BOOL ret = FALSE;
3571 if (IS_STRING_ITEM(flags) && str)
3573 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3574 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3575 if (newstr)
3577 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3578 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3579 HeapFree( GetProcessHeap(), 0, newstr );
3581 return ret;
3583 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3587 /**********************************************************************
3588 * CreatePopupMenu (USER32.@)
3590 HMENU WINAPI CreatePopupMenu(void)
3592 HMENU hmenu;
3593 POPUPMENU *menu;
3595 if (!(hmenu = CreateMenu())) return 0;
3596 menu = MENU_GetMenu( hmenu );
3597 menu->wFlags |= MF_POPUP;
3598 menu->bTimeToHide = FALSE;
3599 return hmenu;
3603 /**********************************************************************
3604 * GetMenuCheckMarkDimensions (USER.417)
3605 * GetMenuCheckMarkDimensions (USER32.@)
3607 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3609 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3613 /**********************************************************************
3614 * SetMenuItemBitmaps (USER32.@)
3616 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3617 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3619 MENUITEM *item;
3620 TRACE("(%p, %04x, %04x, %p, %p)\n",
3621 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3622 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3624 if (!hNewCheck && !hNewUnCheck)
3626 item->fState &= ~MF_USECHECKBITMAPS;
3628 else /* Install new bitmaps */
3630 item->hCheckBit = hNewCheck;
3631 item->hUnCheckBit = hNewUnCheck;
3632 item->fState |= MF_USECHECKBITMAPS;
3634 return TRUE;
3638 /**********************************************************************
3639 * CreateMenu (USER32.@)
3641 HMENU WINAPI CreateMenu(void)
3643 HMENU hMenu;
3644 LPPOPUPMENU menu;
3645 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3646 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3648 ZeroMemory(menu, sizeof(POPUPMENU));
3649 menu->wMagic = MENU_MAGIC;
3650 menu->FocusedItem = NO_SELECTED_ITEM;
3651 menu->bTimeToHide = FALSE;
3653 TRACE("return %p\n", hMenu );
3655 return hMenu;
3659 /**********************************************************************
3660 * DestroyMenu (USER32.@)
3662 BOOL WINAPI DestroyMenu( HMENU hMenu )
3664 TRACE("(%p)\n", hMenu);
3666 /* Silently ignore attempts to destroy default system popup */
3668 if (hMenu && hMenu != MENU_DefSysPopup)
3670 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3672 if (!lppop) return FALSE;
3674 lppop->wMagic = 0; /* Mark it as destroyed */
3676 /* DestroyMenu should not destroy system menu popup owner */
3677 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3679 DestroyWindow( lppop->hWnd );
3680 lppop->hWnd = 0;
3683 if (lppop->items) /* recursively destroy submenus */
3685 int i;
3686 MENUITEM *item = lppop->items;
3687 for (i = lppop->nItems; i > 0; i--, item++)
3689 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3690 MENU_FreeItemData( item );
3692 HeapFree( GetProcessHeap(), 0, lppop->items );
3694 USER_HEAP_FREE( hMenu );
3696 return (hMenu != MENU_DefSysPopup);
3700 /**********************************************************************
3701 * GetSystemMenu (USER32.@)
3703 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3705 WND *wndPtr = WIN_GetPtr( hWnd );
3706 HMENU retvalue = 0;
3708 if (wndPtr == WND_DESKTOP) return 0;
3709 if (wndPtr == WND_OTHER_PROCESS)
3711 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3713 else if (wndPtr)
3715 if( wndPtr->hSysMenu )
3717 if( bRevert )
3719 DestroyMenu(wndPtr->hSysMenu);
3720 wndPtr->hSysMenu = 0;
3722 else
3724 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3725 if( menu )
3727 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3728 menu->items[0].hSubMenu = MENU_CopySysPopup();
3730 else
3732 WARN("Current sys-menu (%p) of wnd %p is broken\n",
3733 wndPtr->hSysMenu, hWnd);
3734 wndPtr->hSysMenu = 0;
3739 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3740 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3742 if( wndPtr->hSysMenu )
3744 POPUPMENU *menu;
3745 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3747 /* Store the dummy sysmenu handle to facilitate the refresh */
3748 /* of the close button if the SC_CLOSE item change */
3749 menu = MENU_GetMenu(retvalue);
3750 if ( menu )
3751 menu->hSysMenuOwner = wndPtr->hSysMenu;
3753 WIN_ReleasePtr( wndPtr );
3755 return bRevert ? 0 : retvalue;
3759 /*******************************************************************
3760 * SetSystemMenu (USER32.@)
3762 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3764 WND *wndPtr = WIN_GetPtr( hwnd );
3766 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3768 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3769 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3770 WIN_ReleasePtr( wndPtr );
3771 return TRUE;
3773 return FALSE;
3777 /**********************************************************************
3778 * GetMenu (USER32.@)
3780 HMENU WINAPI GetMenu( HWND hWnd )
3782 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3783 TRACE("for %p returning %p\n", hWnd, retvalue);
3784 return retvalue;
3787 /**********************************************************************
3788 * GetMenuBarInfo (USER32.@)
3790 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
3792 FIXME( "(%p,0x%08lx,0x%08lx,%p)\n", hwnd, idObject, idItem, pmbi );
3793 return FALSE;
3796 /**********************************************************************
3797 * MENU_SetMenu
3799 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
3800 * SetWindowPos call that would result if SetMenu were called directly.
3802 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
3804 TRACE("(%p, %p);\n", hWnd, hMenu);
3806 if (hMenu && !IsMenu(hMenu))
3808 WARN("hMenu %p is not a menu handle\n", hMenu);
3809 return FALSE;
3811 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3812 return FALSE;
3814 hWnd = WIN_GetFullHandle( hWnd );
3815 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
3817 if (hMenu != 0)
3819 LPPOPUPMENU lpmenu;
3821 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3823 lpmenu->hWnd = hWnd;
3824 lpmenu->Height = 0; /* Make sure we recalculate the size */
3826 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
3827 return TRUE;
3831 /**********************************************************************
3832 * SetMenu (USER32.@)
3834 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3836 if(!MENU_SetMenu(hWnd, hMenu))
3837 return FALSE;
3839 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3840 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3841 return TRUE;
3845 /**********************************************************************
3846 * GetSubMenu (USER32.@)
3848 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3850 MENUITEM * lpmi;
3852 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3853 if (!(lpmi->fType & MF_POPUP)) return 0;
3854 return lpmi->hSubMenu;
3858 /**********************************************************************
3859 * DrawMenuBar (USER32.@)
3861 BOOL WINAPI DrawMenuBar( HWND hWnd )
3863 LPPOPUPMENU lppop;
3864 HMENU hMenu = GetMenu(hWnd);
3866 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3867 return FALSE;
3868 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3870 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3871 lppop->hwndOwner = hWnd;
3872 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3873 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3874 return TRUE;
3877 /***********************************************************************
3878 * DrawMenuBarTemp (USER32.@)
3880 * UNDOCUMENTED !!
3882 * called by W98SE desk.cpl Control Panel Applet
3884 * Not 100% sure about the param names, but close.
3886 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3888 LPPOPUPMENU lppop;
3889 UINT i,retvalue;
3890 HFONT hfontOld = 0;
3891 BOOL flat_menu = FALSE;
3893 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
3895 if (!hMenu)
3896 hMenu = GetMenu(hwnd);
3898 if (!hFont)
3899 hFont = hMenuFont;
3901 lppop = MENU_GetMenu( hMenu );
3902 if (lppop == NULL || lprect == NULL)
3904 retvalue = GetSystemMetrics(SM_CYMENU);
3905 goto END;
3908 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3910 hfontOld = SelectObject( hDC, hFont);
3912 if (lppop->Height == 0)
3913 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3915 lprect->bottom = lprect->top + lppop->Height;
3917 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
3919 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3920 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3921 LineTo( hDC, lprect->right, lprect->bottom );
3923 if (lppop->nItems == 0)
3925 retvalue = GetSystemMetrics(SM_CYMENU);
3926 goto END;
3929 for (i = 0; i < lppop->nItems; i++)
3931 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3932 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3934 retvalue = lppop->Height;
3936 END:
3937 if (hfontOld) SelectObject (hDC, hfontOld);
3938 return retvalue;
3941 /***********************************************************************
3942 * EndMenu (USER.187)
3943 * EndMenu (USER32.@)
3945 void WINAPI EndMenu(void)
3947 /* if we are in the menu code, and it is active */
3948 if (!fEndMenu && top_popup)
3950 /* terminate the menu handling code */
3951 fEndMenu = TRUE;
3953 /* needs to be posted to wakeup the internal menu handler */
3954 /* which will now terminate the menu, in the event that */
3955 /* the main window was minimized, or lost focus, so we */
3956 /* don't end up with an orphaned menu */
3957 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3962 /***********************************************************************
3963 * LookupMenuHandle (USER.217)
3965 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3967 HMENU hmenu32 = HMENU_32(hmenu);
3968 UINT id32 = id;
3969 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3970 else return HMENU_16(hmenu32);
3974 /**********************************************************************
3975 * LoadMenu (USER.150)
3977 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3979 HRSRC16 hRsrc;
3980 HGLOBAL16 handle;
3981 HMENU16 hMenu;
3983 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3984 if (!name) return 0;
3986 instance = GetExePtr( instance );
3987 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3988 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3989 hMenu = LoadMenuIndirect16(LockResource16(handle));
3990 FreeResource16( handle );
3991 return hMenu;
3995 /*****************************************************************
3996 * LoadMenuA (USER32.@)
3998 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4000 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4001 if (!hrsrc) return 0;
4002 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4006 /*****************************************************************
4007 * LoadMenuW (USER32.@)
4009 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4011 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4012 if (!hrsrc) return 0;
4013 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4017 /**********************************************************************
4018 * LoadMenuIndirect (USER.220)
4020 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4022 HMENU hMenu;
4023 WORD version, offset;
4024 LPCSTR p = (LPCSTR)template;
4026 TRACE("(%p)\n", template );
4027 version = GET_WORD(p);
4028 p += sizeof(WORD);
4029 if (version)
4031 WARN("version must be 0 for Win16\n" );
4032 return 0;
4034 offset = GET_WORD(p);
4035 p += sizeof(WORD) + offset;
4036 if (!(hMenu = CreateMenu())) return 0;
4037 if (!MENU_ParseResource( p, hMenu, FALSE ))
4039 DestroyMenu( hMenu );
4040 return 0;
4042 return HMENU_16(hMenu);
4046 /**********************************************************************
4047 * LoadMenuIndirectW (USER32.@)
4049 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4051 HMENU hMenu;
4052 WORD version, offset;
4053 LPCSTR p = (LPCSTR)template;
4055 version = GET_WORD(p);
4056 p += sizeof(WORD);
4057 TRACE("%p, ver %d\n", template, version );
4058 switch (version)
4060 case 0: /* standard format is version of 0 */
4061 offset = GET_WORD(p);
4062 p += sizeof(WORD) + offset;
4063 if (!(hMenu = CreateMenu())) return 0;
4064 if (!MENU_ParseResource( p, hMenu, TRUE ))
4066 DestroyMenu( hMenu );
4067 return 0;
4069 return hMenu;
4070 case 1: /* extended format is version of 1 */
4071 offset = GET_WORD(p);
4072 p += sizeof(WORD) + offset;
4073 if (!(hMenu = CreateMenu())) return 0;
4074 if (!MENUEX_ParseResource( p, hMenu))
4076 DestroyMenu( hMenu );
4077 return 0;
4079 return hMenu;
4080 default:
4081 ERR("version %d not supported.\n", version);
4082 return 0;
4087 /**********************************************************************
4088 * LoadMenuIndirectA (USER32.@)
4090 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4092 return LoadMenuIndirectW( template );
4096 /**********************************************************************
4097 * IsMenu (USER32.@)
4099 BOOL WINAPI IsMenu(HMENU hmenu)
4101 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4102 return menu != NULL;
4105 /**********************************************************************
4106 * GetMenuItemInfo_common
4109 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4110 LPMENUITEMINFOW lpmii, BOOL unicode)
4112 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4114 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4116 if (!menu)
4117 return FALSE;
4119 if (lpmii->fMask & MIIM_TYPE) {
4120 lpmii->fType = menu->fType;
4121 switch (MENU_ITEM_TYPE(menu->fType)) {
4122 case MF_STRING:
4123 break; /* will be done below */
4124 case MF_OWNERDRAW:
4125 case MF_BITMAP:
4126 lpmii->dwTypeData = menu->text;
4127 /* fall through */
4128 default:
4129 lpmii->cch = 0;
4133 /* copy the text string */
4134 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4135 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4137 int len;
4138 if (unicode)
4140 len = strlenW(menu->text);
4141 if(lpmii->dwTypeData && lpmii->cch)
4142 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4144 else
4146 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4147 if(lpmii->dwTypeData && lpmii->cch)
4148 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4149 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4150 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4152 /* if we've copied a substring we return its length */
4153 if(lpmii->dwTypeData && lpmii->cch)
4155 if (lpmii->cch <= len) lpmii->cch--;
4157 else /* return length of string */
4158 lpmii->cch = len;
4161 if (lpmii->fMask & MIIM_FTYPE)
4162 lpmii->fType = menu->fType;
4164 if (lpmii->fMask & MIIM_BITMAP)
4165 lpmii->hbmpItem = menu->hbmpItem;
4167 if (lpmii->fMask & MIIM_STATE)
4168 lpmii->fState = menu->fState;
4170 if (lpmii->fMask & MIIM_ID)
4171 lpmii->wID = menu->wID;
4173 if (lpmii->fMask & MIIM_SUBMENU)
4174 lpmii->hSubMenu = menu->hSubMenu;
4176 if (lpmii->fMask & MIIM_CHECKMARKS) {
4177 lpmii->hbmpChecked = menu->hCheckBit;
4178 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4180 if (lpmii->fMask & MIIM_DATA)
4181 lpmii->dwItemData = menu->dwItemData;
4183 return TRUE;
4186 /**********************************************************************
4187 * GetMenuItemInfoA (USER32.@)
4189 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4190 LPMENUITEMINFOA lpmii)
4192 return GetMenuItemInfo_common (hmenu, item, bypos,
4193 (LPMENUITEMINFOW)lpmii, FALSE);
4196 /**********************************************************************
4197 * GetMenuItemInfoW (USER32.@)
4199 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4200 LPMENUITEMINFOW lpmii)
4202 return GetMenuItemInfo_common (hmenu, item, bypos,
4203 lpmii, TRUE);
4207 /* set a menu item text from a ASCII or Unicode string */
4208 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4210 if (!text)
4212 menu->text = NULL;
4213 menu->fType |= MF_SEPARATOR;
4215 else if (unicode)
4217 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4218 strcpyW( menu->text, text );
4220 else
4222 LPCSTR str = (LPCSTR)text;
4223 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4224 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4225 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4230 /**********************************************************************
4231 * SetMenuItemInfo_common
4234 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4235 const MENUITEMINFOW *lpmii,
4236 BOOL unicode)
4238 if (!menu) return FALSE;
4240 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4242 if (lpmii->fMask & MIIM_TYPE ) {
4243 /* Get rid of old string. */
4244 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4245 HeapFree(GetProcessHeap(), 0, menu->text);
4246 menu->text = NULL;
4249 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4250 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4251 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4253 menu->text = lpmii->dwTypeData;
4255 if (IS_STRING_ITEM(menu->fType))
4256 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4259 if (lpmii->fMask & MIIM_FTYPE ) {
4260 /* free the string when the type is changing */
4261 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4262 HeapFree(GetProcessHeap(), 0, menu->text);
4263 menu->text = NULL;
4265 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4266 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4267 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4268 menu->fType |= MF_SEPARATOR;
4271 if (lpmii->fMask & MIIM_STRING ) {
4272 if (IS_STRING_ITEM(menu->fType)) {
4273 /* free the string when used */
4274 HeapFree(GetProcessHeap(), 0, menu->text);
4275 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4279 if (lpmii->fMask & MIIM_STATE)
4281 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4282 menu->fState = lpmii->fState;
4285 if (lpmii->fMask & MIIM_ID)
4286 menu->wID = lpmii->wID;
4288 if (lpmii->fMask & MIIM_SUBMENU) {
4289 menu->hSubMenu = lpmii->hSubMenu;
4290 if (menu->hSubMenu) {
4291 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4292 if (subMenu) {
4293 subMenu->wFlags |= MF_POPUP;
4294 menu->fType |= MF_POPUP;
4296 else
4297 /* FIXME: Return an error ? */
4298 menu->fType &= ~MF_POPUP;
4300 else
4301 menu->fType &= ~MF_POPUP;
4304 if (lpmii->fMask & MIIM_CHECKMARKS)
4306 if (lpmii->fType & MFT_RADIOCHECK)
4307 menu->fType |= MFT_RADIOCHECK;
4309 menu->hCheckBit = lpmii->hbmpChecked;
4310 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4312 if (lpmii->fMask & MIIM_DATA)
4313 menu->dwItemData = lpmii->dwItemData;
4315 if (lpmii->fMask & MIIM_BITMAP)
4316 menu->hbmpItem = lpmii->hbmpItem;
4318 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4319 return TRUE;
4322 /**********************************************************************
4323 * SetMenuItemInfoA (USER32.@)
4325 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4326 const MENUITEMINFOA *lpmii)
4328 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4329 (const MENUITEMINFOW *)lpmii, FALSE);
4332 /**********************************************************************
4333 * SetMenuItemInfoW (USER32.@)
4335 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4336 const MENUITEMINFOW *lpmii)
4338 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4339 lpmii, TRUE);
4342 /**********************************************************************
4343 * SetMenuDefaultItem (USER32.@)
4346 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4348 UINT i;
4349 POPUPMENU *menu;
4350 MENUITEM *item;
4352 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4354 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4356 /* reset all default-item flags */
4357 item = menu->items;
4358 for (i = 0; i < menu->nItems; i++, item++)
4360 item->fState &= ~MFS_DEFAULT;
4363 /* no default item */
4364 if ( -1 == uItem)
4366 return TRUE;
4369 item = menu->items;
4370 if ( bypos )
4372 if ( uItem >= menu->nItems ) return FALSE;
4373 item[uItem].fState |= MFS_DEFAULT;
4374 return TRUE;
4376 else
4378 for (i = 0; i < menu->nItems; i++, item++)
4380 if (item->wID == uItem)
4382 item->fState |= MFS_DEFAULT;
4383 return TRUE;
4388 return FALSE;
4391 /**********************************************************************
4392 * GetMenuDefaultItem (USER32.@)
4394 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4396 POPUPMENU *menu;
4397 MENUITEM * item;
4398 UINT i = 0;
4400 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4402 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4404 /* find default item */
4405 item = menu->items;
4407 /* empty menu */
4408 if (! item) return -1;
4410 while ( !( item->fState & MFS_DEFAULT ) )
4412 i++; item++;
4413 if (i >= menu->nItems ) return -1;
4416 /* default: don't return disabled items */
4417 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4419 /* search rekursiv when needed */
4420 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4422 UINT ret;
4423 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4424 if ( -1 != ret ) return ret;
4426 /* when item not found in submenu, return the popup item */
4428 return ( bypos ) ? i : item->wID;
4433 /**********************************************************************
4434 * InsertMenuItemA (USER32.@)
4436 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4437 const MENUITEMINFOA *lpmii)
4439 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4440 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4444 /**********************************************************************
4445 * InsertMenuItemW (USER32.@)
4447 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4448 const MENUITEMINFOW *lpmii)
4450 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4451 return SetMenuItemInfo_common(item, lpmii, TRUE);
4454 /**********************************************************************
4455 * CheckMenuRadioItem (USER32.@)
4458 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4459 UINT first, UINT last, UINT check,
4460 UINT bypos)
4462 MENUITEM *mifirst, *milast, *micheck;
4463 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4465 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4467 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4468 milast = MENU_FindItem (&mlast, &last, bypos);
4469 micheck = MENU_FindItem (&mcheck, &check, bypos);
4471 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4472 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4473 micheck > milast || micheck < mifirst)
4474 return FALSE;
4476 while (mifirst <= milast)
4478 if (mifirst == micheck)
4480 mifirst->fType |= MFT_RADIOCHECK;
4481 mifirst->fState |= MFS_CHECKED;
4482 } else {
4483 mifirst->fType &= ~MFT_RADIOCHECK;
4484 mifirst->fState &= ~MFS_CHECKED;
4486 mifirst++;
4489 return TRUE;
4493 /**********************************************************************
4494 * GetMenuItemRect (USER32.@)
4496 * ATTENTION: Here, the returned values in rect are the screen
4497 * coordinates of the item just like if the menu was
4498 * always on the upper left side of the application.
4501 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4502 LPRECT rect)
4504 POPUPMENU *itemMenu;
4505 MENUITEM *item;
4506 HWND referenceHwnd;
4508 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4510 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4511 referenceHwnd = hwnd;
4513 if(!hwnd)
4515 itemMenu = MENU_GetMenu(hMenu);
4516 if (itemMenu == NULL)
4517 return FALSE;
4519 if(itemMenu->hWnd == 0)
4520 return FALSE;
4521 referenceHwnd = itemMenu->hWnd;
4524 if ((rect == NULL) || (item == NULL))
4525 return FALSE;
4527 *rect = item->rect;
4529 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4531 return TRUE;
4535 /**********************************************************************
4536 * SetMenuInfo (USER32.@)
4538 * FIXME
4539 * MIM_APPLYTOSUBMENUS
4540 * actually use the items to draw the menu
4542 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4544 POPUPMENU *menu;
4546 TRACE("(%p %p)\n", hMenu, lpmi);
4548 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4551 if (lpmi->fMask & MIM_BACKGROUND)
4552 menu->hbrBack = lpmi->hbrBack;
4554 if (lpmi->fMask & MIM_HELPID)
4555 menu->dwContextHelpID = lpmi->dwContextHelpID;
4557 if (lpmi->fMask & MIM_MAXHEIGHT)
4558 menu->cyMax = lpmi->cyMax;
4560 if (lpmi->fMask & MIM_MENUDATA)
4561 menu->dwMenuData = lpmi->dwMenuData;
4563 if (lpmi->fMask & MIM_STYLE)
4565 menu->dwStyle = lpmi->dwStyle;
4566 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4567 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4568 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4569 if (menu->dwStyle & MNS_NOCHECK) FIXME("MNS_NOCHECK unimplemented\n");
4570 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4573 return TRUE;
4575 return FALSE;
4578 /**********************************************************************
4579 * GetMenuInfo (USER32.@)
4581 * NOTES
4582 * win98/NT5.0
4585 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4586 { POPUPMENU *menu;
4588 TRACE("(%p %p)\n", hMenu, lpmi);
4590 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4593 if (lpmi->fMask & MIM_BACKGROUND)
4594 lpmi->hbrBack = menu->hbrBack;
4596 if (lpmi->fMask & MIM_HELPID)
4597 lpmi->dwContextHelpID = menu->dwContextHelpID;
4599 if (lpmi->fMask & MIM_MAXHEIGHT)
4600 lpmi->cyMax = menu->cyMax;
4602 if (lpmi->fMask & MIM_MENUDATA)
4603 lpmi->dwMenuData = menu->dwMenuData;
4605 if (lpmi->fMask & MIM_STYLE)
4606 lpmi->dwStyle = menu->dwStyle;
4608 return TRUE;
4610 return FALSE;
4614 /**********************************************************************
4615 * SetMenuContextHelpId (USER32.@)
4617 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4619 LPPOPUPMENU menu;
4621 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4623 if ((menu = MENU_GetMenu(hMenu)))
4625 menu->dwContextHelpID = dwContextHelpID;
4626 return TRUE;
4628 return FALSE;
4632 /**********************************************************************
4633 * GetMenuContextHelpId (USER32.@)
4635 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4637 LPPOPUPMENU menu;
4639 TRACE("(%p)\n", hMenu);
4641 if ((menu = MENU_GetMenu(hMenu)))
4643 return menu->dwContextHelpID;
4645 return 0;
4648 /**********************************************************************
4649 * MenuItemFromPoint (USER32.@)
4651 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4653 POPUPMENU *menu = MENU_GetMenu(hMenu);
4654 UINT pos;
4656 /*FIXME: Do we have to handle hWnd here? */
4657 if (!menu) return -1;
4658 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4659 return pos;
4663 /**********************************************************************
4664 * translate_accelerator
4666 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4667 BYTE fVirt, WORD key, WORD cmd )
4669 INT mask = 0;
4670 UINT mesg = 0;
4672 if (wParam != key) return FALSE;
4674 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4675 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4676 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4678 if (message == WM_CHAR || message == WM_SYSCHAR)
4680 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4682 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4683 goto found;
4686 else
4688 if(fVirt & FVIRTKEY)
4690 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4691 wParam, 0xff & HIWORD(lParam));
4693 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4694 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4696 else
4698 if (!(lParam & 0x01000000)) /* no special_key */
4700 if ((fVirt & FALT) && (lParam & 0x20000000))
4701 { /* ^^ ALT pressed */
4702 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4703 goto found;
4708 return FALSE;
4710 found:
4711 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4712 mesg = 1;
4713 else
4715 HMENU hMenu, hSubMenu, hSysMenu;
4716 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4718 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4719 hSysMenu = get_win_sys_menu( hWnd );
4721 /* find menu item and ask application to initialize it */
4722 /* 1. in the system menu */
4723 hSubMenu = hSysMenu;
4724 nPos = cmd;
4725 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4727 if (GetCapture())
4728 mesg = 2;
4729 if (!IsWindowEnabled(hWnd))
4730 mesg = 3;
4731 else
4733 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4734 if(hSubMenu != hSysMenu)
4736 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4737 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4738 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4740 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4743 else /* 2. in the window's menu */
4745 hSubMenu = hMenu;
4746 nPos = cmd;
4747 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4749 if (GetCapture())
4750 mesg = 2;
4751 if (!IsWindowEnabled(hWnd))
4752 mesg = 3;
4753 else
4755 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4756 if(hSubMenu != hMenu)
4758 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4759 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4760 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4762 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4767 if (mesg == 0)
4769 if (uSysStat != (UINT)-1)
4771 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4772 mesg=4;
4773 else
4774 mesg=WM_SYSCOMMAND;
4776 else
4778 if (uStat != (UINT)-1)
4780 if (IsIconic(hWnd))
4781 mesg=5;
4782 else
4784 if (uStat & (MF_DISABLED|MF_GRAYED))
4785 mesg=6;
4786 else
4787 mesg=WM_COMMAND;
4790 else
4791 mesg=WM_COMMAND;
4796 if( mesg==WM_COMMAND )
4798 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4799 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4801 else if( mesg==WM_SYSCOMMAND )
4803 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4804 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4806 else
4808 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4809 * #0: unknown (please report!)
4810 * #1: for WM_KEYUP,WM_SYSKEYUP
4811 * #2: mouse is captured
4812 * #3: window is disabled
4813 * #4: it's a disabled system menu option
4814 * #5: it's a menu option, but window is iconic
4815 * #6: it's a menu option, but disabled
4817 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4818 if(mesg==0)
4819 ERR_(accel)(" unknown reason - please report!\n");
4821 return TRUE;
4824 /**********************************************************************
4825 * TranslateAccelerator (USER32.@)
4826 * TranslateAcceleratorA (USER32.@)
4828 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
4830 /* YES, Accel16! */
4831 LPACCEL16 lpAccelTbl;
4832 int i;
4833 WPARAM wParam;
4835 if (!hWnd || !msg) return 0;
4837 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4839 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4840 return 0;
4843 wParam = msg->wParam;
4845 switch (msg->message)
4847 case WM_KEYDOWN:
4848 case WM_SYSKEYDOWN:
4849 break;
4851 case WM_CHAR:
4852 case WM_SYSCHAR:
4854 char ch = LOWORD(wParam);
4855 WCHAR wch;
4856 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
4857 wParam = MAKEWPARAM(wch, HIWORD(wParam));
4859 break;
4861 default:
4862 return 0;
4865 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4866 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4867 i = 0;
4870 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
4871 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4872 return 1;
4873 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4875 return 0;
4878 /**********************************************************************
4879 * TranslateAcceleratorW (USER32.@)
4881 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4883 /* YES, Accel16! */
4884 LPACCEL16 lpAccelTbl;
4885 int i;
4887 if (!hWnd || !msg) return 0;
4889 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4891 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4892 return 0;
4895 switch (msg->message)
4897 case WM_KEYDOWN:
4898 case WM_SYSKEYDOWN:
4899 case WM_CHAR:
4900 case WM_SYSCHAR:
4901 break;
4903 default:
4904 return 0;
4907 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4908 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4909 i = 0;
4912 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4913 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4914 return 1;
4915 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4917 return 0;