Cast id to unsigned in GetDlgItem16.
[wine/wine-kai.git] / controls / menu.c
blob36ffec8db229b4490ec40539c222ffae194d97d0
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 */
9 /*
10 * Note: the style MF_MOUSESELECT is used to mark popup items that
11 * have been selected, i.e. their popup menu is currently displayed.
12 * This is probably not the meaning this style has in MS-Windows.
15 #include "config.h"
16 #include "wine/port.h"
18 #include <assert.h>
19 #include <ctype.h>
20 #include <stdlib.h>
21 #include <string.h>
23 #include "windef.h"
24 #include "winnls.h"
25 #include "wingdi.h"
26 #include "wine/winbase16.h"
27 #include "wine/winuser16.h"
28 #include "wine/unicode.h"
29 #include "win.h"
30 #include "controls.h"
31 #include "nonclient.h"
32 #include "user.h"
33 #include "message.h"
35 #include "debugtools.h"
37 DEFAULT_DEBUG_CHANNEL(menu);
38 DECLARE_DEBUG_CHANNEL(accel);
40 /* internal popup menu window messages */
42 #define MM_SETMENUHANDLE (WM_USER + 0)
43 #define MM_GETMENUHANDLE (WM_USER + 1)
45 /* Menu item structure */
46 typedef struct {
47 /* ----------- MENUITEMINFO Stuff ----------- */
48 UINT fType; /* Item type. */
49 UINT fState; /* Item state. */
50 UINT wID; /* Item id. */
51 HMENU hSubMenu; /* Pop-up menu. */
52 HBITMAP hCheckBit; /* Bitmap when checked. */
53 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
54 LPWSTR text; /* Item text or bitmap handle. */
55 DWORD dwItemData; /* Application defined. */
56 DWORD dwTypeData; /* depends on fMask */
57 HBITMAP hbmpItem; /* bitmap in win98 style menus */
58 /* ----------- Wine stuff ----------- */
59 RECT rect; /* Item area (relative to menu window) */
60 UINT xTab; /* X position of text after Tab */
61 } MENUITEM;
63 /* Popup menu structure */
64 typedef struct {
65 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
66 WORD wMagic; /* Magic number */
67 WORD Width; /* Width of the whole menu */
68 WORD Height; /* Height of the whole menu */
69 UINT nItems; /* Number of items in the menu */
70 HWND hWnd; /* Window containing the menu */
71 MENUITEM *items; /* Array of menu items */
72 UINT FocusedItem; /* Currently focused item */
73 HWND hwndOwner; /* window receiving the messages for ownerdraw */
74 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
75 /* ------------ MENUINFO members ------ */
76 DWORD dwStyle; /* Extended mennu style */
77 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
78 HBRUSH hbrBack; /* brush for menu background */
79 DWORD dwContextHelpID;
80 DWORD dwMenuData; /* application defined value */
81 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
82 } POPUPMENU, *LPPOPUPMENU;
84 /* internal flags for menu tracking */
86 #define TF_ENDMENU 0x0001
87 #define TF_SUSPENDPOPUP 0x0002
88 #define TF_SKIPREMOVE 0x0004
90 typedef struct
92 UINT trackFlags;
93 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
94 HMENU hTopMenu; /* initial menu */
95 HWND hOwnerWnd; /* where notifications are sent */
96 POINT pt;
97 } MTRACKER;
99 #define MENU_MAGIC 0x554d /* 'MU' */
101 #define ITEM_PREV -1
102 #define ITEM_NEXT 1
104 /* Internal MENU_TrackMenu() flags */
105 #define TPM_INTERNAL 0xF0000000
106 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
107 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
108 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
110 /* popup menu shade thickness */
111 #define POPUP_XSHADE 4
112 #define POPUP_YSHADE 4
114 /* Space between 2 menu bar items */
115 #define MENU_BAR_ITEMS_SPACE 12
117 /* Minimum width of a tab character */
118 #define MENU_TAB_SPACE 8
120 /* Height of a separator item */
121 #define SEPARATOR_HEIGHT 5
123 /* (other menu->FocusedItem values give the position of the focused item) */
124 #define NO_SELECTED_ITEM 0xffff
126 #define MENU_ITEM_TYPE(flags) \
127 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
129 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
130 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
131 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
133 #define IS_SYSTEM_MENU(menu) \
134 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
136 #define IS_SYSTEM_POPUP(menu) \
137 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
139 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
140 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
141 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
142 MF_POPUP | MF_SYSMENU | MF_HELP)
143 #define STATE_MASK (~TYPE_MASK)
145 /* Dimension of the menu bitmaps */
146 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
148 static HBITMAP hStdMnArrow = 0;
150 /* Minimze/restore/close buttons to be inserted in menubar */
151 static HBITMAP hBmpMinimize = 0;
152 static HBITMAP hBmpMinimizeD = 0;
153 static HBITMAP hBmpMaximize = 0;
154 static HBITMAP hBmpMaximizeD = 0;
155 static HBITMAP hBmpClose = 0;
156 static HBITMAP hBmpCloseD = 0;
159 static HBRUSH hShadeBrush = 0;
160 static HFONT hMenuFont = 0;
161 static HFONT hMenuFontBold = 0;
163 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
165 /* Use global popup window because there's no way 2 menus can
166 * be tracked at the same time. */
167 static HWND top_popup;
169 /* Flag set by EndMenu() to force an exit from menu tracking */
170 static BOOL fEndMenu = FALSE;
172 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
175 /*********************************************************************
176 * menu class descriptor
178 const struct builtin_class_descr MENU_builtin_class =
180 POPUPMENU_CLASS_ATOM, /* name */
181 CS_GLOBALCLASS | CS_SAVEBITS, /* style */
182 NULL, /* procA (winproc is Unicode only) */
183 PopupMenuWndProc, /* procW */
184 sizeof(HMENU), /* extra */
185 IDC_ARROWA, /* cursor */
186 COLOR_MENU+1 /* brush */
190 /***********************************************************************
191 * debug_print_menuitem
193 * Print a menuitem in readable form.
196 #define debug_print_menuitem(pre, mp, post) \
197 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
199 #define MENUOUT(text) \
200 DPRINTF("%s%s", (count++ ? "," : ""), (text))
202 #define MENUFLAG(bit,text) \
203 do { \
204 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
205 } while (0)
207 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
208 const char *postfix)
210 TRACE("%s ", prefix);
211 if (mp) {
212 UINT flags = mp->fType;
213 int typ = MENU_ITEM_TYPE(flags);
214 DPRINTF( "{ ID=0x%x", mp->wID);
215 if (flags & MF_POPUP)
216 DPRINTF( ", Sub=0x%x", mp->hSubMenu);
217 if (flags) {
218 int count = 0;
219 DPRINTF( ", Typ=");
220 if (typ == MFT_STRING)
221 /* Nothing */ ;
222 else if (typ == MFT_SEPARATOR)
223 MENUOUT("sep");
224 else if (typ == MFT_OWNERDRAW)
225 MENUOUT("own");
226 else if (typ == MFT_BITMAP)
227 MENUOUT("bit");
228 else
229 MENUOUT("???");
230 flags -= typ;
232 MENUFLAG(MF_POPUP, "pop");
233 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
234 MENUFLAG(MFT_MENUBREAK, "brk");
235 MENUFLAG(MFT_RADIOCHECK, "radio");
236 MENUFLAG(MFT_RIGHTORDER, "rorder");
237 MENUFLAG(MF_SYSMENU, "sys");
238 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
240 if (flags)
241 DPRINTF( "+0x%x", flags);
243 flags = mp->fState;
244 if (flags) {
245 int count = 0;
246 DPRINTF( ", State=");
247 MENUFLAG(MFS_GRAYED, "grey");
248 MENUFLAG(MFS_DEFAULT, "default");
249 MENUFLAG(MFS_DISABLED, "dis");
250 MENUFLAG(MFS_CHECKED, "check");
251 MENUFLAG(MFS_HILITE, "hi");
252 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
253 MENUFLAG(MF_MOUSESELECT, "mouse");
254 if (flags)
255 DPRINTF( "+0x%x", flags);
257 if (mp->hCheckBit)
258 DPRINTF( ", Chk=0x%x", mp->hCheckBit);
259 if (mp->hUnCheckBit)
260 DPRINTF( ", Unc=0x%x", mp->hUnCheckBit);
262 if (typ == MFT_STRING) {
263 if (mp->text)
264 DPRINTF( ", Text=%s", debugstr_w(mp->text));
265 else
266 DPRINTF( ", Text=Null");
267 } else if (mp->text == NULL)
268 /* Nothing */ ;
269 else
270 DPRINTF( ", Text=%p", mp->text);
271 if (mp->dwItemData)
272 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
273 DPRINTF( " }");
274 } else {
275 DPRINTF( "NULL");
278 DPRINTF(" %s\n", postfix);
281 #undef MENUOUT
282 #undef MENUFLAG
285 /***********************************************************************
286 * MENU_GetMenu
288 * Validate the given menu handle and returns the menu structure pointer.
290 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
292 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
293 if (!menu || menu->wMagic != MENU_MAGIC)
295 WARN("invalid menu handle=%x, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
296 menu = NULL;
298 return menu;
301 /***********************************************************************
302 * get_win_sys_menu
304 * Get the system menu of a window
306 static HMENU get_win_sys_menu( HWND hwnd )
308 HMENU ret = 0;
309 WND *win = WIN_FindWndPtr( hwnd );
310 if (win)
312 ret = win->hSysMenu;
313 WIN_ReleaseWndPtr( win );
315 return ret;
318 /***********************************************************************
319 * MENU_CopySysPopup
321 * Return the default system menu.
323 static HMENU MENU_CopySysPopup(void)
325 HMENU hMenu = LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
327 if( hMenu ) {
328 POPUPMENU* menu = MENU_GetMenu(hMenu);
329 menu->wFlags |= MF_SYSMENU | MF_POPUP;
330 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
332 else
333 ERR("Unable to load default system menu\n" );
335 TRACE("returning %x.\n", hMenu );
337 return hMenu;
341 /**********************************************************************
342 * MENU_GetSysMenu
344 * Create a copy of the system menu. System menu in Windows is
345 * a special menu bar with the single entry - system menu popup.
346 * This popup is presented to the outside world as a "system menu".
347 * However, the real system menu handle is sometimes seen in the
348 * WM_MENUSELECT parameters (and Word 6 likes it this way).
350 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
352 HMENU hMenu;
354 if ((hMenu = CreateMenu()))
356 POPUPMENU *menu = MENU_GetMenu(hMenu);
357 menu->wFlags = MF_SYSMENU;
358 menu->hWnd = WIN_GetFullHandle( hWnd );
360 if (hPopupMenu == (HMENU)(-1))
361 hPopupMenu = MENU_CopySysPopup();
362 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
364 if (hPopupMenu)
366 InsertMenuA( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION, hPopupMenu, NULL );
368 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
369 menu->items[0].fState = 0;
370 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
372 TRACE("GetSysMenu hMenu=%04x (%04x)\n", hMenu, hPopupMenu );
373 return hMenu;
375 DestroyMenu( hMenu );
377 ERR("failed to load system menu!\n");
378 return 0;
382 /***********************************************************************
383 * MENU_Init
385 * Menus initialisation.
387 BOOL MENU_Init()
389 HBITMAP hBitmap;
390 NONCLIENTMETRICSA ncm;
392 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
393 0x55, 0, 0xAA, 0,
394 0x55, 0, 0xAA, 0,
395 0x55, 0, 0xAA, 0 };
397 /* Load menu bitmaps */
398 hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
399 /* Load system buttons bitmaps */
400 hBmpMinimize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCE));
401 hBmpMinimizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCED));
402 hBmpMaximize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORE));
403 hBmpMaximizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORED));
404 hBmpClose = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSE));
405 hBmpCloseD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSED));
407 if (hStdMnArrow)
409 BITMAP bm;
410 GetObjectA( hStdMnArrow, sizeof(bm), &bm );
411 arrow_bitmap_width = bm.bmWidth;
412 arrow_bitmap_height = bm.bmHeight;
413 } else
414 return FALSE;
416 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
417 return FALSE;
419 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
420 return FALSE;
422 DeleteObject( hBitmap );
423 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
424 return FALSE;
426 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
427 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0)))
428 return FALSE;
430 if (!(hMenuFont = CreateFontIndirectA( &ncm.lfMenuFont )))
431 return FALSE;
433 ncm.lfMenuFont.lfWeight += 300;
434 if ( ncm.lfMenuFont.lfWeight > 1000)
435 ncm.lfMenuFont.lfWeight = 1000;
437 if (!(hMenuFontBold = CreateFontIndirectA( &ncm.lfMenuFont )))
438 return FALSE;
440 return TRUE;
443 /***********************************************************************
444 * MENU_InitSysMenuPopup
446 * Grey the appropriate items in System menu.
448 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
450 BOOL gray;
452 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
453 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
454 gray = ((style & WS_MAXIMIZE) != 0);
455 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
456 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
457 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
458 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
459 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
460 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
461 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
462 gray = (clsStyle & CS_NOCLOSE) != 0;
464 /* The menu item must keep its state if it's disabled */
465 if(gray)
466 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
470 /******************************************************************************
472 * UINT MENU_GetStartOfNextColumn(
473 * HMENU hMenu )
475 *****************************************************************************/
477 static UINT MENU_GetStartOfNextColumn(
478 HMENU hMenu )
480 POPUPMENU *menu = MENU_GetMenu(hMenu);
481 UINT i;
483 if(!menu)
484 return NO_SELECTED_ITEM;
486 i = menu->FocusedItem + 1;
487 if( i == NO_SELECTED_ITEM )
488 return i;
490 for( ; i < menu->nItems; ++i ) {
491 if (menu->items[i].fType & MF_MENUBARBREAK)
492 return i;
495 return NO_SELECTED_ITEM;
499 /******************************************************************************
501 * UINT MENU_GetStartOfPrevColumn(
502 * HMENU hMenu )
504 *****************************************************************************/
506 static UINT MENU_GetStartOfPrevColumn(
507 HMENU hMenu )
509 POPUPMENU *menu = MENU_GetMenu(hMenu);
510 UINT i;
512 if( !menu )
513 return NO_SELECTED_ITEM;
515 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
516 return NO_SELECTED_ITEM;
518 /* Find the start of the column */
520 for(i = menu->FocusedItem; i != 0 &&
521 !(menu->items[i].fType & MF_MENUBARBREAK);
522 --i); /* empty */
524 if(i == 0)
525 return NO_SELECTED_ITEM;
527 for(--i; i != 0; --i) {
528 if (menu->items[i].fType & MF_MENUBARBREAK)
529 break;
532 TRACE("ret %d.\n", i );
534 return i;
539 /***********************************************************************
540 * MENU_FindItem
542 * Find a menu item. Return a pointer on the item, and modifies *hmenu
543 * in case the item was in a sub-menu.
545 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
547 POPUPMENU *menu;
548 UINT i;
550 if (((*hmenu)==0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
551 if (wFlags & MF_BYPOSITION)
553 if (*nPos >= menu->nItems) return NULL;
554 return &menu->items[*nPos];
556 else
558 MENUITEM *item = menu->items;
559 for (i = 0; i < menu->nItems; i++, item++)
561 if (item->wID == *nPos)
563 *nPos = i;
564 return item;
566 else if (item->fType & MF_POPUP)
568 HMENU hsubmenu = item->hSubMenu;
569 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
570 if (subitem)
572 *hmenu = hsubmenu;
573 return subitem;
578 return NULL;
581 /***********************************************************************
582 * MENU_FindSubMenu
584 * Find a Sub menu. Return the position of the submenu, and modifies
585 * *hmenu in case it is found in another sub-menu.
586 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
588 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
590 POPUPMENU *menu;
591 UINT i;
592 MENUITEM *item;
593 if (((*hmenu)==0xffff) ||
594 (!(menu = MENU_GetMenu(*hmenu))))
595 return NO_SELECTED_ITEM;
596 item = menu->items;
597 for (i = 0; i < menu->nItems; i++, item++) {
598 if(!(item->fType & MF_POPUP)) continue;
599 if (item->hSubMenu == hSubTarget) {
600 return i;
602 else {
603 HMENU hsubmenu = item->hSubMenu;
604 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
605 if (pos != NO_SELECTED_ITEM) {
606 *hmenu = hsubmenu;
607 return pos;
611 return NO_SELECTED_ITEM;
614 /***********************************************************************
615 * MENU_FreeItemData
617 static void MENU_FreeItemData( MENUITEM* item )
619 /* delete text */
620 if (IS_STRING_ITEM(item->fType) && item->text)
621 HeapFree( GetProcessHeap(), 0, item->text );
624 /***********************************************************************
625 * MENU_FindItemByCoords
627 * Find the item at the specified coordinates (screen coords). Does
628 * not work for child windows and therefore should not be called for
629 * an arbitrary system menu.
631 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
632 POINT pt, UINT *pos )
634 MENUITEM *item;
635 UINT i;
636 RECT wrect;
638 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
639 pt.x -= wrect.left;pt.y -= wrect.top;
640 item = menu->items;
641 for (i = 0; i < menu->nItems; i++, item++)
643 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
644 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
646 if (pos) *pos = i;
647 return item;
650 return NULL;
654 /***********************************************************************
655 * MENU_FindItemByKey
657 * Find the menu item selected by a key press.
658 * Return item id, -1 if none, -2 if we should close the menu.
660 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
661 UINT key, BOOL forceMenuChar )
663 TRACE("\tlooking for '%c' in [%04x]\n", (char)key, (UINT16)hmenu );
665 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
667 if (hmenu)
669 POPUPMENU *menu = MENU_GetMenu( hmenu );
670 MENUITEM *item = menu->items;
671 LONG menuchar;
673 if( !forceMenuChar )
675 UINT i;
677 key = toupper(key);
678 for (i = 0; i < menu->nItems; i++, item++)
680 if (item->text && (IS_STRING_ITEM(item->fType)))
682 WCHAR *p = item->text - 2;
685 p = strchrW (p + 2, '&');
687 while (p != NULL && p [1] == '&');
688 if (p && (toupper(p[1]) == key)) return i;
692 menuchar = SendMessageA( hwndOwner, WM_MENUCHAR,
693 MAKEWPARAM( key, menu->wFlags ), hmenu );
694 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
695 if (HIWORD(menuchar) == 1) return (UINT)(-2);
697 return (UINT)(-1);
699 /***********************************************************************
700 * MENU_LoadMagicItem
702 * Load the bitmap associated with the magic menu item and its style
705 static HBITMAP MENU_LoadMagicItem(UINT id, BOOL hilite, DWORD dwItemData)
708 * Magic menu item id's section
709 * These magic id's are used by windows to insert "standard" mdi
710 * buttons (minimize,restore,close) on menu. Under windows,
711 * these magic id's make sure the right things appear when those
712 * bitmap buttons are pressed/selected/released.
715 switch(id & 0xffff)
716 { case HBMMENU_SYSTEM:
717 return (dwItemData) ?
718 (HBITMAP)dwItemData :
719 (hilite ? hBmpMinimizeD : hBmpMinimize);
720 case HBMMENU_MBAR_RESTORE:
721 return (hilite ? hBmpMaximizeD: hBmpMaximize);
722 case HBMMENU_MBAR_MINIMIZE:
723 return (hilite ? hBmpMinimizeD : hBmpMinimize);
724 case HBMMENU_MBAR_CLOSE:
725 return (hilite ? hBmpCloseD : hBmpClose);
726 case HBMMENU_CALLBACK:
727 case HBMMENU_MBAR_CLOSE_D:
728 case HBMMENU_MBAR_MINIMIZE_D:
729 case HBMMENU_POPUP_CLOSE:
730 case HBMMENU_POPUP_RESTORE:
731 case HBMMENU_POPUP_MAXIMIZE:
732 case HBMMENU_POPUP_MINIMIZE:
733 default:
734 FIXME("Magic 0x%08x not implemented\n", id);
735 return 0;
740 /***********************************************************************
741 * MENU_CalcItemSize
743 * Calculate the size of the menu item and store it in lpitem->rect.
745 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
746 INT orgX, INT orgY, BOOL menuBar )
748 WCHAR *p;
749 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
751 TRACE("dc=0x%04x owner=0x%04x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
752 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
753 (menuBar ? " (MenuBar)" : ""));
755 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
757 if (lpitem->fType & MF_OWNERDRAW)
760 ** Experimentation under Windows reveals that an owner-drawn
761 ** menu is expected to return the size of the content part of
762 ** the menu item, not including the checkmark nor the submenu
763 ** arrow. Windows adds those values itself and returns the
764 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
766 MEASUREITEMSTRUCT mis;
767 mis.CtlType = ODT_MENU;
768 mis.CtlID = 0;
769 mis.itemID = lpitem->wID;
770 mis.itemData = (DWORD)lpitem->dwItemData;
771 mis.itemHeight = 0;
772 mis.itemWidth = 0;
773 SendMessageA( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
774 lpitem->rect.right += mis.itemWidth;
776 if (menuBar)
778 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
781 /* under at least win95 you seem to be given a standard
782 height for the menu and the height value is ignored */
784 if (TWEAK_WineLook == WIN31_LOOK)
785 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
786 else
787 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
789 else
790 lpitem->rect.bottom += mis.itemHeight;
792 TRACE("id=%04x size=%dx%d\n",
793 lpitem->wID, mis.itemWidth, mis.itemHeight);
794 /* Fall through to get check/arrow width calculation. */
797 if (lpitem->fType & MF_SEPARATOR)
799 lpitem->rect.bottom += SEPARATOR_HEIGHT;
800 return;
803 if (!menuBar)
805 lpitem->rect.right += 2 * check_bitmap_width;
806 if (lpitem->fType & MF_POPUP)
807 lpitem->rect.right += arrow_bitmap_width;
810 if (lpitem->fType & MF_OWNERDRAW)
811 return;
813 if (IS_BITMAP_ITEM(lpitem->fType))
815 BITMAP bm;
816 HBITMAP resBmp = 0;
818 /* Check if there is a magic menu item associated with this item */
819 if (IS_MAGIC_ITEM(lpitem->text))
821 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fType & MF_HILITE),
822 lpitem->dwItemData);
824 else
825 resBmp = (HBITMAP)lpitem->text;
827 if (GetObjectA(resBmp, sizeof(bm), &bm ))
829 lpitem->rect.right += bm.bmWidth;
830 lpitem->rect.bottom += bm.bmHeight;
831 if (TWEAK_WineLook == WIN98_LOOK) {
832 /* Leave space for the sunken border */
833 lpitem->rect.right += 2;
834 lpitem->rect.bottom += 2;
841 /* it must be a text item - unless it's the system menu */
842 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
843 { SIZE size;
845 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
847 lpitem->rect.right += size.cx;
848 if (TWEAK_WineLook == WIN31_LOOK)
849 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
850 else
851 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
852 lpitem->xTab = 0;
854 if (menuBar)
856 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
858 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
860 /* Item contains a tab (only meaningful in popup menus) */
861 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
862 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
863 lpitem->rect.right += MENU_TAB_SPACE;
865 else
867 if (strchrW( lpitem->text, '\b' ))
868 lpitem->rect.right += MENU_TAB_SPACE;
869 lpitem->xTab = lpitem->rect.right - check_bitmap_width
870 - arrow_bitmap_width;
873 TRACE("(%d,%d)-(%d,%d)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
877 /***********************************************************************
878 * MENU_PopupMenuCalcSize
880 * Calculate the size of a popup menu.
882 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
884 MENUITEM *lpitem;
885 HDC hdc;
886 int start, i;
887 int orgX, orgY, maxX, maxTab, maxTabWidth;
889 lppop->Width = lppop->Height = 0;
890 if (lppop->nItems == 0) return;
891 hdc = GetDC( 0 );
893 SelectObject( hdc, hMenuFont);
895 start = 0;
896 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
898 while (start < lppop->nItems)
900 lpitem = &lppop->items[start];
901 orgX = maxX;
902 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
904 maxTab = maxTabWidth = 0;
906 /* Parse items until column break or end of menu */
907 for (i = start; i < lppop->nItems; i++, lpitem++)
909 if ((i != start) &&
910 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
912 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
914 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
915 maxX = max( maxX, lpitem->rect.right );
916 orgY = lpitem->rect.bottom;
917 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
919 maxTab = max( maxTab, lpitem->xTab );
920 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
924 /* Finish the column (set all items to the largest width found) */
925 maxX = max( maxX, maxTab + maxTabWidth );
926 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
928 lpitem->rect.right = maxX;
929 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
930 lpitem->xTab = maxTab;
933 lppop->Height = max( lppop->Height, orgY );
936 lppop->Width = maxX;
938 /* space for 3d border */
939 if(TWEAK_WineLook > WIN31_LOOK)
941 lppop->Height += 2;
942 lppop->Width += 2;
945 ReleaseDC( 0, hdc );
949 /***********************************************************************
950 * MENU_MenuBarCalcSize
952 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
953 * height is off by 1 pixel which causes lengthy window relocations when
954 * active document window is maximized/restored.
956 * Calculate the size of the menu bar.
958 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
959 LPPOPUPMENU lppop, HWND hwndOwner )
961 MENUITEM *lpitem;
962 int start, i, orgX, orgY, maxY, helpPos;
964 if ((lprect == NULL) || (lppop == NULL)) return;
965 if (lppop->nItems == 0) return;
966 TRACE("left=%d top=%d right=%d bottom=%d\n",
967 lprect->left, lprect->top, lprect->right, lprect->bottom);
968 lppop->Width = lprect->right - lprect->left;
969 lppop->Height = 0;
970 maxY = lprect->top+1;
971 start = 0;
972 helpPos = -1;
973 while (start < lppop->nItems)
975 lpitem = &lppop->items[start];
976 orgX = lprect->left;
977 orgY = maxY;
979 /* Parse items until line break or end of menu */
980 for (i = start; i < lppop->nItems; i++, lpitem++)
982 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
983 if ((i != start) &&
984 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
986 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
987 orgX, orgY );
988 debug_print_menuitem (" item: ", lpitem, "");
989 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
991 if (lpitem->rect.right > lprect->right)
993 if (i != start) break;
994 else lpitem->rect.right = lprect->right;
996 maxY = max( maxY, lpitem->rect.bottom );
997 orgX = lpitem->rect.right;
1000 /* Finish the line (set all items to the largest height found) */
1001 while (start < i) lppop->items[start++].rect.bottom = maxY;
1004 lprect->bottom = maxY;
1005 lppop->Height = lprect->bottom - lprect->top;
1007 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1008 /* the last item (if several lines, only move the last line) */
1009 lpitem = &lppop->items[lppop->nItems-1];
1010 orgY = lpitem->rect.top;
1011 orgX = lprect->right;
1012 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1013 if ( (helpPos==-1) || (helpPos>i) )
1014 break; /* done */
1015 if (lpitem->rect.top != orgY) break; /* Other line */
1016 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1017 lpitem->rect.left += orgX - lpitem->rect.right;
1018 lpitem->rect.right = orgX;
1019 orgX = lpitem->rect.left;
1023 /***********************************************************************
1024 * MENU_DrawMenuItem
1026 * Draw a single menu item.
1028 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1029 UINT height, BOOL menuBar, UINT odaction )
1031 RECT rect;
1033 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1035 if (lpitem->fType & MF_SYSMENU)
1037 if( !IsIconic(hwnd) ) {
1038 if (TWEAK_WineLook > WIN31_LOOK)
1039 NC_DrawSysButton95( hwnd, hdc,
1040 lpitem->fState &
1041 (MF_HILITE | MF_MOUSESELECT) );
1042 else
1043 NC_DrawSysButton( hwnd, hdc,
1044 lpitem->fState &
1045 (MF_HILITE | MF_MOUSESELECT) );
1048 return;
1051 if (lpitem->fType & MF_OWNERDRAW)
1054 ** Experimentation under Windows reveals that an owner-drawn
1055 ** menu is given the rectangle which includes the space it requested
1056 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1057 ** and a popup-menu arrow. This is the value of lpitem->rect.
1058 ** Windows will leave all drawing to the application except for
1059 ** the popup-menu arrow. Windows always draws that itself, after
1060 ** the menu owner has finished drawing.
1062 DRAWITEMSTRUCT dis;
1064 dis.CtlType = ODT_MENU;
1065 dis.CtlID = 0;
1066 dis.itemID = lpitem->wID;
1067 dis.itemData = (DWORD)lpitem->dwItemData;
1068 dis.itemState = 0;
1069 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1070 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED;
1071 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1072 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1073 dis.hwndItem = (HWND)hmenu;
1074 dis.hDC = hdc;
1075 dis.rcItem = lpitem->rect;
1076 TRACE("Ownerdraw: owner=%04x itemID=%d, itemState=%d, itemAction=%d, "
1077 "hwndItem=%04x, hdc=%04x, rcItem={%d,%d,%d,%d}\n", hwndOwner,
1078 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1079 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1080 dis.rcItem.bottom);
1081 SendMessageA( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1082 /* Fall through to draw popup-menu arrow */
1085 TRACE("rect={%d,%d,%d,%d}\n", lpitem->rect.left, lpitem->rect.top,
1086 lpitem->rect.right,lpitem->rect.bottom);
1088 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1090 rect = lpitem->rect;
1092 if (!(lpitem->fType & MF_OWNERDRAW))
1094 if (lpitem->fState & MF_HILITE)
1096 if(TWEAK_WineLook == WIN98_LOOK)
1098 if(menuBar)
1099 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1100 else
1101 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1103 else /* Not Win98 Look */
1105 if(!IS_BITMAP_ITEM(lpitem->fType))
1106 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1109 else
1110 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1113 SetBkMode( hdc, TRANSPARENT );
1115 if (!(lpitem->fType & MF_OWNERDRAW))
1117 /* vertical separator */
1118 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1120 if (TWEAK_WineLook > WIN31_LOOK)
1122 RECT rc = rect;
1123 rc.top = 3;
1124 rc.bottom = height - 3;
1125 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1127 else
1129 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1130 MoveToEx( hdc, rect.left, 0, NULL );
1131 LineTo( hdc, rect.left, height );
1135 /* horizontal separator */
1136 if (lpitem->fType & MF_SEPARATOR)
1138 if (TWEAK_WineLook > WIN31_LOOK)
1140 RECT rc = rect;
1141 rc.left++;
1142 rc.right--;
1143 rc.top += SEPARATOR_HEIGHT / 2;
1144 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1146 else
1148 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1149 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1150 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1152 return;
1156 /* Setup colors */
1158 if (lpitem->fState & MF_HILITE)
1160 if(TWEAK_WineLook == WIN98_LOOK)
1162 if(menuBar) {
1163 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1164 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1165 } else {
1166 if(lpitem->fState & MF_GRAYED)
1167 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1168 else
1169 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1170 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1173 else /* Not Win98 Look */
1175 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1176 if(!IS_BITMAP_ITEM(lpitem->fType))
1177 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1180 else
1182 if (lpitem->fState & MF_GRAYED)
1183 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1184 else
1185 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1186 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1189 /* helper lines for debugging */
1190 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1191 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1192 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1193 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1196 if (!menuBar)
1198 INT y = rect.top + rect.bottom;
1199 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1200 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1202 if (!(lpitem->fType & MF_OWNERDRAW))
1204 /* Draw the check mark
1206 * FIXME:
1207 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1209 HBITMAP bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1210 if (bm) /* we have a custom bitmap */
1212 HDC hdcMem = CreateCompatibleDC( hdc );
1213 SelectObject( hdcMem, bm );
1214 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1215 check_bitmap_width, check_bitmap_height,
1216 hdcMem, 0, 0, SRCCOPY );
1217 DeleteDC( hdcMem );
1219 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1221 RECT r;
1222 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1223 HDC hdcMem = CreateCompatibleDC( hdc );
1224 SelectObject( hdcMem, bm );
1225 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1226 DrawFrameControl( hdcMem, &r, DFC_MENU,
1227 (lpitem->fType & MFT_RADIOCHECK) ?
1228 DFCS_MENUBULLET : DFCS_MENUCHECK );
1229 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1230 hdcMem, 0, 0, SRCCOPY );
1231 DeleteDC( hdcMem );
1232 DeleteObject( bm );
1236 /* Draw the popup-menu arrow */
1237 if (lpitem->fType & MF_POPUP)
1239 HDC hdcMem = CreateCompatibleDC( hdc );
1240 HBITMAP hOrigBitmap;
1242 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1243 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1244 (y - arrow_bitmap_height) / 2,
1245 arrow_bitmap_width, arrow_bitmap_height,
1246 hdcMem, 0, 0, SRCCOPY );
1247 SelectObject( hdcMem, hOrigBitmap );
1248 DeleteDC( hdcMem );
1251 rect.left += check_bitmap_width;
1252 rect.right -= arrow_bitmap_width;
1255 /* Done for owner-drawn */
1256 if (lpitem->fType & MF_OWNERDRAW)
1257 return;
1259 /* Draw the item text or bitmap */
1260 if (IS_BITMAP_ITEM(lpitem->fType))
1262 int left,top,w,h;
1263 DWORD rop;
1265 HBITMAP resBmp = 0;
1267 HDC hdcMem = CreateCompatibleDC( hdc );
1270 * Check if there is a magic menu item associated with this item
1271 * and load the appropriate bitmap
1273 if (IS_MAGIC_ITEM(lpitem->text))
1275 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fState & MF_HILITE),
1276 lpitem->dwItemData);
1278 else
1279 resBmp = (HBITMAP)lpitem->text;
1281 if (resBmp)
1283 BITMAP bm;
1284 GetObjectA( resBmp, sizeof(bm), &bm );
1286 SelectObject(hdcMem,resBmp );
1288 /* handle fontsize > bitmap_height */
1289 h=rect.bottom - rect.top;
1290 top = (h>bm.bmHeight) ?
1291 rect.top+(h-bm.bmHeight)/2 : rect.top;
1292 w=rect.right - rect.left;
1293 left=rect.left;
1294 if (TWEAK_WineLook == WIN95_LOOK) {
1295 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
1296 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
1297 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1298 } else {
1299 left++;
1300 w-=2;
1301 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text) && (!menuBar)) ? MERGEPAINT : SRCCOPY;
1303 BitBlt( hdc, left, top, w,
1304 h, hdcMem, 0, 0,
1305 rop);
1307 DeleteDC( hdcMem );
1309 return;
1312 /* No bitmap - process text if present */
1313 else if (IS_STRING_ITEM(lpitem->fType))
1315 register int i;
1316 HFONT hfontOld = 0;
1318 UINT uFormat = (menuBar) ?
1319 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1320 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1322 if ( lpitem->fState & MFS_DEFAULT )
1324 hfontOld = SelectObject( hdc, hMenuFontBold);
1327 if (menuBar)
1329 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1330 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1331 i = strlenW( lpitem->text );
1333 else
1335 for (i = 0; lpitem->text[i]; i++)
1336 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1337 break;
1340 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1342 if (!(lpitem->fState & MF_HILITE) )
1344 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1345 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1346 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1347 --rect.left; --rect.top; --rect.right; --rect.bottom;
1349 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1352 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1354 /* paint the shortcut text */
1355 if (lpitem->text[i]) /* There's a tab or flush-right char */
1357 if (lpitem->text[i] == '\t')
1359 rect.left = lpitem->xTab;
1360 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1362 else
1364 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1367 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1369 if (!(lpitem->fState & MF_HILITE) )
1371 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1372 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1373 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1374 --rect.left; --rect.top; --rect.right; --rect.bottom;
1376 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1378 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1381 if (hfontOld)
1382 SelectObject (hdc, hfontOld);
1387 /***********************************************************************
1388 * MENU_DrawPopupMenu
1390 * Paint a popup menu.
1392 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1394 HBRUSH hPrevBrush = 0;
1395 RECT rect;
1397 TRACE("wnd=0x%04x dc=0x%04x menu=0x%04x\n", hwnd, hdc, hmenu);
1399 GetClientRect( hwnd, &rect );
1401 if(TWEAK_WineLook == WIN31_LOOK)
1403 rect.bottom -= POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1404 rect.right -= POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER);
1407 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1408 && (SelectObject( hdc, hMenuFont)))
1410 HPEN hPrevPen;
1412 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1414 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1415 if( hPrevPen )
1417 INT ropPrev, i;
1418 POPUPMENU *menu;
1420 /* draw 3-d shade */
1421 if(TWEAK_WineLook == WIN31_LOOK) {
1422 SelectObject( hdc, hShadeBrush );
1423 SetBkMode( hdc, TRANSPARENT );
1424 ropPrev = SetROP2( hdc, R2_MASKPEN );
1426 i = rect.right; /* why SetBrushOrg() doesn't? */
1427 PatBlt( hdc, i & 0xfffffffe,
1428 rect.top + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER),
1429 i%2 + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1430 rect.bottom - rect.top, 0x00a000c9 );
1431 i = rect.bottom;
1432 PatBlt( hdc, rect.left + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1433 i & 0xfffffffe,rect.right - rect.left,
1434 i%2 + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER), 0x00a000c9 );
1435 SelectObject( hdc, hPrevPen );
1436 SelectObject( hdc, hPrevBrush );
1437 SetROP2( hdc, ropPrev );
1439 else
1440 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1442 /* draw menu items */
1444 menu = MENU_GetMenu( hmenu );
1445 if (menu && menu->nItems)
1447 MENUITEM *item;
1448 UINT u;
1450 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1451 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1452 menu->Height, FALSE, ODA_DRAWENTIRE );
1455 } else
1457 SelectObject( hdc, hPrevBrush );
1462 /***********************************************************************
1463 * MENU_DrawMenuBar
1465 * Paint a menu bar. Returns the height of the menu bar.
1466 * called from [windows/nonclient.c]
1468 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1469 BOOL suppress_draw)
1471 LPPOPUPMENU lppop;
1472 UINT i,retvalue;
1473 HFONT hfontOld = 0;
1474 HMENU hMenu = GetMenu(hwnd);
1476 lppop = MENU_GetMenu( hMenu );
1477 if (lppop == NULL || lprect == NULL)
1479 retvalue = GetSystemMetrics(SM_CYMENU);
1480 goto END;
1483 TRACE("(%04x, %p, %p)\n", hDC, lprect, lppop);
1485 hfontOld = SelectObject( hDC, hMenuFont);
1487 if (lppop->Height == 0)
1488 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1490 lprect->bottom = lprect->top + lppop->Height;
1492 if (suppress_draw)
1494 retvalue = lppop->Height;
1495 goto END;
1498 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
1500 if (TWEAK_WineLook == WIN31_LOOK)
1502 SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1503 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1504 LineTo( hDC, lprect->right, lprect->bottom );
1506 else
1508 SelectObject( hDC, GetSysColorPen(COLOR_3DFACE));
1509 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1510 LineTo( hDC, lprect->right, lprect->bottom );
1513 if (lppop->nItems == 0)
1515 retvalue = GetSystemMetrics(SM_CYMENU);
1516 goto END;
1519 for (i = 0; i < lppop->nItems; i++)
1521 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
1522 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
1524 retvalue = lppop->Height;
1526 END:
1527 if (hfontOld) SelectObject (hDC, hfontOld);
1528 return retvalue;
1532 /***********************************************************************
1533 * MENU_ShowPopup
1535 * Display a popup menu.
1537 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1538 INT x, INT y, INT xanchor, INT yanchor )
1540 POPUPMENU *menu;
1541 UINT width, height;
1543 TRACE("owner=0x%04x hmenu=0x%04x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1544 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1546 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1547 if (menu->FocusedItem != NO_SELECTED_ITEM)
1549 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1550 menu->FocusedItem = NO_SELECTED_ITEM;
1553 /* store the owner for DrawItem */
1554 menu->hwndOwner = hwndOwner;
1557 MENU_PopupMenuCalcSize( menu, hwndOwner );
1559 /* adjust popup menu pos so that it fits within the desktop */
1561 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1562 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1564 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1566 if( xanchor )
1567 x -= width - xanchor;
1568 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1569 x = GetSystemMetrics(SM_CXSCREEN) - width;
1571 if( x < 0 ) x = 0;
1573 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1575 if( yanchor )
1576 y -= height + yanchor;
1577 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1578 y = GetSystemMetrics(SM_CYSCREEN) - height;
1580 if( y < 0 ) y = 0;
1582 if( TWEAK_WineLook == WIN31_LOOK )
1584 width += POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER); /* add space for shading */
1585 height += POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1588 /* NOTE: In Windows, top menu popup is not owned. */
1589 menu->hWnd = CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1590 WS_POPUP, x, y, width, height,
1591 hwndOwner, 0, GetWindowLongA(hwndOwner,GWL_HINSTANCE),
1592 (LPVOID)hmenu );
1593 if( !menu->hWnd ) return FALSE;
1594 if (!top_popup) top_popup = menu->hWnd;
1596 /* Display the window */
1598 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1599 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1600 UpdateWindow( menu->hWnd );
1601 return TRUE;
1605 /***********************************************************************
1606 * MENU_SelectItem
1608 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1609 BOOL sendMenuSelect, HMENU topmenu )
1611 LPPOPUPMENU lppop;
1612 HDC hdc;
1614 TRACE("owner=0x%04x menu=0x%04x index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1616 lppop = MENU_GetMenu( hmenu );
1617 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1619 if (lppop->FocusedItem == wIndex) return;
1620 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1621 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1623 SelectObject( hdc, hMenuFont);
1625 /* Clear previous highlighted item */
1626 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1628 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1629 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1630 lppop->Height, !(lppop->wFlags & MF_POPUP),
1631 ODA_SELECT );
1634 /* Highlight new item (if any) */
1635 lppop->FocusedItem = wIndex;
1636 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1638 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1639 lppop->items[wIndex].fState |= MF_HILITE;
1640 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1641 &lppop->items[wIndex], lppop->Height,
1642 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1644 if (sendMenuSelect)
1646 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1647 SendMessageA( hwndOwner, WM_MENUSELECT,
1648 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1649 ip->fType | ip->fState | MF_MOUSESELECT |
1650 (lppop->wFlags & MF_SYSMENU)), hmenu);
1653 else if (sendMenuSelect) {
1654 if(topmenu){
1655 int pos;
1656 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1657 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1658 MENUITEM *ip = &ptm->items[pos];
1659 SendMessageA( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1660 ip->fType | ip->fState | MF_MOUSESELECT |
1661 (ptm->wFlags & MF_SYSMENU)), topmenu);
1665 ReleaseDC( lppop->hWnd, hdc );
1669 /***********************************************************************
1670 * MENU_MoveSelection
1672 * Moves currently selected item according to the offset parameter.
1673 * If there is no selection then it should select the last item if
1674 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1676 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1678 INT i;
1679 POPUPMENU *menu;
1681 TRACE("hwnd=0x%04x hmenu=0x%04x off=0x%04x\n", hwndOwner, hmenu, offset);
1683 menu = MENU_GetMenu( hmenu );
1684 if ((!menu) || (!menu->items)) return;
1686 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1688 if( menu->nItems == 1 ) return; else
1689 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1690 ; i += offset)
1691 if (!(menu->items[i].fType & MF_SEPARATOR))
1693 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1694 return;
1698 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1699 i >= 0 && i < menu->nItems ; i += offset)
1700 if (!(menu->items[i].fType & MF_SEPARATOR))
1702 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1703 return;
1708 /**********************************************************************
1709 * MENU_SetItemData
1711 * Set an item flags, id and text ptr. Called by InsertMenu() and
1712 * ModifyMenu().
1714 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT id,
1715 LPCWSTR str )
1717 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1719 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1720 TRACE("flags=%x str=%p\n", flags, str);
1722 if (IS_STRING_ITEM(flags))
1724 if (!str)
1726 flags |= MF_SEPARATOR;
1727 item->text = NULL;
1729 else
1731 LPWSTR text;
1732 /* Item beginning with a backspace is a help item */
1733 if (*str == '\b')
1735 flags |= MF_HELP;
1736 str++;
1738 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1739 return FALSE;
1740 strcpyW( text, str );
1741 item->text = text;
1744 else if (IS_BITMAP_ITEM(flags))
1745 item->text = (LPWSTR)(HBITMAP)LOWORD(str);
1746 else item->text = NULL;
1748 if (flags & MF_OWNERDRAW)
1749 item->dwItemData = (DWORD)str;
1750 else
1751 item->dwItemData = 0;
1753 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != id) )
1754 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1756 if (flags & MF_POPUP)
1758 POPUPMENU *menu = MENU_GetMenu((UINT16)id);
1759 if (menu) menu->wFlags |= MF_POPUP;
1760 else
1762 item->wID = 0;
1763 item->hSubMenu = 0;
1764 item->fType = 0;
1765 item->fState = 0;
1766 return FALSE;
1770 item->wID = id;
1771 if (flags & MF_POPUP)
1772 item->hSubMenu = id;
1774 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1775 flags |= MF_POPUP; /* keep popup */
1777 item->fType = flags & TYPE_MASK;
1778 item->fState = (flags & STATE_MASK) &
1779 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1782 /* Don't call SetRectEmpty here! */
1785 if (prevText) HeapFree( GetProcessHeap(), 0, prevText );
1787 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1788 return TRUE;
1792 /**********************************************************************
1793 * MENU_InsertItem
1795 * Insert a new item into a menu.
1797 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1799 MENUITEM *newItems;
1800 POPUPMENU *menu;
1802 if (!(menu = MENU_GetMenu(hMenu)))
1803 return NULL;
1805 /* Find where to insert new item */
1807 if (flags & MF_BYPOSITION) {
1808 if (pos > menu->nItems)
1809 pos = menu->nItems;
1810 } else {
1811 if (!MENU_FindItem( &hMenu, &pos, flags ))
1812 pos = menu->nItems;
1813 else {
1814 if (!(menu = MENU_GetMenu( hMenu )))
1815 return NULL;
1819 /* Create new items array */
1821 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1822 if (!newItems)
1824 WARN("allocation failed\n" );
1825 return NULL;
1827 if (menu->nItems > 0)
1829 /* Copy the old array into the new one */
1830 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1831 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1832 (menu->nItems-pos)*sizeof(MENUITEM) );
1833 HeapFree( GetProcessHeap(), 0, menu->items );
1835 menu->items = newItems;
1836 menu->nItems++;
1837 memset( &newItems[pos], 0, sizeof(*newItems) );
1838 menu->Height = 0; /* force size recalculate */
1839 return &newItems[pos];
1843 /**********************************************************************
1844 * MENU_ParseResource
1846 * Parse a standard menu resource and add items to the menu.
1847 * Return a pointer to the end of the resource.
1849 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1851 WORD flags, id = 0;
1852 LPCSTR str;
1856 flags = GET_WORD(res);
1857 res += sizeof(WORD);
1858 if (!(flags & MF_POPUP))
1860 id = GET_WORD(res);
1861 res += sizeof(WORD);
1863 if (!IS_STRING_ITEM(flags))
1864 ERR("not a string item %04x\n", flags );
1865 str = res;
1866 if (!unicode) res += strlen(str) + 1;
1867 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1868 if (flags & MF_POPUP)
1870 HMENU hSubMenu = CreatePopupMenu();
1871 if (!hSubMenu) return NULL;
1872 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1873 return NULL;
1874 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1875 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1877 else /* Not a popup */
1879 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1880 else AppendMenuW( hMenu, flags, id,
1881 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1883 } while (!(flags & MF_END));
1884 return res;
1888 /**********************************************************************
1889 * MENUEX_ParseResource
1891 * Parse an extended menu resource and add items to the menu.
1892 * Return a pointer to the end of the resource.
1894 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1896 WORD resinfo;
1897 do {
1898 MENUITEMINFOW mii;
1900 mii.cbSize = sizeof(mii);
1901 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1902 mii.fType = GET_DWORD(res);
1903 res += sizeof(DWORD);
1904 mii.fState = GET_DWORD(res);
1905 res += sizeof(DWORD);
1906 mii.wID = GET_DWORD(res);
1907 res += sizeof(DWORD);
1908 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1909 res += sizeof(WORD);
1910 /* Align the text on a word boundary. */
1911 res += (~((int)res - 1)) & 1;
1912 mii.dwTypeData = (LPWSTR) res;
1913 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1914 /* Align the following fields on a dword boundary. */
1915 res += (~((int)res - 1)) & 3;
1917 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1918 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1920 if (resinfo & 1) { /* Pop-up? */
1921 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1922 res += sizeof(DWORD);
1923 mii.hSubMenu = CreatePopupMenu();
1924 if (!mii.hSubMenu)
1925 return NULL;
1926 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1927 DestroyMenu(mii.hSubMenu);
1928 return NULL;
1930 mii.fMask |= MIIM_SUBMENU;
1931 mii.fType |= MF_POPUP;
1933 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1934 } while (!(resinfo & MF_END));
1935 return res;
1939 /***********************************************************************
1940 * MENU_GetSubPopup
1942 * Return the handle of the selected sub-popup menu (if any).
1944 static HMENU MENU_GetSubPopup( HMENU hmenu )
1946 POPUPMENU *menu;
1947 MENUITEM *item;
1949 menu = MENU_GetMenu( hmenu );
1951 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1953 item = &menu->items[menu->FocusedItem];
1954 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1955 return item->hSubMenu;
1956 return 0;
1960 /***********************************************************************
1961 * MENU_HideSubPopups
1963 * Hide the sub-popup menus of this menu.
1965 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1966 BOOL sendMenuSelect )
1968 POPUPMENU *menu = MENU_GetMenu( hmenu );
1970 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1972 if (menu && top_popup)
1974 HMENU hsubmenu;
1975 POPUPMENU *submenu;
1976 MENUITEM *item;
1978 if (menu->FocusedItem != NO_SELECTED_ITEM)
1980 item = &menu->items[menu->FocusedItem];
1981 if (!(item->fType & MF_POPUP) ||
1982 !(item->fState & MF_MOUSESELECT)) return;
1983 item->fState &= ~MF_MOUSESELECT;
1984 hsubmenu = item->hSubMenu;
1985 } else return;
1987 submenu = MENU_GetMenu( hsubmenu );
1988 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1989 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
1990 DestroyWindow( submenu->hWnd );
1991 submenu->hWnd = 0;
1996 /***********************************************************************
1997 * MENU_ShowSubPopup
1999 * Display the sub-menu of the selected item of this menu.
2000 * Return the handle of the submenu, or hmenu if no submenu to display.
2002 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2003 BOOL selectFirst, UINT wFlags )
2005 RECT rect;
2006 POPUPMENU *menu;
2007 MENUITEM *item;
2008 HDC hdc;
2010 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, selectFirst);
2012 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2014 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2016 item = &menu->items[menu->FocusedItem];
2017 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2018 return hmenu;
2020 /* message must be sent before using item,
2021 because nearly everything may be changed by the application ! */
2023 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2024 if (!(wFlags & TPM_NONOTIFY))
2025 SendMessageA( hwndOwner, WM_INITMENUPOPUP, item->hSubMenu,
2026 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2028 item = &menu->items[menu->FocusedItem];
2029 rect = item->rect;
2031 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2032 if (!(item->fState & MF_HILITE))
2034 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2035 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2037 SelectObject( hdc, hMenuFont);
2039 item->fState |= MF_HILITE;
2040 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2041 ReleaseDC( menu->hWnd, hdc );
2043 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2044 item->rect = rect;
2046 item->fState |= MF_MOUSESELECT;
2048 if (IS_SYSTEM_MENU(menu))
2050 MENU_InitSysMenuPopup(item->hSubMenu,
2051 GetWindowLongA( menu->hWnd, GWL_STYLE ),
2052 GetClassLongA( menu->hWnd, GCL_STYLE));
2054 NC_GetSysPopupPos( menu->hWnd, &rect );
2055 rect.top = rect.bottom;
2056 rect.right = GetSystemMetrics(SM_CXSIZE);
2057 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2059 else
2061 GetWindowRect( menu->hWnd, &rect );
2062 if (menu->wFlags & MF_POPUP)
2064 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2065 rect.top += item->rect.top;
2066 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2067 rect.bottom = item->rect.top - item->rect.bottom;
2069 else
2071 rect.left += item->rect.left;
2072 rect.top += item->rect.bottom;
2073 rect.right = item->rect.right - item->rect.left;
2074 rect.bottom = item->rect.bottom - item->rect.top;
2078 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2079 rect.left, rect.top, rect.right, rect.bottom );
2080 if (selectFirst)
2081 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2082 return item->hSubMenu;
2087 /**********************************************************************
2088 * MENU_IsMenuActive
2090 BOOL MENU_IsMenuActive(void)
2092 return (top_popup != 0);
2095 /***********************************************************************
2096 * MENU_PtMenu
2098 * Walks menu chain trying to find a menu pt maps to.
2100 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2102 POPUPMENU *menu = MENU_GetMenu( hMenu );
2103 UINT item = menu->FocusedItem;
2104 HMENU ret;
2106 /* try subpopup first (if any) */
2107 ret = (item != NO_SELECTED_ITEM &&
2108 (menu->items[item].fType & MF_POPUP) &&
2109 (menu->items[item].fState & MF_MOUSESELECT))
2110 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2112 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2114 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2115 if( menu->wFlags & MF_POPUP )
2117 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2119 else if (ht == HTSYSMENU)
2120 ret = get_win_sys_menu( menu->hWnd );
2121 else if (ht == HTMENU)
2122 ret = GetMenu( menu->hWnd );
2124 return ret;
2127 /***********************************************************************
2128 * MENU_ExecFocusedItem
2130 * Execute a menu item (for instance when user pressed Enter).
2131 * Return the wID of the executed item. Otherwise, -1 indicating
2132 * that no menu item was executed;
2133 * Have to receive the flags for the TrackPopupMenu options to avoid
2134 * sending unwanted message.
2137 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2139 MENUITEM *item;
2140 POPUPMENU *menu = MENU_GetMenu( hMenu );
2142 TRACE("%p hmenu=0x%04x\n", pmt, hMenu);
2144 if (!menu || !menu->nItems ||
2145 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2147 item = &menu->items[menu->FocusedItem];
2149 TRACE("%08x %08x %08x\n",
2150 hMenu, item->wID, item->hSubMenu);
2152 if (!(item->fType & MF_POPUP))
2154 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2156 /* If TPM_RETURNCMD is set you return the id, but
2157 do not send a message to the owner */
2158 if(!(wFlags & TPM_RETURNCMD))
2160 if( menu->wFlags & MF_SYSMENU )
2161 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2162 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2163 else
2164 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2166 return item->wID;
2169 else
2170 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2172 return -1;
2175 /***********************************************************************
2176 * MENU_SwitchTracking
2178 * Helper function for menu navigation routines.
2180 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2182 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2183 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2185 TRACE("%p hmenu=0x%04x 0x%04x\n", pmt, hPtMenu, id);
2187 if( pmt->hTopMenu != hPtMenu &&
2188 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2190 /* both are top level menus (system and menu-bar) */
2191 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2192 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2193 pmt->hTopMenu = hPtMenu;
2195 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2196 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2200 /***********************************************************************
2201 * MENU_ButtonDown
2203 * Return TRUE if we can go on with menu tracking.
2205 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2207 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2209 if (hPtMenu)
2211 UINT id = 0;
2212 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2213 MENUITEM *item;
2215 if( IS_SYSTEM_MENU(ptmenu) )
2216 item = ptmenu->items;
2217 else
2218 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2220 if( item )
2222 if( ptmenu->FocusedItem != id )
2223 MENU_SwitchTracking( pmt, hPtMenu, id );
2225 /* If the popup menu is not already "popped" */
2226 if(!(item->fState & MF_MOUSESELECT ))
2228 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2230 /* In win31, a newly popped menu always remains opened for the next buttonup */
2231 if(TWEAK_WineLook == WIN31_LOOK)
2232 ptmenu->bTimeToHide = FALSE;
2235 return TRUE;
2237 /* Else the click was on the menu bar, finish the tracking */
2239 return FALSE;
2242 /***********************************************************************
2243 * MENU_ButtonUp
2245 * Return the value of MENU_ExecFocusedItem if
2246 * the selected item was not a popup. Else open the popup.
2247 * A -1 return value indicates that we go on with menu tracking.
2250 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2252 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2254 if (hPtMenu)
2256 UINT id = 0;
2257 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2258 MENUITEM *item;
2260 if( IS_SYSTEM_MENU(ptmenu) )
2261 item = ptmenu->items;
2262 else
2263 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2265 if( item && (ptmenu->FocusedItem == id ))
2267 if( !(item->fType & MF_POPUP) )
2268 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2270 /* If we are dealing with the top-level menu */
2271 /* and this is a click on an already "popped" item: */
2272 /* Stop the menu tracking and close the opened submenus */
2273 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2274 return 0;
2276 ptmenu->bTimeToHide = TRUE;
2278 return -1;
2282 /***********************************************************************
2283 * MENU_MouseMove
2285 * Return TRUE if we can go on with menu tracking.
2287 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2289 UINT id = NO_SELECTED_ITEM;
2290 POPUPMENU *ptmenu = NULL;
2292 if( hPtMenu )
2294 ptmenu = MENU_GetMenu( hPtMenu );
2295 if( IS_SYSTEM_MENU(ptmenu) )
2296 id = 0;
2297 else
2298 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2301 if( id == NO_SELECTED_ITEM )
2303 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2304 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2307 else if( ptmenu->FocusedItem != id )
2309 MENU_SwitchTracking( pmt, hPtMenu, id );
2310 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2312 return TRUE;
2316 /***********************************************************************
2317 * MENU_DoNextMenu
2319 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2321 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2323 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2325 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2326 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2328 MDINEXTMENU next_menu;
2329 HMENU hNewMenu;
2330 HWND hNewWnd;
2331 UINT id = 0;
2333 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2334 next_menu.hmenuNext = 0;
2335 next_menu.hwndNext = 0;
2336 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2338 TRACE("%04x [%04x] -> %04x [%04x]\n",
2339 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2341 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2343 DWORD style = GetWindowLongA( pmt->hOwnerWnd, GWL_STYLE );
2344 hNewWnd = pmt->hOwnerWnd;
2345 if( IS_SYSTEM_MENU(menu) )
2347 /* switch to the menu bar */
2349 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2351 if( vk == VK_LEFT )
2353 menu = MENU_GetMenu( hNewMenu );
2354 id = menu->nItems - 1;
2357 else if (style & WS_SYSMENU )
2359 /* switch to the system menu */
2360 hNewMenu = get_win_sys_menu( hNewWnd );
2362 else return FALSE;
2364 else /* application returned a new menu to switch to */
2366 hNewMenu = next_menu.hmenuNext;
2367 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2369 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2371 DWORD style = GetWindowLongA( hNewWnd, GWL_STYLE );
2373 if (style & WS_SYSMENU &&
2374 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2376 /* get the real system menu */
2377 hNewMenu = get_win_sys_menu(hNewWnd);
2379 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2381 /* FIXME: Not sure what to do here;
2382 * perhaps try to track hNewMenu as a popup? */
2384 TRACE(" -- got confused.\n");
2385 return FALSE;
2388 else return FALSE;
2391 if( hNewMenu != pmt->hTopMenu )
2393 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2394 FALSE, 0 );
2395 if( pmt->hCurrentMenu != pmt->hTopMenu )
2396 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2399 if( hNewWnd != pmt->hOwnerWnd )
2401 ReleaseCapture();
2402 pmt->hOwnerWnd = hNewWnd;
2403 EVENT_Capture( pmt->hOwnerWnd, HTMENU );
2406 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2407 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2409 return TRUE;
2411 return FALSE;
2414 /***********************************************************************
2415 * MENU_SuspendPopup
2417 * The idea is not to show the popup if the next input message is
2418 * going to hide it anyway.
2420 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2422 MSG msg;
2424 msg.hwnd = pmt->hOwnerWnd;
2426 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2427 pmt->trackFlags |= TF_SKIPREMOVE;
2429 switch( uMsg )
2431 case WM_KEYDOWN:
2432 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2433 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2435 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2436 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2437 if( msg.message == WM_KEYDOWN &&
2438 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2440 pmt->trackFlags |= TF_SUSPENDPOPUP;
2441 return TRUE;
2444 break;
2447 /* failures go through this */
2448 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2449 return FALSE;
2452 /***********************************************************************
2453 * MENU_KeyEscape
2455 * Handle a VK_ESCAPE key event in a menu.
2457 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2459 BOOL bEndMenu = TRUE;
2461 if (pmt->hCurrentMenu != pmt->hTopMenu)
2463 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2465 if (menu->wFlags & MF_POPUP)
2467 HMENU hmenutmp, hmenuprev;
2469 hmenuprev = hmenutmp = pmt->hTopMenu;
2471 /* close topmost popup */
2472 while (hmenutmp != pmt->hCurrentMenu)
2474 hmenuprev = hmenutmp;
2475 hmenutmp = MENU_GetSubPopup( hmenuprev );
2478 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2479 pmt->hCurrentMenu = hmenuprev;
2480 bEndMenu = FALSE;
2484 return bEndMenu;
2487 /***********************************************************************
2488 * MENU_KeyLeft
2490 * Handle a VK_LEFT key event in a menu.
2492 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2494 POPUPMENU *menu;
2495 HMENU hmenutmp, hmenuprev;
2496 UINT prevcol;
2498 hmenuprev = hmenutmp = pmt->hTopMenu;
2499 menu = MENU_GetMenu( hmenutmp );
2501 /* Try to move 1 column left (if possible) */
2502 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2503 NO_SELECTED_ITEM ) {
2505 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2506 prevcol, TRUE, 0 );
2507 return;
2510 /* close topmost popup */
2511 while (hmenutmp != pmt->hCurrentMenu)
2513 hmenuprev = hmenutmp;
2514 hmenutmp = MENU_GetSubPopup( hmenuprev );
2517 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2518 pmt->hCurrentMenu = hmenuprev;
2520 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2522 /* move menu bar selection if no more popups are left */
2524 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2525 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2527 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2529 /* A sublevel menu was displayed - display the next one
2530 * unless there is another displacement coming up */
2532 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2533 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2534 pmt->hTopMenu, TRUE, wFlags);
2540 /***********************************************************************
2541 * MENU_KeyRight
2543 * Handle a VK_RIGHT key event in a menu.
2545 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2547 HMENU hmenutmp;
2548 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2549 UINT nextcol;
2551 TRACE("MENU_KeyRight called, cur %x (%s), top %x (%s).\n",
2552 pmt->hCurrentMenu,
2553 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->
2554 items[0].text),
2555 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2557 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2559 /* If already displaying a popup, try to display sub-popup */
2561 hmenutmp = pmt->hCurrentMenu;
2562 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2564 /* if subpopup was displayed then we are done */
2565 if (hmenutmp != pmt->hCurrentMenu) return;
2568 /* Check to see if there's another column */
2569 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2570 NO_SELECTED_ITEM ) {
2571 TRACE("Going to %d.\n", nextcol );
2572 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2573 nextcol, TRUE, 0 );
2574 return;
2577 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2579 if( pmt->hCurrentMenu != pmt->hTopMenu )
2581 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2582 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2583 } else hmenutmp = 0;
2585 /* try to move to the next item */
2586 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2587 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2589 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2590 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2591 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2592 pmt->hTopMenu, TRUE, wFlags);
2596 /***********************************************************************
2597 * MENU_TrackMenu
2599 * Menu tracking code.
2601 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2602 HWND hwnd, const RECT *lprect )
2604 MSG msg;
2605 POPUPMENU *menu;
2606 BOOL fRemove;
2607 INT executedMenuId = -1;
2608 MTRACKER mt;
2609 BOOL enterIdleSent = FALSE;
2611 mt.trackFlags = 0;
2612 mt.hCurrentMenu = hmenu;
2613 mt.hTopMenu = hmenu;
2614 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2615 mt.pt.x = x;
2616 mt.pt.y = y;
2618 TRACE("hmenu=0x%04x flags=0x%08x (%d,%d) hwnd=0x%04x (%d,%d)-(%d,%d)\n",
2619 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2620 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2622 fEndMenu = FALSE;
2623 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2625 if (wFlags & TPM_BUTTONDOWN)
2627 /* Get the result in order to start the tracking or not */
2628 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2629 fEndMenu = !fRemove;
2632 EVENT_Capture( mt.hOwnerWnd, HTMENU );
2634 while (!fEndMenu)
2636 menu = MENU_GetMenu( mt.hCurrentMenu );
2637 if (!menu) /* sometimes happens if I do a window manager close */
2638 break;
2640 /* we have to keep the message in the queue until it's
2641 * clear that menu loop is not over yet. */
2643 for (;;)
2645 if (PeekMessageA( &msg, 0, 0, 0, PM_NOREMOVE ))
2647 if (!CallMsgFilterA( &msg, MSGF_MENU )) break;
2648 /* remove the message from the queue */
2649 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2651 else
2653 if (!enterIdleSent)
2655 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2656 enterIdleSent = TRUE;
2657 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2659 WaitMessage();
2663 /* check if EndMenu() tried to cancel us, by posting this message */
2664 if(msg.message == WM_CANCELMODE)
2666 /* we are now out of the loop */
2667 fEndMenu = TRUE;
2669 /* remove the message from the queue */
2670 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2672 /* break out of internal loop, ala ESCAPE */
2673 break;
2676 TranslateMessage( &msg );
2677 mt.pt = msg.pt;
2679 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2680 enterIdleSent=FALSE;
2682 fRemove = FALSE;
2683 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2686 * use the mouse coordinates in lParam instead of those in the MSG
2687 * struct to properly handle synthetic messages. lParam coords are
2688 * relative to client area, so they must be converted; since they can
2689 * be negative, we must use SLOWORD/SHIWORD instead of LOWORD/HIWORD.
2691 mt.pt.x = SLOWORD(msg.lParam);
2692 mt.pt.y = SHIWORD(msg.lParam);
2693 ClientToScreen(msg.hwnd,&mt.pt);
2695 /* Find a menu for this mouse event */
2696 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2698 switch(msg.message)
2700 /* no WM_NC... messages in captured state */
2702 case WM_RBUTTONDBLCLK:
2703 case WM_RBUTTONDOWN:
2704 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2705 /* fall through */
2706 case WM_LBUTTONDBLCLK:
2707 case WM_LBUTTONDOWN:
2708 /* If the message belongs to the menu, removes it from the queue */
2709 /* Else, end menu tracking */
2710 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2711 fEndMenu = !fRemove;
2712 break;
2714 case WM_RBUTTONUP:
2715 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2716 /* fall through */
2717 case WM_LBUTTONUP:
2718 /* Check if a menu was selected by the mouse */
2719 if (hmenu)
2721 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2723 /* End the loop if executedMenuId is an item ID */
2724 /* or if the job was done (executedMenuId = 0). */
2725 fEndMenu = fRemove = (executedMenuId != -1);
2727 /* No menu was selected by the mouse */
2728 /* if the function was called by TrackPopupMenu, continue
2729 with the menu tracking. If not, stop it */
2730 else
2731 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2733 break;
2735 case WM_MOUSEMOVE:
2736 /* In win95 winelook, the selected menu item must be changed every time the
2737 mouse moves. In Win31 winelook, the mouse button has to be held down */
2739 if ( (TWEAK_WineLook > WIN31_LOOK) ||
2740 ( (msg.wParam & MK_LBUTTON) ||
2741 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON))) )
2743 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2745 } /* switch(msg.message) - mouse */
2747 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2749 fRemove = TRUE; /* Keyboard messages are always removed */
2750 switch(msg.message)
2752 case WM_KEYDOWN:
2753 switch(msg.wParam)
2755 case VK_HOME:
2756 case VK_END:
2757 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2758 NO_SELECTED_ITEM, FALSE, 0 );
2759 /* fall through */
2760 case VK_UP:
2761 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2762 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2763 break;
2765 case VK_DOWN: /* If on menu bar, pull-down the menu */
2767 menu = MENU_GetMenu( mt.hCurrentMenu );
2768 if (!(menu->wFlags & MF_POPUP))
2769 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2770 else /* otherwise try to move selection */
2771 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2772 break;
2774 case VK_LEFT:
2775 MENU_KeyLeft( &mt, wFlags );
2776 break;
2778 case VK_RIGHT:
2779 MENU_KeyRight( &mt, wFlags );
2780 break;
2782 case VK_ESCAPE:
2783 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2784 break;
2786 case VK_F1:
2788 HELPINFO hi;
2789 hi.cbSize = sizeof(HELPINFO);
2790 hi.iContextType = HELPINFO_MENUITEM;
2791 if (menu->FocusedItem == NO_SELECTED_ITEM)
2792 hi.iCtrlId = 0;
2793 else
2794 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2795 hi.hItemHandle = hmenu;
2796 hi.dwContextId = menu->dwContextHelpID;
2797 hi.MousePos = msg.pt;
2798 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2799 break;
2802 default:
2803 break;
2805 break; /* WM_KEYDOWN */
2807 case WM_SYSKEYDOWN:
2808 switch(msg.wParam)
2810 case VK_MENU:
2811 fEndMenu = TRUE;
2812 break;
2815 break; /* WM_SYSKEYDOWN */
2817 case WM_CHAR:
2819 UINT pos;
2821 if (msg.wParam == '\r' || msg.wParam == ' ')
2823 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2824 fEndMenu = (executedMenuId != -1);
2826 break;
2829 /* Hack to avoid control chars. */
2830 /* We will find a better way real soon... */
2831 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2833 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2834 LOWORD(msg.wParam), FALSE );
2835 if (pos == (UINT)-2) fEndMenu = TRUE;
2836 else if (pos == (UINT)-1) MessageBeep(0);
2837 else
2839 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2840 TRUE, 0 );
2841 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2842 fEndMenu = (executedMenuId != -1);
2845 break;
2846 } /* switch(msg.message) - kbd */
2848 else
2850 DispatchMessageA( &msg );
2853 if (!fEndMenu) fRemove = TRUE;
2855 /* finally remove message from the queue */
2857 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2858 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2859 else mt.trackFlags &= ~TF_SKIPREMOVE;
2862 ReleaseCapture();
2864 /* If dropdown is still painted and the close box is clicked on
2865 then the menu will be destroyed as part of the DispatchMessage above.
2866 This will then invalidate the menu handle in mt.hTopMenu. We should
2867 check for this first. */
2868 if( IsMenu( mt.hTopMenu ) )
2870 menu = MENU_GetMenu( mt.hTopMenu );
2872 if( IsWindow( mt.hOwnerWnd ) )
2874 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2876 if (menu && menu->wFlags & MF_POPUP)
2878 DestroyWindow( menu->hWnd );
2879 menu->hWnd = 0;
2881 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2882 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2885 /* Reset the variable for hiding menu */
2886 if( menu ) menu->bTimeToHide = FALSE;
2889 /* The return value is only used by TrackPopupMenu */
2890 return ((executedMenuId != -1) ? executedMenuId : 0);
2893 /***********************************************************************
2894 * MENU_InitTracking
2896 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2898 TRACE("hwnd=0x%04x hmenu=0x%04x\n", hWnd, hMenu);
2900 HideCaret(0);
2902 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2903 if (!(wFlags & TPM_NONOTIFY))
2904 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2906 SendMessageA( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2908 if (!(wFlags & TPM_NONOTIFY))
2910 POPUPMENU *menu;
2911 SendMessageA( hWnd, WM_INITMENU, hMenu, 0 );
2912 if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
2913 { /* app changed/recreated menu bar entries in WM_INITMENU
2914 Recalculate menu sizes else clicks will not work */
2915 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2916 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2920 return TRUE;
2922 /***********************************************************************
2923 * MENU_ExitTracking
2925 static BOOL MENU_ExitTracking(HWND hWnd)
2927 TRACE("hwnd=0x%04x\n", hWnd);
2929 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
2930 ShowCaret(0);
2931 return TRUE;
2934 /***********************************************************************
2935 * MENU_TrackMouseMenuBar
2937 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2939 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2941 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2942 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2944 TRACE("wnd=%x ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2946 if (IsMenu(hMenu))
2948 /* map point to parent client coordinates */
2949 HWND parent = GetAncestor( hWnd, GA_PARENT );
2950 if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );
2952 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2953 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2954 MENU_ExitTracking(hWnd);
2959 /***********************************************************************
2960 * MENU_TrackKbdMenuBar
2962 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2964 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, INT vkey)
2966 UINT uItem = NO_SELECTED_ITEM;
2967 HMENU hTrackMenu;
2968 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2970 /* find window that has a menu */
2972 while (GetWindowLongA( hwnd, GWL_STYLE ) & WS_CHILD)
2973 if (!(hwnd = GetParent( hwnd ))) return;
2975 /* check if we have to track a system menu */
2977 hTrackMenu = GetMenu( hwnd );
2978 if (!hTrackMenu || IsIconic(hwnd) || vkey == VK_SPACE )
2980 if (!(GetWindowLongA( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
2981 hTrackMenu = get_win_sys_menu( hwnd );
2982 uItem = 0;
2983 wParam |= HTSYSMENU; /* prevent item lookup */
2986 if (!IsMenu( hTrackMenu )) return;
2988 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
2990 if( vkey && vkey != VK_SPACE )
2992 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, vkey, (wParam & HTSYSMENU) );
2993 if( uItem >= (UINT)(-2) )
2995 if( uItem == (UINT)(-1) ) MessageBeep(0);
2996 hTrackMenu = 0;
3000 if( hTrackMenu )
3002 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3004 if( uItem == NO_SELECTED_ITEM )
3005 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3006 else if( vkey )
3007 PostMessageA( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3009 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3011 MENU_ExitTracking( hwnd );
3015 /**********************************************************************
3016 * TrackPopupMenu (USER32.@)
3018 * Like the win32 API, the function return the command ID only if the
3019 * flag TPM_RETURNCMD is on.
3022 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3023 INT nReserved, HWND hWnd, const RECT *lpRect )
3025 BOOL ret = FALSE;
3027 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3029 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3030 if (!(wFlags & TPM_NONOTIFY))
3031 SendMessageA( hWnd, WM_INITMENUPOPUP, hMenu, 0);
3033 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3034 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3035 MENU_ExitTracking(hWnd);
3037 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3038 ret = 1;
3040 return ret;
3043 /**********************************************************************
3044 * TrackPopupMenuEx (USER32.@)
3046 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3047 HWND hWnd, LPTPMPARAMS lpTpm )
3049 FIXME("not fully implemented\n" );
3050 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3051 lpTpm ? &lpTpm->rcExclude : NULL );
3054 /***********************************************************************
3055 * PopupMenuWndProc
3057 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3059 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3061 TRACE("hwnd=0x%04x msg=0x%04x wp=0x%04x lp=0x%08lx\n",
3062 hwnd, message, wParam, lParam);
3064 switch(message)
3066 case WM_CREATE:
3068 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3069 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3070 return 0;
3073 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3074 return MA_NOACTIVATE;
3076 case WM_PAINT:
3078 PAINTSTRUCT ps;
3079 BeginPaint( hwnd, &ps );
3080 MENU_DrawPopupMenu( hwnd, ps.hdc,
3081 (HMENU)GetWindowLongA( hwnd, 0 ) );
3082 EndPaint( hwnd, &ps );
3083 return 0;
3085 case WM_ERASEBKGND:
3086 return 1;
3088 case WM_DESTROY:
3089 /* zero out global pointer in case resident popup window was destroyed. */
3090 if (hwnd == top_popup) top_popup = 0;
3091 break;
3093 case WM_SHOWWINDOW:
3095 if( wParam )
3097 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3099 else
3100 SetWindowLongW( hwnd, 0, 0 );
3101 break;
3103 case MM_SETMENUHANDLE:
3104 SetWindowLongW( hwnd, 0, wParam );
3105 break;
3107 case MM_GETMENUHANDLE:
3108 return GetWindowLongW( hwnd, 0 );
3110 default:
3111 return DefWindowProcW( hwnd, message, wParam, lParam );
3113 return 0;
3117 /***********************************************************************
3118 * MENU_GetMenuBarHeight
3120 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3122 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3123 INT orgX, INT orgY )
3125 HDC hdc;
3126 RECT rectBar;
3127 LPPOPUPMENU lppop;
3129 TRACE("HWND 0x%x, width %d, at (%d, %d).\n",
3130 hwnd, menubarWidth, orgX, orgY );
3132 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3134 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3135 SelectObject( hdc, hMenuFont);
3136 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3137 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3138 ReleaseDC( hwnd, hdc );
3139 return lppop->Height;
3143 /*******************************************************************
3144 * ChangeMenu (USER.153)
3146 BOOL16 WINAPI ChangeMenu16( HMENU16 hMenu, UINT16 pos, SEGPTR data,
3147 UINT16 id, UINT16 flags )
3149 TRACE("menu=%04x pos=%d data=%08lx id=%04x flags=%04x\n",
3150 hMenu, pos, (DWORD)data, id, flags );
3151 if (flags & MF_APPEND) return AppendMenu16( hMenu, flags & ~MF_APPEND,
3152 id, data );
3154 /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
3155 /* for MF_DELETE. We should check the parameters for all others */
3156 /* MF_* actions also (anybody got a doc on ChangeMenu?). */
3158 if (flags & MF_DELETE) return DeleteMenu16(hMenu, pos, flags & ~MF_DELETE);
3159 if (flags & MF_CHANGE) return ModifyMenu16(hMenu, pos, flags & ~MF_CHANGE,
3160 id, data );
3161 if (flags & MF_REMOVE) return RemoveMenu16(hMenu,
3162 flags & MF_BYPOSITION ? pos : id,
3163 flags & ~MF_REMOVE );
3164 /* Default: MF_INSERT */
3165 return InsertMenu16( hMenu, pos, flags, id, data );
3169 /*******************************************************************
3170 * ChangeMenuA (USER32.@)
3172 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3173 UINT id, UINT flags )
3175 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3176 hMenu, pos, (DWORD)data, id, flags );
3177 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3178 id, data );
3179 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3180 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3181 id, data );
3182 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3183 flags & MF_BYPOSITION ? pos : id,
3184 flags & ~MF_REMOVE );
3185 /* Default: MF_INSERT */
3186 return InsertMenuA( hMenu, pos, flags, id, data );
3190 /*******************************************************************
3191 * ChangeMenuW (USER32.@)
3193 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3194 UINT id, UINT flags )
3196 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3197 hMenu, pos, (DWORD)data, id, flags );
3198 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3199 id, data );
3200 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3201 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3202 id, data );
3203 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3204 flags & MF_BYPOSITION ? pos : id,
3205 flags & ~MF_REMOVE );
3206 /* Default: MF_INSERT */
3207 return InsertMenuW( hMenu, pos, flags, id, data );
3211 /*******************************************************************
3212 * CheckMenuItem (USER.154)
3214 BOOL16 WINAPI CheckMenuItem16( HMENU16 hMenu, UINT16 id, UINT16 flags )
3216 return (BOOL16)CheckMenuItem( hMenu, id, flags );
3220 /*******************************************************************
3221 * CheckMenuItem (USER32.@)
3223 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3225 MENUITEM *item;
3226 DWORD ret;
3228 TRACE("menu=%04x id=%04x flags=%04x\n", hMenu, id, flags );
3229 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3230 ret = item->fState & MF_CHECKED;
3231 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3232 else item->fState &= ~MF_CHECKED;
3233 return ret;
3237 /**********************************************************************
3238 * EnableMenuItem (USER.155)
3240 UINT16 WINAPI EnableMenuItem16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3242 return EnableMenuItem( hMenu, wItemID, wFlags );
3246 /**********************************************************************
3247 * EnableMenuItem (USER32.@)
3249 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3251 UINT oldflags;
3252 MENUITEM *item;
3253 POPUPMENU *menu;
3255 TRACE("(%04x, %04X, %04X) !\n",
3256 hMenu, wItemID, wFlags);
3258 /* Get the Popupmenu to access the owner menu */
3259 if (!(menu = MENU_GetMenu(hMenu)))
3260 return (UINT)-1;
3262 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3263 return (UINT)-1;
3265 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3266 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3268 /* In win95 if the close item in the system menu change update the close button */
3269 if (TWEAK_WineLook == WIN95_LOOK)
3270 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3272 if (menu->hSysMenuOwner != 0)
3274 POPUPMENU* parentMenu;
3276 /* Get the parent menu to access*/
3277 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3278 return (UINT)-1;
3280 /* Refresh the frame to reflect the change*/
3281 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3282 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3286 return oldflags;
3290 /*******************************************************************
3291 * GetMenuString (USER.161)
3293 INT16 WINAPI GetMenuString16( HMENU16 hMenu, UINT16 wItemID,
3294 LPSTR str, INT16 nMaxSiz, UINT16 wFlags )
3296 return GetMenuStringA( hMenu, wItemID, str, nMaxSiz, wFlags );
3300 /*******************************************************************
3301 * GetMenuStringA (USER32.@)
3303 INT WINAPI GetMenuStringA(
3304 HMENU hMenu, /* [in] menuhandle */
3305 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3306 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3307 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3308 UINT wFlags /* [in] MF_ flags */
3310 MENUITEM *item;
3312 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3313 hMenu, wItemID, str, nMaxSiz, wFlags );
3314 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3315 if (!IS_STRING_ITEM(item->fType)) return 0;
3316 if (!str || !nMaxSiz) return strlenW(item->text);
3317 str[0] = '\0';
3318 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3319 str[nMaxSiz-1] = 0;
3320 TRACE("returning '%s'\n", str );
3321 return strlen(str);
3325 /*******************************************************************
3326 * GetMenuStringW (USER32.@)
3328 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3329 LPWSTR str, INT nMaxSiz, UINT wFlags )
3331 MENUITEM *item;
3333 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3334 hMenu, wItemID, str, nMaxSiz, wFlags );
3335 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3336 if (!IS_STRING_ITEM(item->fType)) return 0;
3337 if (!str || !nMaxSiz) return strlenW(item->text);
3338 str[0] = '\0';
3339 lstrcpynW( str, item->text, nMaxSiz );
3340 return strlenW(str);
3344 /**********************************************************************
3345 * HiliteMenuItem (USER32.@)
3347 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3348 UINT wHilite )
3350 LPPOPUPMENU menu;
3351 TRACE("(%04x, %04x, %04x, %04x);\n",
3352 hWnd, hMenu, wItemID, wHilite);
3353 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3354 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3355 if (menu->FocusedItem == wItemID) return TRUE;
3356 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3357 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3358 return TRUE;
3362 /**********************************************************************
3363 * GetMenuState (USER.250)
3365 UINT16 WINAPI GetMenuState16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3367 return GetMenuState( hMenu, wItemID, wFlags );
3371 /**********************************************************************
3372 * GetMenuState (USER32.@)
3374 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3376 MENUITEM *item;
3377 TRACE("(menu=%04x, id=%04x, flags=%04x);\n",
3378 hMenu, wItemID, wFlags);
3379 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3380 debug_print_menuitem (" item: ", item, "");
3381 if (item->fType & MF_POPUP)
3383 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3384 if (!menu) return -1;
3385 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3387 else
3389 /* We used to (from way back then) mask the result to 0xff. */
3390 /* I don't know why and it seems wrong as the documented */
3391 /* return flag MF_SEPARATOR is outside that mask. */
3392 return (item->fType | item->fState);
3397 /**********************************************************************
3398 * GetMenuItemCount (USER.263)
3400 INT16 WINAPI GetMenuItemCount16( HMENU16 hMenu )
3402 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3403 if (!menu) return -1;
3404 TRACE("(%04x) returning %d\n",
3405 hMenu, menu->nItems );
3406 return menu->nItems;
3410 /**********************************************************************
3411 * GetMenuItemCount (USER32.@)
3413 INT WINAPI GetMenuItemCount( HMENU hMenu )
3415 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3416 if (!menu) return -1;
3417 TRACE("(%04x) returning %d\n",
3418 hMenu, menu->nItems );
3419 return menu->nItems;
3422 /**********************************************************************
3423 * GetMenuItemID (USER.264)
3425 UINT16 WINAPI GetMenuItemID16( HMENU16 hMenu, INT16 nPos )
3427 return (UINT16) GetMenuItemID (hMenu, nPos);
3430 /**********************************************************************
3431 * GetMenuItemID (USER32.@)
3433 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3435 MENUITEM * lpmi;
3437 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3438 if (lpmi->fType & MF_POPUP) return -1;
3439 return lpmi->wID;
3443 /*******************************************************************
3444 * InsertMenu (USER.410)
3446 BOOL16 WINAPI InsertMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3447 UINT16 id, SEGPTR data )
3449 UINT pos32 = (UINT)pos;
3450 if ((pos == (UINT16)-1) && (flags & MF_BYPOSITION)) pos32 = (UINT)-1;
3451 if (IS_STRING_ITEM(flags) && data)
3452 return InsertMenuA( hMenu, pos32, flags, id, MapSL(data) );
3453 return InsertMenuA( hMenu, pos32, flags, id, (LPSTR)data );
3457 /*******************************************************************
3458 * InsertMenuW (USER32.@)
3460 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3461 UINT id, LPCWSTR str )
3463 MENUITEM *item;
3465 if (IS_STRING_ITEM(flags) && str)
3466 TRACE("hMenu %04x, pos %d, flags %08x, "
3467 "id %04x, str %s\n",
3468 hMenu, pos, flags, id, debugstr_w(str) );
3469 else TRACE("hMenu %04x, pos %d, flags %08x, "
3470 "id %04x, str %08lx (not a string)\n",
3471 hMenu, pos, flags, id, (DWORD)str );
3473 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3475 if (!(MENU_SetItemData( item, flags, id, str )))
3477 RemoveMenu( hMenu, pos, flags );
3478 return FALSE;
3481 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3482 (MENU_GetMenu((HMENU16)id))->wFlags |= MF_POPUP;
3484 item->hCheckBit = item->hUnCheckBit = 0;
3485 return TRUE;
3489 /*******************************************************************
3490 * InsertMenuA (USER32.@)
3492 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3493 UINT id, LPCSTR str )
3495 BOOL ret = FALSE;
3497 if (IS_STRING_ITEM(flags) && str)
3499 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3500 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3501 if (newstr)
3503 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3504 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3505 HeapFree( GetProcessHeap(), 0, newstr );
3507 return ret;
3509 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3513 /*******************************************************************
3514 * AppendMenu (USER.411)
3516 BOOL16 WINAPI AppendMenu16(HMENU16 hMenu, UINT16 flags, UINT16 id, SEGPTR data)
3518 return InsertMenu16( hMenu, -1, flags | MF_BYPOSITION, id, data );
3522 /*******************************************************************
3523 * AppendMenuA (USER32.@)
3525 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3526 UINT id, LPCSTR data )
3528 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3532 /*******************************************************************
3533 * AppendMenuW (USER32.@)
3535 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3536 UINT id, LPCWSTR data )
3538 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3542 /**********************************************************************
3543 * RemoveMenu (USER.412)
3545 BOOL16 WINAPI RemoveMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3547 return RemoveMenu( hMenu, nPos, wFlags );
3551 /**********************************************************************
3552 * RemoveMenu (USER32.@)
3554 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3556 LPPOPUPMENU menu;
3557 MENUITEM *item;
3559 TRACE("(menu=%04x pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3560 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3561 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3563 /* Remove item */
3565 MENU_FreeItemData( item );
3567 if (--menu->nItems == 0)
3569 HeapFree( GetProcessHeap(), 0, menu->items );
3570 menu->items = NULL;
3572 else
3574 while(nPos < menu->nItems)
3576 *item = *(item+1);
3577 item++;
3578 nPos++;
3580 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3581 menu->nItems * sizeof(MENUITEM) );
3583 return TRUE;
3587 /**********************************************************************
3588 * DeleteMenu (USER.413)
3590 BOOL16 WINAPI DeleteMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3592 return DeleteMenu( hMenu, nPos, wFlags );
3596 /**********************************************************************
3597 * DeleteMenu (USER32.@)
3599 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3601 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3602 if (!item) return FALSE;
3603 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3604 /* nPos is now the position of the item */
3605 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3606 return TRUE;
3610 /*******************************************************************
3611 * ModifyMenu (USER.414)
3613 BOOL16 WINAPI ModifyMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3614 UINT16 id, SEGPTR data )
3616 if (IS_STRING_ITEM(flags))
3617 return ModifyMenuA( hMenu, pos, flags, id, MapSL(data) );
3618 return ModifyMenuA( hMenu, pos, flags, id, (LPSTR)data );
3622 /*******************************************************************
3623 * ModifyMenuW (USER32.@)
3625 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3626 UINT id, LPCWSTR str )
3628 MENUITEM *item;
3630 if (IS_STRING_ITEM(flags))
3632 TRACE("%04x %d %04x %04x %s\n",
3633 hMenu, pos, flags, id, debugstr_w(str) );
3634 if (!str) return FALSE;
3636 else
3638 TRACE("%04x %d %04x %04x %08lx\n",
3639 hMenu, pos, flags, id, (DWORD)str );
3642 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3643 return MENU_SetItemData( item, flags, id, str );
3647 /*******************************************************************
3648 * ModifyMenuA (USER32.@)
3650 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3651 UINT id, LPCSTR str )
3653 BOOL ret = FALSE;
3655 if (IS_STRING_ITEM(flags) && str)
3657 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3658 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3659 if (newstr)
3661 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3662 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3663 HeapFree( GetProcessHeap(), 0, newstr );
3665 return ret;
3667 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3671 /**********************************************************************
3672 * CreatePopupMenu (USER.415)
3674 HMENU16 WINAPI CreatePopupMenu16(void)
3676 return CreatePopupMenu();
3680 /**********************************************************************
3681 * CreatePopupMenu (USER32.@)
3683 HMENU WINAPI CreatePopupMenu(void)
3685 HMENU hmenu;
3686 POPUPMENU *menu;
3688 if (!(hmenu = CreateMenu())) return 0;
3689 menu = MENU_GetMenu( hmenu );
3690 menu->wFlags |= MF_POPUP;
3691 menu->bTimeToHide = FALSE;
3692 return hmenu;
3696 /**********************************************************************
3697 * GetMenuCheckMarkDimensions (USER.417)
3698 * GetMenuCheckMarkDimensions (USER32.@)
3700 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3702 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3706 /**********************************************************************
3707 * SetMenuItemBitmaps (USER.418)
3709 BOOL16 WINAPI SetMenuItemBitmaps16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags,
3710 HBITMAP16 hNewUnCheck, HBITMAP16 hNewCheck)
3712 return SetMenuItemBitmaps( hMenu, nPos, wFlags, hNewUnCheck, hNewCheck );
3716 /**********************************************************************
3717 * SetMenuItemBitmaps (USER32.@)
3719 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3720 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3722 MENUITEM *item;
3723 TRACE("(%04x, %04x, %04x, %04x, %04x)\n",
3724 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3725 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3727 if (!hNewCheck && !hNewUnCheck)
3729 item->fState &= ~MF_USECHECKBITMAPS;
3731 else /* Install new bitmaps */
3733 item->hCheckBit = hNewCheck;
3734 item->hUnCheckBit = hNewUnCheck;
3735 item->fState |= MF_USECHECKBITMAPS;
3737 return TRUE;
3741 /**********************************************************************
3742 * CreateMenu (USER.151)
3744 HMENU16 WINAPI CreateMenu16(void)
3746 return CreateMenu();
3750 /**********************************************************************
3751 * CreateMenu (USER32.@)
3753 HMENU WINAPI CreateMenu(void)
3755 HMENU hMenu;
3756 LPPOPUPMENU menu;
3757 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3758 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3760 ZeroMemory(menu, sizeof(POPUPMENU));
3761 menu->wMagic = MENU_MAGIC;
3762 menu->FocusedItem = NO_SELECTED_ITEM;
3763 menu->bTimeToHide = FALSE;
3765 TRACE("return %04x\n", hMenu );
3767 return hMenu;
3771 /**********************************************************************
3772 * DestroyMenu (USER.152)
3774 BOOL16 WINAPI DestroyMenu16( HMENU16 hMenu )
3776 return DestroyMenu( hMenu );
3780 /**********************************************************************
3781 * DestroyMenu (USER32.@)
3783 BOOL WINAPI DestroyMenu( HMENU hMenu )
3785 TRACE("(%04x)\n", hMenu);
3787 /* Silently ignore attempts to destroy default system popup */
3789 if (hMenu && hMenu != MENU_DefSysPopup)
3791 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3793 if (!lppop) return FALSE;
3795 lppop->wMagic = 0; /* Mark it as destroyed */
3797 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3799 DestroyWindow( lppop->hWnd );
3800 lppop->hWnd = 0;
3803 if (lppop->items) /* recursively destroy submenus */
3805 int i;
3806 MENUITEM *item = lppop->items;
3807 for (i = lppop->nItems; i > 0; i--, item++)
3809 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3810 MENU_FreeItemData( item );
3812 HeapFree( GetProcessHeap(), 0, lppop->items );
3814 USER_HEAP_FREE( hMenu );
3816 return (hMenu != MENU_DefSysPopup);
3820 /**********************************************************************
3821 * GetSystemMenu (USER32.@)
3823 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3825 WND *wndPtr = WIN_FindWndPtr( hWnd );
3826 HMENU retvalue = 0;
3828 if (wndPtr)
3830 if( wndPtr->hSysMenu )
3832 if( bRevert )
3834 DestroyMenu(wndPtr->hSysMenu);
3835 wndPtr->hSysMenu = 0;
3837 else
3839 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3840 if( menu )
3842 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3843 menu->items[0].hSubMenu = MENU_CopySysPopup();
3845 else
3847 WARN("Current sys-menu (%04x) of wnd %04x is broken\n",
3848 wndPtr->hSysMenu, hWnd);
3849 wndPtr->hSysMenu = 0;
3854 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3855 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3857 if( wndPtr->hSysMenu )
3859 POPUPMENU *menu;
3860 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3862 /* Store the dummy sysmenu handle to facilitate the refresh */
3863 /* of the close button if the SC_CLOSE item change */
3864 menu = MENU_GetMenu(retvalue);
3865 if ( menu )
3866 menu->hSysMenuOwner = wndPtr->hSysMenu;
3868 WIN_ReleaseWndPtr(wndPtr);
3870 return bRevert ? 0 : retvalue;
3874 /*******************************************************************
3875 * SetSystemMenu (USER32.@)
3877 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3879 WND *wndPtr = WIN_FindWndPtr(hwnd);
3881 if (wndPtr)
3883 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3884 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3885 WIN_ReleaseWndPtr(wndPtr);
3886 return TRUE;
3888 return FALSE;
3892 /**********************************************************************
3893 * GetMenu (USER32.@)
3895 HMENU WINAPI GetMenu( HWND hWnd )
3897 HMENU retvalue = (HMENU)GetWindowLongA( hWnd, GWL_ID );
3898 TRACE("for %04x returning %04x\n", hWnd, retvalue);
3899 return retvalue;
3903 /**********************************************************************
3904 * SetMenu (USER32.@)
3906 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3908 TRACE("(%04x, %04x);\n", hWnd, hMenu);
3910 if (hMenu && !IsMenu(hMenu))
3912 WARN("hMenu %x is not a menu handle\n", hMenu);
3913 return FALSE;
3915 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3917 hWnd = WIN_GetFullHandle( hWnd );
3918 if (GetCapture() == hWnd) ReleaseCapture();
3920 if (hMenu != 0)
3922 LPPOPUPMENU lpmenu;
3924 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3926 lpmenu->hWnd = hWnd;
3927 lpmenu->Height = 0; /* Make sure we recalculate the size */
3929 SetWindowLongA( hWnd, GWL_ID, hMenu );
3931 if (IsWindowVisible(hWnd))
3932 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3933 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3934 return TRUE;
3939 /**********************************************************************
3940 * GetSubMenu (USER.159)
3942 HMENU16 WINAPI GetSubMenu16( HMENU16 hMenu, INT16 nPos )
3944 return GetSubMenu( hMenu, nPos );
3948 /**********************************************************************
3949 * GetSubMenu (USER32.@)
3951 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3953 MENUITEM * lpmi;
3955 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3956 if (!(lpmi->fType & MF_POPUP)) return 0;
3957 return lpmi->hSubMenu;
3961 /**********************************************************************
3962 * DrawMenuBar (USER32.@)
3964 BOOL WINAPI DrawMenuBar( HWND hWnd )
3966 LPPOPUPMENU lppop;
3967 HMENU hMenu = GetMenu(hWnd);
3969 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3970 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3972 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3973 lppop->hwndOwner = hWnd;
3974 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3975 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3976 return TRUE;
3979 /***********************************************************************
3980 * DrawMenuBarTemp (USER32.@)
3982 DWORD WINAPI DrawMenuBarTemp(DWORD p1, DWORD p2)
3984 FIXME("(%08lx %08lx): stub\n", p1, p2);
3985 return 0;
3988 /***********************************************************************
3989 * EndMenu (USER.187)
3990 * EndMenu (USER32.@)
3992 void WINAPI EndMenu(void)
3994 /* if we are in the menu code, and it is active */
3995 if (!fEndMenu && top_popup)
3997 /* terminate the menu handling code */
3998 fEndMenu = TRUE;
4000 /* needs to be posted to wakeup the internal menu handler */
4001 /* which will now terminate the menu, in the event that */
4002 /* the main window was minimized, or lost focus, so we */
4003 /* don't end up with an orphaned menu */
4004 PostMessageA( top_popup, WM_CANCELMODE, 0, 0);
4009 /***********************************************************************
4010 * LookupMenuHandle (USER.217)
4012 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4014 HMENU hmenu32 = hmenu;
4015 UINT id32 = id;
4016 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4017 else return hmenu32;
4021 /**********************************************************************
4022 * LoadMenu (USER.150)
4024 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4026 HRSRC16 hRsrc;
4027 HGLOBAL16 handle;
4028 HMENU16 hMenu;
4030 TRACE("(%04x,%s)\n", instance, debugres_a(name) );
4032 if (HIWORD(name))
4034 if (name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4037 if (!name) return 0;
4039 /* check for Win32 module */
4040 if (HIWORD(instance)) return LoadMenuA( instance, name );
4041 instance = GetExePtr( instance );
4043 if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
4044 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4045 hMenu = LoadMenuIndirect16(LockResource16(handle));
4046 FreeResource16( handle );
4047 return hMenu;
4051 /*****************************************************************
4052 * LoadMenuA (USER32.@)
4054 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4056 HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
4057 if (!hrsrc) return 0;
4058 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4062 /*****************************************************************
4063 * LoadMenuW (USER32.@)
4065 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4067 HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
4068 if (!hrsrc) return 0;
4069 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4073 /**********************************************************************
4074 * LoadMenuIndirect (USER.220)
4076 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4078 HMENU16 hMenu;
4079 WORD version, offset;
4080 LPCSTR p = (LPCSTR)template;
4082 TRACE("(%p)\n", template );
4083 version = GET_WORD(p);
4084 p += sizeof(WORD);
4085 if (version)
4087 WARN("version must be 0 for Win16\n" );
4088 return 0;
4090 offset = GET_WORD(p);
4091 p += sizeof(WORD) + offset;
4092 if (!(hMenu = CreateMenu())) return 0;
4093 if (!MENU_ParseResource( p, hMenu, FALSE ))
4095 DestroyMenu( hMenu );
4096 return 0;
4098 return hMenu;
4102 /**********************************************************************
4103 * LoadMenuIndirectA (USER32.@)
4105 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4107 HMENU16 hMenu;
4108 WORD version, offset;
4109 LPCSTR p = (LPCSTR)template;
4111 TRACE("%p\n", template );
4112 version = GET_WORD(p);
4113 p += sizeof(WORD);
4114 switch (version)
4116 case 0:
4117 offset = GET_WORD(p);
4118 p += sizeof(WORD) + offset;
4119 if (!(hMenu = CreateMenu())) return 0;
4120 if (!MENU_ParseResource( p, hMenu, TRUE ))
4122 DestroyMenu( hMenu );
4123 return 0;
4125 return hMenu;
4126 case 1:
4127 offset = GET_WORD(p);
4128 p += sizeof(WORD) + offset;
4129 if (!(hMenu = CreateMenu())) return 0;
4130 if (!MENUEX_ParseResource( p, hMenu))
4132 DestroyMenu( hMenu );
4133 return 0;
4135 return hMenu;
4136 default:
4137 ERR("version %d not supported.\n", version);
4138 return 0;
4143 /**********************************************************************
4144 * LoadMenuIndirectW (USER32.@)
4146 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4148 /* FIXME: is there anything different between A and W? */
4149 return LoadMenuIndirectA( template );
4153 /**********************************************************************
4154 * IsMenu (USER.358)
4156 BOOL16 WINAPI IsMenu16( HMENU16 hmenu )
4158 return IsMenu( hmenu );
4162 /**********************************************************************
4163 * IsMenu (USER32.@)
4165 BOOL WINAPI IsMenu(HMENU hmenu)
4167 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4168 return menu != NULL;
4171 /**********************************************************************
4172 * GetMenuItemInfo_common
4175 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4176 LPMENUITEMINFOW lpmii, BOOL unicode)
4178 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4180 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4182 if (!menu)
4183 return FALSE;
4185 if (lpmii->fMask & MIIM_TYPE) {
4186 lpmii->fType = menu->fType;
4187 switch (MENU_ITEM_TYPE(menu->fType)) {
4188 case MF_STRING:
4189 break; /* will be done below */
4190 case MF_OWNERDRAW:
4191 case MF_BITMAP:
4192 lpmii->dwTypeData = menu->text;
4193 /* fall through */
4194 default:
4195 lpmii->cch = 0;
4199 /* copy the text string */
4200 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4201 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4203 int len;
4204 if (unicode)
4206 len = strlenW(menu->text);
4207 if(lpmii->dwTypeData && lpmii->cch)
4208 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4210 else
4212 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4213 if(lpmii->dwTypeData && lpmii->cch)
4214 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4215 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4216 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4218 /* if we've copied a substring we return its length */
4219 if(lpmii->dwTypeData && lpmii->cch)
4221 if (lpmii->cch <= len) lpmii->cch--;
4223 else /* return length of string */
4224 lpmii->cch = len;
4227 if (lpmii->fMask & MIIM_FTYPE)
4228 lpmii->fType = menu->fType;
4230 if (lpmii->fMask & MIIM_BITMAP)
4231 lpmii->hbmpItem = menu->hbmpItem;
4233 if (lpmii->fMask & MIIM_STATE)
4234 lpmii->fState = menu->fState;
4236 if (lpmii->fMask & MIIM_ID)
4237 lpmii->wID = menu->wID;
4239 if (lpmii->fMask & MIIM_SUBMENU)
4240 lpmii->hSubMenu = menu->hSubMenu;
4242 if (lpmii->fMask & MIIM_CHECKMARKS) {
4243 lpmii->hbmpChecked = menu->hCheckBit;
4244 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4246 if (lpmii->fMask & MIIM_DATA)
4247 lpmii->dwItemData = menu->dwItemData;
4249 return TRUE;
4252 /**********************************************************************
4253 * GetMenuItemInfoA (USER32.@)
4255 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4256 LPMENUITEMINFOA lpmii)
4258 return GetMenuItemInfo_common (hmenu, item, bypos,
4259 (LPMENUITEMINFOW)lpmii, FALSE);
4262 /**********************************************************************
4263 * GetMenuItemInfoW (USER32.@)
4265 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4266 LPMENUITEMINFOW lpmii)
4268 return GetMenuItemInfo_common (hmenu, item, bypos,
4269 lpmii, TRUE);
4273 /* set a menu item text from a ASCII or Unicode string */
4274 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4276 if (!text)
4278 menu->text = NULL;
4279 menu->fType |= MF_SEPARATOR;
4281 else if (unicode)
4283 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4284 strcpyW( menu->text, text );
4286 else
4288 LPCSTR str = (LPCSTR)text;
4289 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4290 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4291 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4296 /**********************************************************************
4297 * SetMenuItemInfo_common
4300 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4301 const MENUITEMINFOW *lpmii,
4302 BOOL unicode)
4304 if (!menu) return FALSE;
4306 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4308 if (lpmii->fMask & MIIM_TYPE ) {
4309 /* Get rid of old string. */
4310 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4311 HeapFree(GetProcessHeap(), 0, menu->text);
4312 menu->text = NULL;
4315 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4316 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4317 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4319 menu->text = lpmii->dwTypeData;
4321 if (IS_STRING_ITEM(menu->fType))
4322 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4325 if (lpmii->fMask & MIIM_FTYPE ) {
4326 /* free the string when the type is changing */
4327 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4328 HeapFree(GetProcessHeap(), 0, menu->text);
4329 menu->text = NULL;
4331 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4332 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4333 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4334 menu->fType |= MF_SEPARATOR;
4337 if (lpmii->fMask & MIIM_STRING ) {
4338 /* free the string when used */
4339 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4340 HeapFree(GetProcessHeap(), 0, menu->text);
4341 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4345 if (lpmii->fMask & MIIM_STATE)
4347 /* fixme: MFS_DEFAULT do we have to reset the other menu items? */
4348 menu->fState = lpmii->fState;
4351 if (lpmii->fMask & MIIM_ID)
4352 menu->wID = lpmii->wID;
4354 if (lpmii->fMask & MIIM_SUBMENU) {
4355 menu->hSubMenu = lpmii->hSubMenu;
4356 if (menu->hSubMenu) {
4357 POPUPMENU *subMenu = MENU_GetMenu((UINT16)menu->hSubMenu);
4358 if (subMenu) {
4359 subMenu->wFlags |= MF_POPUP;
4360 menu->fType |= MF_POPUP;
4362 else
4363 /* FIXME: Return an error ? */
4364 menu->fType &= ~MF_POPUP;
4366 else
4367 menu->fType &= ~MF_POPUP;
4370 if (lpmii->fMask & MIIM_CHECKMARKS)
4372 if (lpmii->fType & MFT_RADIOCHECK)
4373 menu->fType |= MFT_RADIOCHECK;
4375 menu->hCheckBit = lpmii->hbmpChecked;
4376 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4378 if (lpmii->fMask & MIIM_DATA)
4379 menu->dwItemData = lpmii->dwItemData;
4381 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4382 return TRUE;
4385 /**********************************************************************
4386 * SetMenuItemInfoA (USER32.@)
4388 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4389 const MENUITEMINFOA *lpmii)
4391 if ((lpmii->fType & (MF_HILITE|MF_POPUP)) || (lpmii->fState)) {
4392 /* QuickTime does pass invalid data into SetMenuItemInfo.
4393 * do some of the checks Windows does.
4395 WARN("Bad masks for type (0x%08x) or state (0x%08x)\n",
4396 lpmii->fType,lpmii->fState );
4397 return FALSE;
4400 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4401 (const MENUITEMINFOW *)lpmii, FALSE);
4404 /**********************************************************************
4405 * SetMenuItemInfoW (USER32.@)
4407 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4408 const MENUITEMINFOW *lpmii)
4410 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4411 lpmii, TRUE);
4414 /**********************************************************************
4415 * SetMenuDefaultItem (USER32.@)
4418 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4420 UINT i;
4421 POPUPMENU *menu;
4422 MENUITEM *item;
4424 TRACE("(0x%x,%d,%d)\n", hmenu, uItem, bypos);
4426 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4428 /* reset all default-item flags */
4429 item = menu->items;
4430 for (i = 0; i < menu->nItems; i++, item++)
4432 item->fState &= ~MFS_DEFAULT;
4435 /* no default item */
4436 if ( -1 == uItem)
4438 return TRUE;
4441 item = menu->items;
4442 if ( bypos )
4444 if ( uItem >= menu->nItems ) return FALSE;
4445 item[uItem].fState |= MFS_DEFAULT;
4446 return TRUE;
4448 else
4450 for (i = 0; i < menu->nItems; i++, item++)
4452 if (item->wID == uItem)
4454 item->fState |= MFS_DEFAULT;
4455 return TRUE;
4460 return FALSE;
4463 /**********************************************************************
4464 * GetMenuDefaultItem (USER32.@)
4466 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4468 POPUPMENU *menu;
4469 MENUITEM * item;
4470 UINT i = 0;
4472 TRACE("(0x%x,%d,%d)\n", hmenu, bypos, flags);
4474 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4476 /* find default item */
4477 item = menu->items;
4479 /* empty menu */
4480 if (! item) return -1;
4482 while ( !( item->fState & MFS_DEFAULT ) )
4484 i++; item++;
4485 if (i >= menu->nItems ) return -1;
4488 /* default: don't return disabled items */
4489 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4491 /* search rekursiv when needed */
4492 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4494 UINT ret;
4495 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4496 if ( -1 != ret ) return ret;
4498 /* when item not found in submenu, return the popup item */
4500 return ( bypos ) ? i : item->wID;
4504 /*******************************************************************
4505 * InsertMenuItem (USER.441)
4507 * FIXME: untested
4509 BOOL16 WINAPI InsertMenuItem16( HMENU16 hmenu, UINT16 pos, BOOL16 byposition,
4510 const MENUITEMINFO16 *mii )
4512 MENUITEMINFOA miia;
4514 miia.cbSize = sizeof(miia);
4515 miia.fMask = mii->fMask;
4516 miia.dwTypeData = (LPSTR)mii->dwTypeData;
4517 miia.fType = mii->fType;
4518 miia.fState = mii->fState;
4519 miia.wID = mii->wID;
4520 miia.hSubMenu = mii->hSubMenu;
4521 miia.hbmpChecked = mii->hbmpChecked;
4522 miia.hbmpUnchecked = mii->hbmpUnchecked;
4523 miia.dwItemData = mii->dwItemData;
4524 miia.cch = mii->cch;
4525 if (IS_STRING_ITEM(miia.fType))
4526 miia.dwTypeData = MapSL(mii->dwTypeData);
4527 return InsertMenuItemA( hmenu, pos, byposition, &miia );
4531 /**********************************************************************
4532 * InsertMenuItemA (USER32.@)
4534 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4535 const MENUITEMINFOA *lpmii)
4537 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4538 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4542 /**********************************************************************
4543 * InsertMenuItemW (USER32.@)
4545 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4546 const MENUITEMINFOW *lpmii)
4548 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4549 return SetMenuItemInfo_common(item, lpmii, TRUE);
4552 /**********************************************************************
4553 * CheckMenuRadioItem (USER32.@)
4556 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4557 UINT first, UINT last, UINT check,
4558 UINT bypos)
4560 MENUITEM *mifirst, *milast, *micheck;
4561 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4563 TRACE("ox%x: %d-%d, check %d, bypos=%d\n",
4564 hMenu, first, last, check, bypos);
4566 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4567 milast = MENU_FindItem (&mlast, &last, bypos);
4568 micheck = MENU_FindItem (&mcheck, &check, bypos);
4570 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4571 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4572 micheck > milast || micheck < mifirst)
4573 return FALSE;
4575 while (mifirst <= milast)
4577 if (mifirst == micheck)
4579 mifirst->fType |= MFT_RADIOCHECK;
4580 mifirst->fState |= MFS_CHECKED;
4581 } else {
4582 mifirst->fType &= ~MFT_RADIOCHECK;
4583 mifirst->fState &= ~MFS_CHECKED;
4585 mifirst++;
4588 return TRUE;
4591 /**********************************************************************
4592 * CheckMenuRadioItem (USER.666)
4594 BOOL16 WINAPI CheckMenuRadioItem16(HMENU16 hMenu,
4595 UINT16 first, UINT16 last, UINT16 check,
4596 BOOL16 bypos)
4598 return CheckMenuRadioItem (hMenu, first, last, check, bypos);
4601 /**********************************************************************
4602 * GetMenuItemRect (USER32.@)
4604 * ATTENTION: Here, the returned values in rect are the screen
4605 * coordinates of the item just like if the menu was
4606 * always on the upper left side of the application.
4609 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4610 LPRECT rect)
4612 POPUPMENU *itemMenu;
4613 MENUITEM *item;
4614 HWND referenceHwnd;
4616 TRACE("(0x%x,0x%x,%d,%p)\n", hwnd, hMenu, uItem, rect);
4618 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4619 referenceHwnd = hwnd;
4621 if(!hwnd)
4623 itemMenu = MENU_GetMenu(hMenu);
4624 if (itemMenu == NULL)
4625 return FALSE;
4627 if(itemMenu->hWnd == 0)
4628 return FALSE;
4629 referenceHwnd = itemMenu->hWnd;
4632 if ((rect == NULL) || (item == NULL))
4633 return FALSE;
4635 *rect = item->rect;
4637 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4639 return TRUE;
4643 /**********************************************************************
4644 * SetMenuInfo (USER32.@)
4646 * FIXME
4647 * MIM_APPLYTOSUBMENUS
4648 * actually use the items to draw the menu
4650 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4652 POPUPMENU *menu;
4654 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4656 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4659 if (lpmi->fMask & MIM_BACKGROUND)
4660 menu->hbrBack = lpmi->hbrBack;
4662 if (lpmi->fMask & MIM_HELPID)
4663 menu->dwContextHelpID = lpmi->dwContextHelpID;
4665 if (lpmi->fMask & MIM_MAXHEIGHT)
4666 menu->cyMax = lpmi->cyMax;
4668 if (lpmi->fMask & MIM_MENUDATA)
4669 menu->dwMenuData = lpmi->dwMenuData;
4671 if (lpmi->fMask & MIM_STYLE)
4672 menu->dwStyle = lpmi->dwStyle;
4674 return TRUE;
4676 return FALSE;
4679 /**********************************************************************
4680 * GetMenuInfo (USER32.@)
4682 * NOTES
4683 * win98/NT5.0
4686 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4687 { POPUPMENU *menu;
4689 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4691 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4694 if (lpmi->fMask & MIM_BACKGROUND)
4695 lpmi->hbrBack = menu->hbrBack;
4697 if (lpmi->fMask & MIM_HELPID)
4698 lpmi->dwContextHelpID = menu->dwContextHelpID;
4700 if (lpmi->fMask & MIM_MAXHEIGHT)
4701 lpmi->cyMax = menu->cyMax;
4703 if (lpmi->fMask & MIM_MENUDATA)
4704 lpmi->dwMenuData = menu->dwMenuData;
4706 if (lpmi->fMask & MIM_STYLE)
4707 lpmi->dwStyle = menu->dwStyle;
4709 return TRUE;
4711 return FALSE;
4714 /**********************************************************************
4715 * SetMenuContextHelpId (USER.384)
4717 BOOL16 WINAPI SetMenuContextHelpId16( HMENU16 hMenu, DWORD dwContextHelpID)
4719 return SetMenuContextHelpId( hMenu, dwContextHelpID );
4723 /**********************************************************************
4724 * SetMenuContextHelpId (USER32.@)
4726 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4728 LPPOPUPMENU menu;
4730 TRACE("(0x%04x 0x%08lx)\n", hMenu, dwContextHelpID);
4732 if ((menu = MENU_GetMenu(hMenu)))
4734 menu->dwContextHelpID = dwContextHelpID;
4735 return TRUE;
4737 return FALSE;
4740 /**********************************************************************
4741 * GetMenuContextHelpId (USER.385)
4743 DWORD WINAPI GetMenuContextHelpId16( HMENU16 hMenu )
4745 return GetMenuContextHelpId( hMenu );
4748 /**********************************************************************
4749 * GetMenuContextHelpId (USER32.@)
4751 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4753 LPPOPUPMENU menu;
4755 TRACE("(0x%04x)\n", hMenu);
4757 if ((menu = MENU_GetMenu(hMenu)))
4759 return menu->dwContextHelpID;
4761 return 0;
4764 /**********************************************************************
4765 * MenuItemFromPoint (USER32.@)
4767 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4769 FIXME("(0x%04x,0x%04x,(%ld,%ld)):stub\n",
4770 hWnd, hMenu, ptScreen.x, ptScreen.y);
4771 return 0;
4775 /**********************************************************************
4776 * translate_accelerator
4778 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4779 BYTE fVirt, WORD key, WORD cmd )
4781 UINT mesg = 0;
4783 if (wParam != key) return FALSE;
4785 if (message == WM_CHAR)
4787 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4789 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4790 goto found;
4793 else
4795 if(fVirt & FVIRTKEY)
4797 INT mask = 0;
4798 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4799 wParam, 0xff & HIWORD(lParam));
4800 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4801 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4802 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4803 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4804 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4806 else
4808 if (!(lParam & 0x01000000)) /* no special_key */
4810 if ((fVirt & FALT) && (lParam & 0x20000000))
4811 { /* ^^ ALT pressed */
4812 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4813 goto found;
4818 return FALSE;
4820 found:
4821 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4822 mesg = 1;
4823 else if (GetCapture())
4824 mesg = 2;
4825 else if (!IsWindowEnabled(hWnd))
4826 mesg = 3;
4827 else
4829 HMENU hMenu, hSubMenu, hSysMenu;
4830 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4832 hMenu = (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4833 hSysMenu = get_win_sys_menu( hWnd );
4835 /* find menu item and ask application to initialize it */
4836 /* 1. in the system menu */
4837 hSubMenu = hSysMenu;
4838 nPos = cmd;
4839 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4841 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4842 if(hSubMenu != hSysMenu)
4844 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4845 TRACE_(accel)("hSysMenu = %04x, hSubMenu = %04x, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4846 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4848 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4850 else /* 2. in the window's menu */
4852 hSubMenu = hMenu;
4853 nPos = cmd;
4854 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4856 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4857 if(hSubMenu != hMenu)
4859 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4860 TRACE_(accel)("hMenu = %04x, hSubMenu = %04x, nPos = %d\n", hMenu, hSubMenu, nPos);
4861 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4863 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4867 if (uSysStat != (UINT)-1)
4869 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4870 mesg=4;
4871 else
4872 mesg=WM_SYSCOMMAND;
4874 else
4876 if (uStat != (UINT)-1)
4878 if (IsIconic(hWnd))
4879 mesg=5;
4880 else
4882 if (uStat & (MF_DISABLED|MF_GRAYED))
4883 mesg=6;
4884 else
4885 mesg=WM_COMMAND;
4888 else
4889 mesg=WM_COMMAND;
4893 if( mesg==WM_COMMAND )
4895 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4896 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
4898 else if( mesg==WM_SYSCOMMAND )
4900 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4901 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
4903 else
4905 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4906 * #0: unknown (please report!)
4907 * #1: for WM_KEYUP,WM_SYSKEYUP
4908 * #2: mouse is captured
4909 * #3: window is disabled
4910 * #4: it's a disabled system menu option
4911 * #5: it's a menu option, but window is iconic
4912 * #6: it's a menu option, but disabled
4914 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4915 if(mesg==0)
4916 ERR_(accel)(" unknown reason - please report!");
4918 return TRUE;
4921 /**********************************************************************
4922 * TranslateAccelerator (USER32.@)
4923 * TranslateAcceleratorA (USER32.@)
4924 * TranslateAcceleratorW (USER32.@)
4926 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
4928 /* YES, Accel16! */
4929 LPACCEL16 lpAccelTbl;
4930 int i;
4932 if (msg == NULL)
4934 WARN_(accel)("msg null; should hang here to be win compatible\n");
4935 return 0;
4937 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
4939 WARN_(accel)("invalid accel handle=%x\n", hAccel);
4940 return 0;
4942 if ((msg->message != WM_KEYDOWN &&
4943 msg->message != WM_KEYUP &&
4944 msg->message != WM_SYSKEYDOWN &&
4945 msg->message != WM_SYSKEYUP &&
4946 msg->message != WM_CHAR)) return 0;
4948 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
4949 "msg->hwnd=%04x, msg->message=%04x, wParam=%08x, lParam=%lx\n",
4950 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4952 i = 0;
4955 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4956 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4957 return 1;
4958 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4959 WARN_(accel)("couldn't translate accelerator key\n");
4960 return 0;