Added support for composite Unicode characters in MultiByteToWideChar
[wine/hacks.git] / controls / menu.c
blob56e6807cab9d4ee4511abe7654d6a25790ecccfc
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 <assert.h>
16 #include <ctype.h>
17 #include <stdlib.h>
18 #include <string.h>
20 #include "windef.h"
21 #include "winnls.h"
22 #include "wingdi.h"
23 #include "wine/winbase16.h"
24 #include "wine/winuser16.h"
25 #include "wine/unicode.h"
26 #include "wine/port.h"
27 #include "win.h"
28 #include "task.h"
29 #include "heap.h"
30 #include "controls.h"
31 #include "nonclient.h"
32 #include "user.h"
33 #include "message.h"
34 #include "queue.h"
35 #include "tweak.h"
37 #include "debugtools.h"
39 DEFAULT_DEBUG_CHANNEL(menu);
40 DECLARE_DEBUG_CHANNEL(accel);
42 /* internal popup menu window messages */
44 #define MM_SETMENUHANDLE (WM_USER + 0)
45 #define MM_GETMENUHANDLE (WM_USER + 1)
47 /* Menu item structure */
48 typedef struct {
49 /* ----------- MENUITEMINFO Stuff ----------- */
50 UINT fType; /* Item type. */
51 UINT fState; /* Item state. */
52 UINT wID; /* Item id. */
53 HMENU hSubMenu; /* Pop-up menu. */
54 HBITMAP hCheckBit; /* Bitmap when checked. */
55 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
56 LPWSTR text; /* Item text or bitmap handle. */
57 DWORD dwItemData; /* Application defined. */
58 DWORD dwTypeData; /* depends on fMask */
59 HBITMAP hbmpItem; /* bitmap in win98 style menus */
60 /* ----------- Wine stuff ----------- */
61 RECT rect; /* Item area (relative to menu window) */
62 UINT xTab; /* X position of text after Tab */
63 } MENUITEM;
65 /* Popup menu structure */
66 typedef struct {
67 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
68 WORD wMagic; /* Magic number */
69 HQUEUE16 hTaskQ; /* Task queue for this menu */
70 WORD Width; /* Width of the whole menu */
71 WORD Height; /* Height of the whole menu */
72 WORD nItems; /* Number of items in the menu */
73 HWND hWnd; /* Window containing the menu */
74 MENUITEM *items; /* Array of menu items */
75 UINT FocusedItem; /* Currently focused item */
76 HWND hwndOwner; /* window receiving the messages for ownerdraw */
77 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
78 /* ------------ MENUINFO members ------ */
79 DWORD dwStyle; /* Extended mennu style */
80 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
81 HBRUSH hbrBack; /* brush for menu background */
82 DWORD dwContextHelpID;
83 DWORD dwMenuData; /* application defined value */
84 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
85 } POPUPMENU, *LPPOPUPMENU;
87 /* internal flags for menu tracking */
89 #define TF_ENDMENU 0x0001
90 #define TF_SUSPENDPOPUP 0x0002
91 #define TF_SKIPREMOVE 0x0004
93 typedef struct
95 UINT trackFlags;
96 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
97 HMENU hTopMenu; /* initial menu */
98 HWND hOwnerWnd; /* where notifications are sent */
99 POINT pt;
100 } MTRACKER;
102 #define MENU_MAGIC 0x554d /* 'MU' */
103 #define IS_A_MENU(pmenu) ((pmenu) && (pmenu)->wMagic == MENU_MAGIC)
105 #define ITEM_PREV -1
106 #define ITEM_NEXT 1
108 /* Internal MENU_TrackMenu() flags */
109 #define TPM_INTERNAL 0xF0000000
110 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
111 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
112 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
114 /* popup menu shade thickness */
115 #define POPUP_XSHADE 4
116 #define POPUP_YSHADE 4
118 /* Space between 2 menu bar items */
119 #define MENU_BAR_ITEMS_SPACE 12
121 /* Minimum width of a tab character */
122 #define MENU_TAB_SPACE 8
124 /* Height of a separator item */
125 #define SEPARATOR_HEIGHT 5
127 /* (other menu->FocusedItem values give the position of the focused item) */
128 #define NO_SELECTED_ITEM 0xffff
130 #define MENU_ITEM_TYPE(flags) \
131 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
133 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
134 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
136 #define IS_SYSTEM_MENU(menu) \
137 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
139 #define IS_SYSTEM_POPUP(menu) \
140 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
142 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
143 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
144 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
145 MF_POPUP | MF_SYSMENU | MF_HELP)
146 #define STATE_MASK (~TYPE_MASK)
148 /* Dimension of the menu bitmaps */
149 static WORD check_bitmap_width = 0, check_bitmap_height = 0;
150 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
152 static HBITMAP hStdRadioCheck = 0;
153 static HBITMAP hStdCheck = 0;
154 static HBITMAP hStdMnArrow = 0;
156 /* Minimze/restore/close buttons to be inserted in menubar */
157 static HBITMAP hBmpMinimize = 0;
158 static HBITMAP hBmpMinimizeD = 0;
159 static HBITMAP hBmpMaximize = 0;
160 static HBITMAP hBmpMaximizeD = 0;
161 static HBITMAP hBmpClose = 0;
162 static HBITMAP hBmpCloseD = 0;
165 static HBRUSH hShadeBrush = 0;
166 static HFONT hMenuFont = 0;
167 static HFONT hMenuFontBold = 0;
169 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
171 /* Use global popup window because there's no way 2 menus can
172 * be tracked at the same time. */
174 static WND* pTopPopupWnd = 0;
175 static UINT uSubPWndLevel = 0;
177 /* Flag set by EndMenu() to force an exit from menu tracking */
178 static BOOL fEndMenu = FALSE;
180 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
183 /*********************************************************************
184 * menu class descriptor
186 const struct builtin_class_descr MENU_builtin_class =
188 POPUPMENU_CLASS_ATOM, /* name */
189 CS_GLOBALCLASS | CS_SAVEBITS, /* style */
190 NULL, /* procA (winproc is Unicode only) */
191 PopupMenuWndProc, /* procW */
192 sizeof(HMENU), /* extra */
193 IDC_ARROWA, /* cursor */
194 COLOR_MENU+1 /* brush */
198 /***********************************************************************
199 * debug_print_menuitem
201 * Print a menuitem in readable form.
204 #define debug_print_menuitem(pre, mp, post) \
205 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
207 #define MENUOUT(text) \
208 DPRINTF("%s%s", (count++ ? "," : ""), (text))
210 #define MENUFLAG(bit,text) \
211 do { \
212 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
213 } while (0)
215 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
216 const char *postfix)
218 TRACE("%s ", prefix);
219 if (mp) {
220 UINT flags = mp->fType;
221 int typ = MENU_ITEM_TYPE(flags);
222 DPRINTF( "{ ID=0x%x", mp->wID);
223 if (flags & MF_POPUP)
224 DPRINTF( ", Sub=0x%x", mp->hSubMenu);
225 if (flags) {
226 int count = 0;
227 DPRINTF( ", Typ=");
228 if (typ == MFT_STRING)
229 /* Nothing */ ;
230 else if (typ == MFT_SEPARATOR)
231 MENUOUT("sep");
232 else if (typ == MFT_OWNERDRAW)
233 MENUOUT("own");
234 else if (typ == MFT_BITMAP)
235 MENUOUT("bit");
236 else
237 MENUOUT("???");
238 flags -= typ;
240 MENUFLAG(MF_POPUP, "pop");
241 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
242 MENUFLAG(MFT_MENUBREAK, "brk");
243 MENUFLAG(MFT_RADIOCHECK, "radio");
244 MENUFLAG(MFT_RIGHTORDER, "rorder");
245 MENUFLAG(MF_SYSMENU, "sys");
246 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
248 if (flags)
249 DPRINTF( "+0x%x", flags);
251 flags = mp->fState;
252 if (flags) {
253 int count = 0;
254 DPRINTF( ", State=");
255 MENUFLAG(MFS_GRAYED, "grey");
256 MENUFLAG(MFS_DEFAULT, "default");
257 MENUFLAG(MFS_DISABLED, "dis");
258 MENUFLAG(MFS_CHECKED, "check");
259 MENUFLAG(MFS_HILITE, "hi");
260 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
261 MENUFLAG(MF_MOUSESELECT, "mouse");
262 if (flags)
263 DPRINTF( "+0x%x", flags);
265 if (mp->hCheckBit)
266 DPRINTF( ", Chk=0x%x", mp->hCheckBit);
267 if (mp->hUnCheckBit)
268 DPRINTF( ", Unc=0x%x", mp->hUnCheckBit);
270 if (typ == MFT_STRING) {
271 if (mp->text)
272 DPRINTF( ", Text=\"%s\"", debugstr_w(mp->text));
273 else
274 DPRINTF( ", Text=Null");
275 } else if (mp->text == NULL)
276 /* Nothing */ ;
277 else
278 DPRINTF( ", Text=%p", mp->text);
279 if (mp->dwItemData)
280 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
281 DPRINTF( " }");
282 } else {
283 DPRINTF( "NULL");
286 DPRINTF(" %s\n", postfix);
289 #undef MENUOUT
290 #undef MENUFLAG
293 /***********************************************************************
294 * MENU_GetMenu
296 * Validate the given menu handle and returns the menu structure pointer.
298 POPUPMENU *MENU_GetMenu(HMENU hMenu)
300 POPUPMENU *menu;
301 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR(hMenu);
302 if (!IS_A_MENU(menu))
304 WARN("invalid menu handle=%x, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
305 menu = NULL;
307 return menu;
310 /***********************************************************************
311 * MENU_CopySysPopup
313 * Return the default system menu.
315 static HMENU MENU_CopySysPopup(void)
317 HMENU hMenu = LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
319 if( hMenu ) {
320 POPUPMENU* menu = (POPUPMENU *) USER_HEAP_LIN_ADDR(hMenu);
321 menu->wFlags |= MF_SYSMENU | MF_POPUP;
322 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
324 else {
325 hMenu = 0;
326 ERR("Unable to load default system menu\n" );
329 TRACE("returning %x.\n", hMenu );
331 return hMenu;
334 /***********************************************************************
335 * MENU_GetTopPopupWnd()
337 * Return the locked pointer pTopPopupWnd.
339 static WND *MENU_GetTopPopupWnd()
341 return WIN_LockWndPtr(pTopPopupWnd);
343 /***********************************************************************
344 * MENU_ReleaseTopPopupWnd()
346 * Release the locked pointer pTopPopupWnd.
348 static void MENU_ReleaseTopPopupWnd()
350 WIN_ReleaseWndPtr(pTopPopupWnd);
352 /***********************************************************************
353 * MENU_DestroyTopPopupWnd()
355 * Destroy the locked pointer pTopPopupWnd.
357 static void MENU_DestroyTopPopupWnd()
359 WND *tmpWnd = pTopPopupWnd;
360 pTopPopupWnd = NULL;
361 WIN_ReleaseWndPtr(tmpWnd);
366 /**********************************************************************
367 * MENU_GetSysMenu
369 * Create a copy of the system menu. System menu in Windows is
370 * a special menu bar with the single entry - system menu popup.
371 * This popup is presented to the outside world as a "system menu".
372 * However, the real system menu handle is sometimes seen in the
373 * WM_MENUSELECT parameters (and Word 6 likes it this way).
375 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
377 HMENU hMenu;
379 if ((hMenu = CreateMenu()))
381 POPUPMENU *menu = (POPUPMENU*) USER_HEAP_LIN_ADDR(hMenu);
382 menu->wFlags = MF_SYSMENU;
383 menu->hWnd = hWnd;
385 if (hPopupMenu == (HMENU)(-1))
386 hPopupMenu = MENU_CopySysPopup();
387 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
389 if (hPopupMenu)
391 InsertMenuA( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION, hPopupMenu, NULL );
393 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
394 menu->items[0].fState = 0;
395 menu = (POPUPMENU*) USER_HEAP_LIN_ADDR(hPopupMenu);
396 menu->wFlags |= MF_SYSMENU;
398 TRACE("GetSysMenu hMenu=%04x (%04x)\n", hMenu, hPopupMenu );
399 return hMenu;
401 DestroyMenu( hMenu );
403 ERR("failed to load system menu!\n");
404 return 0;
408 /***********************************************************************
409 * MENU_Init
411 * Menus initialisation.
413 BOOL MENU_Init()
415 HBITMAP hBitmap;
416 NONCLIENTMETRICSA ncm;
418 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
419 0x55, 0, 0xAA, 0,
420 0x55, 0, 0xAA, 0,
421 0x55, 0, 0xAA, 0 };
423 /* Load menu bitmaps */
424 hStdCheck = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_CHECK));
425 hStdRadioCheck = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_RADIOCHECK));
426 hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
427 /* Load system buttons bitmaps */
428 hBmpMinimize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCE));
429 hBmpMinimizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCED));
430 hBmpMaximize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORE));
431 hBmpMaximizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORED));
432 hBmpClose = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSE));
433 hBmpCloseD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSED));
435 if (hStdCheck)
437 BITMAP bm;
438 GetObjectA( hStdCheck, sizeof(bm), &bm );
439 check_bitmap_width = bm.bmWidth;
440 check_bitmap_height = bm.bmHeight;
441 } else
442 return FALSE;
444 /* Assume that radio checks have the same size as regular checks. */
445 if (!hStdRadioCheck)
446 return FALSE;
448 if (hStdMnArrow)
450 BITMAP bm;
451 GetObjectA( hStdMnArrow, sizeof(bm), &bm );
452 arrow_bitmap_width = bm.bmWidth;
453 arrow_bitmap_height = bm.bmHeight;
454 } else
455 return FALSE;
457 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
458 return FALSE;
460 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
461 return FALSE;
463 DeleteObject( hBitmap );
464 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
465 return FALSE;
467 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
468 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0)))
469 return FALSE;
471 if (!(hMenuFont = CreateFontIndirectA( &ncm.lfMenuFont )))
472 return FALSE;
474 ncm.lfMenuFont.lfWeight += 300;
475 if ( ncm.lfMenuFont.lfWeight > 1000)
476 ncm.lfMenuFont.lfWeight = 1000;
478 if (!(hMenuFontBold = CreateFontIndirectA( &ncm.lfMenuFont )))
479 return FALSE;
481 return TRUE;
484 /***********************************************************************
485 * MENU_InitSysMenuPopup
487 * Grey the appropriate items in System menu.
489 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
491 BOOL gray;
493 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
494 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
495 gray = ((style & WS_MAXIMIZE) != 0);
496 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
497 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
498 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
499 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
500 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
501 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
502 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
503 gray = (clsStyle & CS_NOCLOSE) != 0;
505 /* The menu item must keep its state if it's disabled */
506 if(gray)
507 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
511 /******************************************************************************
513 * UINT MENU_GetStartOfNextColumn(
514 * HMENU hMenu )
516 *****************************************************************************/
518 static UINT MENU_GetStartOfNextColumn(
519 HMENU hMenu )
521 POPUPMENU *menu = (POPUPMENU *)USER_HEAP_LIN_ADDR(hMenu);
522 UINT i = menu->FocusedItem + 1;
524 if(!menu)
525 return NO_SELECTED_ITEM;
527 if( i == NO_SELECTED_ITEM )
528 return i;
530 for( ; i < menu->nItems; ++i ) {
531 if (menu->items[i].fType & MF_MENUBARBREAK)
532 return i;
535 return NO_SELECTED_ITEM;
539 /******************************************************************************
541 * UINT MENU_GetStartOfPrevColumn(
542 * HMENU hMenu )
544 *****************************************************************************/
546 static UINT MENU_GetStartOfPrevColumn(
547 HMENU hMenu )
549 POPUPMENU const *menu = (POPUPMENU *)USER_HEAP_LIN_ADDR(hMenu);
550 UINT i;
552 if( !menu )
553 return NO_SELECTED_ITEM;
555 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
556 return NO_SELECTED_ITEM;
558 /* Find the start of the column */
560 for(i = menu->FocusedItem; i != 0 &&
561 !(menu->items[i].fType & MF_MENUBARBREAK);
562 --i); /* empty */
564 if(i == 0)
565 return NO_SELECTED_ITEM;
567 for(--i; i != 0; --i) {
568 if (menu->items[i].fType & MF_MENUBARBREAK)
569 break;
572 TRACE("ret %d.\n", i );
574 return i;
579 /***********************************************************************
580 * MENU_FindItem
582 * Find a menu item. Return a pointer on the item, and modifies *hmenu
583 * in case the item was in a sub-menu.
585 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
587 POPUPMENU *menu;
588 UINT i;
590 if (((*hmenu)==0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
591 if (wFlags & MF_BYPOSITION)
593 if (*nPos >= menu->nItems) return NULL;
594 return &menu->items[*nPos];
596 else
598 MENUITEM *item = menu->items;
599 for (i = 0; i < menu->nItems; i++, item++)
601 if (item->wID == *nPos)
603 *nPos = i;
604 return item;
606 else if (item->fType & MF_POPUP)
608 HMENU hsubmenu = item->hSubMenu;
609 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
610 if (subitem)
612 *hmenu = hsubmenu;
613 return subitem;
618 return NULL;
621 /***********************************************************************
622 * MENU_FindSubMenu
624 * Find a Sub menu. Return the position of the submenu, and modifies
625 * *hmenu in case it is found in another sub-menu.
626 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
628 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
630 POPUPMENU *menu;
631 UINT i;
632 MENUITEM *item;
633 if (((*hmenu)==0xffff) ||
634 (!(menu = MENU_GetMenu(*hmenu))))
635 return NO_SELECTED_ITEM;
636 item = menu->items;
637 for (i = 0; i < menu->nItems; i++, item++) {
638 if(!(item->fType & MF_POPUP)) continue;
639 if (item->hSubMenu == hSubTarget) {
640 return i;
642 else {
643 HMENU hsubmenu = item->hSubMenu;
644 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
645 if (pos != NO_SELECTED_ITEM) {
646 *hmenu = hsubmenu;
647 return pos;
651 return NO_SELECTED_ITEM;
654 /***********************************************************************
655 * MENU_FreeItemData
657 static void MENU_FreeItemData( MENUITEM* item )
659 /* delete text */
660 if (IS_STRING_ITEM(item->fType) && item->text)
661 HeapFree( SystemHeap, 0, item->text );
664 /***********************************************************************
665 * MENU_FindItemByCoords
667 * Find the item at the specified coordinates (screen coords). Does
668 * not work for child windows and therefore should not be called for
669 * an arbitrary system menu.
671 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
672 POINT pt, UINT *pos )
674 MENUITEM *item;
675 UINT i;
676 RECT wrect;
678 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
679 pt.x -= wrect.left;pt.y -= wrect.top;
680 item = menu->items;
681 for (i = 0; i < menu->nItems; i++, item++)
683 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
684 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
686 if (pos) *pos = i;
687 return item;
690 return NULL;
694 /***********************************************************************
695 * MENU_FindItemByKey
697 * Find the menu item selected by a key press.
698 * Return item id, -1 if none, -2 if we should close the menu.
700 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
701 UINT key, BOOL forceMenuChar )
703 TRACE("\tlooking for '%c' in [%04x]\n", (char)key, (UINT16)hmenu );
705 if (!IsMenu( hmenu ))
707 WND* w = WIN_FindWndPtr(hwndOwner);
708 hmenu = GetSubMenu(w->hSysMenu, 0);
709 WIN_ReleaseWndPtr(w);
712 if (hmenu)
714 POPUPMENU *menu = MENU_GetMenu( hmenu );
715 MENUITEM *item = menu->items;
716 LONG menuchar;
718 if( !forceMenuChar )
720 UINT i;
722 key = toupper(key);
723 for (i = 0; i < menu->nItems; i++, item++)
725 if (item->text && (IS_STRING_ITEM(item->fType)))
727 WCHAR *p = item->text - 2;
730 p = strchrW (p + 2, '&');
732 while (p != NULL && p [1] == '&');
733 if (p && (toupper(p[1]) == key)) return i;
737 menuchar = SendMessageA( hwndOwner, WM_MENUCHAR,
738 MAKEWPARAM( key, menu->wFlags ), hmenu );
739 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
740 if (HIWORD(menuchar) == 1) return (UINT)(-2);
742 return (UINT)(-1);
744 /***********************************************************************
745 * MENU_LoadMagicItem
747 * Load the bitmap associated with the magic menu item and its style
750 static HBITMAP MENU_LoadMagicItem(UINT id, BOOL hilite, DWORD dwItemData)
753 * Magic menu item id's section
754 * These magic id's are used by windows to insert "standard" mdi
755 * buttons (minimize,restore,close) on menu. Under windows,
756 * these magic id's make sure the right things appear when those
757 * bitmap buttons are pressed/selected/released.
760 switch(id & 0xffff)
761 { case HBMMENU_SYSTEM:
762 return (dwItemData) ?
763 (HBITMAP)dwItemData :
764 (hilite ? hBmpMinimizeD : hBmpMinimize);
765 case HBMMENU_MBAR_RESTORE:
766 return (hilite ? hBmpMaximizeD: hBmpMaximize);
767 case HBMMENU_MBAR_MINIMIZE:
768 return (hilite ? hBmpMinimizeD : hBmpMinimize);
769 case HBMMENU_MBAR_CLOSE:
770 return (hilite ? hBmpCloseD : hBmpClose);
771 case HBMMENU_CALLBACK:
772 case HBMMENU_MBAR_CLOSE_D:
773 case HBMMENU_MBAR_MINIMIZE_D:
774 case HBMMENU_POPUP_CLOSE:
775 case HBMMENU_POPUP_RESTORE:
776 case HBMMENU_POPUP_MAXIMIZE:
777 case HBMMENU_POPUP_MINIMIZE:
778 default:
779 FIXME("Magic 0x%08x not implemented\n", id);
780 return 0;
785 /***********************************************************************
786 * MENU_CalcItemSize
788 * Calculate the size of the menu item and store it in lpitem->rect.
790 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
791 INT orgX, INT orgY, BOOL menuBar )
793 WCHAR *p;
795 TRACE("dc=0x%04x owner=0x%04x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
796 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
797 (menuBar ? " (MenuBar)" : ""));
799 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
801 if (lpitem->fType & MF_OWNERDRAW)
804 ** Experimentation under Windows reveals that an owner-drawn
805 ** menu is expected to return the size of the content part of
806 ** the menu item, not including the checkmark nor the submenu
807 ** arrow. Windows adds those values itself and returns the
808 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
810 MEASUREITEMSTRUCT mis;
811 mis.CtlType = ODT_MENU;
812 mis.CtlID = 0;
813 mis.itemID = lpitem->wID;
814 mis.itemData = (DWORD)lpitem->dwItemData;
815 mis.itemHeight = 0;
816 mis.itemWidth = 0;
817 SendMessageA( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
818 lpitem->rect.right += mis.itemWidth;
820 if (menuBar)
822 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
825 /* under at least win95 you seem to be given a standard
826 height for the menu and the height value is ignored */
828 if (TWEAK_WineLook == WIN31_LOOK)
829 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
830 else
831 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
833 else
834 lpitem->rect.bottom += mis.itemHeight;
836 TRACE("id=%04x size=%dx%d\n",
837 lpitem->wID, mis.itemWidth, mis.itemHeight);
838 /* Fall through to get check/arrow width calculation. */
841 if (lpitem->fType & MF_SEPARATOR)
843 lpitem->rect.bottom += SEPARATOR_HEIGHT;
844 return;
847 if (!menuBar)
849 lpitem->rect.right += 2 * check_bitmap_width;
850 if (lpitem->fType & MF_POPUP)
851 lpitem->rect.right += arrow_bitmap_width;
854 if (lpitem->fType & MF_OWNERDRAW)
855 return;
857 if (IS_BITMAP_ITEM(lpitem->fType))
859 BITMAP bm;
860 HBITMAP resBmp = 0;
862 /* Check if there is a magic menu item associated with this item */
863 if((LOWORD((int)lpitem->text))<12)
865 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fType & MF_HILITE),
866 lpitem->dwItemData);
868 else
869 resBmp = (HBITMAP)lpitem->text;
871 if (GetObjectA(resBmp, sizeof(bm), &bm ))
873 lpitem->rect.right += bm.bmWidth;
874 lpitem->rect.bottom += bm.bmHeight;
875 if (TWEAK_WineLook == WIN98_LOOK) {
876 /* Leave space for the sunken border */
877 lpitem->rect.right += 2;
878 lpitem->rect.bottom += 2;
885 /* If we get here, then it must be a text item */
886 if (IS_STRING_ITEM( lpitem->fType ))
887 { SIZE size;
889 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
891 lpitem->rect.right += size.cx;
892 if (TWEAK_WineLook == WIN31_LOOK)
893 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
894 else
895 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
896 lpitem->xTab = 0;
898 if (menuBar)
900 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
902 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
904 /* Item contains a tab (only meaningful in popup menus) */
905 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
906 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
907 lpitem->rect.right += MENU_TAB_SPACE;
909 else
911 if (strchrW( lpitem->text, '\b' ))
912 lpitem->rect.right += MENU_TAB_SPACE;
913 lpitem->xTab = lpitem->rect.right - check_bitmap_width
914 - arrow_bitmap_width;
917 TRACE("(%d,%d)-(%d,%d)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
921 /***********************************************************************
922 * MENU_PopupMenuCalcSize
924 * Calculate the size of a popup menu.
926 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
928 MENUITEM *lpitem;
929 HDC hdc;
930 int start, i;
931 int orgX, orgY, maxX, maxTab, maxTabWidth;
933 lppop->Width = lppop->Height = 0;
934 if (lppop->nItems == 0) return;
935 hdc = GetDC( 0 );
937 SelectObject( hdc, hMenuFont);
939 start = 0;
940 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
942 while (start < lppop->nItems)
944 lpitem = &lppop->items[start];
945 orgX = maxX;
946 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
948 maxTab = maxTabWidth = 0;
950 /* Parse items until column break or end of menu */
951 for (i = start; i < lppop->nItems; i++, lpitem++)
953 if ((i != start) &&
954 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
956 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
958 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
959 maxX = max( maxX, lpitem->rect.right );
960 orgY = lpitem->rect.bottom;
961 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
963 maxTab = max( maxTab, lpitem->xTab );
964 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
968 /* Finish the column (set all items to the largest width found) */
969 maxX = max( maxX, maxTab + maxTabWidth );
970 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
972 lpitem->rect.right = maxX;
973 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
974 lpitem->xTab = maxTab;
977 lppop->Height = max( lppop->Height, orgY );
980 lppop->Width = maxX;
982 /* space for 3d border */
983 if(TWEAK_WineLook > WIN31_LOOK)
985 lppop->Height += 2;
986 lppop->Width += 2;
989 ReleaseDC( 0, hdc );
993 /***********************************************************************
994 * MENU_MenuBarCalcSize
996 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
997 * height is off by 1 pixel which causes lengthy window relocations when
998 * active document window is maximized/restored.
1000 * Calculate the size of the menu bar.
1002 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1003 LPPOPUPMENU lppop, HWND hwndOwner )
1005 MENUITEM *lpitem;
1006 int start, i, orgX, orgY, maxY, helpPos;
1008 if ((lprect == NULL) || (lppop == NULL)) return;
1009 if (lppop->nItems == 0) return;
1010 TRACE("left=%d top=%d right=%d bottom=%d\n",
1011 lprect->left, lprect->top, lprect->right, lprect->bottom);
1012 lppop->Width = lprect->right - lprect->left;
1013 lppop->Height = 0;
1014 maxY = lprect->top+1;
1015 start = 0;
1016 helpPos = -1;
1017 while (start < lppop->nItems)
1019 lpitem = &lppop->items[start];
1020 orgX = lprect->left;
1021 orgY = maxY;
1023 /* Parse items until line break or end of menu */
1024 for (i = start; i < lppop->nItems; i++, lpitem++)
1026 if ((helpPos == -1) && (lpitem->fType & MF_HELP)) helpPos = i;
1027 if ((i != start) &&
1028 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1030 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1031 orgX, orgY );
1032 debug_print_menuitem (" item: ", lpitem, "");
1033 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
1035 if (lpitem->rect.right > lprect->right)
1037 if (i != start) break;
1038 else lpitem->rect.right = lprect->right;
1040 maxY = max( maxY, lpitem->rect.bottom );
1041 orgX = lpitem->rect.right;
1044 /* Finish the line (set all items to the largest height found) */
1045 while (start < i) lppop->items[start++].rect.bottom = maxY;
1048 lprect->bottom = maxY;
1049 lppop->Height = lprect->bottom - lprect->top;
1051 if (TWEAK_WineLook == WIN31_LOOK) {
1052 /* Flush right all magic items and items between the MF_HELP and */
1053 /* the last item (if several lines, only move the last line) */
1054 lpitem = &lppop->items[lppop->nItems-1];
1055 orgY = lpitem->rect.top;
1056 orgX = lprect->right;
1057 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1058 if ( !IS_BITMAP_ITEM(lpitem->fType) && ((helpPos==-1) || (helpPos>i) ))
1059 break; /* done */
1060 if (lpitem->rect.top != orgY) break; /* Other line */
1061 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1062 lpitem->rect.left += orgX - lpitem->rect.right;
1063 lpitem->rect.right = orgX;
1064 orgX = lpitem->rect.left;
1069 /***********************************************************************
1070 * MENU_DrawMenuItem
1072 * Draw a single menu item.
1074 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1075 UINT height, BOOL menuBar, UINT odaction )
1077 RECT rect;
1079 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1081 if (lpitem->fType & MF_SYSMENU)
1083 if( !IsIconic(hwnd) ) {
1084 if (TWEAK_WineLook > WIN31_LOOK)
1085 NC_DrawSysButton95( hwnd, hdc,
1086 lpitem->fState &
1087 (MF_HILITE | MF_MOUSESELECT) );
1088 else
1089 NC_DrawSysButton( hwnd, hdc,
1090 lpitem->fState &
1091 (MF_HILITE | MF_MOUSESELECT) );
1094 return;
1097 if (lpitem->fType & MF_OWNERDRAW)
1100 ** Experimentation under Windows reveals that an owner-drawn
1101 ** menu is given the rectangle which includes the space it requested
1102 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1103 ** and a popup-menu arrow. This is the value of lpitem->rect.
1104 ** Windows will leave all drawing to the application except for
1105 ** the popup-menu arrow. Windows always draws that itself, after
1106 ** the menu owner has finished drawing.
1108 DRAWITEMSTRUCT dis;
1110 dis.CtlType = ODT_MENU;
1111 dis.CtlID = 0;
1112 dis.itemID = lpitem->wID;
1113 dis.itemData = (DWORD)lpitem->dwItemData;
1114 dis.itemState = 0;
1115 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1116 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED;
1117 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1118 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1119 dis.hwndItem = hmenu;
1120 dis.hDC = hdc;
1121 dis.rcItem = lpitem->rect;
1122 TRACE("Ownerdraw: owner=%04x itemID=%d, itemState=%d, itemAction=%d, "
1123 "hwndItem=%04x, hdc=%04x, rcItem={%d,%d,%d,%d}\n", hwndOwner,
1124 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1125 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1126 dis.rcItem.bottom);
1127 SendMessageA( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1128 /* Fall through to draw popup-menu arrow */
1131 TRACE("rect={%d,%d,%d,%d}\n", lpitem->rect.left, lpitem->rect.top,
1132 lpitem->rect.right,lpitem->rect.bottom);
1134 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1136 rect = lpitem->rect;
1138 if (!(lpitem->fType & MF_OWNERDRAW))
1140 if (lpitem->fState & MF_HILITE)
1142 if(TWEAK_WineLook == WIN98_LOOK)
1144 if(menuBar)
1145 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1146 else
1147 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1149 else /* Not Win98 Look */
1151 if(!IS_BITMAP_ITEM(lpitem->fType))
1152 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1155 else
1156 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1159 SetBkMode( hdc, TRANSPARENT );
1161 if (!(lpitem->fType & MF_OWNERDRAW))
1163 /* vertical separator */
1164 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1166 if (TWEAK_WineLook > WIN31_LOOK)
1168 RECT rc = rect;
1169 rc.top = 3;
1170 rc.bottom = height - 3;
1171 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1173 else
1175 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1176 MoveToEx( hdc, rect.left, 0, NULL );
1177 LineTo( hdc, rect.left, height );
1181 /* horizontal separator */
1182 if (lpitem->fType & MF_SEPARATOR)
1184 if (TWEAK_WineLook > WIN31_LOOK)
1186 RECT rc = rect;
1187 rc.left++;
1188 rc.right--;
1189 rc.top += SEPARATOR_HEIGHT / 2;
1190 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1192 else
1194 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1195 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1196 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1198 return;
1202 /* Setup colors */
1204 if (lpitem->fState & MF_HILITE)
1206 if(TWEAK_WineLook == WIN98_LOOK)
1208 if(menuBar)
1209 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1210 else
1212 if(lpitem->fState & MF_GRAYED)
1213 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1214 else
1215 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1217 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1219 else /* Not Win98 Look */
1221 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1222 if(!IS_BITMAP_ITEM(lpitem->fType))
1223 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1224 else
1225 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1228 else
1230 if (lpitem->fState & MF_GRAYED)
1231 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1232 else
1233 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1234 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1237 /* helper lines for debugging */
1238 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1239 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1240 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1241 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1244 if (!menuBar)
1246 INT y = rect.top + rect.bottom;
1248 if (!(lpitem->fType & MF_OWNERDRAW))
1250 /* Draw the check mark
1252 * FIXME:
1253 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1256 if (lpitem->fState & MF_CHECKED)
1258 HBITMAP bm = lpitem->hCheckBit ? lpitem->hCheckBit :
1259 ((lpitem->fType & MFT_RADIOCHECK) ? hStdRadioCheck : hStdCheck);
1260 HDC hdcMem = CreateCompatibleDC( hdc );
1262 SelectObject( hdcMem, bm );
1263 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1264 check_bitmap_width, check_bitmap_height,
1265 hdcMem, 0, 0, SRCCOPY );
1266 DeleteDC( hdcMem );
1268 else if (lpitem->hUnCheckBit)
1270 HDC hdcMem = CreateCompatibleDC( hdc );
1272 SelectObject( hdcMem, lpitem->hUnCheckBit );
1273 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1274 check_bitmap_width, check_bitmap_height,
1275 hdcMem, 0, 0, SRCCOPY );
1276 DeleteDC( hdcMem );
1280 /* Draw the popup-menu arrow */
1281 if (lpitem->fType & MF_POPUP)
1283 HDC hdcMem = CreateCompatibleDC( hdc );
1284 HBITMAP hOrigBitmap;
1286 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1287 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1288 (y - arrow_bitmap_height) / 2,
1289 arrow_bitmap_width, arrow_bitmap_height,
1290 hdcMem, 0, 0, SRCCOPY );
1291 SelectObject( hdcMem, hOrigBitmap );
1292 DeleteDC( hdcMem );
1295 rect.left += check_bitmap_width;
1296 rect.right -= arrow_bitmap_width;
1299 /* Done for owner-drawn */
1300 if (lpitem->fType & MF_OWNERDRAW)
1301 return;
1303 /* Draw the item text or bitmap */
1304 if (IS_BITMAP_ITEM(lpitem->fType))
1305 { int top;
1307 HBITMAP resBmp = 0;
1309 HDC hdcMem = CreateCompatibleDC( hdc );
1312 * Check if there is a magic menu item associated with this item
1313 * and load the appropriate bitmap
1315 if((LOWORD((int)lpitem->text)) < 12)
1317 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fState & MF_HILITE),
1318 lpitem->dwItemData);
1320 else
1321 resBmp = (HBITMAP)lpitem->text;
1323 if (resBmp)
1325 BITMAP bm;
1326 GetObjectA( resBmp, sizeof(bm), &bm );
1328 SelectObject(hdcMem,resBmp );
1330 /* handle fontsize > bitmap_height */
1331 top = ((rect.bottom-rect.top)>bm.bmHeight) ?
1332 rect.top+(rect.bottom-rect.top-bm.bmHeight)/2 : rect.top;
1334 BitBlt( hdc, rect.left, top, rect.right - rect.left,
1335 rect.bottom - rect.top, hdcMem, 0, 0,
1336 ((lpitem->fState & MF_HILITE) && !((LOWORD((DWORD)lpitem->text)) < 12)) ? NOTSRCCOPY : SRCCOPY );
1338 DeleteDC( hdcMem );
1340 return;
1343 /* No bitmap - process text if present */
1344 else if (IS_STRING_ITEM(lpitem->fType))
1346 register int i;
1347 HFONT hfontOld = 0;
1349 UINT uFormat = (menuBar) ?
1350 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1351 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1353 if ( lpitem->fState & MFS_DEFAULT )
1355 hfontOld = SelectObject( hdc, hMenuFontBold);
1358 if (menuBar)
1360 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1361 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1362 i = strlenW( lpitem->text );
1364 else
1366 for (i = 0; lpitem->text[i]; i++)
1367 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1368 break;
1371 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1373 if (!(lpitem->fState & MF_HILITE) )
1375 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1376 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1377 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1378 --rect.left; --rect.top; --rect.right; --rect.bottom;
1380 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1383 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1385 /* paint the shortcut text */
1386 if (lpitem->text[i]) /* There's a tab or flush-right char */
1388 if (lpitem->text[i] == '\t')
1390 rect.left = lpitem->xTab;
1391 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1393 else
1395 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1398 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1400 if (!(lpitem->fState & MF_HILITE) )
1402 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1403 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1404 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1405 --rect.left; --rect.top; --rect.right; --rect.bottom;
1407 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1409 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1412 if (hfontOld)
1413 SelectObject (hdc, hfontOld);
1418 /***********************************************************************
1419 * MENU_DrawPopupMenu
1421 * Paint a popup menu.
1423 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1425 HBRUSH hPrevBrush = 0;
1426 RECT rect;
1428 TRACE("wnd=0x%04x dc=0x%04x menu=0x%04x\n", hwnd, hdc, hmenu);
1430 GetClientRect( hwnd, &rect );
1432 if(TWEAK_WineLook == WIN31_LOOK)
1434 rect.bottom -= POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1435 rect.right -= POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER);
1438 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1439 && (SelectObject( hdc, hMenuFont)))
1441 HPEN hPrevPen;
1443 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1445 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1446 if( hPrevPen )
1448 INT ropPrev, i;
1449 POPUPMENU *menu;
1451 /* draw 3-d shade */
1452 if(TWEAK_WineLook == WIN31_LOOK) {
1453 SelectObject( hdc, hShadeBrush );
1454 SetBkMode( hdc, TRANSPARENT );
1455 ropPrev = SetROP2( hdc, R2_MASKPEN );
1457 i = rect.right; /* why SetBrushOrg() doesn't? */
1458 PatBlt( hdc, i & 0xfffffffe,
1459 rect.top + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER),
1460 i%2 + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1461 rect.bottom - rect.top, 0x00a000c9 );
1462 i = rect.bottom;
1463 PatBlt( hdc, rect.left + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1464 i & 0xfffffffe,rect.right - rect.left,
1465 i%2 + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER), 0x00a000c9 );
1466 SelectObject( hdc, hPrevPen );
1467 SelectObject( hdc, hPrevBrush );
1468 SetROP2( hdc, ropPrev );
1470 else
1471 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1473 /* draw menu items */
1475 menu = MENU_GetMenu( hmenu );
1476 if (menu && menu->nItems)
1478 MENUITEM *item;
1479 UINT u;
1481 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1482 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1483 menu->Height, FALSE, ODA_DRAWENTIRE );
1486 } else
1488 SelectObject( hdc, hPrevBrush );
1493 /***********************************************************************
1494 * MENU_DrawMenuBar
1496 * Paint a menu bar. Returns the height of the menu bar.
1497 * called from [windows/nonclient.c]
1499 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1500 BOOL suppress_draw)
1502 LPPOPUPMENU lppop;
1503 UINT i,retvalue;
1504 HFONT hfontOld = 0;
1506 WND *wndPtr = WIN_FindWndPtr( hwnd );
1508 lppop = MENU_GetMenu ((HMENU)wndPtr->wIDmenu );
1509 if (lppop == NULL || lprect == NULL)
1511 retvalue = GetSystemMetrics(SM_CYMENU);
1512 goto END;
1515 TRACE("(%04x, %p, %p)\n", hDC, lprect, lppop);
1517 hfontOld = SelectObject( hDC, hMenuFont);
1519 if (lppop->Height == 0)
1520 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1522 lprect->bottom = lprect->top + lppop->Height;
1524 if (suppress_draw)
1526 retvalue = lppop->Height;
1527 goto END;
1530 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
1532 if (TWEAK_WineLook == WIN31_LOOK)
1534 SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1535 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1536 LineTo( hDC, lprect->right, lprect->bottom );
1538 else
1540 SelectObject( hDC, GetSysColorPen(COLOR_3DFACE));
1541 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1542 LineTo( hDC, lprect->right, lprect->bottom );
1545 if (lppop->nItems == 0)
1547 retvalue = GetSystemMetrics(SM_CYMENU);
1548 goto END;
1551 for (i = 0; i < lppop->nItems; i++)
1553 MENU_DrawMenuItem( hwnd, (HMENU)wndPtr->wIDmenu, hwnd,
1554 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
1556 retvalue = lppop->Height;
1558 END:
1559 if (hfontOld)
1560 SelectObject (hDC, hfontOld);
1562 WIN_ReleaseWndPtr(wndPtr);
1563 return retvalue;
1566 /***********************************************************************
1567 * MENU_PatchResidentPopup
1569 BOOL MENU_PatchResidentPopup( HQUEUE16 checkQueue, WND* checkWnd )
1571 WND *pTPWnd = MENU_GetTopPopupWnd();
1573 if( pTPWnd )
1575 HTASK16 hTask = 0;
1577 TRACE("patching resident popup: %04x %04x [%04x %04x]\n",
1578 checkQueue, checkWnd ? checkWnd->hwndSelf : 0, pTPWnd->hmemTaskQ,
1579 pTPWnd->owner ? pTPWnd->owner->hwndSelf : 0);
1581 switch( checkQueue )
1583 case 0: /* checkWnd is the new popup owner */
1584 if( checkWnd )
1586 pTPWnd->owner = checkWnd;
1587 if( pTPWnd->hmemTaskQ != checkWnd->hmemTaskQ )
1588 hTask = QUEUE_GetQueueTask( checkWnd->hmemTaskQ );
1590 break;
1592 case 0xFFFF: /* checkWnd is destroyed */
1593 if( pTPWnd->owner == checkWnd )
1594 pTPWnd->owner = NULL;
1595 MENU_ReleaseTopPopupWnd();
1596 return TRUE;
1598 default: /* checkQueue is exiting */
1599 if( pTPWnd->hmemTaskQ == checkQueue )
1601 hTask = QUEUE_GetQueueTask( pTPWnd->hmemTaskQ );
1602 hTask = TASK_GetNextTask( hTask );
1604 break;
1607 if( hTask )
1609 TDB* task = (TDB*)GlobalLock16( hTask );
1610 if( task )
1612 pTPWnd->hInstance = task->hInstance;
1613 pTPWnd->hmemTaskQ = task->hQueue;
1614 MENU_ReleaseTopPopupWnd();
1615 return TRUE;
1617 else WARN("failed to patch resident popup.\n");
1620 MENU_ReleaseTopPopupWnd();
1621 return FALSE;
1624 /***********************************************************************
1625 * MENU_ShowPopup
1627 * Display a popup menu.
1629 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1630 INT x, INT y, INT xanchor, INT yanchor )
1632 POPUPMENU *menu;
1633 WND *wndOwner = NULL;
1635 TRACE("owner=0x%04x hmenu=0x%04x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1636 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1638 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1639 if (menu->FocusedItem != NO_SELECTED_ITEM)
1641 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1642 menu->FocusedItem = NO_SELECTED_ITEM;
1645 /* store the owner for DrawItem */
1646 menu->hwndOwner = hwndOwner;
1648 if( (wndOwner = WIN_FindWndPtr( hwndOwner )) )
1650 UINT width, height;
1652 MENU_PopupMenuCalcSize( menu, hwndOwner );
1654 /* adjust popup menu pos so that it fits within the desktop */
1656 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1657 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1659 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1661 if( xanchor )
1662 x -= width - xanchor;
1663 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1664 x = GetSystemMetrics(SM_CXSCREEN) - width;
1666 if( x < 0 ) x = 0;
1668 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1670 if( yanchor )
1671 y -= height + yanchor;
1672 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1673 y = GetSystemMetrics(SM_CYSCREEN) - height;
1675 if( y < 0 ) y = 0;
1677 if( TWEAK_WineLook == WIN31_LOOK )
1679 width += POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER); /* add space for shading */
1680 height += POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1683 /* NOTE: In Windows, top menu popup is not owned. */
1684 if (!pTopPopupWnd) /* create top level popup menu window */
1686 assert( uSubPWndLevel == 0 );
1688 pTopPopupWnd = WIN_FindWndPtr(CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1689 WS_POPUP, x, y, width, height,
1690 hwndOwner, 0, wndOwner->hInstance,
1691 (LPVOID)hmenu ));
1692 if (!pTopPopupWnd)
1694 WIN_ReleaseWndPtr(wndOwner);
1695 return FALSE;
1697 menu->hWnd = pTopPopupWnd->hwndSelf;
1698 MENU_ReleaseTopPopupWnd();
1700 else
1701 if( uSubPWndLevel )
1703 /* create a new window for the submenu */
1705 menu->hWnd = CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1706 WS_POPUP, x, y, width, height,
1707 hwndOwner, 0, wndOwner->hInstance,
1708 (LPVOID)hmenu );
1709 if( !menu->hWnd )
1711 WIN_ReleaseWndPtr(wndOwner);
1712 return FALSE;
1715 else /* top level popup menu window already exists */
1717 WND *pTPWnd = MENU_GetTopPopupWnd();
1718 menu->hWnd = pTPWnd->hwndSelf;
1720 MENU_PatchResidentPopup( 0, wndOwner );
1721 SendMessageA( pTPWnd->hwndSelf, MM_SETMENUHANDLE, (WPARAM16)hmenu, 0L);
1723 /* adjust its size */
1725 SetWindowPos( menu->hWnd, 0, x, y, width, height,
1726 SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW);
1727 MENU_ReleaseTopPopupWnd();
1730 uSubPWndLevel++; /* menu level counter */
1732 /* Display the window */
1734 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1735 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1736 UpdateWindow( menu->hWnd );
1737 WIN_ReleaseWndPtr(wndOwner);
1738 return TRUE;
1740 return FALSE;
1744 /***********************************************************************
1745 * MENU_SelectItem
1747 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1748 BOOL sendMenuSelect, HMENU topmenu )
1750 LPPOPUPMENU lppop;
1751 HDC hdc;
1753 TRACE("owner=0x%04x menu=0x%04x index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1755 lppop = MENU_GetMenu( hmenu );
1756 if ((!lppop) || (!lppop->nItems)) return;
1758 if (lppop->FocusedItem == wIndex) return;
1759 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1760 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1762 SelectObject( hdc, hMenuFont);
1764 /* Clear previous highlighted item */
1765 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1767 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1768 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1769 lppop->Height, !(lppop->wFlags & MF_POPUP),
1770 ODA_SELECT );
1773 /* Highlight new item (if any) */
1774 lppop->FocusedItem = wIndex;
1775 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1777 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1778 lppop->items[wIndex].fState |= MF_HILITE;
1779 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1780 &lppop->items[wIndex], lppop->Height,
1781 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1783 if (sendMenuSelect)
1785 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1786 SendMessageA( hwndOwner, WM_MENUSELECT,
1787 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1788 ip->fType | ip->fState | MF_MOUSESELECT |
1789 (lppop->wFlags & MF_SYSMENU)), hmenu);
1792 else if (sendMenuSelect) {
1793 if(topmenu){
1794 int pos;
1795 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1796 POPUPMENU *ptm = (POPUPMENU *) USER_HEAP_LIN_ADDR( topmenu );
1797 MENUITEM *ip = &ptm->items[pos];
1798 SendMessageA( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1799 ip->fType | ip->fState | MF_MOUSESELECT |
1800 (ptm->wFlags & MF_SYSMENU)), topmenu);
1804 ReleaseDC( lppop->hWnd, hdc );
1808 /***********************************************************************
1809 * MENU_MoveSelection
1811 * Moves currently selected item according to the offset parameter.
1812 * If there is no selection then it should select the last item if
1813 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1815 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1817 INT i;
1818 POPUPMENU *menu;
1820 TRACE("hwnd=0x%04x hmenu=0x%04x off=0x%04x\n", hwndOwner, hmenu, offset);
1822 menu = MENU_GetMenu( hmenu );
1823 if ((!menu) || (!menu->items)) return;
1825 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1827 if( menu->nItems == 1 ) return; else
1828 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1829 ; i += offset)
1830 if (!(menu->items[i].fType & MF_SEPARATOR))
1832 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1833 return;
1837 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1838 i >= 0 && i < menu->nItems ; i += offset)
1839 if (!(menu->items[i].fType & MF_SEPARATOR))
1841 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1842 return;
1847 /**********************************************************************
1848 * MENU_SetItemData
1850 * Set an item flags, id and text ptr. Called by InsertMenu() and
1851 * ModifyMenu().
1853 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT id,
1854 LPCWSTR str )
1856 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1858 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1860 if (IS_STRING_ITEM(flags))
1862 if (!str || !*str)
1864 flags |= MF_SEPARATOR;
1865 item->text = NULL;
1867 else
1869 LPWSTR text;
1870 /* Item beginning with a backspace is a help item */
1871 if (*str == '\b')
1873 flags |= MF_HELP;
1874 str++;
1876 if (!(text = HEAP_strdupW( SystemHeap, 0, str ))) return FALSE;
1877 item->text = text;
1880 else if (IS_BITMAP_ITEM(flags))
1881 item->text = (LPWSTR)(HBITMAP)LOWORD(str);
1882 else item->text = NULL;
1884 if (flags & MF_OWNERDRAW)
1885 item->dwItemData = (DWORD)str;
1886 else
1887 item->dwItemData = 0;
1889 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != id) )
1890 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1892 if (flags & MF_POPUP)
1894 POPUPMENU *menu = MENU_GetMenu((UINT16)id);
1895 if (menu) menu->wFlags |= MF_POPUP;
1896 else
1898 item->wID = 0;
1899 item->hSubMenu = 0;
1900 item->fType = 0;
1901 item->fState = 0;
1902 return FALSE;
1906 item->wID = id;
1907 if (flags & MF_POPUP)
1908 item->hSubMenu = id;
1910 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1911 flags |= MF_POPUP; /* keep popup */
1913 item->fType = flags & TYPE_MASK;
1914 item->fState = (flags & STATE_MASK) &
1915 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1918 /* Don't call SetRectEmpty here! */
1921 if (prevText) HeapFree( SystemHeap, 0, prevText );
1923 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1924 return TRUE;
1928 /**********************************************************************
1929 * MENU_InsertItem
1931 * Insert a new item into a menu.
1933 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1935 MENUITEM *newItems;
1936 POPUPMENU *menu;
1938 if (!(menu = MENU_GetMenu(hMenu)))
1939 return NULL;
1941 /* Find where to insert new item */
1943 if (flags & MF_BYPOSITION) {
1944 if (pos > menu->nItems)
1945 pos = menu->nItems;
1946 } else {
1947 if (!MENU_FindItem( &hMenu, &pos, flags ))
1948 pos = menu->nItems;
1949 else {
1950 if (!(menu = MENU_GetMenu( hMenu )))
1951 return NULL;
1955 /* Create new items array */
1957 newItems = HeapAlloc( SystemHeap, 0, sizeof(MENUITEM) * (menu->nItems+1) );
1958 if (!newItems)
1960 WARN("allocation failed\n" );
1961 return NULL;
1963 if (menu->nItems > 0)
1965 /* Copy the old array into the new one */
1966 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1967 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1968 (menu->nItems-pos)*sizeof(MENUITEM) );
1969 HeapFree( SystemHeap, 0, menu->items );
1971 menu->items = newItems;
1972 menu->nItems++;
1973 memset( &newItems[pos], 0, sizeof(*newItems) );
1974 menu->Height = 0; /* force size recalculate */
1975 return &newItems[pos];
1979 /**********************************************************************
1980 * MENU_ParseResource
1982 * Parse a standard menu resource and add items to the menu.
1983 * Return a pointer to the end of the resource.
1985 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1987 WORD flags, id = 0;
1988 LPCSTR str;
1992 flags = GET_WORD(res);
1993 res += sizeof(WORD);
1994 if (!(flags & MF_POPUP))
1996 id = GET_WORD(res);
1997 res += sizeof(WORD);
1999 if (!IS_STRING_ITEM(flags))
2000 ERR("not a string item %04x\n", flags );
2001 str = res;
2002 if (!unicode) res += strlen(str) + 1;
2003 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2004 if (flags & MF_POPUP)
2006 HMENU hSubMenu = CreatePopupMenu();
2007 if (!hSubMenu) return NULL;
2008 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2009 return NULL;
2010 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
2011 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
2013 else /* Not a popup */
2015 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2016 else AppendMenuW( hMenu, flags, id,
2017 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2019 } while (!(flags & MF_END));
2020 return res;
2024 /**********************************************************************
2025 * MENUEX_ParseResource
2027 * Parse an extended menu resource and add items to the menu.
2028 * Return a pointer to the end of the resource.
2030 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2032 WORD resinfo;
2033 do {
2034 MENUITEMINFOW mii;
2036 mii.cbSize = sizeof(mii);
2037 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2038 mii.fType = GET_DWORD(res);
2039 res += sizeof(DWORD);
2040 mii.fState = GET_DWORD(res);
2041 res += sizeof(DWORD);
2042 mii.wID = GET_DWORD(res);
2043 res += sizeof(DWORD);
2044 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2045 res += sizeof(WORD);
2046 /* Align the text on a word boundary. */
2047 res += (~((int)res - 1)) & 1;
2048 mii.dwTypeData = (LPWSTR) res;
2049 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2050 /* Align the following fields on a dword boundary. */
2051 res += (~((int)res - 1)) & 3;
2053 /* FIXME: This is inefficient and cannot be optimised away by gcc. */
2055 LPSTR newstr = HEAP_strdupWtoA(GetProcessHeap(),
2056 0, mii.dwTypeData);
2057 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2058 mii.fType, mii.fState, mii.wID, resinfo, newstr);
2059 HeapFree( GetProcessHeap(), 0, newstr );
2062 if (resinfo & 1) { /* Pop-up? */
2063 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2064 res += sizeof(DWORD);
2065 mii.hSubMenu = CreatePopupMenu();
2066 if (!mii.hSubMenu)
2067 return NULL;
2068 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2069 DestroyMenu(mii.hSubMenu);
2070 return NULL;
2072 mii.fMask |= MIIM_SUBMENU;
2073 mii.fType |= MF_POPUP;
2075 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2076 } while (!(resinfo & MF_END));
2077 return res;
2081 /***********************************************************************
2082 * MENU_GetSubPopup
2084 * Return the handle of the selected sub-popup menu (if any).
2086 static HMENU MENU_GetSubPopup( HMENU hmenu )
2088 POPUPMENU *menu;
2089 MENUITEM *item;
2091 menu = MENU_GetMenu( hmenu );
2093 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2095 item = &menu->items[menu->FocusedItem];
2096 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2097 return item->hSubMenu;
2098 return 0;
2102 /***********************************************************************
2103 * MENU_HideSubPopups
2105 * Hide the sub-popup menus of this menu.
2107 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2108 BOOL sendMenuSelect )
2110 POPUPMENU *menu = MENU_GetMenu( hmenu );
2112 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2114 if (menu && uSubPWndLevel)
2116 HMENU hsubmenu;
2117 POPUPMENU *submenu;
2118 MENUITEM *item;
2120 if (menu->FocusedItem != NO_SELECTED_ITEM)
2122 item = &menu->items[menu->FocusedItem];
2123 if (!(item->fType & MF_POPUP) ||
2124 !(item->fState & MF_MOUSESELECT)) return;
2125 item->fState &= ~MF_MOUSESELECT;
2126 hsubmenu = item->hSubMenu;
2127 } else return;
2129 submenu = MENU_GetMenu( hsubmenu );
2130 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2131 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2133 if (submenu->hWnd == MENU_GetTopPopupWnd()->hwndSelf )
2135 ShowWindow( submenu->hWnd, SW_HIDE );
2136 uSubPWndLevel = 0;
2138 else
2140 DestroyWindow( submenu->hWnd );
2141 submenu->hWnd = 0;
2143 MENU_ReleaseTopPopupWnd();
2148 /***********************************************************************
2149 * MENU_ShowSubPopup
2151 * Display the sub-menu of the selected item of this menu.
2152 * Return the handle of the submenu, or hmenu if no submenu to display.
2154 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2155 BOOL selectFirst, UINT wFlags )
2157 RECT rect;
2158 POPUPMENU *menu;
2159 MENUITEM *item;
2160 WND *wndPtr;
2161 HDC hdc;
2163 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, selectFirst);
2165 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2167 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd )) ||
2168 (menu->FocusedItem == NO_SELECTED_ITEM))
2170 WIN_ReleaseWndPtr(wndPtr);
2171 return hmenu;
2174 item = &menu->items[menu->FocusedItem];
2175 if (!(item->fType & MF_POPUP) ||
2176 (item->fState & (MF_GRAYED | MF_DISABLED)))
2178 WIN_ReleaseWndPtr(wndPtr);
2179 return hmenu;
2182 /* message must be sent before using item,
2183 because nearly everything may be changed by the application ! */
2185 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2186 if (!(wFlags & TPM_NONOTIFY))
2187 SendMessageA( hwndOwner, WM_INITMENUPOPUP, item->hSubMenu,
2188 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2190 item = &menu->items[menu->FocusedItem];
2191 rect = item->rect;
2193 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2194 if (!(item->fState & MF_HILITE))
2196 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2197 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2199 SelectObject( hdc, hMenuFont);
2201 item->fState |= MF_HILITE;
2202 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2203 ReleaseDC( menu->hWnd, hdc );
2205 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2206 item->rect = rect;
2208 item->fState |= MF_MOUSESELECT;
2210 if (IS_SYSTEM_MENU(menu))
2212 MENU_InitSysMenuPopup(item->hSubMenu, wndPtr->dwStyle, GetClassLongA(wndPtr->hwndSelf, GCL_STYLE));
2214 NC_GetSysPopupPos( wndPtr, &rect );
2215 rect.top = rect.bottom;
2216 rect.right = GetSystemMetrics(SM_CXSIZE);
2217 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2219 else
2221 if (menu->wFlags & MF_POPUP)
2223 rect.left = wndPtr->rectWindow.left + item->rect.right - GetSystemMetrics(SM_CXBORDER);
2224 rect.top = wndPtr->rectWindow.top + item->rect.top;
2225 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2226 rect.bottom = item->rect.top - item->rect.bottom;
2228 else
2230 rect.left = wndPtr->rectWindow.left + item->rect.left;
2231 rect.top = wndPtr->rectWindow.top + item->rect.bottom;
2232 rect.right = item->rect.right - item->rect.left;
2233 rect.bottom = item->rect.bottom - item->rect.top;
2237 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2238 rect.left, rect.top, rect.right, rect.bottom );
2239 if (selectFirst)
2240 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2241 WIN_ReleaseWndPtr(wndPtr);
2242 return item->hSubMenu;
2247 /**********************************************************************
2248 * MENU_IsMenuActive
2250 BOOL MENU_IsMenuActive(void)
2252 return pTopPopupWnd && (pTopPopupWnd->dwStyle & WS_VISIBLE);
2255 /***********************************************************************
2256 * MENU_PtMenu
2258 * Walks menu chain trying to find a menu pt maps to.
2260 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2262 POPUPMENU *menu = MENU_GetMenu( hMenu );
2263 register UINT ht = menu->FocusedItem;
2265 /* try subpopup first (if any) */
2266 ht = (ht != NO_SELECTED_ITEM &&
2267 (menu->items[ht].fType & MF_POPUP) &&
2268 (menu->items[ht].fState & MF_MOUSESELECT))
2269 ? (UINT) MENU_PtMenu(menu->items[ht].hSubMenu, pt) : 0;
2271 if( !ht ) /* check the current window (avoiding WM_HITTEST) */
2273 ht = (UINT)NC_HandleNCHitTest( menu->hWnd, pt );
2274 if( menu->wFlags & MF_POPUP )
2275 ht = (ht != (UINT)HTNOWHERE &&
2276 ht != (UINT)HTERROR) ? (UINT)hMenu : 0;
2277 else
2279 WND* wndPtr = WIN_FindWndPtr(menu->hWnd);
2281 ht = ( ht == HTSYSMENU ) ? (UINT)(wndPtr->hSysMenu)
2282 : ( ht == HTMENU ) ? (UINT)(wndPtr->wIDmenu) : 0;
2283 WIN_ReleaseWndPtr(wndPtr);
2286 return (HMENU)ht;
2289 /***********************************************************************
2290 * MENU_ExecFocusedItem
2292 * Execute a menu item (for instance when user pressed Enter).
2293 * Return the wID of the executed item. Otherwise, -1 indicating
2294 * that no menu item was executed;
2295 * Have to receive the flags for the TrackPopupMenu options to avoid
2296 * sending unwanted message.
2299 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2301 MENUITEM *item;
2302 POPUPMENU *menu = MENU_GetMenu( hMenu );
2304 TRACE("%p hmenu=0x%04x\n", pmt, hMenu);
2306 if (!menu || !menu->nItems ||
2307 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2309 item = &menu->items[menu->FocusedItem];
2311 TRACE("%08x %08x %08x\n",
2312 hMenu, item->wID, item->hSubMenu);
2314 if (!(item->fType & MF_POPUP))
2316 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2318 /* If TPM_RETURNCMD is set you return the id, but
2319 do not send a message to the owner */
2320 if(!(wFlags & TPM_RETURNCMD))
2322 if( menu->wFlags & MF_SYSMENU )
2323 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2324 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2325 else
2326 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2328 return item->wID;
2331 else
2332 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2334 return -1;
2337 /***********************************************************************
2338 * MENU_SwitchTracking
2340 * Helper function for menu navigation routines.
2342 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2344 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2345 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2347 TRACE("%p hmenu=0x%04x 0x%04x\n", pmt, hPtMenu, id);
2349 if( pmt->hTopMenu != hPtMenu &&
2350 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2352 /* both are top level menus (system and menu-bar) */
2353 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2354 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2355 pmt->hTopMenu = hPtMenu;
2357 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2358 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2362 /***********************************************************************
2363 * MENU_ButtonDown
2365 * Return TRUE if we can go on with menu tracking.
2367 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2369 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2371 if (hPtMenu)
2373 UINT id = 0;
2374 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2375 MENUITEM *item;
2377 if( IS_SYSTEM_MENU(ptmenu) )
2378 item = ptmenu->items;
2379 else
2380 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2382 if( item )
2384 if( ptmenu->FocusedItem != id )
2385 MENU_SwitchTracking( pmt, hPtMenu, id );
2387 /* If the popup menu is not already "popped" */
2388 if(!(item->fState & MF_MOUSESELECT ))
2390 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2392 /* In win31, a newly popped menu always remains opened for the next buttonup */
2393 if(TWEAK_WineLook == WIN31_LOOK)
2394 ptmenu->bTimeToHide = FALSE;
2397 return TRUE;
2399 /* Else the click was on the menu bar, finish the tracking */
2401 return FALSE;
2404 /***********************************************************************
2405 * MENU_ButtonUp
2407 * Return the value of MENU_ExecFocusedItem if
2408 * the selected item was not a popup. Else open the popup.
2409 * A -1 return value indicates that we go on with menu tracking.
2412 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2414 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2416 if (hPtMenu)
2418 UINT id = 0;
2419 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2420 MENUITEM *item;
2422 if( IS_SYSTEM_MENU(ptmenu) )
2423 item = ptmenu->items;
2424 else
2425 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2427 if( item && (ptmenu->FocusedItem == id ))
2429 if( !(item->fType & MF_POPUP) )
2430 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2432 /* If we are dealing with the top-level menu */
2433 /* and this is a click on an already "popped" item: */
2434 /* Stop the menu tracking and close the opened submenus */
2435 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2436 return 0;
2438 ptmenu->bTimeToHide = TRUE;
2440 return -1;
2444 /***********************************************************************
2445 * MENU_MouseMove
2447 * Return TRUE if we can go on with menu tracking.
2449 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2451 UINT id = NO_SELECTED_ITEM;
2452 POPUPMENU *ptmenu = NULL;
2454 if( hPtMenu )
2456 ptmenu = MENU_GetMenu( hPtMenu );
2457 if( IS_SYSTEM_MENU(ptmenu) )
2458 id = 0;
2459 else
2460 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2463 if( id == NO_SELECTED_ITEM )
2465 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2466 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2469 else if( ptmenu->FocusedItem != id )
2471 MENU_SwitchTracking( pmt, hPtMenu, id );
2472 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2474 return TRUE;
2478 /***********************************************************************
2479 * MENU_DoNextMenu
2481 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2483 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2485 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2487 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2488 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2490 WND* wndPtr;
2491 HMENU hNewMenu;
2492 HWND hNewWnd;
2493 UINT id = 0;
2494 LRESULT l = SendMessageA( pmt->hOwnerWnd, WM_NEXTMENU, vk,
2495 (IS_SYSTEM_MENU(menu)) ? GetSubMenu16(pmt->hTopMenu,0) : pmt->hTopMenu );
2497 TRACE("%04x [%04x] -> %04x [%04x]\n",
2498 (UINT16)pmt->hCurrentMenu, (UINT16)pmt->hOwnerWnd, LOWORD(l), HIWORD(l) );
2500 if( l == 0 )
2502 wndPtr = WIN_FindWndPtr(pmt->hOwnerWnd);
2504 hNewWnd = pmt->hOwnerWnd;
2505 if( IS_SYSTEM_MENU(menu) )
2507 /* switch to the menu bar */
2509 if( wndPtr->dwStyle & WS_CHILD || !wndPtr->wIDmenu )
2511 WIN_ReleaseWndPtr(wndPtr);
2512 return FALSE;
2515 hNewMenu = wndPtr->wIDmenu;
2516 if( vk == VK_LEFT )
2518 menu = MENU_GetMenu( hNewMenu );
2519 id = menu->nItems - 1;
2522 else if( wndPtr->dwStyle & WS_SYSMENU )
2524 /* switch to the system menu */
2525 hNewMenu = wndPtr->hSysMenu;
2527 else
2529 WIN_ReleaseWndPtr(wndPtr);
2530 return FALSE;
2532 WIN_ReleaseWndPtr(wndPtr);
2534 else /* application returned a new menu to switch to */
2536 hNewMenu = LOWORD(l); hNewWnd = HIWORD(l);
2538 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2540 wndPtr = WIN_FindWndPtr(hNewWnd);
2542 if( wndPtr->dwStyle & WS_SYSMENU &&
2543 GetSubMenu16(wndPtr->hSysMenu, 0) == hNewMenu )
2545 /* get the real system menu */
2546 hNewMenu = wndPtr->hSysMenu;
2548 else if( wndPtr->dwStyle & WS_CHILD || wndPtr->wIDmenu != hNewMenu )
2550 /* FIXME: Not sure what to do here;
2551 * perhaps try to track hNewMenu as a popup? */
2553 TRACE(" -- got confused.\n");
2554 WIN_ReleaseWndPtr(wndPtr);
2555 return FALSE;
2557 WIN_ReleaseWndPtr(wndPtr);
2559 else return FALSE;
2562 if( hNewMenu != pmt->hTopMenu )
2564 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2565 FALSE, 0 );
2566 if( pmt->hCurrentMenu != pmt->hTopMenu )
2567 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2570 if( hNewWnd != pmt->hOwnerWnd )
2572 ReleaseCapture();
2573 pmt->hOwnerWnd = hNewWnd;
2574 EVENT_Capture( pmt->hOwnerWnd, HTMENU );
2577 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2578 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2580 return TRUE;
2582 return FALSE;
2585 /***********************************************************************
2586 * MENU_SuspendPopup
2588 * The idea is not to show the popup if the next input message is
2589 * going to hide it anyway.
2591 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2593 MSG msg;
2595 msg.hwnd = pmt->hOwnerWnd;
2597 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2598 pmt->trackFlags |= TF_SKIPREMOVE;
2600 switch( uMsg )
2602 case WM_KEYDOWN:
2603 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2604 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2606 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2607 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2608 if( msg.message == WM_KEYDOWN &&
2609 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2611 pmt->trackFlags |= TF_SUSPENDPOPUP;
2612 return TRUE;
2615 break;
2618 /* failures go through this */
2619 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2620 return FALSE;
2623 /***********************************************************************
2624 * MENU_KeyLeft
2626 * Handle a VK_LEFT key event in a menu.
2628 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2630 POPUPMENU *menu;
2631 HMENU hmenutmp, hmenuprev;
2632 UINT prevcol;
2634 hmenuprev = hmenutmp = pmt->hTopMenu;
2635 menu = MENU_GetMenu( hmenutmp );
2637 /* Try to move 1 column left (if possible) */
2638 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2639 NO_SELECTED_ITEM ) {
2641 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2642 prevcol, TRUE, 0 );
2643 return;
2646 /* close topmost popup */
2647 while (hmenutmp != pmt->hCurrentMenu)
2649 hmenuprev = hmenutmp;
2650 hmenutmp = MENU_GetSubPopup( hmenuprev );
2653 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2654 pmt->hCurrentMenu = hmenuprev;
2656 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2658 /* move menu bar selection if no more popups are left */
2660 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2661 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2663 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2665 /* A sublevel menu was displayed - display the next one
2666 * unless there is another displacement coming up */
2668 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2669 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2670 pmt->hTopMenu, TRUE, wFlags);
2676 /***********************************************************************
2677 * MENU_KeyRight
2679 * Handle a VK_RIGHT key event in a menu.
2681 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2683 HMENU hmenutmp;
2684 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2685 UINT nextcol;
2687 TRACE("MENU_KeyRight called, cur %x (%s), top %x (%s).\n",
2688 pmt->hCurrentMenu,
2689 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->
2690 items[0].text),
2691 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2693 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2695 /* If already displaying a popup, try to display sub-popup */
2697 hmenutmp = pmt->hCurrentMenu;
2698 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2700 /* if subpopup was displayed then we are done */
2701 if (hmenutmp != pmt->hCurrentMenu) return;
2704 /* Check to see if there's another column */
2705 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2706 NO_SELECTED_ITEM ) {
2707 TRACE("Going to %d.\n", nextcol );
2708 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2709 nextcol, TRUE, 0 );
2710 return;
2713 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2715 if( pmt->hCurrentMenu != pmt->hTopMenu )
2717 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2718 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2719 } else hmenutmp = 0;
2721 /* try to move to the next item */
2722 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2723 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2725 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2726 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2727 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2728 pmt->hTopMenu, TRUE, wFlags);
2732 /***********************************************************************
2733 * MENU_TrackMenu
2735 * Menu tracking code.
2737 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2738 HWND hwnd, const RECT *lprect )
2740 MSG msg;
2741 POPUPMENU *menu;
2742 BOOL fRemove;
2743 INT executedMenuId = -1;
2744 MTRACKER mt;
2745 BOOL enterIdleSent = FALSE;
2747 mt.trackFlags = 0;
2748 mt.hCurrentMenu = hmenu;
2749 mt.hTopMenu = hmenu;
2750 mt.hOwnerWnd = hwnd;
2751 mt.pt.x = x;
2752 mt.pt.y = y;
2754 TRACE("hmenu=0x%04x flags=0x%08x (%d,%d) hwnd=0x%04x (%d,%d)-(%d,%d)\n",
2755 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2756 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2758 fEndMenu = FALSE;
2759 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2761 if (wFlags & TPM_BUTTONDOWN)
2763 /* Get the result in order to start the tracking or not */
2764 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2765 fEndMenu = !fRemove;
2768 EVENT_Capture( mt.hOwnerWnd, HTMENU );
2770 while (!fEndMenu)
2772 menu = MENU_GetMenu( mt.hCurrentMenu );
2773 msg.hwnd = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2775 /* we have to keep the message in the queue until it's
2776 * clear that menu loop is not over yet. */
2778 if (!MSG_InternalGetMessage( QMSG_WIN32A, &msg, msg.hwnd, mt.hOwnerWnd,
2779 MSGF_MENU, PM_NOREMOVE, !enterIdleSent, &enterIdleSent )) break;
2781 /* check if EndMenu() tried to cancel us, by posting this message */
2782 if(msg.message == WM_CANCELMODE)
2784 /* we are now out of the loop */
2785 fEndMenu = TRUE;
2787 /* remove the message from the queue */
2788 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2790 /* break out of internal loop, ala ESCAPE */
2791 break;
2794 TranslateMessage( &msg );
2795 mt.pt = msg.pt;
2797 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2798 enterIdleSent=FALSE;
2800 fRemove = FALSE;
2801 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2804 * use the mouse coordinates in lParam instead of those in the MSG
2805 * struct to properly handle synthetic messages. lParam coords are
2806 * relative to client area, so they must be converted; since they can
2807 * be negative, we must use SLOWORD/SHIWORD instead of LOWORD/HIWORD.
2809 mt.pt.x = SLOWORD(msg.lParam);
2810 mt.pt.y = SHIWORD(msg.lParam);
2811 ClientToScreen(msg.hwnd,&mt.pt);
2813 /* Find a menu for this mouse event */
2814 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2816 switch(msg.message)
2818 /* no WM_NC... messages in captured state */
2820 case WM_RBUTTONDBLCLK:
2821 case WM_RBUTTONDOWN:
2822 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2823 /* fall through */
2824 case WM_LBUTTONDBLCLK:
2825 case WM_LBUTTONDOWN:
2826 /* If the message belongs to the menu, removes it from the queue */
2827 /* Else, end menu tracking */
2828 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2829 fEndMenu = !fRemove;
2830 break;
2832 case WM_RBUTTONUP:
2833 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2834 /* fall through */
2835 case WM_LBUTTONUP:
2836 /* Check if a menu was selected by the mouse */
2837 if (hmenu)
2839 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2841 /* End the loop if executedMenuId is an item ID */
2842 /* or if the job was done (executedMenuId = 0). */
2843 fEndMenu = fRemove = (executedMenuId != -1);
2845 /* No menu was selected by the mouse */
2846 /* if the function was called by TrackPopupMenu, continue
2847 with the menu tracking. If not, stop it */
2848 else
2849 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2851 break;
2853 case WM_MOUSEMOVE:
2854 /* In win95 winelook, the selected menu item must be changed every time the
2855 mouse moves. In Win31 winelook, the mouse button has to be held down */
2857 if ( (TWEAK_WineLook > WIN31_LOOK) ||
2858 ( (msg.wParam & MK_LBUTTON) ||
2859 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON))) )
2861 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2863 } /* switch(msg.message) - mouse */
2865 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2867 fRemove = TRUE; /* Keyboard messages are always removed */
2868 switch(msg.message)
2870 case WM_KEYDOWN:
2871 switch(msg.wParam)
2873 case VK_HOME:
2874 case VK_END:
2875 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2876 NO_SELECTED_ITEM, FALSE, 0 );
2877 /* fall through */
2878 case VK_UP:
2879 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2880 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2881 break;
2883 case VK_DOWN: /* If on menu bar, pull-down the menu */
2885 menu = MENU_GetMenu( mt.hCurrentMenu );
2886 if (!(menu->wFlags & MF_POPUP))
2887 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2888 else /* otherwise try to move selection */
2889 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2890 break;
2892 case VK_LEFT:
2893 MENU_KeyLeft( &mt, wFlags );
2894 break;
2896 case VK_RIGHT:
2897 MENU_KeyRight( &mt, wFlags );
2898 break;
2900 case VK_ESCAPE:
2901 fEndMenu = TRUE;
2902 break;
2904 case VK_F1:
2906 HELPINFO hi;
2907 hi.cbSize = sizeof(HELPINFO);
2908 hi.iContextType = HELPINFO_MENUITEM;
2909 if (menu->FocusedItem == NO_SELECTED_ITEM)
2910 hi.iCtrlId = 0;
2911 else
2912 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2913 hi.hItemHandle = hmenu;
2914 hi.dwContextId = menu->dwContextHelpID;
2915 hi.MousePos = msg.pt;
2916 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2917 break;
2920 default:
2921 break;
2923 break; /* WM_KEYDOWN */
2925 case WM_SYSKEYDOWN:
2926 switch(msg.wParam)
2928 case VK_MENU:
2929 fEndMenu = TRUE;
2930 break;
2933 break; /* WM_SYSKEYDOWN */
2935 case WM_CHAR:
2937 UINT pos;
2939 if (msg.wParam == '\r' || msg.wParam == ' ')
2941 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2942 fEndMenu = (executedMenuId != -1);
2944 break;
2947 /* Hack to avoid control chars. */
2948 /* We will find a better way real soon... */
2949 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2951 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2952 LOWORD(msg.wParam), FALSE );
2953 if (pos == (UINT)-2) fEndMenu = TRUE;
2954 else if (pos == (UINT)-1) MessageBeep(0);
2955 else
2957 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2958 TRUE, 0 );
2959 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2960 fEndMenu = (executedMenuId != -1);
2963 break;
2964 } /* switch(msg.message) - kbd */
2966 else
2968 DispatchMessageA( &msg );
2971 if (!fEndMenu) fRemove = TRUE;
2973 /* finally remove message from the queue */
2975 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2976 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2977 else mt.trackFlags &= ~TF_SKIPREMOVE;
2980 ReleaseCapture();
2982 /* If dropdown is still painted and the close box is clicked on
2983 then the menu will be destroyed as part of the DispatchMessage above.
2984 This will then invalidate the menu handle in mt.hTopMenu. We should
2985 check for this first. */
2986 if( IsMenu( mt.hTopMenu ) )
2988 menu = MENU_GetMenu( mt.hTopMenu );
2990 if( IsWindow( mt.hOwnerWnd ) )
2992 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2994 if (menu && menu->wFlags & MF_POPUP)
2996 ShowWindow( menu->hWnd, SW_HIDE );
2997 uSubPWndLevel = 0;
2999 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3000 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3003 /* Reset the variable for hiding menu */
3004 if( menu ) menu->bTimeToHide = FALSE;
3007 /* The return value is only used by TrackPopupMenu */
3008 return ((executedMenuId != -1) ? executedMenuId : 0);
3011 /***********************************************************************
3012 * MENU_InitTracking
3014 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3016 TRACE("hwnd=0x%04x hmenu=0x%04x\n", hWnd, hMenu);
3018 HideCaret(0);
3020 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3021 if (!(wFlags & TPM_NONOTIFY))
3022 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3024 SendMessageA( hWnd, WM_SETCURSOR, hWnd, HTCAPTION );
3026 if (!(wFlags & TPM_NONOTIFY))
3027 SendMessageA( hWnd, WM_INITMENU, hMenu, 0 );
3029 return TRUE;
3031 /***********************************************************************
3032 * MENU_ExitTracking
3034 static BOOL MENU_ExitTracking(HWND hWnd)
3036 TRACE("hwnd=0x%04x\n", hWnd);
3038 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
3039 ShowCaret(0);
3040 return TRUE;
3043 /***********************************************************************
3044 * MENU_TrackMouseMenuBar
3046 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3048 void MENU_TrackMouseMenuBar( WND* wndPtr, INT ht, POINT pt )
3050 HWND hWnd = wndPtr->hwndSelf;
3051 HMENU hMenu = (ht == HTSYSMENU) ? wndPtr->hSysMenu : wndPtr->wIDmenu;
3052 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3054 TRACE("pwnd=%p ht=0x%04x (%ld,%ld)\n", wndPtr, ht, pt.x, pt.y);
3056 if (IsMenu(hMenu))
3058 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3059 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3060 MENU_ExitTracking(hWnd);
3065 /***********************************************************************
3066 * MENU_TrackKbdMenuBar
3068 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3070 void MENU_TrackKbdMenuBar( WND* wndPtr, UINT wParam, INT vkey)
3072 UINT uItem = NO_SELECTED_ITEM;
3073 HMENU hTrackMenu;
3074 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3076 /* find window that has a menu */
3078 while( wndPtr->dwStyle & WS_CHILD)
3079 if( !(wndPtr = wndPtr->parent) ) return;
3081 /* check if we have to track a system menu */
3083 if( (wndPtr->dwStyle & (WS_CHILD | WS_MINIMIZE)) ||
3084 !wndPtr->wIDmenu || vkey == VK_SPACE )
3086 if( !(wndPtr->dwStyle & WS_SYSMENU) ) return;
3087 hTrackMenu = wndPtr->hSysMenu;
3088 uItem = 0;
3089 wParam |= HTSYSMENU; /* prevent item lookup */
3091 else
3092 hTrackMenu = wndPtr->wIDmenu;
3094 if (IsMenu( hTrackMenu ))
3096 MENU_InitTracking( wndPtr->hwndSelf, hTrackMenu, FALSE, wFlags );
3098 if( vkey && vkey != VK_SPACE )
3100 uItem = MENU_FindItemByKey( wndPtr->hwndSelf, hTrackMenu,
3101 vkey, (wParam & HTSYSMENU) );
3102 if( uItem >= (UINT)(-2) )
3104 if( uItem == (UINT)(-1) ) MessageBeep(0);
3105 hTrackMenu = 0;
3109 if( hTrackMenu )
3111 MENU_SelectItem( wndPtr->hwndSelf, hTrackMenu, uItem, TRUE, 0 );
3113 if( uItem == NO_SELECTED_ITEM )
3114 MENU_MoveSelection( wndPtr->hwndSelf, hTrackMenu, ITEM_NEXT );
3115 else if( vkey )
3116 PostMessageA( wndPtr->hwndSelf, WM_KEYDOWN, VK_DOWN, 0L );
3118 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, wndPtr->hwndSelf, NULL );
3121 MENU_ExitTracking (wndPtr->hwndSelf);
3126 /**********************************************************************
3127 * TrackPopupMenu16 (USER.416)
3129 BOOL16 WINAPI TrackPopupMenu16( HMENU16 hMenu, UINT16 wFlags, INT16 x, INT16 y,
3130 INT16 nReserved, HWND16 hWnd, const RECT16 *lpRect )
3132 RECT r;
3133 if (lpRect)
3134 CONV_RECT16TO32( lpRect, &r );
3135 return TrackPopupMenu( hMenu, wFlags, x, y, nReserved, hWnd,
3136 lpRect ? &r : NULL );
3140 /**********************************************************************
3141 * TrackPopupMenu (USER32.549)
3143 * Like the win32 API, the function return the command ID only if the
3144 * flag TPM_RETURNCMD is on.
3147 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3148 INT nReserved, HWND hWnd, const RECT *lpRect )
3150 BOOL ret = FALSE;
3152 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3154 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3155 if (!(wFlags & TPM_NONOTIFY))
3156 SendMessageA( hWnd, WM_INITMENUPOPUP, hMenu, 0);
3158 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3159 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3160 MENU_ExitTracking(hWnd);
3162 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3163 ret = 1;
3165 return ret;
3168 /**********************************************************************
3169 * TrackPopupMenuEx (USER32.550)
3171 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3172 HWND hWnd, LPTPMPARAMS lpTpm )
3174 FIXME("not fully implemented\n" );
3175 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3176 lpTpm ? &lpTpm->rcExclude : NULL );
3179 /***********************************************************************
3180 * PopupMenuWndProc
3182 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3184 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3186 TRACE("hwnd=0x%04x msg=0x%04x wp=0x%04x lp=0x%08lx\n",
3187 hwnd, message, wParam, lParam);
3189 switch(message)
3191 case WM_CREATE:
3193 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3194 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3195 return 0;
3198 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3199 return MA_NOACTIVATE;
3201 case WM_PAINT:
3203 PAINTSTRUCT ps;
3204 BeginPaint( hwnd, &ps );
3205 MENU_DrawPopupMenu( hwnd, ps.hdc,
3206 (HMENU)GetWindowLongA( hwnd, 0 ) );
3207 EndPaint( hwnd, &ps );
3208 return 0;
3210 case WM_ERASEBKGND:
3211 return 1;
3213 case WM_DESTROY:
3215 /* zero out global pointer in case resident popup window
3216 * was somehow destroyed. */
3218 if(MENU_GetTopPopupWnd() )
3220 if( hwnd == pTopPopupWnd->hwndSelf )
3222 ERR("resident popup destroyed!\n");
3224 MENU_DestroyTopPopupWnd();
3225 uSubPWndLevel = 0;
3227 else
3228 uSubPWndLevel--;
3229 MENU_ReleaseTopPopupWnd();
3231 break;
3233 case WM_SHOWWINDOW:
3235 if( wParam )
3237 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3239 else
3240 SetWindowLongW( hwnd, 0, 0 );
3241 break;
3243 case MM_SETMENUHANDLE:
3244 SetWindowLongW( hwnd, 0, wParam );
3245 break;
3247 case MM_GETMENUHANDLE:
3248 return GetWindowLongW( hwnd, 0 );
3250 default:
3251 return DefWindowProcW( hwnd, message, wParam, lParam );
3253 return 0;
3257 /***********************************************************************
3258 * MENU_GetMenuBarHeight
3260 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3262 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3263 INT orgX, INT orgY )
3265 HDC hdc;
3266 RECT rectBar;
3267 WND *wndPtr;
3268 LPPOPUPMENU lppop;
3269 UINT retvalue;
3271 TRACE("HWND 0x%x, width %d, at (%d, %d).\n",
3272 hwnd, menubarWidth, orgX, orgY );
3274 if (!(wndPtr = WIN_FindWndPtr( hwnd )))
3275 return 0;
3277 if (!(lppop = MENU_GetMenu((HMENU16)wndPtr->wIDmenu)))
3279 WIN_ReleaseWndPtr(wndPtr);
3280 return 0;
3283 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3284 SelectObject( hdc, hMenuFont);
3285 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3286 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3287 ReleaseDC( hwnd, hdc );
3288 retvalue = lppop->Height;
3289 WIN_ReleaseWndPtr(wndPtr);
3290 return retvalue;
3294 /*******************************************************************
3295 * ChangeMenu16 (USER.153)
3297 BOOL16 WINAPI ChangeMenu16( HMENU16 hMenu, UINT16 pos, SEGPTR data,
3298 UINT16 id, UINT16 flags )
3300 TRACE("menu=%04x pos=%d data=%08lx id=%04x flags=%04x\n",
3301 hMenu, pos, (DWORD)data, id, flags );
3302 if (flags & MF_APPEND) return AppendMenu16( hMenu, flags & ~MF_APPEND,
3303 id, data );
3305 /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
3306 /* for MF_DELETE. We should check the parameters for all others */
3307 /* MF_* actions also (anybody got a doc on ChangeMenu?). */
3309 if (flags & MF_DELETE) return DeleteMenu16(hMenu, pos, flags & ~MF_DELETE);
3310 if (flags & MF_CHANGE) return ModifyMenu16(hMenu, pos, flags & ~MF_CHANGE,
3311 id, data );
3312 if (flags & MF_REMOVE) return RemoveMenu16(hMenu,
3313 flags & MF_BYPOSITION ? pos : id,
3314 flags & ~MF_REMOVE );
3315 /* Default: MF_INSERT */
3316 return InsertMenu16( hMenu, pos, flags, id, data );
3320 /*******************************************************************
3321 * ChangeMenuA (USER32.23)
3323 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3324 UINT id, UINT flags )
3326 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3327 hMenu, pos, (DWORD)data, id, flags );
3328 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3329 id, data );
3330 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3331 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3332 id, data );
3333 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3334 flags & MF_BYPOSITION ? pos : id,
3335 flags & ~MF_REMOVE );
3336 /* Default: MF_INSERT */
3337 return InsertMenuA( hMenu, pos, flags, id, data );
3341 /*******************************************************************
3342 * ChangeMenuW (USER32.24)
3344 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3345 UINT id, UINT flags )
3347 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3348 hMenu, pos, (DWORD)data, id, flags );
3349 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3350 id, data );
3351 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3352 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3353 id, data );
3354 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3355 flags & MF_BYPOSITION ? pos : id,
3356 flags & ~MF_REMOVE );
3357 /* Default: MF_INSERT */
3358 return InsertMenuW( hMenu, pos, flags, id, data );
3362 /*******************************************************************
3363 * CheckMenuItem16 (USER.154)
3365 BOOL16 WINAPI CheckMenuItem16( HMENU16 hMenu, UINT16 id, UINT16 flags )
3367 return (BOOL16)CheckMenuItem( hMenu, id, flags );
3371 /*******************************************************************
3372 * CheckMenuItem (USER32.46)
3374 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3376 MENUITEM *item;
3377 DWORD ret;
3379 TRACE("menu=%04x id=%04x flags=%04x\n", hMenu, id, flags );
3380 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3381 ret = item->fState & MF_CHECKED;
3382 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3383 else item->fState &= ~MF_CHECKED;
3384 return ret;
3388 /**********************************************************************
3389 * EnableMenuItem16 (USER.155)
3391 UINT16 WINAPI EnableMenuItem16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3393 return EnableMenuItem( hMenu, wItemID, wFlags );
3397 /**********************************************************************
3398 * EnableMenuItem (USER32.170)
3400 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3402 UINT oldflags;
3403 MENUITEM *item;
3404 POPUPMENU *menu;
3406 TRACE("(%04x, %04X, %04X) !\n",
3407 hMenu, wItemID, wFlags);
3409 /* Get the Popupmenu to access the owner menu */
3410 if (!(menu = MENU_GetMenu(hMenu)))
3411 return (UINT)-1;
3413 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3414 return (UINT)-1;
3416 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3417 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3419 /* In win95 if the close item in the system menu change update the close button */
3420 if (TWEAK_WineLook == WIN95_LOOK)
3421 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3423 if (menu->hSysMenuOwner != 0)
3425 POPUPMENU* parentMenu;
3427 /* Get the parent menu to access*/
3428 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3429 return (UINT)-1;
3431 /* Refresh the frame to reflect the change*/
3432 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3433 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3437 return oldflags;
3441 /*******************************************************************
3442 * GetMenuString16 (USER.161)
3444 INT16 WINAPI GetMenuString16( HMENU16 hMenu, UINT16 wItemID,
3445 LPSTR str, INT16 nMaxSiz, UINT16 wFlags )
3447 return GetMenuStringA( hMenu, wItemID, str, nMaxSiz, wFlags );
3451 /*******************************************************************
3452 * GetMenuStringA (USER32.268)
3454 INT WINAPI GetMenuStringA(
3455 HMENU hMenu, /* [in] menuhandle */
3456 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3457 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3458 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3459 UINT wFlags /* [in] MF_ flags */
3461 MENUITEM *item;
3463 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3464 hMenu, wItemID, str, nMaxSiz, wFlags );
3465 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3466 if (!IS_STRING_ITEM(item->fType)) return 0;
3467 if (!str || !nMaxSiz) return strlenW(item->text);
3468 str[0] = '\0';
3469 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3470 str[nMaxSiz-1] = 0;
3471 TRACE("returning '%s'\n", str );
3472 return strlen(str);
3476 /*******************************************************************
3477 * GetMenuStringW (USER32.269)
3479 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3480 LPWSTR str, INT nMaxSiz, UINT wFlags )
3482 MENUITEM *item;
3484 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3485 hMenu, wItemID, str, nMaxSiz, wFlags );
3486 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3487 if (!IS_STRING_ITEM(item->fType)) return 0;
3488 if (!str || !nMaxSiz) return strlenW(item->text);
3489 str[0] = '\0';
3490 lstrcpynW( str, item->text, nMaxSiz );
3491 return strlenW(str);
3495 /**********************************************************************
3496 * HiliteMenuItem16 (USER.162)
3498 BOOL16 WINAPI HiliteMenuItem16( HWND16 hWnd, HMENU16 hMenu, UINT16 wItemID,
3499 UINT16 wHilite )
3501 return HiliteMenuItem( hWnd, hMenu, wItemID, wHilite );
3505 /**********************************************************************
3506 * HiliteMenuItem (USER32.318)
3508 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3509 UINT wHilite )
3511 LPPOPUPMENU menu;
3512 TRACE("(%04x, %04x, %04x, %04x);\n",
3513 hWnd, hMenu, wItemID, wHilite);
3514 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3515 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3516 if (menu->FocusedItem == wItemID) return TRUE;
3517 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3518 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3519 return TRUE;
3523 /**********************************************************************
3524 * GetMenuState16 (USER.250)
3526 UINT16 WINAPI GetMenuState16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3528 return GetMenuState( hMenu, wItemID, wFlags );
3532 /**********************************************************************
3533 * GetMenuState (USER32.267)
3535 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3537 MENUITEM *item;
3538 TRACE("(menu=%04x, id=%04x, flags=%04x);\n",
3539 hMenu, wItemID, wFlags);
3540 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3541 debug_print_menuitem (" item: ", item, "");
3542 if (item->fType & MF_POPUP)
3544 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3545 if (!menu) return -1;
3546 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3548 else
3550 /* We used to (from way back then) mask the result to 0xff. */
3551 /* I don't know why and it seems wrong as the documented */
3552 /* return flag MF_SEPARATOR is outside that mask. */
3553 return (item->fType | item->fState);
3558 /**********************************************************************
3559 * GetMenuItemCount16 (USER.263)
3561 INT16 WINAPI GetMenuItemCount16( HMENU16 hMenu )
3563 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3564 if (!menu) return -1;
3565 TRACE("(%04x) returning %d\n",
3566 hMenu, menu->nItems );
3567 return menu->nItems;
3571 /**********************************************************************
3572 * GetMenuItemCount (USER32.262)
3574 INT WINAPI GetMenuItemCount( HMENU hMenu )
3576 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3577 if (!menu) return -1;
3578 TRACE("(%04x) returning %d\n",
3579 hMenu, menu->nItems );
3580 return menu->nItems;
3583 /**********************************************************************
3584 * GetMenuItemID16 (USER.264)
3586 UINT16 WINAPI GetMenuItemID16( HMENU16 hMenu, INT16 nPos )
3588 return (UINT16) GetMenuItemID (hMenu, nPos);
3591 /**********************************************************************
3592 * GetMenuItemID (USER32.263)
3594 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3596 MENUITEM * lpmi;
3598 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3599 if (lpmi->fType & MF_POPUP) return -1;
3600 return lpmi->wID;
3604 /*******************************************************************
3605 * InsertMenu16 (USER.410)
3607 BOOL16 WINAPI InsertMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3608 UINT16 id, SEGPTR data )
3610 UINT pos32 = (UINT)pos;
3611 if ((pos == (UINT16)-1) && (flags & MF_BYPOSITION)) pos32 = (UINT)-1;
3612 if (IS_STRING_ITEM(flags) && data)
3613 return InsertMenuA( hMenu, pos32, flags, id, MapSL(data) );
3614 return InsertMenuA( hMenu, pos32, flags, id, (LPSTR)data );
3618 /*******************************************************************
3619 * InsertMenuW (USER32.325)
3621 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3622 UINT id, LPCWSTR str )
3624 MENUITEM *item;
3626 if (IS_STRING_ITEM(flags) && str)
3627 TRACE("hMenu %04x, pos %d, flags %08x, "
3628 "id %04x, str '%s'\n",
3629 hMenu, pos, flags, id, debugstr_w(str) );
3630 else TRACE("hMenu %04x, pos %d, flags %08x, "
3631 "id %04x, str %08lx (not a string)\n",
3632 hMenu, pos, flags, id, (DWORD)str );
3634 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3636 if (!(MENU_SetItemData( item, flags, id, str )))
3638 RemoveMenu( hMenu, pos, flags );
3639 return FALSE;
3642 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3643 (MENU_GetMenu((HMENU16)id))->wFlags |= MF_POPUP;
3645 item->hCheckBit = item->hUnCheckBit = 0;
3646 return TRUE;
3650 /*******************************************************************
3651 * InsertMenuA (USER32.322)
3653 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3654 UINT id, LPCSTR str )
3656 BOOL ret;
3658 if (IS_STRING_ITEM(flags) && str)
3660 LPWSTR newstr = HEAP_strdupAtoW( GetProcessHeap(), 0, str );
3661 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3662 HeapFree( GetProcessHeap(), 0, newstr );
3663 return ret;
3665 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3669 /*******************************************************************
3670 * AppendMenu16 (USER.411)
3672 BOOL16 WINAPI AppendMenu16(HMENU16 hMenu, UINT16 flags, UINT16 id, SEGPTR data)
3674 return InsertMenu16( hMenu, -1, flags | MF_BYPOSITION, id, data );
3678 /*******************************************************************
3679 * AppendMenuA (USER32.5)
3681 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3682 UINT id, LPCSTR data )
3684 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3688 /*******************************************************************
3689 * AppendMenuW (USER32.6)
3691 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3692 UINT id, LPCWSTR data )
3694 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3698 /**********************************************************************
3699 * RemoveMenu16 (USER.412)
3701 BOOL16 WINAPI RemoveMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3703 return RemoveMenu( hMenu, nPos, wFlags );
3707 /**********************************************************************
3708 * RemoveMenu (USER32.441)
3710 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3712 LPPOPUPMENU menu;
3713 MENUITEM *item;
3715 TRACE("(menu=%04x pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3716 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3717 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3719 /* Remove item */
3721 MENU_FreeItemData( item );
3723 if (--menu->nItems == 0)
3725 HeapFree( SystemHeap, 0, menu->items );
3726 menu->items = NULL;
3728 else
3730 while(nPos < menu->nItems)
3732 *item = *(item+1);
3733 item++;
3734 nPos++;
3736 menu->items = HeapReAlloc( SystemHeap, 0, menu->items,
3737 menu->nItems * sizeof(MENUITEM) );
3739 return TRUE;
3743 /**********************************************************************
3744 * DeleteMenu16 (USER.413)
3746 BOOL16 WINAPI DeleteMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3748 return DeleteMenu( hMenu, nPos, wFlags );
3752 /**********************************************************************
3753 * DeleteMenu (USER32.129)
3755 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3757 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3758 if (!item) return FALSE;
3759 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3760 /* nPos is now the position of the item */
3761 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3762 return TRUE;
3766 /*******************************************************************
3767 * ModifyMenu16 (USER.414)
3769 BOOL16 WINAPI ModifyMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3770 UINT16 id, SEGPTR data )
3772 if (IS_STRING_ITEM(flags))
3773 return ModifyMenuA( hMenu, pos, flags, id, MapSL(data) );
3774 return ModifyMenuA( hMenu, pos, flags, id, (LPSTR)data );
3778 /*******************************************************************
3779 * ModifyMenuW (USER32.398)
3781 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3782 UINT id, LPCWSTR str )
3784 MENUITEM *item;
3786 if (IS_STRING_ITEM(flags))
3788 TRACE("%04x %d %04x %04x '%s'\n",
3789 hMenu, pos, flags, id, str ? debugstr_w(str) : "#NULL#" );
3790 if (!str) return FALSE;
3792 else
3794 TRACE("%04x %d %04x %04x %08lx\n",
3795 hMenu, pos, flags, id, (DWORD)str );
3798 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3799 return MENU_SetItemData( item, flags, id, str );
3803 /*******************************************************************
3804 * ModifyMenuA (USER32.397)
3806 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3807 UINT id, LPCSTR str )
3809 BOOL ret;
3811 if (IS_STRING_ITEM(flags) && str)
3813 LPWSTR newstr = HEAP_strdupAtoW( GetProcessHeap(), 0, str );
3814 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3815 HeapFree( GetProcessHeap(), 0, newstr );
3816 return ret;
3818 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3822 /**********************************************************************
3823 * CreatePopupMenu16 (USER.415)
3825 HMENU16 WINAPI CreatePopupMenu16(void)
3827 return CreatePopupMenu();
3831 /**********************************************************************
3832 * CreatePopupMenu (USER32.82)
3834 HMENU WINAPI CreatePopupMenu(void)
3836 HMENU hmenu;
3837 POPUPMENU *menu;
3839 if (!(hmenu = CreateMenu())) return 0;
3840 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
3841 menu->wFlags |= MF_POPUP;
3842 menu->bTimeToHide = FALSE;
3843 return hmenu;
3847 /**********************************************************************
3848 * GetMenuCheckMarkDimensions (USER.417) (USER32.258)
3850 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3852 return MAKELONG( check_bitmap_width, check_bitmap_height );
3856 /**********************************************************************
3857 * SetMenuItemBitmaps16 (USER.418)
3859 BOOL16 WINAPI SetMenuItemBitmaps16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags,
3860 HBITMAP16 hNewUnCheck, HBITMAP16 hNewCheck)
3862 return SetMenuItemBitmaps( hMenu, nPos, wFlags, hNewUnCheck, hNewCheck );
3866 /**********************************************************************
3867 * SetMenuItemBitmaps (USER32.490)
3869 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3870 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3872 MENUITEM *item;
3873 TRACE("(%04x, %04x, %04x, %04x, %04x)\n",
3874 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3875 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3877 if (!hNewCheck && !hNewUnCheck)
3879 item->fState &= ~MF_USECHECKBITMAPS;
3881 else /* Install new bitmaps */
3883 item->hCheckBit = hNewCheck;
3884 item->hUnCheckBit = hNewUnCheck;
3885 item->fState |= MF_USECHECKBITMAPS;
3887 return TRUE;
3891 /**********************************************************************
3892 * CreateMenu16 (USER.151)
3894 HMENU16 WINAPI CreateMenu16(void)
3896 return CreateMenu();
3900 /**********************************************************************
3901 * CreateMenu (USER32.81)
3903 HMENU WINAPI CreateMenu(void)
3905 HMENU hMenu;
3906 LPPOPUPMENU menu;
3907 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3908 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3910 ZeroMemory(menu, sizeof(POPUPMENU));
3911 menu->wMagic = MENU_MAGIC;
3912 menu->FocusedItem = NO_SELECTED_ITEM;
3913 menu->bTimeToHide = FALSE;
3915 TRACE("return %04x\n", hMenu );
3917 return hMenu;
3921 /**********************************************************************
3922 * DestroyMenu16 (USER.152)
3924 BOOL16 WINAPI DestroyMenu16( HMENU16 hMenu )
3926 return DestroyMenu( hMenu );
3930 /**********************************************************************
3931 * DestroyMenu (USER32.134)
3933 BOOL WINAPI DestroyMenu( HMENU hMenu )
3935 TRACE("(%04x)\n", hMenu);
3937 /* Silently ignore attempts to destroy default system popup */
3939 if (hMenu && hMenu != MENU_DefSysPopup)
3941 LPPOPUPMENU lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3942 WND *pTPWnd = MENU_GetTopPopupWnd();
3944 if( pTPWnd && (hMenu == *(HMENU*)pTPWnd->wExtra) )
3945 *(UINT*)pTPWnd->wExtra = 0;
3947 if (!IS_A_MENU(lppop)) lppop = NULL;
3948 if ( lppop )
3950 lppop->wMagic = 0; /* Mark it as destroyed */
3952 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd &&
3953 (!pTPWnd || (lppop->hWnd != pTPWnd->hwndSelf)))
3954 DestroyWindow( lppop->hWnd );
3956 if (lppop->items) /* recursively destroy submenus */
3958 int i;
3959 MENUITEM *item = lppop->items;
3960 for (i = lppop->nItems; i > 0; i--, item++)
3962 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3963 MENU_FreeItemData( item );
3965 HeapFree( SystemHeap, 0, lppop->items );
3967 USER_HEAP_FREE( hMenu );
3968 MENU_ReleaseTopPopupWnd();
3970 else
3972 MENU_ReleaseTopPopupWnd();
3973 return FALSE;
3976 return (hMenu != MENU_DefSysPopup);
3980 /**********************************************************************
3981 * GetSystemMenu16 (USER.156)
3983 HMENU16 WINAPI GetSystemMenu16( HWND16 hWnd, BOOL16 bRevert )
3985 return GetSystemMenu( hWnd, bRevert );
3989 /**********************************************************************
3990 * GetSystemMenu (USER32.291)
3992 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3994 WND *wndPtr = WIN_FindWndPtr( hWnd );
3995 HMENU retvalue = 0;
3997 if (wndPtr)
3999 if( wndPtr->hSysMenu )
4001 if( bRevert )
4003 DestroyMenu(wndPtr->hSysMenu);
4004 wndPtr->hSysMenu = 0;
4006 else
4008 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
4009 if( menu )
4011 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
4012 menu->items[0].hSubMenu = MENU_CopySysPopup();
4014 else
4016 WARN("Current sys-menu (%04x) of wnd %04x is broken\n",
4017 wndPtr->hSysMenu, hWnd);
4018 wndPtr->hSysMenu = 0;
4023 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4024 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
4026 if( wndPtr->hSysMenu )
4028 POPUPMENU *menu;
4029 retvalue = GetSubMenu16(wndPtr->hSysMenu, 0);
4031 /* Store the dummy sysmenu handle to facilitate the refresh */
4032 /* of the close button if the SC_CLOSE item change */
4033 menu = MENU_GetMenu(retvalue);
4034 if ( menu )
4035 menu->hSysMenuOwner = wndPtr->hSysMenu;
4037 WIN_ReleaseWndPtr(wndPtr);
4039 return bRevert ? 0 : retvalue;
4043 /*******************************************************************
4044 * SetSystemMenu16 (USER.280)
4046 BOOL16 WINAPI SetSystemMenu16( HWND16 hwnd, HMENU16 hMenu )
4048 return SetSystemMenu( hwnd, hMenu );
4052 /*******************************************************************
4053 * SetSystemMenu (USER32.508)
4055 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4057 WND *wndPtr = WIN_FindWndPtr(hwnd);
4059 if (wndPtr)
4061 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4062 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4063 WIN_ReleaseWndPtr(wndPtr);
4064 return TRUE;
4066 return FALSE;
4070 /**********************************************************************
4071 * GetMenu16 (USER.157)
4073 HMENU16 WINAPI GetMenu16( HWND16 hWnd )
4075 return (HMENU16)GetMenu(hWnd);
4079 /**********************************************************************
4080 * GetMenu (USER32.257)
4082 HMENU WINAPI GetMenu( HWND hWnd )
4084 HMENU retvalue;
4085 WND * wndPtr = WIN_FindWndPtr(hWnd);
4087 if (!wndPtr) return 0;
4089 retvalue = (HMENU)wndPtr->wIDmenu;
4090 TRACE("for %swindow %04x returning %04x\n",
4091 (wndPtr->dwStyle & WS_CHILD) ? "child " : "", hWnd, retvalue);
4092 WIN_ReleaseWndPtr(wndPtr);
4093 return retvalue;
4097 /**********************************************************************
4098 * SetMenu16 (USER.158)
4100 BOOL16 WINAPI SetMenu16( HWND16 hWnd, HMENU16 hMenu )
4102 return SetMenu( hWnd, hMenu );
4106 /**********************************************************************
4107 * SetMenu (USER32.487)
4109 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4111 WND * wndPtr = WIN_FindWndPtr(hWnd);
4112 BOOL res = FALSE;
4114 TRACE("(%04x, %04x);\n", hWnd, hMenu);
4116 if (hMenu && !IsMenu(hMenu))
4118 WARN("hMenu is not a menu handle\n");
4119 goto exit;
4122 if (wndPtr && !(wndPtr->dwStyle & WS_CHILD))
4124 if (GetCapture() == hWnd) ReleaseCapture();
4126 wndPtr->wIDmenu = (UINT)hMenu;
4127 if (hMenu != 0)
4129 LPPOPUPMENU lpmenu;
4131 if (!(lpmenu = MENU_GetMenu(hMenu)))
4132 goto exit;
4134 lpmenu->hWnd = hWnd;
4135 lpmenu->Height = 0; /* Make sure we recalculate the size */
4137 if (IsWindowVisible(hWnd))
4138 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4139 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4140 res = TRUE;
4142 exit:
4143 WIN_ReleaseWndPtr(wndPtr);
4144 return res;
4149 /**********************************************************************
4150 * GetSubMenu16 (USER.159)
4152 HMENU16 WINAPI GetSubMenu16( HMENU16 hMenu, INT16 nPos )
4154 return GetSubMenu( hMenu, nPos );
4158 /**********************************************************************
4159 * GetSubMenu (USER32.288)
4161 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4163 MENUITEM * lpmi;
4165 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
4166 if (!(lpmi->fType & MF_POPUP)) return 0;
4167 return lpmi->hSubMenu;
4171 /**********************************************************************
4172 * DrawMenuBar16 (USER.160)
4174 void WINAPI DrawMenuBar16( HWND16 hWnd )
4176 DrawMenuBar( hWnd );
4180 /**********************************************************************
4181 * DrawMenuBar (USER32.161)
4183 BOOL WINAPI DrawMenuBar( HWND hWnd )
4185 LPPOPUPMENU lppop;
4186 WND *wndPtr = WIN_FindWndPtr(hWnd);
4187 if (wndPtr && !(wndPtr->dwStyle & WS_CHILD) && wndPtr->wIDmenu)
4189 lppop = MENU_GetMenu((HMENU16)wndPtr->wIDmenu);
4190 if (lppop == NULL)
4192 WIN_ReleaseWndPtr(wndPtr);
4193 return FALSE;
4196 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4197 lppop->hwndOwner = hWnd;
4198 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4199 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4200 WIN_ReleaseWndPtr(wndPtr);
4201 return TRUE;
4203 WIN_ReleaseWndPtr(wndPtr);
4204 return FALSE;
4208 /***********************************************************************
4209 * EndMenu (USER.187) (USER32.175)
4211 void WINAPI EndMenu(void)
4213 /* if we are in the menu code, and it is active */
4214 if (fEndMenu == FALSE && MENU_IsMenuActive())
4216 /* terminate the menu handling code */
4217 fEndMenu = TRUE;
4219 /* needs to be posted to wakeup the internal menu handler */
4220 /* which will now terminate the menu, in the event that */
4221 /* the main window was minimized, or lost focus, so we */
4222 /* don't end up with an orphaned menu */
4223 PostMessageA( pTopPopupWnd->hwndSelf, WM_CANCELMODE, 0, 0);
4228 /***********************************************************************
4229 * LookupMenuHandle (USER.217)
4231 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4233 HMENU hmenu32 = hmenu;
4234 UINT id32 = id;
4235 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4236 else return hmenu32;
4240 /**********************************************************************
4241 * LoadMenu16 (USER.150)
4243 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4245 HRSRC16 hRsrc;
4246 HGLOBAL16 handle;
4247 HMENU16 hMenu;
4249 TRACE("(%04x,%s)\n", instance, debugres_a(name) );
4251 if (HIWORD(name))
4253 if (name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4256 if (!name) return 0;
4258 /* check for Win32 module */
4259 if (HIWORD(instance)) return LoadMenuA( instance, name );
4260 instance = GetExePtr( instance );
4262 if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
4263 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4264 hMenu = LoadMenuIndirect16(LockResource16(handle));
4265 FreeResource16( handle );
4266 return hMenu;
4270 /*****************************************************************
4271 * LoadMenuA (USER32.370)
4273 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4275 HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
4276 if (!hrsrc) return 0;
4277 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4281 /*****************************************************************
4282 * LoadMenuW (USER32.373)
4284 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4286 HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
4287 if (!hrsrc) return 0;
4288 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4292 /**********************************************************************
4293 * LoadMenuIndirect16 (USER.220)
4295 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4297 HMENU16 hMenu;
4298 WORD version, offset;
4299 LPCSTR p = (LPCSTR)template;
4301 TRACE("(%p)\n", template );
4302 version = GET_WORD(p);
4303 p += sizeof(WORD);
4304 if (version)
4306 WARN("version must be 0 for Win16\n" );
4307 return 0;
4309 offset = GET_WORD(p);
4310 p += sizeof(WORD) + offset;
4311 if (!(hMenu = CreateMenu())) return 0;
4312 if (!MENU_ParseResource( p, hMenu, FALSE ))
4314 DestroyMenu( hMenu );
4315 return 0;
4317 return hMenu;
4321 /**********************************************************************
4322 * LoadMenuIndirectA (USER32.371)
4324 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4326 HMENU16 hMenu;
4327 WORD version, offset;
4328 LPCSTR p = (LPCSTR)template;
4330 TRACE("%p\n", template );
4331 version = GET_WORD(p);
4332 p += sizeof(WORD);
4333 switch (version)
4335 case 0:
4336 offset = GET_WORD(p);
4337 p += sizeof(WORD) + offset;
4338 if (!(hMenu = CreateMenu())) return 0;
4339 if (!MENU_ParseResource( p, hMenu, TRUE ))
4341 DestroyMenu( hMenu );
4342 return 0;
4344 return hMenu;
4345 case 1:
4346 offset = GET_WORD(p);
4347 p += sizeof(WORD) + offset;
4348 if (!(hMenu = CreateMenu())) return 0;
4349 if (!MENUEX_ParseResource( p, hMenu))
4351 DestroyMenu( hMenu );
4352 return 0;
4354 return hMenu;
4355 default:
4356 ERR("version %d not supported.\n", version);
4357 return 0;
4362 /**********************************************************************
4363 * LoadMenuIndirectW (USER32.372)
4365 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4367 /* FIXME: is there anything different between A and W? */
4368 return LoadMenuIndirectA( template );
4372 /**********************************************************************
4373 * IsMenu16 (USER.358)
4375 BOOL16 WINAPI IsMenu16( HMENU16 hmenu )
4377 LPPOPUPMENU menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hmenu);
4378 return IS_A_MENU(menu);
4382 /**********************************************************************
4383 * IsMenu (USER32.346)
4385 BOOL WINAPI IsMenu(HMENU hmenu)
4387 LPPOPUPMENU menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hmenu);
4388 return IS_A_MENU(menu);
4391 /**********************************************************************
4392 * GetMenuItemInfo_common
4395 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4396 LPMENUITEMINFOW lpmii, BOOL unicode)
4398 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4400 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4402 if (!menu)
4403 return FALSE;
4405 if (lpmii->fMask & MIIM_TYPE) {
4406 lpmii->fType = menu->fType;
4407 switch (MENU_ITEM_TYPE(menu->fType)) {
4408 case MF_STRING:
4409 break; /* will be done below */
4410 case MF_OWNERDRAW:
4411 case MF_BITMAP:
4412 lpmii->dwTypeData = menu->text;
4413 /* fall through */
4414 default:
4415 lpmii->cch = 0;
4419 /* copy the text string */
4420 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4421 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4423 int len;
4424 if (unicode)
4426 len = strlenW(menu->text);
4427 if(lpmii->dwTypeData && lpmii->cch)
4428 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4430 else
4432 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4433 if(lpmii->dwTypeData && lpmii->cch)
4434 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4435 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4436 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4438 /* if we've copied a substring we return its length */
4439 if(lpmii->dwTypeData && lpmii->cch)
4441 if (lpmii->cch <= len) lpmii->cch--;
4443 else /* return length of string */
4444 lpmii->cch = len;
4447 if (lpmii->fMask & MIIM_FTYPE)
4448 lpmii->fType = menu->fType;
4450 if (lpmii->fMask & MIIM_BITMAP)
4451 lpmii->hbmpItem = menu->hbmpItem;
4453 if (lpmii->fMask & MIIM_STATE)
4454 lpmii->fState = menu->fState;
4456 if (lpmii->fMask & MIIM_ID)
4457 lpmii->wID = menu->wID;
4459 if (lpmii->fMask & MIIM_SUBMENU)
4460 lpmii->hSubMenu = menu->hSubMenu;
4462 if (lpmii->fMask & MIIM_CHECKMARKS) {
4463 lpmii->hbmpChecked = menu->hCheckBit;
4464 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4466 if (lpmii->fMask & MIIM_DATA)
4467 lpmii->dwItemData = menu->dwItemData;
4469 return TRUE;
4472 /**********************************************************************
4473 * GetMenuItemInfoA (USER32.264)
4475 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4476 LPMENUITEMINFOA lpmii)
4478 return GetMenuItemInfo_common (hmenu, item, bypos,
4479 (LPMENUITEMINFOW)lpmii, FALSE);
4482 /**********************************************************************
4483 * GetMenuItemInfoW (USER32.265)
4485 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4486 LPMENUITEMINFOW lpmii)
4488 return GetMenuItemInfo_common (hmenu, item, bypos,
4489 lpmii, TRUE);
4492 /**********************************************************************
4493 * SetMenuItemInfo_common
4496 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4497 const MENUITEMINFOW *lpmii,
4498 BOOL unicode)
4500 if (!menu) return FALSE;
4502 if (lpmii->fMask & MIIM_TYPE ) {
4503 /* Get rid of old string. */
4504 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4505 HeapFree(SystemHeap, 0, menu->text);
4506 menu->text = NULL;
4509 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4510 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4511 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4513 menu->text = lpmii->dwTypeData;
4515 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4516 if (unicode)
4517 menu->text = HEAP_strdupW(SystemHeap, 0, lpmii->dwTypeData);
4518 else
4519 menu->text = HEAP_strdupAtoW(SystemHeap, 0, (LPSTR)lpmii->dwTypeData);
4523 if (lpmii->fMask & MIIM_FTYPE ) {
4524 /* free the string when the type is changing */
4525 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4526 HeapFree(SystemHeap, 0, menu->text);
4527 menu->text = NULL;
4529 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4530 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4533 if (lpmii->fMask & MIIM_STRING ) {
4534 /* free the string when used */
4535 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4536 HeapFree(SystemHeap, 0, menu->text);
4537 if (unicode)
4538 menu->text = HEAP_strdupW(SystemHeap, 0, lpmii->dwTypeData);
4539 else
4540 menu->text = HEAP_strdupAtoW(SystemHeap, 0, (LPSTR) lpmii->dwTypeData);
4544 if (lpmii->fMask & MIIM_STATE)
4546 /* fixme: MFS_DEFAULT do we have to reset the other menu items? */
4547 menu->fState = lpmii->fState;
4550 if (lpmii->fMask & MIIM_ID)
4551 menu->wID = lpmii->wID;
4553 if (lpmii->fMask & MIIM_SUBMENU) {
4554 menu->hSubMenu = lpmii->hSubMenu;
4555 if (menu->hSubMenu) {
4556 POPUPMENU *subMenu = MENU_GetMenu((UINT16)menu->hSubMenu);
4557 if (subMenu) {
4558 subMenu->wFlags |= MF_POPUP;
4559 menu->fType |= MF_POPUP;
4561 else
4562 /* FIXME: Return an error ? */
4563 menu->fType &= ~MF_POPUP;
4565 else
4566 menu->fType &= ~MF_POPUP;
4569 if (lpmii->fMask & MIIM_CHECKMARKS)
4571 if (lpmii->fType & MFT_RADIOCHECK)
4572 menu->fType |= MFT_RADIOCHECK;
4574 menu->hCheckBit = lpmii->hbmpChecked;
4575 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4577 if (lpmii->fMask & MIIM_DATA)
4578 menu->dwItemData = lpmii->dwItemData;
4580 debug_print_menuitem("SetMenuItemInfo_common: ", menu, "");
4581 return TRUE;
4584 /**********************************************************************
4585 * SetMenuItemInfoA (USER32.491)
4587 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4588 const MENUITEMINFOA *lpmii)
4590 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4591 (const MENUITEMINFOW *)lpmii, FALSE);
4594 /**********************************************************************
4595 * SetMenuItemInfoW (USER32.492)
4597 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4598 const MENUITEMINFOW *lpmii)
4600 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4601 lpmii, TRUE);
4604 /**********************************************************************
4605 * SetMenuDefaultItem (USER32.489)
4608 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4610 UINT i;
4611 POPUPMENU *menu;
4612 MENUITEM *item;
4614 TRACE("(0x%x,%d,%d)\n", hmenu, uItem, bypos);
4616 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4618 /* reset all default-item flags */
4619 item = menu->items;
4620 for (i = 0; i < menu->nItems; i++, item++)
4622 item->fState &= ~MFS_DEFAULT;
4625 /* no default item */
4626 if ( -1 == uItem)
4628 return TRUE;
4631 item = menu->items;
4632 if ( bypos )
4634 if ( uItem >= menu->nItems ) return FALSE;
4635 item[uItem].fState |= MFS_DEFAULT;
4636 return TRUE;
4638 else
4640 for (i = 0; i < menu->nItems; i++, item++)
4642 if (item->wID == uItem)
4644 item->fState |= MFS_DEFAULT;
4645 return TRUE;
4650 return FALSE;
4653 /**********************************************************************
4654 * GetMenuDefaultItem (USER32.260)
4656 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4658 POPUPMENU *menu;
4659 MENUITEM * item;
4660 UINT i = 0;
4662 TRACE("(0x%x,%d,%d)\n", hmenu, bypos, flags);
4664 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4666 /* find default item */
4667 item = menu->items;
4669 /* empty menu */
4670 if (! item) return -1;
4672 while ( !( item->fState & MFS_DEFAULT ) )
4674 i++; item++;
4675 if (i >= menu->nItems ) return -1;
4678 /* default: don't return disabled items */
4679 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4681 /* search rekursiv when needed */
4682 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4684 UINT ret;
4685 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4686 if ( -1 != ret ) return ret;
4688 /* when item not found in submenu, return the popup item */
4690 return ( bypos ) ? i : item->wID;
4694 /*******************************************************************
4695 * InsertMenuItem16 (USER.441)
4697 * FIXME: untested
4699 BOOL16 WINAPI InsertMenuItem16( HMENU16 hmenu, UINT16 pos, BOOL16 byposition,
4700 const MENUITEMINFO16 *mii )
4702 MENUITEMINFOA miia;
4704 miia.cbSize = sizeof(miia);
4705 miia.fMask = mii->fMask;
4706 miia.dwTypeData = (LPSTR)mii->dwTypeData;
4707 miia.fType = mii->fType;
4708 miia.fState = mii->fState;
4709 miia.wID = mii->wID;
4710 miia.hSubMenu = mii->hSubMenu;
4711 miia.hbmpChecked = mii->hbmpChecked;
4712 miia.hbmpUnchecked = mii->hbmpUnchecked;
4713 miia.dwItemData = mii->dwItemData;
4714 miia.cch = mii->cch;
4715 if (IS_STRING_ITEM(miia.fType))
4716 miia.dwTypeData = MapSL(mii->dwTypeData);
4717 return InsertMenuItemA( hmenu, pos, byposition, &miia );
4721 /**********************************************************************
4722 * InsertMenuItemA (USER32.323)
4724 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4725 const MENUITEMINFOA *lpmii)
4727 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4728 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4732 /**********************************************************************
4733 * InsertMenuItemW (USER32.324)
4735 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4736 const MENUITEMINFOW *lpmii)
4738 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4739 return SetMenuItemInfo_common(item, lpmii, TRUE);
4742 /**********************************************************************
4743 * CheckMenuRadioItem (USER32.47)
4746 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4747 UINT first, UINT last, UINT check,
4748 UINT bypos)
4750 MENUITEM *mifirst, *milast, *micheck;
4751 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4753 TRACE("ox%x: %d-%d, check %d, bypos=%d\n",
4754 hMenu, first, last, check, bypos);
4756 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4757 milast = MENU_FindItem (&mlast, &last, bypos);
4758 micheck = MENU_FindItem (&mcheck, &check, bypos);
4760 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4761 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4762 micheck > milast || micheck < mifirst)
4763 return FALSE;
4765 while (mifirst <= milast)
4767 if (mifirst == micheck)
4769 mifirst->fType |= MFT_RADIOCHECK;
4770 mifirst->fState |= MFS_CHECKED;
4771 } else {
4772 mifirst->fType &= ~MFT_RADIOCHECK;
4773 mifirst->fState &= ~MFS_CHECKED;
4775 mifirst++;
4778 return TRUE;
4781 /**********************************************************************
4782 * CheckMenuRadioItem16 (not a Windows API)
4785 BOOL16 WINAPI CheckMenuRadioItem16(HMENU16 hMenu,
4786 UINT16 first, UINT16 last, UINT16 check,
4787 BOOL16 bypos)
4789 return CheckMenuRadioItem (hMenu, first, last, check, bypos);
4792 /**********************************************************************
4793 * GetMenuItemRect (USER32.266)
4795 * ATTENTION: Here, the returned values in rect are the screen
4796 * coordinates of the item just like if the menu was
4797 * always on the upper left side of the application.
4800 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4801 LPRECT rect)
4803 POPUPMENU *itemMenu;
4804 MENUITEM *item;
4805 HWND referenceHwnd;
4807 TRACE("(0x%x,0x%x,%d,%p)\n", hwnd, hMenu, uItem, rect);
4809 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4810 referenceHwnd = hwnd;
4812 if(!hwnd)
4814 itemMenu = MENU_GetMenu(hMenu);
4815 if (itemMenu == NULL)
4816 return FALSE;
4818 if(itemMenu->hWnd == 0)
4819 return FALSE;
4820 referenceHwnd = itemMenu->hWnd;
4823 if ((rect == NULL) || (item == NULL))
4824 return FALSE;
4826 *rect = item->rect;
4828 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4830 return TRUE;
4833 /**********************************************************************
4834 * GetMenuItemRect16 (USER.665)
4837 BOOL16 WINAPI GetMenuItemRect16 (HWND16 hwnd, HMENU16 hMenu, UINT16 uItem,
4838 LPRECT16 rect)
4840 RECT r32;
4841 BOOL res;
4843 if (!rect) return FALSE;
4844 res = GetMenuItemRect (hwnd, hMenu, uItem, &r32);
4845 CONV_RECT32TO16 (&r32, rect);
4846 return res;
4849 /**********************************************************************
4850 * SetMenuInfo
4852 * FIXME
4853 * MIM_APPLYTOSUBMENUS
4854 * actually use the items to draw the menu
4856 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4858 POPUPMENU *menu;
4860 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4862 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4865 if (lpmi->fMask & MIM_BACKGROUND)
4866 menu->hbrBack = lpmi->hbrBack;
4868 if (lpmi->fMask & MIM_HELPID)
4869 menu->dwContextHelpID = lpmi->dwContextHelpID;
4871 if (lpmi->fMask & MIM_MAXHEIGHT)
4872 menu->cyMax = lpmi->cyMax;
4874 if (lpmi->fMask & MIM_MENUDATA)
4875 menu->dwMenuData = lpmi->dwMenuData;
4877 if (lpmi->fMask & MIM_STYLE)
4878 menu->dwStyle = lpmi->dwStyle;
4880 return TRUE;
4882 return FALSE;
4885 /**********************************************************************
4886 * GetMenuInfo
4888 * NOTES
4889 * win98/NT5.0
4892 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4893 { POPUPMENU *menu;
4895 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4897 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4900 if (lpmi->fMask & MIM_BACKGROUND)
4901 lpmi->hbrBack = menu->hbrBack;
4903 if (lpmi->fMask & MIM_HELPID)
4904 lpmi->dwContextHelpID = menu->dwContextHelpID;
4906 if (lpmi->fMask & MIM_MAXHEIGHT)
4907 lpmi->cyMax = menu->cyMax;
4909 if (lpmi->fMask & MIM_MENUDATA)
4910 lpmi->dwMenuData = menu->dwMenuData;
4912 if (lpmi->fMask & MIM_STYLE)
4913 lpmi->dwStyle = menu->dwStyle;
4915 return TRUE;
4917 return FALSE;
4920 /**********************************************************************
4921 * SetMenuContextHelpId16 (USER.384)
4923 BOOL16 WINAPI SetMenuContextHelpId16( HMENU16 hMenu, DWORD dwContextHelpID)
4925 return SetMenuContextHelpId( hMenu, dwContextHelpID );
4929 /**********************************************************************
4930 * SetMenuContextHelpId (USER32.488)
4932 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4934 LPPOPUPMENU menu;
4936 TRACE("(0x%04x 0x%08lx)\n", hMenu, dwContextHelpID);
4938 if ((menu = MENU_GetMenu(hMenu)))
4940 menu->dwContextHelpID = dwContextHelpID;
4941 return TRUE;
4943 return FALSE;
4946 /**********************************************************************
4947 * GetMenuContextHelpId16 (USER.385)
4949 DWORD WINAPI GetMenuContextHelpId16( HMENU16 hMenu )
4951 return GetMenuContextHelpId( hMenu );
4954 /**********************************************************************
4955 * GetMenuContextHelpId (USER32.488)
4957 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4959 LPPOPUPMENU menu;
4961 TRACE("(0x%04x)\n", hMenu);
4963 if ((menu = MENU_GetMenu(hMenu)))
4965 return menu->dwContextHelpID;
4967 return 0;
4970 /**********************************************************************
4971 * MenuItemFromPoint (USER32.387)
4973 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4975 FIXME("(0x%04x,0x%04x,(%ld,%ld)):stub\n",
4976 hWnd, hMenu, ptScreen.x, ptScreen.y);
4977 return 0;
4981 /**********************************************************************
4982 * translate_accelerator
4984 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4985 BYTE fVirt, WORD key, WORD cmd )
4987 UINT mesg = 0;
4989 if (wParam != key) return FALSE;
4991 if (message == WM_CHAR)
4993 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4995 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4996 goto found;
4999 else
5001 if(fVirt & FVIRTKEY)
5003 INT mask = 0;
5004 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
5005 wParam, 0xff & HIWORD(lParam));
5006 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5007 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5008 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5009 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5010 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5012 else
5014 if (!(lParam & 0x01000000)) /* no special_key */
5016 if ((fVirt & FALT) && (lParam & 0x20000000))
5017 { /* ^^ ALT pressed */
5018 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5019 goto found;
5024 return FALSE;
5026 found:
5027 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5028 mesg = 1;
5029 else if (GetCapture())
5030 mesg = 2;
5031 else if (!IsWindowEnabled(hWnd))
5032 mesg = 3;
5033 else
5035 HMENU hMenu, hSubMenu, hSysMenu;
5036 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5037 WND* wndPtr = WIN_FindWndPtr(hWnd);
5039 hMenu = (wndPtr->dwStyle & WS_CHILD) ? 0 : (HMENU)wndPtr->wIDmenu;
5040 hSysMenu = wndPtr->hSysMenu;
5041 WIN_ReleaseWndPtr(wndPtr);
5043 /* find menu item and ask application to initialize it */
5044 /* 1. in the system menu */
5045 hSubMenu = hSysMenu;
5046 nPos = cmd;
5047 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5049 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5050 if(hSubMenu != hSysMenu)
5052 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5053 TRACE_(accel)("hSysMenu = %04x, hSubMenu = %04x, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5054 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5056 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5058 else /* 2. in the window's menu */
5060 hSubMenu = hMenu;
5061 nPos = cmd;
5062 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5064 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5065 if(hSubMenu != hMenu)
5067 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5068 TRACE_(accel)("hMenu = %04x, hSubMenu = %04x, nPos = %d\n", hMenu, hSubMenu, nPos);
5069 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5071 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5075 if (uSysStat != (UINT)-1)
5077 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5078 mesg=4;
5079 else
5080 mesg=WM_SYSCOMMAND;
5082 else
5084 if (uStat != (UINT)-1)
5086 if (IsIconic(hWnd))
5087 mesg=5;
5088 else
5090 if (uStat & (MF_DISABLED|MF_GRAYED))
5091 mesg=6;
5092 else
5093 mesg=WM_COMMAND;
5096 else
5097 mesg=WM_COMMAND;
5101 if( mesg==WM_COMMAND )
5103 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5104 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
5106 else if( mesg==WM_SYSCOMMAND )
5108 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5109 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
5111 else
5113 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5114 * #0: unknown (please report!)
5115 * #1: for WM_KEYUP,WM_SYSKEYUP
5116 * #2: mouse is captured
5117 * #3: window is disabled
5118 * #4: it's a disabled system menu option
5119 * #5: it's a menu option, but window is iconic
5120 * #6: it's a menu option, but disabled
5122 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5123 if(mesg==0)
5124 ERR_(accel)(" unknown reason - please report!");
5126 return TRUE;
5129 /**********************************************************************
5130 * TranslateAccelerator (USER32.551)(USER32.552)(USER32.553)
5132 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
5134 /* YES, Accel16! */
5135 LPACCEL16 lpAccelTbl;
5136 int i;
5138 if (msg == NULL)
5140 WARN_(accel)("msg null; should hang here to be win compatible\n");
5141 return 0;
5143 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
5145 WARN_(accel)("invalid accel handle=%x\n", hAccel);
5146 return 0;
5148 if ((msg->message != WM_KEYDOWN &&
5149 msg->message != WM_KEYUP &&
5150 msg->message != WM_SYSKEYDOWN &&
5151 msg->message != WM_SYSKEYUP &&
5152 msg->message != WM_CHAR)) return 0;
5154 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5155 "msg->hwnd=%04x, msg->message=%04x, wParam=%08x, lParam=%lx\n",
5156 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5158 i = 0;
5161 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5162 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5163 return 1;
5164 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5165 WARN_(accel)("couldn't translate accelerator key\n");
5166 return 0;
5170 /**********************************************************************
5171 * TranslateAccelerator16 (USER.178)
5173 INT16 WINAPI TranslateAccelerator16( HWND16 hWnd, HACCEL16 hAccel, LPMSG16 msg )
5175 LPACCEL16 lpAccelTbl;
5176 int i;
5178 if (msg == NULL)
5180 WARN_(accel)("msg null; should hang here to be win compatible\n");
5181 return 0;
5183 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
5185 WARN_(accel)("invalid accel handle=%x\n", hAccel);
5186 return 0;
5188 if ((msg->message != WM_KEYDOWN &&
5189 msg->message != WM_KEYUP &&
5190 msg->message != WM_SYSKEYDOWN &&
5191 msg->message != WM_SYSKEYUP &&
5192 msg->message != WM_CHAR)) return 0;
5194 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5195 "msg->hwnd=%04x, msg->message=%04x, wParam=%04x, lParam=%lx\n",
5196 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5198 i = 0;
5201 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5202 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd ))
5203 return 1;
5204 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5205 WARN_(accel)("couldn't translate accelerator key\n");
5206 return 0;