Added some stubs.
[wine/dcerpc.git] / controls / menu.c
blob7ad400ccc375ae25c58afef9fe56b28013b17717
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Note: the style MF_MOUSESELECT is used to mark popup items that
25 * have been selected, i.e. their popup menu is currently displayed.
26 * This is probably not the meaning this style has in MS-Windows.
29 #include "config.h"
30 #include "wine/port.h"
32 #include <assert.h>
33 #include <ctype.h>
34 #include <stdlib.h>
35 #include <string.h>
37 #include "windef.h"
38 #include "winnls.h"
39 #include "wingdi.h"
40 #include "wine/winbase16.h"
41 #include "wine/winuser16.h"
42 #include "wine/unicode.h"
43 #include "win.h"
44 #include "controls.h"
45 #include "nonclient.h"
46 #include "user.h"
47 #include "message.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(menu);
52 WINE_DECLARE_DEBUG_CHANNEL(accel);
54 /* internal popup menu window messages */
56 #define MM_SETMENUHANDLE (WM_USER + 0)
57 #define MM_GETMENUHANDLE (WM_USER + 1)
59 /* Menu item structure */
60 typedef struct {
61 /* ----------- MENUITEMINFO Stuff ----------- */
62 UINT fType; /* Item type. */
63 UINT fState; /* Item state. */
64 UINT wID; /* Item id. */
65 HMENU hSubMenu; /* Pop-up menu. */
66 HBITMAP hCheckBit; /* Bitmap when checked. */
67 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
68 LPWSTR text; /* Item text or bitmap handle. */
69 DWORD dwItemData; /* Application defined. */
70 DWORD dwTypeData; /* depends on fMask */
71 HBITMAP hbmpItem; /* bitmap in win98 style menus */
72 /* ----------- Wine stuff ----------- */
73 RECT rect; /* Item area (relative to menu window) */
74 UINT xTab; /* X position of text after Tab */
75 } MENUITEM;
77 /* Popup menu structure */
78 typedef struct {
79 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
80 WORD wMagic; /* Magic number */
81 WORD Width; /* Width of the whole menu */
82 WORD Height; /* Height of the whole menu */
83 UINT nItems; /* Number of items in the menu */
84 HWND hWnd; /* Window containing the menu */
85 MENUITEM *items; /* Array of menu items */
86 UINT FocusedItem; /* Currently focused item */
87 HWND hwndOwner; /* window receiving the messages for ownerdraw */
88 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
89 /* ------------ MENUINFO members ------ */
90 DWORD dwStyle; /* Extended mennu style */
91 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
92 HBRUSH hbrBack; /* brush for menu background */
93 DWORD dwContextHelpID;
94 DWORD dwMenuData; /* application defined value */
95 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
96 } POPUPMENU, *LPPOPUPMENU;
98 /* internal flags for menu tracking */
100 #define TF_ENDMENU 0x0001
101 #define TF_SUSPENDPOPUP 0x0002
102 #define TF_SKIPREMOVE 0x0004
104 typedef struct
106 UINT trackFlags;
107 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
108 HMENU hTopMenu; /* initial menu */
109 HWND hOwnerWnd; /* where notifications are sent */
110 POINT pt;
111 } MTRACKER;
113 #define MENU_MAGIC 0x554d /* 'MU' */
115 #define ITEM_PREV -1
116 #define ITEM_NEXT 1
118 /* Internal MENU_TrackMenu() flags */
119 #define TPM_INTERNAL 0xF0000000
120 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
121 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
122 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
124 /* popup menu shade thickness */
125 #define POPUP_XSHADE 4
126 #define POPUP_YSHADE 4
128 /* Space between 2 menu bar items */
129 #define MENU_BAR_ITEMS_SPACE 12
131 /* Minimum width of a tab character */
132 #define MENU_TAB_SPACE 8
134 /* Height of a separator item */
135 #define SEPARATOR_HEIGHT 5
137 /* (other menu->FocusedItem values give the position of the focused item) */
138 #define NO_SELECTED_ITEM 0xffff
140 #define MENU_ITEM_TYPE(flags) \
141 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
143 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
144 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
145 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
147 #define IS_SYSTEM_MENU(menu) \
148 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
150 #define IS_SYSTEM_POPUP(menu) \
151 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
153 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
154 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
155 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
156 MF_POPUP | MF_SYSMENU | MF_HELP)
157 #define STATE_MASK (~TYPE_MASK)
159 /* Dimension of the menu bitmaps */
160 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
162 static HBITMAP hStdMnArrow = 0;
163 static HBITMAP hBmpSysMenu = 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. */
173 static HWND top_popup;
175 /* Flag set by EndMenu() to force an exit from menu tracking */
176 static BOOL fEndMenu = FALSE;
178 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
181 /*********************************************************************
182 * menu class descriptor
184 const struct builtin_class_descr MENU_builtin_class =
186 POPUPMENU_CLASS_ATOM, /* name */
187 CS_GLOBALCLASS | CS_SAVEBITS, /* style */
188 NULL, /* procA (winproc is Unicode only) */
189 PopupMenuWndProc, /* procW */
190 sizeof(HMENU), /* extra */
191 IDC_ARROWA, /* cursor */
192 COLOR_MENU+1 /* brush */
196 /***********************************************************************
197 * debug_print_menuitem
199 * Print a menuitem in readable form.
202 #define debug_print_menuitem(pre, mp, post) \
203 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
205 #define MENUOUT(text) \
206 DPRINTF("%s%s", (count++ ? "," : ""), (text))
208 #define MENUFLAG(bit,text) \
209 do { \
210 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
211 } while (0)
213 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
214 const char *postfix)
216 TRACE("%s ", prefix);
217 if (mp) {
218 UINT flags = mp->fType;
219 int typ = MENU_ITEM_TYPE(flags);
220 DPRINTF( "{ ID=0x%x", mp->wID);
221 if (flags & MF_POPUP)
222 DPRINTF( ", Sub=0x%x", mp->hSubMenu);
223 if (flags) {
224 int count = 0;
225 DPRINTF( ", Typ=");
226 if (typ == MFT_STRING)
227 /* Nothing */ ;
228 else if (typ == MFT_SEPARATOR)
229 MENUOUT("sep");
230 else if (typ == MFT_OWNERDRAW)
231 MENUOUT("own");
232 else if (typ == MFT_BITMAP)
233 MENUOUT("bit");
234 else
235 MENUOUT("???");
236 flags -= typ;
238 MENUFLAG(MF_POPUP, "pop");
239 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
240 MENUFLAG(MFT_MENUBREAK, "brk");
241 MENUFLAG(MFT_RADIOCHECK, "radio");
242 MENUFLAG(MFT_RIGHTORDER, "rorder");
243 MENUFLAG(MF_SYSMENU, "sys");
244 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
246 if (flags)
247 DPRINTF( "+0x%x", flags);
249 flags = mp->fState;
250 if (flags) {
251 int count = 0;
252 DPRINTF( ", State=");
253 MENUFLAG(MFS_GRAYED, "grey");
254 MENUFLAG(MFS_DEFAULT, "default");
255 MENUFLAG(MFS_DISABLED, "dis");
256 MENUFLAG(MFS_CHECKED, "check");
257 MENUFLAG(MFS_HILITE, "hi");
258 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
259 MENUFLAG(MF_MOUSESELECT, "mouse");
260 if (flags)
261 DPRINTF( "+0x%x", flags);
263 if (mp->hCheckBit)
264 DPRINTF( ", Chk=0x%x", mp->hCheckBit);
265 if (mp->hUnCheckBit)
266 DPRINTF( ", Unc=0x%x", mp->hUnCheckBit);
268 if (typ == MFT_STRING) {
269 if (mp->text)
270 DPRINTF( ", Text=%s", debugstr_w(mp->text));
271 else
272 DPRINTF( ", Text=Null");
273 } else if (mp->text == NULL)
274 /* Nothing */ ;
275 else
276 DPRINTF( ", Text=%p", mp->text);
277 if (mp->dwItemData)
278 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
279 DPRINTF( " }");
280 } else {
281 DPRINTF( "NULL");
284 DPRINTF(" %s\n", postfix);
287 #undef MENUOUT
288 #undef MENUFLAG
291 /***********************************************************************
292 * MENU_GetMenu
294 * Validate the given menu handle and returns the menu structure pointer.
296 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
298 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
299 if (!menu || menu->wMagic != MENU_MAGIC)
301 WARN("invalid menu handle=%x, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
302 menu = NULL;
304 return menu;
307 /***********************************************************************
308 * get_win_sys_menu
310 * Get the system menu of a window
312 static HMENU get_win_sys_menu( HWND hwnd )
314 HMENU ret = 0;
315 WND *win = WIN_FindWndPtr( hwnd );
316 if (win)
318 ret = win->hSysMenu;
319 WIN_ReleaseWndPtr( win );
321 return ret;
324 /***********************************************************************
325 * MENU_CopySysPopup
327 * Return the default system menu.
329 static HMENU MENU_CopySysPopup(void)
331 HMENU hMenu = LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
333 if( hMenu ) {
334 POPUPMENU* menu = MENU_GetMenu(hMenu);
335 menu->wFlags |= MF_SYSMENU | MF_POPUP;
336 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
338 else
339 ERR("Unable to load default system menu\n" );
341 TRACE("returning %x.\n", hMenu );
343 return hMenu;
347 /**********************************************************************
348 * MENU_GetSysMenu
350 * Create a copy of the system menu. System menu in Windows is
351 * a special menu bar with the single entry - system menu popup.
352 * This popup is presented to the outside world as a "system menu".
353 * However, the real system menu handle is sometimes seen in the
354 * WM_MENUSELECT parameters (and Word 6 likes it this way).
356 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
358 HMENU hMenu;
360 if ((hMenu = CreateMenu()))
362 POPUPMENU *menu = MENU_GetMenu(hMenu);
363 menu->wFlags = MF_SYSMENU;
364 menu->hWnd = WIN_GetFullHandle( hWnd );
366 if (hPopupMenu == (HMENU)(-1))
367 hPopupMenu = MENU_CopySysPopup();
368 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
370 if (hPopupMenu)
372 InsertMenuA( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION, hPopupMenu, NULL );
374 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
375 menu->items[0].fState = 0;
376 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
378 TRACE("GetSysMenu hMenu=%04x (%04x)\n", hMenu, hPopupMenu );
379 return hMenu;
381 DestroyMenu( hMenu );
383 ERR("failed to load system menu!\n");
384 return 0;
388 /***********************************************************************
389 * MENU_Init
391 * Menus initialisation.
393 BOOL MENU_Init()
395 HBITMAP hBitmap;
396 NONCLIENTMETRICSA ncm;
398 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
399 0x55, 0, 0xAA, 0,
400 0x55, 0, 0xAA, 0,
401 0x55, 0, 0xAA, 0 };
403 /* Load menu bitmaps */
404 hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
405 /* Load system buttons bitmaps */
406 hBmpSysMenu = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_CLOSE));
408 if (hStdMnArrow)
410 BITMAP bm;
411 GetObjectA( hStdMnArrow, sizeof(bm), &bm );
412 arrow_bitmap_width = bm.bmWidth;
413 arrow_bitmap_height = bm.bmHeight;
414 } else
415 return FALSE;
417 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
418 return FALSE;
420 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
421 return FALSE;
423 DeleteObject( hBitmap );
424 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
425 return FALSE;
427 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
428 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0)))
429 return FALSE;
431 if (!(hMenuFont = CreateFontIndirectA( &ncm.lfMenuFont )))
432 return FALSE;
434 ncm.lfMenuFont.lfWeight += 300;
435 if ( ncm.lfMenuFont.lfWeight > 1000)
436 ncm.lfMenuFont.lfWeight = 1000;
438 if (!(hMenuFontBold = CreateFontIndirectA( &ncm.lfMenuFont )))
439 return FALSE;
441 return TRUE;
444 /***********************************************************************
445 * MENU_InitSysMenuPopup
447 * Grey the appropriate items in System menu.
449 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
451 BOOL gray;
453 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
454 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
455 gray = ((style & WS_MAXIMIZE) != 0);
456 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
457 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
458 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
459 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
460 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
461 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
462 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
463 gray = (clsStyle & CS_NOCLOSE) != 0;
465 /* The menu item must keep its state if it's disabled */
466 if(gray)
467 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
471 /******************************************************************************
473 * UINT MENU_GetStartOfNextColumn(
474 * HMENU hMenu )
476 *****************************************************************************/
478 static UINT MENU_GetStartOfNextColumn(
479 HMENU hMenu )
481 POPUPMENU *menu = MENU_GetMenu(hMenu);
482 UINT i;
484 if(!menu)
485 return NO_SELECTED_ITEM;
487 i = menu->FocusedItem + 1;
488 if( i == NO_SELECTED_ITEM )
489 return i;
491 for( ; i < menu->nItems; ++i ) {
492 if (menu->items[i].fType & MF_MENUBARBREAK)
493 return i;
496 return NO_SELECTED_ITEM;
500 /******************************************************************************
502 * UINT MENU_GetStartOfPrevColumn(
503 * HMENU hMenu )
505 *****************************************************************************/
507 static UINT MENU_GetStartOfPrevColumn(
508 HMENU hMenu )
510 POPUPMENU *menu = MENU_GetMenu(hMenu);
511 UINT i;
513 if( !menu )
514 return NO_SELECTED_ITEM;
516 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
517 return NO_SELECTED_ITEM;
519 /* Find the start of the column */
521 for(i = menu->FocusedItem; i != 0 &&
522 !(menu->items[i].fType & MF_MENUBARBREAK);
523 --i); /* empty */
525 if(i == 0)
526 return NO_SELECTED_ITEM;
528 for(--i; i != 0; --i) {
529 if (menu->items[i].fType & MF_MENUBARBREAK)
530 break;
533 TRACE("ret %d.\n", i );
535 return i;
540 /***********************************************************************
541 * MENU_FindItem
543 * Find a menu item. Return a pointer on the item, and modifies *hmenu
544 * in case the item was in a sub-menu.
546 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
548 POPUPMENU *menu;
549 UINT i;
551 if (((*hmenu)==0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
552 if (wFlags & MF_BYPOSITION)
554 if (*nPos >= menu->nItems) return NULL;
555 return &menu->items[*nPos];
557 else
559 MENUITEM *item = menu->items;
560 for (i = 0; i < menu->nItems; i++, item++)
562 if (item->wID == *nPos)
564 *nPos = i;
565 return item;
567 else if (item->fType & MF_POPUP)
569 HMENU hsubmenu = item->hSubMenu;
570 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
571 if (subitem)
573 *hmenu = hsubmenu;
574 return subitem;
579 return NULL;
582 /***********************************************************************
583 * MENU_FindSubMenu
585 * Find a Sub menu. Return the position of the submenu, and modifies
586 * *hmenu in case it is found in another sub-menu.
587 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
589 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
591 POPUPMENU *menu;
592 UINT i;
593 MENUITEM *item;
594 if (((*hmenu)==0xffff) ||
595 (!(menu = MENU_GetMenu(*hmenu))))
596 return NO_SELECTED_ITEM;
597 item = menu->items;
598 for (i = 0; i < menu->nItems; i++, item++) {
599 if(!(item->fType & MF_POPUP)) continue;
600 if (item->hSubMenu == hSubTarget) {
601 return i;
603 else {
604 HMENU hsubmenu = item->hSubMenu;
605 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
606 if (pos != NO_SELECTED_ITEM) {
607 *hmenu = hsubmenu;
608 return pos;
612 return NO_SELECTED_ITEM;
615 /***********************************************************************
616 * MENU_FreeItemData
618 static void MENU_FreeItemData( MENUITEM* item )
620 /* delete text */
621 if (IS_STRING_ITEM(item->fType) && item->text)
622 HeapFree( GetProcessHeap(), 0, item->text );
625 /***********************************************************************
626 * MENU_FindItemByCoords
628 * Find the item at the specified coordinates (screen coords). Does
629 * not work for child windows and therefore should not be called for
630 * an arbitrary system menu.
632 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
633 POINT pt, UINT *pos )
635 MENUITEM *item;
636 UINT i;
637 RECT wrect;
639 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
640 pt.x -= wrect.left;pt.y -= wrect.top;
641 item = menu->items;
642 for (i = 0; i < menu->nItems; i++, item++)
644 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
645 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
647 if (pos) *pos = i;
648 return item;
651 return NULL;
655 /***********************************************************************
656 * MENU_FindItemByKey
658 * Find the menu item selected by a key press.
659 * Return item id, -1 if none, -2 if we should close the menu.
661 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
662 UINT key, BOOL forceMenuChar )
664 TRACE("\tlooking for '%c' in [%04x]\n", (char)key, (UINT16)hmenu );
666 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
668 if (hmenu)
670 POPUPMENU *menu = MENU_GetMenu( hmenu );
671 MENUITEM *item = menu->items;
672 LONG menuchar;
674 if( !forceMenuChar )
676 UINT i;
678 key = toupper(key);
679 for (i = 0; i < menu->nItems; i++, item++)
681 if (item->text && (IS_STRING_ITEM(item->fType)))
683 WCHAR *p = item->text - 2;
686 p = strchrW (p + 2, '&');
688 while (p != NULL && p [1] == '&');
689 if (p && (toupper(p[1]) == key)) return i;
693 menuchar = SendMessageA( hwndOwner, WM_MENUCHAR,
694 MAKEWPARAM( key, menu->wFlags ), hmenu );
695 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
696 if (HIWORD(menuchar) == 1) return (UINT)(-2);
698 return (UINT)(-1);
702 /***********************************************************************
703 * MENU_GetBitmapItemSize
705 * Get the size of a bitmap item.
707 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
709 BITMAP bm;
710 HBITMAP bmp = (HBITMAP)id;
712 size->cx = size->cy = 0;
714 /* check if there is a magic menu item associated with this item */
715 if (id && IS_MAGIC_ITEM( id ))
717 switch(LOWORD(id))
719 case HBMMENU_SYSTEM:
720 if (data)
722 bmp = (HBITMAP)data;
723 break;
725 /* fall through */
726 case HBMMENU_MBAR_RESTORE:
727 case HBMMENU_MBAR_MINIMIZE:
728 case HBMMENU_MBAR_MINIMIZE_D:
729 case HBMMENU_MBAR_CLOSE:
730 case HBMMENU_MBAR_CLOSE_D:
731 size->cx = GetSystemMetrics( SM_CXSIZE );
732 size->cy = GetSystemMetrics( SM_CYSIZE );
733 return;
734 case HBMMENU_CALLBACK:
735 case HBMMENU_POPUP_CLOSE:
736 case HBMMENU_POPUP_RESTORE:
737 case HBMMENU_POPUP_MAXIMIZE:
738 case HBMMENU_POPUP_MINIMIZE:
739 default:
740 FIXME("Magic 0x%08x not implemented\n", id);
741 return;
744 if (GetObjectA(bmp, sizeof(bm), &bm ))
746 size->cx = bm.bmWidth;
747 size->cy = bm.bmHeight;
751 /***********************************************************************
752 * MENU_DrawBitmapItem
754 * Draw a bitmap item.
756 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar )
758 BITMAP bm;
759 DWORD rop;
760 HDC hdcMem;
761 HBITMAP bmp = (HBITMAP)lpitem->text;
762 int w = rect->right - rect->left;
763 int h = rect->bottom - rect->top;
764 int bmp_xoffset = 0;
765 int left, top;
767 /* Check if there is a magic menu item associated with this item */
768 if (lpitem->text && IS_MAGIC_ITEM(lpitem->text))
770 UINT flags = 0;
771 RECT r;
773 switch(LOWORD(lpitem->text))
775 case HBMMENU_SYSTEM:
776 if (lpitem->dwItemData)
778 bmp = (HBITMAP)lpitem->dwItemData;
779 if (!GetObjectA( bmp, sizeof(bm), &bm )) return;
781 else
783 bmp = hBmpSysMenu;
784 if (!GetObjectA( bmp, sizeof(bm), &bm )) return;
785 /* only use right half of the bitmap */
786 bmp_xoffset = bm.bmWidth / 2;
787 bm.bmWidth -= bmp_xoffset;
789 goto got_bitmap;
790 case HBMMENU_MBAR_RESTORE:
791 flags = DFCS_CAPTIONRESTORE;
792 break;
793 case HBMMENU_MBAR_MINIMIZE:
794 flags = DFCS_CAPTIONMIN;
795 break;
796 case HBMMENU_MBAR_MINIMIZE_D:
797 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
798 break;
799 case HBMMENU_MBAR_CLOSE:
800 flags = DFCS_CAPTIONCLOSE;
801 break;
802 case HBMMENU_MBAR_CLOSE_D:
803 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
804 break;
805 case HBMMENU_CALLBACK:
806 case HBMMENU_POPUP_CLOSE:
807 case HBMMENU_POPUP_RESTORE:
808 case HBMMENU_POPUP_MAXIMIZE:
809 case HBMMENU_POPUP_MINIMIZE:
810 default:
811 FIXME("Magic 0x%08x not implemented\n", LOWORD(lpitem->text));
812 return;
814 r = *rect;
815 InflateRect( &r, -1, -1 );
816 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
817 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
818 return;
821 if (!bmp || !GetObjectA( bmp, sizeof(bm), &bm )) return;
823 got_bitmap:
824 hdcMem = CreateCompatibleDC( hdc );
825 SelectObject( hdcMem, bmp );
827 /* handle fontsize > bitmap_height */
828 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
829 left=rect->left;
830 if (TWEAK_WineLook == WIN95_LOOK) {
831 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
832 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
833 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
834 } else {
835 left++;
836 w-=2;
837 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text) && (!menuBar)) ? MERGEPAINT : SRCCOPY;
839 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
840 DeleteDC( hdcMem );
844 /***********************************************************************
845 * MENU_CalcItemSize
847 * Calculate the size of the menu item and store it in lpitem->rect.
849 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
850 INT orgX, INT orgY, BOOL menuBar )
852 WCHAR *p;
853 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
855 TRACE("dc=0x%04x owner=0x%04x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
856 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
857 (menuBar ? " (MenuBar)" : ""));
859 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
861 if (lpitem->fType & MF_OWNERDRAW)
864 ** Experimentation under Windows reveals that an owner-drawn
865 ** menu is expected to return the size of the content part of
866 ** the menu item, not including the checkmark nor the submenu
867 ** arrow. Windows adds those values itself and returns the
868 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
870 MEASUREITEMSTRUCT mis;
871 mis.CtlType = ODT_MENU;
872 mis.CtlID = 0;
873 mis.itemID = lpitem->wID;
874 mis.itemData = (DWORD)lpitem->dwItemData;
875 mis.itemHeight = 0;
876 mis.itemWidth = 0;
877 SendMessageA( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
878 lpitem->rect.right += mis.itemWidth;
880 if (menuBar)
882 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
885 /* under at least win95 you seem to be given a standard
886 height for the menu and the height value is ignored */
888 if (TWEAK_WineLook == WIN31_LOOK)
889 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
890 else
891 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
893 else
894 lpitem->rect.bottom += mis.itemHeight;
896 TRACE("id=%04x size=%dx%d\n",
897 lpitem->wID, mis.itemWidth, mis.itemHeight);
898 /* Fall through to get check/arrow width calculation. */
901 if (lpitem->fType & MF_SEPARATOR)
903 lpitem->rect.bottom += SEPARATOR_HEIGHT;
904 return;
907 if (!menuBar)
909 lpitem->rect.right += 2 * check_bitmap_width;
910 if (lpitem->fType & MF_POPUP)
911 lpitem->rect.right += arrow_bitmap_width;
914 if (lpitem->fType & MF_OWNERDRAW)
915 return;
917 if (IS_BITMAP_ITEM(lpitem->fType))
919 SIZE size;
921 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
922 lpitem->rect.right += size.cx;
923 lpitem->rect.bottom += size.cy;
924 if (TWEAK_WineLook == WIN98_LOOK)
926 /* Leave space for the sunken border */
927 lpitem->rect.right += 2;
928 lpitem->rect.bottom += 2;
933 /* it must be a text item - unless it's the system menu */
934 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
935 { SIZE size;
937 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
939 lpitem->rect.right += size.cx;
940 if (TWEAK_WineLook == WIN31_LOOK)
941 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
942 else
943 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
944 lpitem->xTab = 0;
946 if (menuBar)
948 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
950 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
952 /* Item contains a tab (only meaningful in popup menus) */
953 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
954 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
955 lpitem->rect.right += MENU_TAB_SPACE;
957 else
959 if (strchrW( lpitem->text, '\b' ))
960 lpitem->rect.right += MENU_TAB_SPACE;
961 lpitem->xTab = lpitem->rect.right - check_bitmap_width
962 - arrow_bitmap_width;
965 TRACE("(%d,%d)-(%d,%d)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
969 /***********************************************************************
970 * MENU_PopupMenuCalcSize
972 * Calculate the size of a popup menu.
974 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
976 MENUITEM *lpitem;
977 HDC hdc;
978 int start, i;
979 int orgX, orgY, maxX, maxTab, maxTabWidth;
981 lppop->Width = lppop->Height = 0;
982 if (lppop->nItems == 0) return;
983 hdc = GetDC( 0 );
985 SelectObject( hdc, hMenuFont);
987 start = 0;
988 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
990 while (start < lppop->nItems)
992 lpitem = &lppop->items[start];
993 orgX = maxX;
994 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
996 maxTab = maxTabWidth = 0;
998 /* Parse items until column break or end of menu */
999 for (i = start; i < lppop->nItems; i++, lpitem++)
1001 if ((i != start) &&
1002 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1004 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
1006 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1007 maxX = max( maxX, lpitem->rect.right );
1008 orgY = lpitem->rect.bottom;
1009 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1011 maxTab = max( maxTab, lpitem->xTab );
1012 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1016 /* Finish the column (set all items to the largest width found) */
1017 maxX = max( maxX, maxTab + maxTabWidth );
1018 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1020 lpitem->rect.right = maxX;
1021 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1022 lpitem->xTab = maxTab;
1025 lppop->Height = max( lppop->Height, orgY );
1028 lppop->Width = maxX;
1030 /* space for 3d border */
1031 if(TWEAK_WineLook > WIN31_LOOK)
1033 lppop->Height += 2;
1034 lppop->Width += 2;
1037 ReleaseDC( 0, hdc );
1041 /***********************************************************************
1042 * MENU_MenuBarCalcSize
1044 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1045 * height is off by 1 pixel which causes lengthy window relocations when
1046 * active document window is maximized/restored.
1048 * Calculate the size of the menu bar.
1050 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1051 LPPOPUPMENU lppop, HWND hwndOwner )
1053 MENUITEM *lpitem;
1054 int start, i, orgX, orgY, maxY, helpPos;
1056 if ((lprect == NULL) || (lppop == NULL)) return;
1057 if (lppop->nItems == 0) return;
1058 TRACE("left=%d top=%d right=%d bottom=%d\n",
1059 lprect->left, lprect->top, lprect->right, lprect->bottom);
1060 lppop->Width = lprect->right - lprect->left;
1061 lppop->Height = 0;
1062 maxY = lprect->top+1;
1063 start = 0;
1064 helpPos = -1;
1065 while (start < lppop->nItems)
1067 lpitem = &lppop->items[start];
1068 orgX = lprect->left;
1069 orgY = maxY;
1071 /* Parse items until line break or end of menu */
1072 for (i = start; i < lppop->nItems; i++, lpitem++)
1074 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1075 if ((i != start) &&
1076 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1078 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1079 orgX, orgY );
1080 debug_print_menuitem (" item: ", lpitem, "");
1081 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
1083 if (lpitem->rect.right > lprect->right)
1085 if (i != start) break;
1086 else lpitem->rect.right = lprect->right;
1088 maxY = max( maxY, lpitem->rect.bottom );
1089 orgX = lpitem->rect.right;
1092 /* Finish the line (set all items to the largest height found) */
1093 while (start < i) lppop->items[start++].rect.bottom = maxY;
1096 lprect->bottom = maxY;
1097 lppop->Height = lprect->bottom - lprect->top;
1099 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1100 /* the last item (if several lines, only move the last line) */
1101 lpitem = &lppop->items[lppop->nItems-1];
1102 orgY = lpitem->rect.top;
1103 orgX = lprect->right;
1104 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1105 if ( (helpPos==-1) || (helpPos>i) )
1106 break; /* done */
1107 if (lpitem->rect.top != orgY) break; /* Other line */
1108 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1109 lpitem->rect.left += orgX - lpitem->rect.right;
1110 lpitem->rect.right = orgX;
1111 orgX = lpitem->rect.left;
1115 /***********************************************************************
1116 * MENU_DrawMenuItem
1118 * Draw a single menu item.
1120 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1121 UINT height, BOOL menuBar, UINT odaction )
1123 RECT rect;
1125 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1127 if (lpitem->fType & MF_SYSMENU)
1129 if( !IsIconic(hwnd) ) {
1130 if (TWEAK_WineLook > WIN31_LOOK)
1131 NC_DrawSysButton95( hwnd, hdc,
1132 lpitem->fState &
1133 (MF_HILITE | MF_MOUSESELECT) );
1134 else
1135 NC_DrawSysButton( hwnd, hdc,
1136 lpitem->fState &
1137 (MF_HILITE | MF_MOUSESELECT) );
1140 return;
1143 if (lpitem->fType & MF_OWNERDRAW)
1146 ** Experimentation under Windows reveals that an owner-drawn
1147 ** menu is given the rectangle which includes the space it requested
1148 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1149 ** and a popup-menu arrow. This is the value of lpitem->rect.
1150 ** Windows will leave all drawing to the application except for
1151 ** the popup-menu arrow. Windows always draws that itself, after
1152 ** the menu owner has finished drawing.
1154 DRAWITEMSTRUCT dis;
1156 dis.CtlType = ODT_MENU;
1157 dis.CtlID = 0;
1158 dis.itemID = lpitem->wID;
1159 dis.itemData = (DWORD)lpitem->dwItemData;
1160 dis.itemState = 0;
1161 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1162 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED;
1163 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1164 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1165 dis.hwndItem = (HWND)hmenu;
1166 dis.hDC = hdc;
1167 dis.rcItem = lpitem->rect;
1168 TRACE("Ownerdraw: owner=%04x itemID=%d, itemState=%d, itemAction=%d, "
1169 "hwndItem=%04x, hdc=%04x, rcItem={%d,%d,%d,%d}\n", hwndOwner,
1170 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1171 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1172 dis.rcItem.bottom);
1173 SendMessageA( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1174 /* Fall through to draw popup-menu arrow */
1177 TRACE("rect={%d,%d,%d,%d}\n", lpitem->rect.left, lpitem->rect.top,
1178 lpitem->rect.right,lpitem->rect.bottom);
1180 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1182 rect = lpitem->rect;
1184 if (!(lpitem->fType & MF_OWNERDRAW))
1186 if (lpitem->fState & MF_HILITE)
1188 if(TWEAK_WineLook == WIN98_LOOK)
1190 if(menuBar)
1191 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1192 else
1193 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1195 else /* Not Win98 Look */
1197 if(!IS_BITMAP_ITEM(lpitem->fType))
1198 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1201 else
1202 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1205 SetBkMode( hdc, TRANSPARENT );
1207 if (!(lpitem->fType & MF_OWNERDRAW))
1209 /* vertical separator */
1210 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1212 if (TWEAK_WineLook > WIN31_LOOK)
1214 RECT rc = rect;
1215 rc.top = 3;
1216 rc.bottom = height - 3;
1217 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1219 else
1221 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1222 MoveToEx( hdc, rect.left, 0, NULL );
1223 LineTo( hdc, rect.left, height );
1227 /* horizontal separator */
1228 if (lpitem->fType & MF_SEPARATOR)
1230 if (TWEAK_WineLook > WIN31_LOOK)
1232 RECT rc = rect;
1233 rc.left++;
1234 rc.right--;
1235 rc.top += SEPARATOR_HEIGHT / 2;
1236 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1238 else
1240 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1241 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1242 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1244 return;
1248 /* Setup colors */
1250 if (lpitem->fState & MF_HILITE)
1252 if(TWEAK_WineLook == WIN98_LOOK)
1254 if(menuBar) {
1255 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1256 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1257 } else {
1258 if(lpitem->fState & MF_GRAYED)
1259 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1260 else
1261 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1262 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1265 else /* Not Win98 Look */
1267 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1268 if(!IS_BITMAP_ITEM(lpitem->fType))
1269 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1272 else
1274 if (lpitem->fState & MF_GRAYED)
1275 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1276 else
1277 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1278 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1281 /* helper lines for debugging */
1282 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1283 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1284 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1285 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1288 if (!menuBar)
1290 INT y = rect.top + rect.bottom;
1291 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1292 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1294 if (!(lpitem->fType & MF_OWNERDRAW))
1296 /* Draw the check mark
1298 * FIXME:
1299 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1301 HBITMAP bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1302 if (bm) /* we have a custom bitmap */
1304 HDC hdcMem = CreateCompatibleDC( hdc );
1305 SelectObject( hdcMem, bm );
1306 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1307 check_bitmap_width, check_bitmap_height,
1308 hdcMem, 0, 0, SRCCOPY );
1309 DeleteDC( hdcMem );
1311 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1313 RECT r;
1314 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1315 HDC hdcMem = CreateCompatibleDC( hdc );
1316 SelectObject( hdcMem, bm );
1317 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1318 DrawFrameControl( hdcMem, &r, DFC_MENU,
1319 (lpitem->fType & MFT_RADIOCHECK) ?
1320 DFCS_MENUBULLET : DFCS_MENUCHECK );
1321 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1322 hdcMem, 0, 0, SRCCOPY );
1323 DeleteDC( hdcMem );
1324 DeleteObject( bm );
1328 /* Draw the popup-menu arrow */
1329 if (lpitem->fType & MF_POPUP)
1331 HDC hdcMem = CreateCompatibleDC( hdc );
1332 HBITMAP hOrigBitmap;
1334 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1335 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1336 (y - arrow_bitmap_height) / 2,
1337 arrow_bitmap_width, arrow_bitmap_height,
1338 hdcMem, 0, 0, SRCCOPY );
1339 SelectObject( hdcMem, hOrigBitmap );
1340 DeleteDC( hdcMem );
1343 rect.left += check_bitmap_width;
1344 rect.right -= arrow_bitmap_width;
1347 /* Done for owner-drawn */
1348 if (lpitem->fType & MF_OWNERDRAW)
1349 return;
1351 /* Draw the item text or bitmap */
1352 if (IS_BITMAP_ITEM(lpitem->fType))
1354 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar );
1355 return;
1358 /* No bitmap - process text if present */
1359 else if (IS_STRING_ITEM(lpitem->fType))
1361 register int i;
1362 HFONT hfontOld = 0;
1364 UINT uFormat = (menuBar) ?
1365 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1366 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1368 if ( lpitem->fState & MFS_DEFAULT )
1370 hfontOld = SelectObject( hdc, hMenuFontBold);
1373 if (menuBar)
1375 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1376 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1377 i = strlenW( lpitem->text );
1379 else
1381 for (i = 0; lpitem->text[i]; i++)
1382 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1383 break;
1386 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1388 if (!(lpitem->fState & MF_HILITE) )
1390 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1391 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1392 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1393 --rect.left; --rect.top; --rect.right; --rect.bottom;
1395 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1398 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1400 /* paint the shortcut text */
1401 if (lpitem->text[i]) /* There's a tab or flush-right char */
1403 if (lpitem->text[i] == '\t')
1405 rect.left = lpitem->xTab;
1406 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1408 else
1410 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1413 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1415 if (!(lpitem->fState & MF_HILITE) )
1417 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1418 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1419 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1420 --rect.left; --rect.top; --rect.right; --rect.bottom;
1422 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1424 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1427 if (hfontOld)
1428 SelectObject (hdc, hfontOld);
1433 /***********************************************************************
1434 * MENU_DrawPopupMenu
1436 * Paint a popup menu.
1438 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1440 HBRUSH hPrevBrush = 0;
1441 RECT rect;
1443 TRACE("wnd=0x%04x dc=0x%04x menu=0x%04x\n", hwnd, hdc, hmenu);
1445 GetClientRect( hwnd, &rect );
1447 if(TWEAK_WineLook == WIN31_LOOK)
1449 rect.bottom -= POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1450 rect.right -= POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER);
1453 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1454 && (SelectObject( hdc, hMenuFont)))
1456 HPEN hPrevPen;
1458 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1460 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1461 if( hPrevPen )
1463 INT ropPrev, i;
1464 POPUPMENU *menu;
1466 /* draw 3-d shade */
1467 if(TWEAK_WineLook == WIN31_LOOK) {
1468 SelectObject( hdc, hShadeBrush );
1469 SetBkMode( hdc, TRANSPARENT );
1470 ropPrev = SetROP2( hdc, R2_MASKPEN );
1472 i = rect.right; /* why SetBrushOrg() doesn't? */
1473 PatBlt( hdc, i & 0xfffffffe,
1474 rect.top + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER),
1475 i%2 + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1476 rect.bottom - rect.top, 0x00a000c9 );
1477 i = rect.bottom;
1478 PatBlt( hdc, rect.left + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1479 i & 0xfffffffe,rect.right - rect.left,
1480 i%2 + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER), 0x00a000c9 );
1481 SelectObject( hdc, hPrevPen );
1482 SelectObject( hdc, hPrevBrush );
1483 SetROP2( hdc, ropPrev );
1485 else
1486 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1488 /* draw menu items */
1490 menu = MENU_GetMenu( hmenu );
1491 if (menu && menu->nItems)
1493 MENUITEM *item;
1494 UINT u;
1496 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1497 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1498 menu->Height, FALSE, ODA_DRAWENTIRE );
1501 } else
1503 SelectObject( hdc, hPrevBrush );
1508 /***********************************************************************
1509 * MENU_DrawMenuBar
1511 * Paint a menu bar. Returns the height of the menu bar.
1512 * called from [windows/nonclient.c]
1514 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1515 BOOL suppress_draw)
1517 LPPOPUPMENU lppop;
1518 UINT i,retvalue;
1519 HFONT hfontOld = 0;
1520 HMENU hMenu = GetMenu(hwnd);
1522 lppop = MENU_GetMenu( hMenu );
1523 if (lppop == NULL || lprect == NULL)
1525 retvalue = GetSystemMetrics(SM_CYMENU);
1526 goto END;
1529 TRACE("(%04x, %p, %p)\n", hDC, lprect, lppop);
1531 hfontOld = SelectObject( hDC, hMenuFont);
1533 if (lppop->Height == 0)
1534 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1536 lprect->bottom = lprect->top + lppop->Height;
1538 if (suppress_draw)
1540 retvalue = lppop->Height;
1541 goto END;
1544 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
1546 if (TWEAK_WineLook == WIN31_LOOK)
1548 SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1549 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1550 LineTo( hDC, lprect->right, lprect->bottom );
1552 else
1554 SelectObject( hDC, GetSysColorPen(COLOR_3DFACE));
1555 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1556 LineTo( hDC, lprect->right, lprect->bottom );
1559 if (lppop->nItems == 0)
1561 retvalue = GetSystemMetrics(SM_CYMENU);
1562 goto END;
1565 for (i = 0; i < lppop->nItems; i++)
1567 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
1568 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
1570 retvalue = lppop->Height;
1572 END:
1573 if (hfontOld) SelectObject (hDC, hfontOld);
1574 return retvalue;
1578 /***********************************************************************
1579 * MENU_ShowPopup
1581 * Display a popup menu.
1583 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1584 INT x, INT y, INT xanchor, INT yanchor )
1586 POPUPMENU *menu;
1587 UINT width, height;
1589 TRACE("owner=0x%04x hmenu=0x%04x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1590 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1592 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1593 if (menu->FocusedItem != NO_SELECTED_ITEM)
1595 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1596 menu->FocusedItem = NO_SELECTED_ITEM;
1599 /* store the owner for DrawItem */
1600 menu->hwndOwner = hwndOwner;
1603 MENU_PopupMenuCalcSize( menu, hwndOwner );
1605 /* adjust popup menu pos so that it fits within the desktop */
1607 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1608 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1610 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1612 if( xanchor )
1613 x -= width - xanchor;
1614 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1615 x = GetSystemMetrics(SM_CXSCREEN) - width;
1617 if( x < 0 ) x = 0;
1619 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1621 if( yanchor )
1622 y -= height + yanchor;
1623 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1624 y = GetSystemMetrics(SM_CYSCREEN) - height;
1626 if( y < 0 ) y = 0;
1628 if( TWEAK_WineLook == WIN31_LOOK )
1630 width += POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER); /* add space for shading */
1631 height += POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1634 /* NOTE: In Windows, top menu popup is not owned. */
1635 menu->hWnd = CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1636 WS_POPUP, x, y, width, height,
1637 hwndOwner, 0, GetWindowLongA(hwndOwner,GWL_HINSTANCE),
1638 (LPVOID)hmenu );
1639 if( !menu->hWnd ) return FALSE;
1640 if (!top_popup) top_popup = menu->hWnd;
1642 /* Display the window */
1644 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1645 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1646 UpdateWindow( menu->hWnd );
1647 return TRUE;
1651 /***********************************************************************
1652 * MENU_SelectItem
1654 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1655 BOOL sendMenuSelect, HMENU topmenu )
1657 LPPOPUPMENU lppop;
1658 HDC hdc;
1660 TRACE("owner=0x%04x menu=0x%04x index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1662 lppop = MENU_GetMenu( hmenu );
1663 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1665 if (lppop->FocusedItem == wIndex) return;
1666 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1667 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1669 SelectObject( hdc, hMenuFont);
1671 /* Clear previous highlighted item */
1672 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1674 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1675 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1676 lppop->Height, !(lppop->wFlags & MF_POPUP),
1677 ODA_SELECT );
1680 /* Highlight new item (if any) */
1681 lppop->FocusedItem = wIndex;
1682 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1684 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1685 lppop->items[wIndex].fState |= MF_HILITE;
1686 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1687 &lppop->items[wIndex], lppop->Height,
1688 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1690 if (sendMenuSelect)
1692 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1693 SendMessageA( hwndOwner, WM_MENUSELECT,
1694 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1695 ip->fType | ip->fState | MF_MOUSESELECT |
1696 (lppop->wFlags & MF_SYSMENU)), hmenu);
1699 else if (sendMenuSelect) {
1700 if(topmenu){
1701 int pos;
1702 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1703 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1704 MENUITEM *ip = &ptm->items[pos];
1705 SendMessageA( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1706 ip->fType | ip->fState | MF_MOUSESELECT |
1707 (ptm->wFlags & MF_SYSMENU)), topmenu);
1711 ReleaseDC( lppop->hWnd, hdc );
1715 /***********************************************************************
1716 * MENU_MoveSelection
1718 * Moves currently selected item according to the offset parameter.
1719 * If there is no selection then it should select the last item if
1720 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1722 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1724 INT i;
1725 POPUPMENU *menu;
1727 TRACE("hwnd=0x%04x hmenu=0x%04x off=0x%04x\n", hwndOwner, hmenu, offset);
1729 menu = MENU_GetMenu( hmenu );
1730 if ((!menu) || (!menu->items)) return;
1732 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1734 if( menu->nItems == 1 ) return; else
1735 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1736 ; i += offset)
1737 if (!(menu->items[i].fType & MF_SEPARATOR))
1739 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1740 return;
1744 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1745 i >= 0 && i < menu->nItems ; i += offset)
1746 if (!(menu->items[i].fType & MF_SEPARATOR))
1748 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1749 return;
1754 /**********************************************************************
1755 * MENU_SetItemData
1757 * Set an item flags, id and text ptr. Called by InsertMenu() and
1758 * ModifyMenu().
1760 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT id,
1761 LPCWSTR str )
1763 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1765 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1766 TRACE("flags=%x str=%p\n", flags, str);
1768 if (IS_STRING_ITEM(flags))
1770 if (!str)
1772 flags |= MF_SEPARATOR;
1773 item->text = NULL;
1775 else
1777 LPWSTR text;
1778 /* Item beginning with a backspace is a help item */
1779 if (*str == '\b')
1781 flags |= MF_HELP;
1782 str++;
1784 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1785 return FALSE;
1786 strcpyW( text, str );
1787 item->text = text;
1790 else if (IS_BITMAP_ITEM(flags))
1791 item->text = (LPWSTR)(HBITMAP)LOWORD(str);
1792 else item->text = NULL;
1794 if (flags & MF_OWNERDRAW)
1795 item->dwItemData = (DWORD)str;
1796 else
1797 item->dwItemData = 0;
1799 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != id) )
1800 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1802 if (flags & MF_POPUP)
1804 POPUPMENU *menu = MENU_GetMenu((UINT16)id);
1805 if (menu) menu->wFlags |= MF_POPUP;
1806 else
1808 item->wID = 0;
1809 item->hSubMenu = 0;
1810 item->fType = 0;
1811 item->fState = 0;
1812 return FALSE;
1816 item->wID = id;
1817 if (flags & MF_POPUP)
1818 item->hSubMenu = id;
1820 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1821 flags |= MF_POPUP; /* keep popup */
1823 item->fType = flags & TYPE_MASK;
1824 item->fState = (flags & STATE_MASK) &
1825 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1828 /* Don't call SetRectEmpty here! */
1831 if (prevText) HeapFree( GetProcessHeap(), 0, prevText );
1833 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1834 return TRUE;
1838 /**********************************************************************
1839 * MENU_InsertItem
1841 * Insert a new item into a menu.
1843 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1845 MENUITEM *newItems;
1846 POPUPMENU *menu;
1848 if (!(menu = MENU_GetMenu(hMenu)))
1849 return NULL;
1851 /* Find where to insert new item */
1853 if (flags & MF_BYPOSITION) {
1854 if (pos > menu->nItems)
1855 pos = menu->nItems;
1856 } else {
1857 if (!MENU_FindItem( &hMenu, &pos, flags ))
1858 pos = menu->nItems;
1859 else {
1860 if (!(menu = MENU_GetMenu( hMenu )))
1861 return NULL;
1865 /* Create new items array */
1867 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1868 if (!newItems)
1870 WARN("allocation failed\n" );
1871 return NULL;
1873 if (menu->nItems > 0)
1875 /* Copy the old array into the new one */
1876 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1877 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1878 (menu->nItems-pos)*sizeof(MENUITEM) );
1879 HeapFree( GetProcessHeap(), 0, menu->items );
1881 menu->items = newItems;
1882 menu->nItems++;
1883 memset( &newItems[pos], 0, sizeof(*newItems) );
1884 menu->Height = 0; /* force size recalculate */
1885 return &newItems[pos];
1889 /**********************************************************************
1890 * MENU_ParseResource
1892 * Parse a standard menu resource and add items to the menu.
1893 * Return a pointer to the end of the resource.
1895 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1897 WORD flags, id = 0;
1898 LPCSTR str;
1902 flags = GET_WORD(res);
1903 res += sizeof(WORD);
1904 if (!(flags & MF_POPUP))
1906 id = GET_WORD(res);
1907 res += sizeof(WORD);
1909 if (!IS_STRING_ITEM(flags))
1910 ERR("not a string item %04x\n", flags );
1911 str = res;
1912 if (!unicode) res += strlen(str) + 1;
1913 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1914 if (flags & MF_POPUP)
1916 HMENU hSubMenu = CreatePopupMenu();
1917 if (!hSubMenu) return NULL;
1918 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1919 return NULL;
1920 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1921 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1923 else /* Not a popup */
1925 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1926 else AppendMenuW( hMenu, flags, id,
1927 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1929 } while (!(flags & MF_END));
1930 return res;
1934 /**********************************************************************
1935 * MENUEX_ParseResource
1937 * Parse an extended menu resource and add items to the menu.
1938 * Return a pointer to the end of the resource.
1940 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1942 WORD resinfo;
1943 do {
1944 MENUITEMINFOW mii;
1946 mii.cbSize = sizeof(mii);
1947 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1948 mii.fType = GET_DWORD(res);
1949 res += sizeof(DWORD);
1950 mii.fState = GET_DWORD(res);
1951 res += sizeof(DWORD);
1952 mii.wID = GET_DWORD(res);
1953 res += sizeof(DWORD);
1954 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1955 res += sizeof(WORD);
1956 /* Align the text on a word boundary. */
1957 res += (~((int)res - 1)) & 1;
1958 mii.dwTypeData = (LPWSTR) res;
1959 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1960 /* Align the following fields on a dword boundary. */
1961 res += (~((int)res - 1)) & 3;
1963 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1964 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1966 if (resinfo & 1) { /* Pop-up? */
1967 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1968 res += sizeof(DWORD);
1969 mii.hSubMenu = CreatePopupMenu();
1970 if (!mii.hSubMenu)
1971 return NULL;
1972 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1973 DestroyMenu(mii.hSubMenu);
1974 return NULL;
1976 mii.fMask |= MIIM_SUBMENU;
1977 mii.fType |= MF_POPUP;
1979 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1980 } while (!(resinfo & MF_END));
1981 return res;
1985 /***********************************************************************
1986 * MENU_GetSubPopup
1988 * Return the handle of the selected sub-popup menu (if any).
1990 static HMENU MENU_GetSubPopup( HMENU hmenu )
1992 POPUPMENU *menu;
1993 MENUITEM *item;
1995 menu = MENU_GetMenu( hmenu );
1997 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1999 item = &menu->items[menu->FocusedItem];
2000 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2001 return item->hSubMenu;
2002 return 0;
2006 /***********************************************************************
2007 * MENU_HideSubPopups
2009 * Hide the sub-popup menus of this menu.
2011 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2012 BOOL sendMenuSelect )
2014 POPUPMENU *menu = MENU_GetMenu( hmenu );
2016 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2018 if (menu && top_popup)
2020 HMENU hsubmenu;
2021 POPUPMENU *submenu;
2022 MENUITEM *item;
2024 if (menu->FocusedItem != NO_SELECTED_ITEM)
2026 item = &menu->items[menu->FocusedItem];
2027 if (!(item->fType & MF_POPUP) ||
2028 !(item->fState & MF_MOUSESELECT)) return;
2029 item->fState &= ~MF_MOUSESELECT;
2030 hsubmenu = item->hSubMenu;
2031 } else return;
2033 submenu = MENU_GetMenu( hsubmenu );
2034 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2035 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2036 DestroyWindow( submenu->hWnd );
2037 submenu->hWnd = 0;
2042 /***********************************************************************
2043 * MENU_ShowSubPopup
2045 * Display the sub-menu of the selected item of this menu.
2046 * Return the handle of the submenu, or hmenu if no submenu to display.
2048 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2049 BOOL selectFirst, UINT wFlags )
2051 RECT rect;
2052 POPUPMENU *menu;
2053 MENUITEM *item;
2054 HDC hdc;
2056 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, selectFirst);
2058 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2060 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2062 item = &menu->items[menu->FocusedItem];
2063 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2064 return hmenu;
2066 /* message must be sent before using item,
2067 because nearly everything may be changed by the application ! */
2069 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2070 if (!(wFlags & TPM_NONOTIFY))
2071 SendMessageA( hwndOwner, WM_INITMENUPOPUP, item->hSubMenu,
2072 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2074 item = &menu->items[menu->FocusedItem];
2075 rect = item->rect;
2077 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2078 if (!(item->fState & MF_HILITE))
2080 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2081 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2083 SelectObject( hdc, hMenuFont);
2085 item->fState |= MF_HILITE;
2086 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2087 ReleaseDC( menu->hWnd, hdc );
2089 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2090 item->rect = rect;
2092 item->fState |= MF_MOUSESELECT;
2094 if (IS_SYSTEM_MENU(menu))
2096 MENU_InitSysMenuPopup(item->hSubMenu,
2097 GetWindowLongA( menu->hWnd, GWL_STYLE ),
2098 GetClassLongA( menu->hWnd, GCL_STYLE));
2100 NC_GetSysPopupPos( menu->hWnd, &rect );
2101 rect.top = rect.bottom;
2102 rect.right = GetSystemMetrics(SM_CXSIZE);
2103 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2105 else
2107 GetWindowRect( menu->hWnd, &rect );
2108 if (menu->wFlags & MF_POPUP)
2110 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2111 rect.top += item->rect.top;
2112 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2113 rect.bottom = item->rect.top - item->rect.bottom;
2115 else
2117 rect.left += item->rect.left;
2118 rect.top += item->rect.bottom;
2119 rect.right = item->rect.right - item->rect.left;
2120 rect.bottom = item->rect.bottom - item->rect.top;
2124 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2125 rect.left, rect.top, rect.right, rect.bottom );
2126 if (selectFirst)
2127 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2128 return item->hSubMenu;
2133 /**********************************************************************
2134 * MENU_IsMenuActive
2136 BOOL MENU_IsMenuActive(void)
2138 return (top_popup != 0);
2141 /***********************************************************************
2142 * MENU_PtMenu
2144 * Walks menu chain trying to find a menu pt maps to.
2146 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2148 POPUPMENU *menu = MENU_GetMenu( hMenu );
2149 UINT item = menu->FocusedItem;
2150 HMENU ret;
2152 /* try subpopup first (if any) */
2153 ret = (item != NO_SELECTED_ITEM &&
2154 (menu->items[item].fType & MF_POPUP) &&
2155 (menu->items[item].fState & MF_MOUSESELECT))
2156 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2158 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2160 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2161 if( menu->wFlags & MF_POPUP )
2163 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2165 else if (ht == HTSYSMENU)
2166 ret = get_win_sys_menu( menu->hWnd );
2167 else if (ht == HTMENU)
2168 ret = GetMenu( menu->hWnd );
2170 return ret;
2173 /***********************************************************************
2174 * MENU_ExecFocusedItem
2176 * Execute a menu item (for instance when user pressed Enter).
2177 * Return the wID of the executed item. Otherwise, -1 indicating
2178 * that no menu item was executed;
2179 * Have to receive the flags for the TrackPopupMenu options to avoid
2180 * sending unwanted message.
2183 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2185 MENUITEM *item;
2186 POPUPMENU *menu = MENU_GetMenu( hMenu );
2188 TRACE("%p hmenu=0x%04x\n", pmt, hMenu);
2190 if (!menu || !menu->nItems ||
2191 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2193 item = &menu->items[menu->FocusedItem];
2195 TRACE("%08x %08x %08x\n",
2196 hMenu, item->wID, item->hSubMenu);
2198 if (!(item->fType & MF_POPUP))
2200 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2202 /* If TPM_RETURNCMD is set you return the id, but
2203 do not send a message to the owner */
2204 if(!(wFlags & TPM_RETURNCMD))
2206 if( menu->wFlags & MF_SYSMENU )
2207 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2208 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2209 else
2210 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2212 return item->wID;
2215 else
2216 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2218 return -1;
2221 /***********************************************************************
2222 * MENU_SwitchTracking
2224 * Helper function for menu navigation routines.
2226 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2228 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2229 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2231 TRACE("%p hmenu=0x%04x 0x%04x\n", pmt, hPtMenu, id);
2233 if( pmt->hTopMenu != hPtMenu &&
2234 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2236 /* both are top level menus (system and menu-bar) */
2237 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2238 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2239 pmt->hTopMenu = hPtMenu;
2241 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2242 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2246 /***********************************************************************
2247 * MENU_ButtonDown
2249 * Return TRUE if we can go on with menu tracking.
2251 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2253 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2255 if (hPtMenu)
2257 UINT id = 0;
2258 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2259 MENUITEM *item;
2261 if( IS_SYSTEM_MENU(ptmenu) )
2262 item = ptmenu->items;
2263 else
2264 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2266 if( item )
2268 if( ptmenu->FocusedItem != id )
2269 MENU_SwitchTracking( pmt, hPtMenu, id );
2271 /* If the popup menu is not already "popped" */
2272 if(!(item->fState & MF_MOUSESELECT ))
2274 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2276 /* In win31, a newly popped menu always remains opened for the next buttonup */
2277 if(TWEAK_WineLook == WIN31_LOOK)
2278 ptmenu->bTimeToHide = FALSE;
2281 return TRUE;
2283 /* Else the click was on the menu bar, finish the tracking */
2285 return FALSE;
2288 /***********************************************************************
2289 * MENU_ButtonUp
2291 * Return the value of MENU_ExecFocusedItem if
2292 * the selected item was not a popup. Else open the popup.
2293 * A -1 return value indicates that we go on with menu tracking.
2296 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2298 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2300 if (hPtMenu)
2302 UINT id = 0;
2303 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2304 MENUITEM *item;
2306 if( IS_SYSTEM_MENU(ptmenu) )
2307 item = ptmenu->items;
2308 else
2309 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2311 if( item && (ptmenu->FocusedItem == id ))
2313 if( !(item->fType & MF_POPUP) )
2314 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2316 /* If we are dealing with the top-level menu */
2317 /* and this is a click on an already "popped" item: */
2318 /* Stop the menu tracking and close the opened submenus */
2319 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2320 return 0;
2322 ptmenu->bTimeToHide = TRUE;
2324 return -1;
2328 /***********************************************************************
2329 * MENU_MouseMove
2331 * Return TRUE if we can go on with menu tracking.
2333 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2335 UINT id = NO_SELECTED_ITEM;
2336 POPUPMENU *ptmenu = NULL;
2338 if( hPtMenu )
2340 ptmenu = MENU_GetMenu( hPtMenu );
2341 if( IS_SYSTEM_MENU(ptmenu) )
2342 id = 0;
2343 else
2344 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2347 if( id == NO_SELECTED_ITEM )
2349 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2350 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2353 else if( ptmenu->FocusedItem != id )
2355 MENU_SwitchTracking( pmt, hPtMenu, id );
2356 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2358 return TRUE;
2362 /***********************************************************************
2363 * MENU_DoNextMenu
2365 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2367 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2369 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2371 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2372 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2374 MDINEXTMENU next_menu;
2375 HMENU hNewMenu;
2376 HWND hNewWnd;
2377 UINT id = 0;
2379 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2380 next_menu.hmenuNext = 0;
2381 next_menu.hwndNext = 0;
2382 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2384 TRACE("%04x [%04x] -> %04x [%04x]\n",
2385 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2387 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2389 DWORD style = GetWindowLongA( pmt->hOwnerWnd, GWL_STYLE );
2390 hNewWnd = pmt->hOwnerWnd;
2391 if( IS_SYSTEM_MENU(menu) )
2393 /* switch to the menu bar */
2395 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2397 if( vk == VK_LEFT )
2399 menu = MENU_GetMenu( hNewMenu );
2400 id = menu->nItems - 1;
2403 else if (style & WS_SYSMENU )
2405 /* switch to the system menu */
2406 hNewMenu = get_win_sys_menu( hNewWnd );
2408 else return FALSE;
2410 else /* application returned a new menu to switch to */
2412 hNewMenu = next_menu.hmenuNext;
2413 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2415 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2417 DWORD style = GetWindowLongA( hNewWnd, GWL_STYLE );
2419 if (style & WS_SYSMENU &&
2420 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2422 /* get the real system menu */
2423 hNewMenu = get_win_sys_menu(hNewWnd);
2425 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2427 /* FIXME: Not sure what to do here;
2428 * perhaps try to track hNewMenu as a popup? */
2430 TRACE(" -- got confused.\n");
2431 return FALSE;
2434 else return FALSE;
2437 if( hNewMenu != pmt->hTopMenu )
2439 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2440 FALSE, 0 );
2441 if( pmt->hCurrentMenu != pmt->hTopMenu )
2442 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2445 if( hNewWnd != pmt->hOwnerWnd )
2447 ReleaseCapture();
2448 pmt->hOwnerWnd = hNewWnd;
2449 EVENT_Capture( pmt->hOwnerWnd, HTMENU );
2452 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2453 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2455 return TRUE;
2457 return FALSE;
2460 /***********************************************************************
2461 * MENU_SuspendPopup
2463 * The idea is not to show the popup if the next input message is
2464 * going to hide it anyway.
2466 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2468 MSG msg;
2470 msg.hwnd = pmt->hOwnerWnd;
2472 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2473 pmt->trackFlags |= TF_SKIPREMOVE;
2475 switch( uMsg )
2477 case WM_KEYDOWN:
2478 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2479 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2481 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2482 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2483 if( msg.message == WM_KEYDOWN &&
2484 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2486 pmt->trackFlags |= TF_SUSPENDPOPUP;
2487 return TRUE;
2490 break;
2493 /* failures go through this */
2494 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2495 return FALSE;
2498 /***********************************************************************
2499 * MENU_KeyEscape
2501 * Handle a VK_ESCAPE key event in a menu.
2503 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2505 BOOL bEndMenu = TRUE;
2507 if (pmt->hCurrentMenu != pmt->hTopMenu)
2509 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2511 if (menu->wFlags & MF_POPUP)
2513 HMENU hmenutmp, hmenuprev;
2515 hmenuprev = hmenutmp = pmt->hTopMenu;
2517 /* close topmost popup */
2518 while (hmenutmp != pmt->hCurrentMenu)
2520 hmenuprev = hmenutmp;
2521 hmenutmp = MENU_GetSubPopup( hmenuprev );
2524 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2525 pmt->hCurrentMenu = hmenuprev;
2526 bEndMenu = FALSE;
2530 return bEndMenu;
2533 /***********************************************************************
2534 * MENU_KeyLeft
2536 * Handle a VK_LEFT key event in a menu.
2538 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2540 POPUPMENU *menu;
2541 HMENU hmenutmp, hmenuprev;
2542 UINT prevcol;
2544 hmenuprev = hmenutmp = pmt->hTopMenu;
2545 menu = MENU_GetMenu( hmenutmp );
2547 /* Try to move 1 column left (if possible) */
2548 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2549 NO_SELECTED_ITEM ) {
2551 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2552 prevcol, TRUE, 0 );
2553 return;
2556 /* close topmost popup */
2557 while (hmenutmp != pmt->hCurrentMenu)
2559 hmenuprev = hmenutmp;
2560 hmenutmp = MENU_GetSubPopup( hmenuprev );
2563 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2564 pmt->hCurrentMenu = hmenuprev;
2566 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2568 /* move menu bar selection if no more popups are left */
2570 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2571 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2573 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2575 /* A sublevel menu was displayed - display the next one
2576 * unless there is another displacement coming up */
2578 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2579 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2580 pmt->hTopMenu, TRUE, wFlags);
2586 /***********************************************************************
2587 * MENU_KeyRight
2589 * Handle a VK_RIGHT key event in a menu.
2591 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2593 HMENU hmenutmp;
2594 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2595 UINT nextcol;
2597 TRACE("MENU_KeyRight called, cur %x (%s), top %x (%s).\n",
2598 pmt->hCurrentMenu,
2599 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->
2600 items[0].text),
2601 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2603 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2605 /* If already displaying a popup, try to display sub-popup */
2607 hmenutmp = pmt->hCurrentMenu;
2608 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2610 /* if subpopup was displayed then we are done */
2611 if (hmenutmp != pmt->hCurrentMenu) return;
2614 /* Check to see if there's another column */
2615 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2616 NO_SELECTED_ITEM ) {
2617 TRACE("Going to %d.\n", nextcol );
2618 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2619 nextcol, TRUE, 0 );
2620 return;
2623 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2625 if( pmt->hCurrentMenu != pmt->hTopMenu )
2627 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2628 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2629 } else hmenutmp = 0;
2631 /* try to move to the next item */
2632 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2633 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2635 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2636 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2637 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2638 pmt->hTopMenu, TRUE, wFlags);
2642 /***********************************************************************
2643 * MENU_TrackMenu
2645 * Menu tracking code.
2647 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2648 HWND hwnd, const RECT *lprect )
2650 MSG msg;
2651 POPUPMENU *menu;
2652 BOOL fRemove;
2653 INT executedMenuId = -1;
2654 MTRACKER mt;
2655 BOOL enterIdleSent = FALSE;
2657 mt.trackFlags = 0;
2658 mt.hCurrentMenu = hmenu;
2659 mt.hTopMenu = hmenu;
2660 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2661 mt.pt.x = x;
2662 mt.pt.y = y;
2664 TRACE("hmenu=0x%04x flags=0x%08x (%d,%d) hwnd=0x%04x (%d,%d)-(%d,%d)\n",
2665 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2666 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2668 fEndMenu = FALSE;
2669 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2671 if (wFlags & TPM_BUTTONDOWN)
2673 /* Get the result in order to start the tracking or not */
2674 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2675 fEndMenu = !fRemove;
2678 EVENT_Capture( mt.hOwnerWnd, HTMENU );
2680 while (!fEndMenu)
2682 menu = MENU_GetMenu( mt.hCurrentMenu );
2683 if (!menu) /* sometimes happens if I do a window manager close */
2684 break;
2686 /* we have to keep the message in the queue until it's
2687 * clear that menu loop is not over yet. */
2689 for (;;)
2691 if (PeekMessageA( &msg, 0, 0, 0, PM_NOREMOVE ))
2693 if (!CallMsgFilterA( &msg, MSGF_MENU )) break;
2694 /* remove the message from the queue */
2695 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2697 else
2699 if (!enterIdleSent)
2701 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2702 enterIdleSent = TRUE;
2703 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2705 WaitMessage();
2709 /* check if EndMenu() tried to cancel us, by posting this message */
2710 if(msg.message == WM_CANCELMODE)
2712 /* we are now out of the loop */
2713 fEndMenu = TRUE;
2715 /* remove the message from the queue */
2716 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2718 /* break out of internal loop, ala ESCAPE */
2719 break;
2722 TranslateMessage( &msg );
2723 mt.pt = msg.pt;
2725 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2726 enterIdleSent=FALSE;
2728 fRemove = FALSE;
2729 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2732 * use the mouse coordinates in lParam instead of those in the MSG
2733 * struct to properly handle synthetic messages. lParam coords are
2734 * relative to client area, so they must be converted; since they can
2735 * be negative, we must use SLOWORD/SHIWORD instead of LOWORD/HIWORD.
2737 mt.pt.x = SLOWORD(msg.lParam);
2738 mt.pt.y = SHIWORD(msg.lParam);
2739 ClientToScreen(msg.hwnd,&mt.pt);
2741 /* Find a menu for this mouse event */
2742 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2744 switch(msg.message)
2746 /* no WM_NC... messages in captured state */
2748 case WM_RBUTTONDBLCLK:
2749 case WM_RBUTTONDOWN:
2750 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2751 /* fall through */
2752 case WM_LBUTTONDBLCLK:
2753 case WM_LBUTTONDOWN:
2754 /* If the message belongs to the menu, removes it from the queue */
2755 /* Else, end menu tracking */
2756 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2757 fEndMenu = !fRemove;
2758 break;
2760 case WM_RBUTTONUP:
2761 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2762 /* fall through */
2763 case WM_LBUTTONUP:
2764 /* Check if a menu was selected by the mouse */
2765 if (hmenu)
2767 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2769 /* End the loop if executedMenuId is an item ID */
2770 /* or if the job was done (executedMenuId = 0). */
2771 fEndMenu = fRemove = (executedMenuId != -1);
2773 /* No menu was selected by the mouse */
2774 /* if the function was called by TrackPopupMenu, continue
2775 with the menu tracking. If not, stop it */
2776 else
2777 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2779 break;
2781 case WM_MOUSEMOVE:
2782 /* In win95 winelook, the selected menu item must be changed every time the
2783 mouse moves. In Win31 winelook, the mouse button has to be held down */
2785 if ( (TWEAK_WineLook > WIN31_LOOK) ||
2786 ( (msg.wParam & MK_LBUTTON) ||
2787 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON))) )
2789 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2791 } /* switch(msg.message) - mouse */
2793 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2795 fRemove = TRUE; /* Keyboard messages are always removed */
2796 switch(msg.message)
2798 case WM_KEYDOWN:
2799 switch(msg.wParam)
2801 case VK_HOME:
2802 case VK_END:
2803 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2804 NO_SELECTED_ITEM, FALSE, 0 );
2805 /* fall through */
2806 case VK_UP:
2807 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2808 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2809 break;
2811 case VK_DOWN: /* If on menu bar, pull-down the menu */
2813 menu = MENU_GetMenu( mt.hCurrentMenu );
2814 if (!(menu->wFlags & MF_POPUP))
2815 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2816 else /* otherwise try to move selection */
2817 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2818 break;
2820 case VK_LEFT:
2821 MENU_KeyLeft( &mt, wFlags );
2822 break;
2824 case VK_RIGHT:
2825 MENU_KeyRight( &mt, wFlags );
2826 break;
2828 case VK_ESCAPE:
2829 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2830 break;
2832 case VK_F1:
2834 HELPINFO hi;
2835 hi.cbSize = sizeof(HELPINFO);
2836 hi.iContextType = HELPINFO_MENUITEM;
2837 if (menu->FocusedItem == NO_SELECTED_ITEM)
2838 hi.iCtrlId = 0;
2839 else
2840 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2841 hi.hItemHandle = hmenu;
2842 hi.dwContextId = menu->dwContextHelpID;
2843 hi.MousePos = msg.pt;
2844 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2845 break;
2848 default:
2849 break;
2851 break; /* WM_KEYDOWN */
2853 case WM_SYSKEYDOWN:
2854 switch(msg.wParam)
2856 case VK_MENU:
2857 fEndMenu = TRUE;
2858 break;
2861 break; /* WM_SYSKEYDOWN */
2863 case WM_CHAR:
2865 UINT pos;
2867 if (msg.wParam == '\r' || msg.wParam == ' ')
2869 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2870 fEndMenu = (executedMenuId != -1);
2872 break;
2875 /* Hack to avoid control chars. */
2876 /* We will find a better way real soon... */
2877 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2879 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2880 LOWORD(msg.wParam), FALSE );
2881 if (pos == (UINT)-2) fEndMenu = TRUE;
2882 else if (pos == (UINT)-1) MessageBeep(0);
2883 else
2885 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2886 TRUE, 0 );
2887 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2888 fEndMenu = (executedMenuId != -1);
2891 break;
2892 } /* switch(msg.message) - kbd */
2894 else
2896 DispatchMessageA( &msg );
2899 if (!fEndMenu) fRemove = TRUE;
2901 /* finally remove message from the queue */
2903 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2904 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2905 else mt.trackFlags &= ~TF_SKIPREMOVE;
2908 ReleaseCapture();
2910 /* If dropdown is still painted and the close box is clicked on
2911 then the menu will be destroyed as part of the DispatchMessage above.
2912 This will then invalidate the menu handle in mt.hTopMenu. We should
2913 check for this first. */
2914 if( IsMenu( mt.hTopMenu ) )
2916 menu = MENU_GetMenu( mt.hTopMenu );
2918 if( IsWindow( mt.hOwnerWnd ) )
2920 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2922 if (menu && menu->wFlags & MF_POPUP)
2924 DestroyWindow( menu->hWnd );
2925 menu->hWnd = 0;
2927 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2928 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2931 /* Reset the variable for hiding menu */
2932 if( menu ) menu->bTimeToHide = FALSE;
2935 /* The return value is only used by TrackPopupMenu */
2936 return ((executedMenuId != -1) ? executedMenuId : 0);
2939 /***********************************************************************
2940 * MENU_InitTracking
2942 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2944 TRACE("hwnd=0x%04x hmenu=0x%04x\n", hWnd, hMenu);
2946 HideCaret(0);
2948 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2949 if (!(wFlags & TPM_NONOTIFY))
2950 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2952 SendMessageA( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2954 if (!(wFlags & TPM_NONOTIFY))
2956 POPUPMENU *menu;
2957 SendMessageA( hWnd, WM_INITMENU, hMenu, 0 );
2958 if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
2959 { /* app changed/recreated menu bar entries in WM_INITMENU
2960 Recalculate menu sizes else clicks will not work */
2961 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2962 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2966 return TRUE;
2968 /***********************************************************************
2969 * MENU_ExitTracking
2971 static BOOL MENU_ExitTracking(HWND hWnd)
2973 TRACE("hwnd=0x%04x\n", hWnd);
2975 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
2976 ShowCaret(0);
2977 return TRUE;
2980 /***********************************************************************
2981 * MENU_TrackMouseMenuBar
2983 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2985 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2987 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2988 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2990 TRACE("wnd=%x ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2992 if (IsMenu(hMenu))
2994 /* map point to parent client coordinates */
2995 HWND parent = GetAncestor( hWnd, GA_PARENT );
2996 if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );
2998 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2999 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3000 MENU_ExitTracking(hWnd);
3005 /***********************************************************************
3006 * MENU_TrackKbdMenuBar
3008 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3010 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, INT vkey)
3012 UINT uItem = NO_SELECTED_ITEM;
3013 HMENU hTrackMenu;
3014 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3016 /* find window that has a menu */
3018 while (GetWindowLongA( hwnd, GWL_STYLE ) & WS_CHILD)
3019 if (!(hwnd = GetParent( hwnd ))) return;
3021 /* check if we have to track a system menu */
3023 hTrackMenu = GetMenu( hwnd );
3024 if (!hTrackMenu || IsIconic(hwnd) || vkey == VK_SPACE )
3026 if (!(GetWindowLongA( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3027 hTrackMenu = get_win_sys_menu( hwnd );
3028 uItem = 0;
3029 wParam |= HTSYSMENU; /* prevent item lookup */
3032 if (!IsMenu( hTrackMenu )) return;
3034 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3036 if( vkey && vkey != VK_SPACE )
3038 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, vkey, (wParam & HTSYSMENU) );
3039 if( uItem >= (UINT)(-2) )
3041 if( uItem == (UINT)(-1) ) MessageBeep(0);
3042 hTrackMenu = 0;
3046 if( hTrackMenu )
3048 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3050 if( uItem == NO_SELECTED_ITEM )
3051 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3052 else if( vkey )
3053 PostMessageA( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3055 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3057 MENU_ExitTracking( hwnd );
3061 /**********************************************************************
3062 * TrackPopupMenu (USER32.@)
3064 * Like the win32 API, the function return the command ID only if the
3065 * flag TPM_RETURNCMD is on.
3068 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3069 INT nReserved, HWND hWnd, const RECT *lpRect )
3071 BOOL ret = FALSE;
3073 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3075 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3076 if (!(wFlags & TPM_NONOTIFY))
3077 SendMessageA( hWnd, WM_INITMENUPOPUP, hMenu, 0);
3079 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3080 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3081 MENU_ExitTracking(hWnd);
3083 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3084 ret = 1;
3086 return ret;
3089 /**********************************************************************
3090 * TrackPopupMenuEx (USER32.@)
3092 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3093 HWND hWnd, LPTPMPARAMS lpTpm )
3095 FIXME("not fully implemented\n" );
3096 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3097 lpTpm ? &lpTpm->rcExclude : NULL );
3100 /***********************************************************************
3101 * PopupMenuWndProc
3103 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3105 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3107 TRACE("hwnd=0x%04x msg=0x%04x wp=0x%04x lp=0x%08lx\n",
3108 hwnd, message, wParam, lParam);
3110 switch(message)
3112 case WM_CREATE:
3114 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3115 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3116 return 0;
3119 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3120 return MA_NOACTIVATE;
3122 case WM_PAINT:
3124 PAINTSTRUCT ps;
3125 BeginPaint( hwnd, &ps );
3126 MENU_DrawPopupMenu( hwnd, ps.hdc,
3127 (HMENU)GetWindowLongA( hwnd, 0 ) );
3128 EndPaint( hwnd, &ps );
3129 return 0;
3131 case WM_ERASEBKGND:
3132 return 1;
3134 case WM_DESTROY:
3135 /* zero out global pointer in case resident popup window was destroyed. */
3136 if (hwnd == top_popup) top_popup = 0;
3137 break;
3139 case WM_SHOWWINDOW:
3141 if( wParam )
3143 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3145 else
3146 SetWindowLongW( hwnd, 0, 0 );
3147 break;
3149 case MM_SETMENUHANDLE:
3150 SetWindowLongW( hwnd, 0, wParam );
3151 break;
3153 case MM_GETMENUHANDLE:
3154 return GetWindowLongW( hwnd, 0 );
3156 default:
3157 return DefWindowProcW( hwnd, message, wParam, lParam );
3159 return 0;
3163 /***********************************************************************
3164 * MENU_GetMenuBarHeight
3166 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3168 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3169 INT orgX, INT orgY )
3171 HDC hdc;
3172 RECT rectBar;
3173 LPPOPUPMENU lppop;
3175 TRACE("HWND 0x%x, width %d, at (%d, %d).\n",
3176 hwnd, menubarWidth, orgX, orgY );
3178 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3180 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3181 SelectObject( hdc, hMenuFont);
3182 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3183 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3184 ReleaseDC( hwnd, hdc );
3185 return lppop->Height;
3189 /*******************************************************************
3190 * ChangeMenu (USER.153)
3192 BOOL16 WINAPI ChangeMenu16( HMENU16 hMenu, UINT16 pos, SEGPTR data,
3193 UINT16 id, UINT16 flags )
3195 TRACE("menu=%04x pos=%d data=%08lx id=%04x flags=%04x\n",
3196 hMenu, pos, (DWORD)data, id, flags );
3197 if (flags & MF_APPEND) return AppendMenu16( hMenu, flags & ~MF_APPEND,
3198 id, data );
3200 /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
3201 /* for MF_DELETE. We should check the parameters for all others */
3202 /* MF_* actions also (anybody got a doc on ChangeMenu?). */
3204 if (flags & MF_DELETE) return DeleteMenu16(hMenu, pos, flags & ~MF_DELETE);
3205 if (flags & MF_CHANGE) return ModifyMenu16(hMenu, pos, flags & ~MF_CHANGE,
3206 id, data );
3207 if (flags & MF_REMOVE) return RemoveMenu16(hMenu,
3208 flags & MF_BYPOSITION ? pos : id,
3209 flags & ~MF_REMOVE );
3210 /* Default: MF_INSERT */
3211 return InsertMenu16( hMenu, pos, flags, id, data );
3215 /*******************************************************************
3216 * ChangeMenuA (USER32.@)
3218 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3219 UINT id, UINT flags )
3221 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3222 hMenu, pos, (DWORD)data, id, flags );
3223 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3224 id, data );
3225 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3226 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3227 id, data );
3228 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3229 flags & MF_BYPOSITION ? pos : id,
3230 flags & ~MF_REMOVE );
3231 /* Default: MF_INSERT */
3232 return InsertMenuA( hMenu, pos, flags, id, data );
3236 /*******************************************************************
3237 * ChangeMenuW (USER32.@)
3239 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3240 UINT id, UINT flags )
3242 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3243 hMenu, pos, (DWORD)data, id, flags );
3244 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3245 id, data );
3246 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3247 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3248 id, data );
3249 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3250 flags & MF_BYPOSITION ? pos : id,
3251 flags & ~MF_REMOVE );
3252 /* Default: MF_INSERT */
3253 return InsertMenuW( hMenu, pos, flags, id, data );
3257 /*******************************************************************
3258 * CheckMenuItem (USER.154)
3260 BOOL16 WINAPI CheckMenuItem16( HMENU16 hMenu, UINT16 id, UINT16 flags )
3262 return (BOOL16)CheckMenuItem( hMenu, id, flags );
3266 /*******************************************************************
3267 * CheckMenuItem (USER32.@)
3269 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3271 MENUITEM *item;
3272 DWORD ret;
3274 TRACE("menu=%04x id=%04x flags=%04x\n", hMenu, id, flags );
3275 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3276 ret = item->fState & MF_CHECKED;
3277 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3278 else item->fState &= ~MF_CHECKED;
3279 return ret;
3283 /**********************************************************************
3284 * EnableMenuItem (USER.155)
3286 UINT16 WINAPI EnableMenuItem16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3288 return EnableMenuItem( hMenu, wItemID, wFlags );
3292 /**********************************************************************
3293 * EnableMenuItem (USER32.@)
3295 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3297 UINT oldflags;
3298 MENUITEM *item;
3299 POPUPMENU *menu;
3301 TRACE("(%04x, %04X, %04X) !\n",
3302 hMenu, wItemID, wFlags);
3304 /* Get the Popupmenu to access the owner menu */
3305 if (!(menu = MENU_GetMenu(hMenu)))
3306 return (UINT)-1;
3308 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3309 return (UINT)-1;
3311 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3312 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3314 /* In win95 if the close item in the system menu change update the close button */
3315 if (TWEAK_WineLook == WIN95_LOOK)
3316 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3318 if (menu->hSysMenuOwner != 0)
3320 POPUPMENU* parentMenu;
3322 /* Get the parent menu to access*/
3323 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3324 return (UINT)-1;
3326 /* Refresh the frame to reflect the change*/
3327 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3328 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3332 return oldflags;
3336 /*******************************************************************
3337 * GetMenuString (USER.161)
3339 INT16 WINAPI GetMenuString16( HMENU16 hMenu, UINT16 wItemID,
3340 LPSTR str, INT16 nMaxSiz, UINT16 wFlags )
3342 return GetMenuStringA( hMenu, wItemID, str, nMaxSiz, wFlags );
3346 /*******************************************************************
3347 * GetMenuStringA (USER32.@)
3349 INT WINAPI GetMenuStringA(
3350 HMENU hMenu, /* [in] menuhandle */
3351 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3352 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3353 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3354 UINT wFlags /* [in] MF_ flags */
3356 MENUITEM *item;
3358 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3359 hMenu, wItemID, str, nMaxSiz, wFlags );
3360 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3361 if (!IS_STRING_ITEM(item->fType)) return 0;
3362 if (!str || !nMaxSiz) return strlenW(item->text);
3363 str[0] = '\0';
3364 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3365 str[nMaxSiz-1] = 0;
3366 TRACE("returning '%s'\n", str );
3367 return strlen(str);
3371 /*******************************************************************
3372 * GetMenuStringW (USER32.@)
3374 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3375 LPWSTR str, INT nMaxSiz, UINT wFlags )
3377 MENUITEM *item;
3379 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3380 hMenu, wItemID, str, nMaxSiz, wFlags );
3381 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3382 if (!IS_STRING_ITEM(item->fType)) return 0;
3383 if (!str || !nMaxSiz) return strlenW(item->text);
3384 str[0] = '\0';
3385 lstrcpynW( str, item->text, nMaxSiz );
3386 return strlenW(str);
3390 /**********************************************************************
3391 * HiliteMenuItem (USER32.@)
3393 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3394 UINT wHilite )
3396 LPPOPUPMENU menu;
3397 TRACE("(%04x, %04x, %04x, %04x);\n",
3398 hWnd, hMenu, wItemID, wHilite);
3399 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3400 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3401 if (menu->FocusedItem == wItemID) return TRUE;
3402 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3403 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3404 return TRUE;
3408 /**********************************************************************
3409 * GetMenuState (USER.250)
3411 UINT16 WINAPI GetMenuState16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3413 return GetMenuState( hMenu, wItemID, wFlags );
3417 /**********************************************************************
3418 * GetMenuState (USER32.@)
3420 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3422 MENUITEM *item;
3423 TRACE("(menu=%04x, id=%04x, flags=%04x);\n",
3424 hMenu, wItemID, wFlags);
3425 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3426 debug_print_menuitem (" item: ", item, "");
3427 if (item->fType & MF_POPUP)
3429 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3430 if (!menu) return -1;
3431 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3433 else
3435 /* We used to (from way back then) mask the result to 0xff. */
3436 /* I don't know why and it seems wrong as the documented */
3437 /* return flag MF_SEPARATOR is outside that mask. */
3438 return (item->fType | item->fState);
3443 /**********************************************************************
3444 * GetMenuItemCount (USER.263)
3446 INT16 WINAPI GetMenuItemCount16( HMENU16 hMenu )
3448 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3449 if (!menu) return -1;
3450 TRACE("(%04x) returning %d\n",
3451 hMenu, menu->nItems );
3452 return menu->nItems;
3456 /**********************************************************************
3457 * GetMenuItemCount (USER32.@)
3459 INT WINAPI GetMenuItemCount( HMENU hMenu )
3461 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3462 if (!menu) return -1;
3463 TRACE("(%04x) returning %d\n",
3464 hMenu, menu->nItems );
3465 return menu->nItems;
3468 /**********************************************************************
3469 * GetMenuItemID (USER.264)
3471 UINT16 WINAPI GetMenuItemID16( HMENU16 hMenu, INT16 nPos )
3473 return (UINT16) GetMenuItemID (hMenu, nPos);
3476 /**********************************************************************
3477 * GetMenuItemID (USER32.@)
3479 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3481 MENUITEM * lpmi;
3483 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3484 if (lpmi->fType & MF_POPUP) return -1;
3485 return lpmi->wID;
3489 /*******************************************************************
3490 * InsertMenu (USER.410)
3492 BOOL16 WINAPI InsertMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3493 UINT16 id, SEGPTR data )
3495 UINT pos32 = (UINT)pos;
3496 if ((pos == (UINT16)-1) && (flags & MF_BYPOSITION)) pos32 = (UINT)-1;
3497 if (IS_STRING_ITEM(flags) && data)
3498 return InsertMenuA( hMenu, pos32, flags, id, MapSL(data) );
3499 return InsertMenuA( hMenu, pos32, flags, id, (LPSTR)data );
3503 /*******************************************************************
3504 * InsertMenuW (USER32.@)
3506 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3507 UINT id, LPCWSTR str )
3509 MENUITEM *item;
3511 if (IS_STRING_ITEM(flags) && str)
3512 TRACE("hMenu %04x, pos %d, flags %08x, "
3513 "id %04x, str %s\n",
3514 hMenu, pos, flags, id, debugstr_w(str) );
3515 else TRACE("hMenu %04x, pos %d, flags %08x, "
3516 "id %04x, str %08lx (not a string)\n",
3517 hMenu, pos, flags, id, (DWORD)str );
3519 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3521 if (!(MENU_SetItemData( item, flags, id, str )))
3523 RemoveMenu( hMenu, pos, flags );
3524 return FALSE;
3527 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3528 (MENU_GetMenu((HMENU16)id))->wFlags |= MF_POPUP;
3530 item->hCheckBit = item->hUnCheckBit = 0;
3531 return TRUE;
3535 /*******************************************************************
3536 * InsertMenuA (USER32.@)
3538 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3539 UINT id, LPCSTR str )
3541 BOOL ret = FALSE;
3543 if (IS_STRING_ITEM(flags) && str)
3545 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3546 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3547 if (newstr)
3549 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3550 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3551 HeapFree( GetProcessHeap(), 0, newstr );
3553 return ret;
3555 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3559 /*******************************************************************
3560 * AppendMenu (USER.411)
3562 BOOL16 WINAPI AppendMenu16(HMENU16 hMenu, UINT16 flags, UINT16 id, SEGPTR data)
3564 return InsertMenu16( hMenu, -1, flags | MF_BYPOSITION, id, data );
3568 /*******************************************************************
3569 * AppendMenuA (USER32.@)
3571 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3572 UINT id, LPCSTR data )
3574 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3578 /*******************************************************************
3579 * AppendMenuW (USER32.@)
3581 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3582 UINT id, LPCWSTR data )
3584 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3588 /**********************************************************************
3589 * RemoveMenu (USER.412)
3591 BOOL16 WINAPI RemoveMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3593 return RemoveMenu( hMenu, nPos, wFlags );
3597 /**********************************************************************
3598 * RemoveMenu (USER32.@)
3600 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3602 LPPOPUPMENU menu;
3603 MENUITEM *item;
3605 TRACE("(menu=%04x pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3606 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3607 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3609 /* Remove item */
3611 MENU_FreeItemData( item );
3613 if (--menu->nItems == 0)
3615 HeapFree( GetProcessHeap(), 0, menu->items );
3616 menu->items = NULL;
3618 else
3620 while(nPos < menu->nItems)
3622 *item = *(item+1);
3623 item++;
3624 nPos++;
3626 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3627 menu->nItems * sizeof(MENUITEM) );
3629 return TRUE;
3633 /**********************************************************************
3634 * DeleteMenu (USER.413)
3636 BOOL16 WINAPI DeleteMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3638 return DeleteMenu( hMenu, nPos, wFlags );
3642 /**********************************************************************
3643 * DeleteMenu (USER32.@)
3645 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3647 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3648 if (!item) return FALSE;
3649 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3650 /* nPos is now the position of the item */
3651 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3652 return TRUE;
3656 /*******************************************************************
3657 * ModifyMenu (USER.414)
3659 BOOL16 WINAPI ModifyMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3660 UINT16 id, SEGPTR data )
3662 if (IS_STRING_ITEM(flags))
3663 return ModifyMenuA( hMenu, pos, flags, id, MapSL(data) );
3664 return ModifyMenuA( hMenu, pos, flags, id, (LPSTR)data );
3668 /*******************************************************************
3669 * ModifyMenuW (USER32.@)
3671 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3672 UINT id, LPCWSTR str )
3674 MENUITEM *item;
3676 if (IS_STRING_ITEM(flags))
3678 TRACE("%04x %d %04x %04x %s\n",
3679 hMenu, pos, flags, id, debugstr_w(str) );
3680 if (!str) return FALSE;
3682 else
3684 TRACE("%04x %d %04x %04x %08lx\n",
3685 hMenu, pos, flags, id, (DWORD)str );
3688 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3689 return MENU_SetItemData( item, flags, id, str );
3693 /*******************************************************************
3694 * ModifyMenuA (USER32.@)
3696 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3697 UINT id, LPCSTR str )
3699 BOOL ret = FALSE;
3701 if (IS_STRING_ITEM(flags) && str)
3703 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3704 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3705 if (newstr)
3707 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3708 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3709 HeapFree( GetProcessHeap(), 0, newstr );
3711 return ret;
3713 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3717 /**********************************************************************
3718 * CreatePopupMenu (USER.415)
3720 HMENU16 WINAPI CreatePopupMenu16(void)
3722 return CreatePopupMenu();
3726 /**********************************************************************
3727 * CreatePopupMenu (USER32.@)
3729 HMENU WINAPI CreatePopupMenu(void)
3731 HMENU hmenu;
3732 POPUPMENU *menu;
3734 if (!(hmenu = CreateMenu())) return 0;
3735 menu = MENU_GetMenu( hmenu );
3736 menu->wFlags |= MF_POPUP;
3737 menu->bTimeToHide = FALSE;
3738 return hmenu;
3742 /**********************************************************************
3743 * GetMenuCheckMarkDimensions (USER.417)
3744 * GetMenuCheckMarkDimensions (USER32.@)
3746 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3748 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3752 /**********************************************************************
3753 * SetMenuItemBitmaps (USER.418)
3755 BOOL16 WINAPI SetMenuItemBitmaps16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags,
3756 HBITMAP16 hNewUnCheck, HBITMAP16 hNewCheck)
3758 return SetMenuItemBitmaps( hMenu, nPos, wFlags, hNewUnCheck, hNewCheck );
3762 /**********************************************************************
3763 * SetMenuItemBitmaps (USER32.@)
3765 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3766 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3768 MENUITEM *item;
3769 TRACE("(%04x, %04x, %04x, %04x, %04x)\n",
3770 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3771 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3773 if (!hNewCheck && !hNewUnCheck)
3775 item->fState &= ~MF_USECHECKBITMAPS;
3777 else /* Install new bitmaps */
3779 item->hCheckBit = hNewCheck;
3780 item->hUnCheckBit = hNewUnCheck;
3781 item->fState |= MF_USECHECKBITMAPS;
3783 return TRUE;
3787 /**********************************************************************
3788 * CreateMenu (USER.151)
3790 HMENU16 WINAPI CreateMenu16(void)
3792 return CreateMenu();
3796 /**********************************************************************
3797 * CreateMenu (USER32.@)
3799 HMENU WINAPI CreateMenu(void)
3801 HMENU hMenu;
3802 LPPOPUPMENU menu;
3803 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3804 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3806 ZeroMemory(menu, sizeof(POPUPMENU));
3807 menu->wMagic = MENU_MAGIC;
3808 menu->FocusedItem = NO_SELECTED_ITEM;
3809 menu->bTimeToHide = FALSE;
3811 TRACE("return %04x\n", hMenu );
3813 return hMenu;
3817 /**********************************************************************
3818 * DestroyMenu (USER.152)
3820 BOOL16 WINAPI DestroyMenu16( HMENU16 hMenu )
3822 return DestroyMenu( hMenu );
3826 /**********************************************************************
3827 * DestroyMenu (USER32.@)
3829 BOOL WINAPI DestroyMenu( HMENU hMenu )
3831 TRACE("(%04x)\n", hMenu);
3833 /* Silently ignore attempts to destroy default system popup */
3835 if (hMenu && hMenu != MENU_DefSysPopup)
3837 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3839 if (!lppop) return FALSE;
3841 lppop->wMagic = 0; /* Mark it as destroyed */
3843 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3845 DestroyWindow( lppop->hWnd );
3846 lppop->hWnd = 0;
3849 if (lppop->items) /* recursively destroy submenus */
3851 int i;
3852 MENUITEM *item = lppop->items;
3853 for (i = lppop->nItems; i > 0; i--, item++)
3855 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3856 MENU_FreeItemData( item );
3858 HeapFree( GetProcessHeap(), 0, lppop->items );
3860 USER_HEAP_FREE( hMenu );
3862 return (hMenu != MENU_DefSysPopup);
3866 /**********************************************************************
3867 * GetSystemMenu (USER32.@)
3869 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3871 WND *wndPtr = WIN_FindWndPtr( hWnd );
3872 HMENU retvalue = 0;
3874 if (wndPtr)
3876 if( wndPtr->hSysMenu )
3878 if( bRevert )
3880 DestroyMenu(wndPtr->hSysMenu);
3881 wndPtr->hSysMenu = 0;
3883 else
3885 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3886 if( menu )
3888 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3889 menu->items[0].hSubMenu = MENU_CopySysPopup();
3891 else
3893 WARN("Current sys-menu (%04x) of wnd %04x is broken\n",
3894 wndPtr->hSysMenu, hWnd);
3895 wndPtr->hSysMenu = 0;
3900 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3901 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3903 if( wndPtr->hSysMenu )
3905 POPUPMENU *menu;
3906 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3908 /* Store the dummy sysmenu handle to facilitate the refresh */
3909 /* of the close button if the SC_CLOSE item change */
3910 menu = MENU_GetMenu(retvalue);
3911 if ( menu )
3912 menu->hSysMenuOwner = wndPtr->hSysMenu;
3914 WIN_ReleaseWndPtr(wndPtr);
3916 return bRevert ? 0 : retvalue;
3920 /*******************************************************************
3921 * SetSystemMenu (USER32.@)
3923 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3925 WND *wndPtr = WIN_FindWndPtr(hwnd);
3927 if (wndPtr)
3929 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3930 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3931 WIN_ReleaseWndPtr(wndPtr);
3932 return TRUE;
3934 return FALSE;
3938 /**********************************************************************
3939 * GetMenu (USER32.@)
3941 HMENU WINAPI GetMenu( HWND hWnd )
3943 HMENU retvalue = (HMENU)GetWindowLongA( hWnd, GWL_ID );
3944 TRACE("for %04x returning %04x\n", hWnd, retvalue);
3945 return retvalue;
3949 /**********************************************************************
3950 * SetMenu (USER32.@)
3952 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3954 TRACE("(%04x, %04x);\n", hWnd, hMenu);
3956 if (hMenu && !IsMenu(hMenu))
3958 WARN("hMenu %x is not a menu handle\n", hMenu);
3959 return FALSE;
3961 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3963 hWnd = WIN_GetFullHandle( hWnd );
3964 if (GetCapture() == hWnd) ReleaseCapture();
3966 if (hMenu != 0)
3968 LPPOPUPMENU lpmenu;
3970 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3972 lpmenu->hWnd = hWnd;
3973 lpmenu->Height = 0; /* Make sure we recalculate the size */
3975 SetWindowLongA( hWnd, GWL_ID, hMenu );
3977 if (IsWindowVisible(hWnd))
3978 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3979 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3980 return TRUE;
3985 /**********************************************************************
3986 * GetSubMenu (USER.159)
3988 HMENU16 WINAPI GetSubMenu16( HMENU16 hMenu, INT16 nPos )
3990 return GetSubMenu( hMenu, nPos );
3994 /**********************************************************************
3995 * GetSubMenu (USER32.@)
3997 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3999 MENUITEM * lpmi;
4001 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
4002 if (!(lpmi->fType & MF_POPUP)) return 0;
4003 return lpmi->hSubMenu;
4007 /**********************************************************************
4008 * DrawMenuBar (USER32.@)
4010 BOOL WINAPI DrawMenuBar( HWND hWnd )
4012 LPPOPUPMENU lppop;
4013 HMENU hMenu = GetMenu(hWnd);
4015 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
4016 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4018 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4019 lppop->hwndOwner = hWnd;
4020 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4021 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4022 return TRUE;
4025 /***********************************************************************
4026 * DrawMenuBarTemp (USER32.@)
4028 * UNDOCUMENTED !!
4030 * called by W98SE desk.cpl Control Panel Applet
4032 * Not 100% sure about the param names, but close.
4034 DWORD WINAPI DrawMenuBarTemp(HWND someHWND, HDC someHDC, LPRECT someRECT, HMENU someHMENU, HFONT someFONT)
4036 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, 0x%08x): stub\n", someHWND, someHDC, someRECT, someHMENU, someFONT);
4037 return 0;
4040 /***********************************************************************
4041 * EndMenu (USER.187)
4042 * EndMenu (USER32.@)
4044 void WINAPI EndMenu(void)
4046 /* if we are in the menu code, and it is active */
4047 if (!fEndMenu && top_popup)
4049 /* terminate the menu handling code */
4050 fEndMenu = TRUE;
4052 /* needs to be posted to wakeup the internal menu handler */
4053 /* which will now terminate the menu, in the event that */
4054 /* the main window was minimized, or lost focus, so we */
4055 /* don't end up with an orphaned menu */
4056 PostMessageA( top_popup, WM_CANCELMODE, 0, 0);
4061 /***********************************************************************
4062 * LookupMenuHandle (USER.217)
4064 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4066 HMENU hmenu32 = hmenu;
4067 UINT id32 = id;
4068 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4069 else return hmenu32;
4073 /**********************************************************************
4074 * LoadMenu (USER.150)
4076 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4078 HRSRC16 hRsrc;
4079 HGLOBAL16 handle;
4080 HMENU16 hMenu;
4082 TRACE("(%04x,%s)\n", instance, debugres_a(name) );
4084 if (HIWORD(name))
4086 if (name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4089 if (!name) return 0;
4091 /* check for Win32 module */
4092 if (HIWORD(instance)) return LoadMenuA( instance, name );
4093 instance = GetExePtr( instance );
4095 if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
4096 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4097 hMenu = LoadMenuIndirect16(LockResource16(handle));
4098 FreeResource16( handle );
4099 return hMenu;
4103 /*****************************************************************
4104 * LoadMenuA (USER32.@)
4106 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4108 HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
4109 if (!hrsrc) return 0;
4110 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4114 /*****************************************************************
4115 * LoadMenuW (USER32.@)
4117 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4119 HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
4120 if (!hrsrc) return 0;
4121 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4125 /**********************************************************************
4126 * LoadMenuIndirect (USER.220)
4128 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4130 HMENU16 hMenu;
4131 WORD version, offset;
4132 LPCSTR p = (LPCSTR)template;
4134 TRACE("(%p)\n", template );
4135 version = GET_WORD(p);
4136 p += sizeof(WORD);
4137 if (version)
4139 WARN("version must be 0 for Win16\n" );
4140 return 0;
4142 offset = GET_WORD(p);
4143 p += sizeof(WORD) + offset;
4144 if (!(hMenu = CreateMenu())) return 0;
4145 if (!MENU_ParseResource( p, hMenu, FALSE ))
4147 DestroyMenu( hMenu );
4148 return 0;
4150 return hMenu;
4154 /**********************************************************************
4155 * LoadMenuIndirectA (USER32.@)
4157 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4159 HMENU16 hMenu;
4160 WORD version, offset;
4161 LPCSTR p = (LPCSTR)template;
4163 TRACE("%p\n", template );
4164 version = GET_WORD(p);
4165 p += sizeof(WORD);
4166 switch (version)
4168 case 0:
4169 offset = GET_WORD(p);
4170 p += sizeof(WORD) + offset;
4171 if (!(hMenu = CreateMenu())) return 0;
4172 if (!MENU_ParseResource( p, hMenu, TRUE ))
4174 DestroyMenu( hMenu );
4175 return 0;
4177 return hMenu;
4178 case 1:
4179 offset = GET_WORD(p);
4180 p += sizeof(WORD) + offset;
4181 if (!(hMenu = CreateMenu())) return 0;
4182 if (!MENUEX_ParseResource( p, hMenu))
4184 DestroyMenu( hMenu );
4185 return 0;
4187 return hMenu;
4188 default:
4189 ERR("version %d not supported.\n", version);
4190 return 0;
4195 /**********************************************************************
4196 * LoadMenuIndirectW (USER32.@)
4198 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4200 /* FIXME: is there anything different between A and W? */
4201 return LoadMenuIndirectA( template );
4205 /**********************************************************************
4206 * IsMenu (USER.358)
4208 BOOL16 WINAPI IsMenu16( HMENU16 hmenu )
4210 return IsMenu( hmenu );
4214 /**********************************************************************
4215 * IsMenu (USER32.@)
4217 BOOL WINAPI IsMenu(HMENU hmenu)
4219 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4220 return menu != NULL;
4223 /**********************************************************************
4224 * GetMenuItemInfo_common
4227 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4228 LPMENUITEMINFOW lpmii, BOOL unicode)
4230 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4232 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4234 if (!menu)
4235 return FALSE;
4237 if (lpmii->fMask & MIIM_TYPE) {
4238 lpmii->fType = menu->fType;
4239 switch (MENU_ITEM_TYPE(menu->fType)) {
4240 case MF_STRING:
4241 break; /* will be done below */
4242 case MF_OWNERDRAW:
4243 case MF_BITMAP:
4244 lpmii->dwTypeData = menu->text;
4245 /* fall through */
4246 default:
4247 lpmii->cch = 0;
4251 /* copy the text string */
4252 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4253 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4255 int len;
4256 if (unicode)
4258 len = strlenW(menu->text);
4259 if(lpmii->dwTypeData && lpmii->cch)
4260 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4262 else
4264 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4265 if(lpmii->dwTypeData && lpmii->cch)
4266 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4267 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4268 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4270 /* if we've copied a substring we return its length */
4271 if(lpmii->dwTypeData && lpmii->cch)
4273 if (lpmii->cch <= len) lpmii->cch--;
4275 else /* return length of string */
4276 lpmii->cch = len;
4279 if (lpmii->fMask & MIIM_FTYPE)
4280 lpmii->fType = menu->fType;
4282 if (lpmii->fMask & MIIM_BITMAP)
4283 lpmii->hbmpItem = menu->hbmpItem;
4285 if (lpmii->fMask & MIIM_STATE)
4286 lpmii->fState = menu->fState;
4288 if (lpmii->fMask & MIIM_ID)
4289 lpmii->wID = menu->wID;
4291 if (lpmii->fMask & MIIM_SUBMENU)
4292 lpmii->hSubMenu = menu->hSubMenu;
4294 if (lpmii->fMask & MIIM_CHECKMARKS) {
4295 lpmii->hbmpChecked = menu->hCheckBit;
4296 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4298 if (lpmii->fMask & MIIM_DATA)
4299 lpmii->dwItemData = menu->dwItemData;
4301 return TRUE;
4304 /**********************************************************************
4305 * GetMenuItemInfoA (USER32.@)
4307 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4308 LPMENUITEMINFOA lpmii)
4310 return GetMenuItemInfo_common (hmenu, item, bypos,
4311 (LPMENUITEMINFOW)lpmii, FALSE);
4314 /**********************************************************************
4315 * GetMenuItemInfoW (USER32.@)
4317 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4318 LPMENUITEMINFOW lpmii)
4320 return GetMenuItemInfo_common (hmenu, item, bypos,
4321 lpmii, TRUE);
4325 /* set a menu item text from a ASCII or Unicode string */
4326 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4328 if (!text)
4330 menu->text = NULL;
4331 menu->fType |= MF_SEPARATOR;
4333 else if (unicode)
4335 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4336 strcpyW( menu->text, text );
4338 else
4340 LPCSTR str = (LPCSTR)text;
4341 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4342 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4343 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4348 /**********************************************************************
4349 * SetMenuItemInfo_common
4352 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4353 const MENUITEMINFOW *lpmii,
4354 BOOL unicode)
4356 if (!menu) return FALSE;
4358 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4360 if (lpmii->fMask & MIIM_TYPE ) {
4361 /* Get rid of old string. */
4362 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4363 HeapFree(GetProcessHeap(), 0, menu->text);
4364 menu->text = NULL;
4367 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4368 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4369 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4371 menu->text = lpmii->dwTypeData;
4373 if (IS_STRING_ITEM(menu->fType))
4374 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4377 if (lpmii->fMask & MIIM_FTYPE ) {
4378 /* free the string when the type is changing */
4379 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4380 HeapFree(GetProcessHeap(), 0, menu->text);
4381 menu->text = NULL;
4383 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4384 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4385 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4386 menu->fType |= MF_SEPARATOR;
4389 if (lpmii->fMask & MIIM_STRING ) {
4390 /* free the string when used */
4391 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4392 HeapFree(GetProcessHeap(), 0, menu->text);
4393 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4397 if (lpmii->fMask & MIIM_STATE)
4399 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4400 menu->fState = lpmii->fState;
4403 if (lpmii->fMask & MIIM_ID)
4404 menu->wID = lpmii->wID;
4406 if (lpmii->fMask & MIIM_SUBMENU) {
4407 menu->hSubMenu = lpmii->hSubMenu;
4408 if (menu->hSubMenu) {
4409 POPUPMENU *subMenu = MENU_GetMenu((UINT16)menu->hSubMenu);
4410 if (subMenu) {
4411 subMenu->wFlags |= MF_POPUP;
4412 menu->fType |= MF_POPUP;
4414 else
4415 /* FIXME: Return an error ? */
4416 menu->fType &= ~MF_POPUP;
4418 else
4419 menu->fType &= ~MF_POPUP;
4422 if (lpmii->fMask & MIIM_CHECKMARKS)
4424 if (lpmii->fType & MFT_RADIOCHECK)
4425 menu->fType |= MFT_RADIOCHECK;
4427 menu->hCheckBit = lpmii->hbmpChecked;
4428 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4430 if (lpmii->fMask & MIIM_DATA)
4431 menu->dwItemData = lpmii->dwItemData;
4433 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4434 return TRUE;
4437 /**********************************************************************
4438 * SetMenuItemInfoA (USER32.@)
4440 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4441 const MENUITEMINFOA *lpmii)
4443 if ((lpmii->fType & (MF_HILITE|MF_POPUP)) || (lpmii->fState)) {
4444 /* QuickTime does pass invalid data into SetMenuItemInfo.
4445 * do some of the checks Windows does.
4447 WARN("Bad masks for type (0x%08x) or state (0x%08x)\n",
4448 lpmii->fType,lpmii->fState );
4449 return FALSE;
4452 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4453 (const MENUITEMINFOW *)lpmii, FALSE);
4456 /**********************************************************************
4457 * SetMenuItemInfoW (USER32.@)
4459 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4460 const MENUITEMINFOW *lpmii)
4462 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4463 lpmii, TRUE);
4466 /**********************************************************************
4467 * SetMenuDefaultItem (USER32.@)
4470 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4472 UINT i;
4473 POPUPMENU *menu;
4474 MENUITEM *item;
4476 TRACE("(0x%x,%d,%d)\n", hmenu, uItem, bypos);
4478 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4480 /* reset all default-item flags */
4481 item = menu->items;
4482 for (i = 0; i < menu->nItems; i++, item++)
4484 item->fState &= ~MFS_DEFAULT;
4487 /* no default item */
4488 if ( -1 == uItem)
4490 return TRUE;
4493 item = menu->items;
4494 if ( bypos )
4496 if ( uItem >= menu->nItems ) return FALSE;
4497 item[uItem].fState |= MFS_DEFAULT;
4498 return TRUE;
4500 else
4502 for (i = 0; i < menu->nItems; i++, item++)
4504 if (item->wID == uItem)
4506 item->fState |= MFS_DEFAULT;
4507 return TRUE;
4512 return FALSE;
4515 /**********************************************************************
4516 * GetMenuDefaultItem (USER32.@)
4518 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4520 POPUPMENU *menu;
4521 MENUITEM * item;
4522 UINT i = 0;
4524 TRACE("(0x%x,%d,%d)\n", hmenu, bypos, flags);
4526 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4528 /* find default item */
4529 item = menu->items;
4531 /* empty menu */
4532 if (! item) return -1;
4534 while ( !( item->fState & MFS_DEFAULT ) )
4536 i++; item++;
4537 if (i >= menu->nItems ) return -1;
4540 /* default: don't return disabled items */
4541 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4543 /* search rekursiv when needed */
4544 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4546 UINT ret;
4547 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4548 if ( -1 != ret ) return ret;
4550 /* when item not found in submenu, return the popup item */
4552 return ( bypos ) ? i : item->wID;
4556 /*******************************************************************
4557 * InsertMenuItem (USER.441)
4559 * FIXME: untested
4561 BOOL16 WINAPI InsertMenuItem16( HMENU16 hmenu, UINT16 pos, BOOL16 byposition,
4562 const MENUITEMINFO16 *mii )
4564 MENUITEMINFOA miia;
4566 miia.cbSize = sizeof(miia);
4567 miia.fMask = mii->fMask;
4568 miia.dwTypeData = (LPSTR)mii->dwTypeData;
4569 miia.fType = mii->fType;
4570 miia.fState = mii->fState;
4571 miia.wID = mii->wID;
4572 miia.hSubMenu = mii->hSubMenu;
4573 miia.hbmpChecked = mii->hbmpChecked;
4574 miia.hbmpUnchecked = mii->hbmpUnchecked;
4575 miia.dwItemData = mii->dwItemData;
4576 miia.cch = mii->cch;
4577 if (IS_STRING_ITEM(miia.fType))
4578 miia.dwTypeData = MapSL(mii->dwTypeData);
4579 return InsertMenuItemA( hmenu, pos, byposition, &miia );
4583 /**********************************************************************
4584 * InsertMenuItemA (USER32.@)
4586 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4587 const MENUITEMINFOA *lpmii)
4589 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4590 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4594 /**********************************************************************
4595 * InsertMenuItemW (USER32.@)
4597 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4598 const MENUITEMINFOW *lpmii)
4600 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4601 return SetMenuItemInfo_common(item, lpmii, TRUE);
4604 /**********************************************************************
4605 * CheckMenuRadioItem (USER32.@)
4608 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4609 UINT first, UINT last, UINT check,
4610 UINT bypos)
4612 MENUITEM *mifirst, *milast, *micheck;
4613 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4615 TRACE("ox%x: %d-%d, check %d, bypos=%d\n",
4616 hMenu, first, last, check, bypos);
4618 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4619 milast = MENU_FindItem (&mlast, &last, bypos);
4620 micheck = MENU_FindItem (&mcheck, &check, bypos);
4622 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4623 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4624 micheck > milast || micheck < mifirst)
4625 return FALSE;
4627 while (mifirst <= milast)
4629 if (mifirst == micheck)
4631 mifirst->fType |= MFT_RADIOCHECK;
4632 mifirst->fState |= MFS_CHECKED;
4633 } else {
4634 mifirst->fType &= ~MFT_RADIOCHECK;
4635 mifirst->fState &= ~MFS_CHECKED;
4637 mifirst++;
4640 return TRUE;
4643 /**********************************************************************
4644 * CheckMenuRadioItem (USER.666)
4646 BOOL16 WINAPI CheckMenuRadioItem16(HMENU16 hMenu,
4647 UINT16 first, UINT16 last, UINT16 check,
4648 BOOL16 bypos)
4650 return CheckMenuRadioItem (hMenu, first, last, check, bypos);
4653 /**********************************************************************
4654 * GetMenuItemRect (USER32.@)
4656 * ATTENTION: Here, the returned values in rect are the screen
4657 * coordinates of the item just like if the menu was
4658 * always on the upper left side of the application.
4661 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4662 LPRECT rect)
4664 POPUPMENU *itemMenu;
4665 MENUITEM *item;
4666 HWND referenceHwnd;
4668 TRACE("(0x%x,0x%x,%d,%p)\n", hwnd, hMenu, uItem, rect);
4670 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4671 referenceHwnd = hwnd;
4673 if(!hwnd)
4675 itemMenu = MENU_GetMenu(hMenu);
4676 if (itemMenu == NULL)
4677 return FALSE;
4679 if(itemMenu->hWnd == 0)
4680 return FALSE;
4681 referenceHwnd = itemMenu->hWnd;
4684 if ((rect == NULL) || (item == NULL))
4685 return FALSE;
4687 *rect = item->rect;
4689 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4691 return TRUE;
4695 /**********************************************************************
4696 * SetMenuInfo (USER32.@)
4698 * FIXME
4699 * MIM_APPLYTOSUBMENUS
4700 * actually use the items to draw the menu
4702 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4704 POPUPMENU *menu;
4706 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4708 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4711 if (lpmi->fMask & MIM_BACKGROUND)
4712 menu->hbrBack = lpmi->hbrBack;
4714 if (lpmi->fMask & MIM_HELPID)
4715 menu->dwContextHelpID = lpmi->dwContextHelpID;
4717 if (lpmi->fMask & MIM_MAXHEIGHT)
4718 menu->cyMax = lpmi->cyMax;
4720 if (lpmi->fMask & MIM_MENUDATA)
4721 menu->dwMenuData = lpmi->dwMenuData;
4723 if (lpmi->fMask & MIM_STYLE)
4724 menu->dwStyle = lpmi->dwStyle;
4726 return TRUE;
4728 return FALSE;
4731 /**********************************************************************
4732 * GetMenuInfo (USER32.@)
4734 * NOTES
4735 * win98/NT5.0
4738 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4739 { POPUPMENU *menu;
4741 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4743 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4746 if (lpmi->fMask & MIM_BACKGROUND)
4747 lpmi->hbrBack = menu->hbrBack;
4749 if (lpmi->fMask & MIM_HELPID)
4750 lpmi->dwContextHelpID = menu->dwContextHelpID;
4752 if (lpmi->fMask & MIM_MAXHEIGHT)
4753 lpmi->cyMax = menu->cyMax;
4755 if (lpmi->fMask & MIM_MENUDATA)
4756 lpmi->dwMenuData = menu->dwMenuData;
4758 if (lpmi->fMask & MIM_STYLE)
4759 lpmi->dwStyle = menu->dwStyle;
4761 return TRUE;
4763 return FALSE;
4766 /**********************************************************************
4767 * SetMenuContextHelpId (USER.384)
4769 BOOL16 WINAPI SetMenuContextHelpId16( HMENU16 hMenu, DWORD dwContextHelpID)
4771 return SetMenuContextHelpId( hMenu, dwContextHelpID );
4775 /**********************************************************************
4776 * SetMenuContextHelpId (USER32.@)
4778 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4780 LPPOPUPMENU menu;
4782 TRACE("(0x%04x 0x%08lx)\n", hMenu, dwContextHelpID);
4784 if ((menu = MENU_GetMenu(hMenu)))
4786 menu->dwContextHelpID = dwContextHelpID;
4787 return TRUE;
4789 return FALSE;
4792 /**********************************************************************
4793 * GetMenuContextHelpId (USER.385)
4795 DWORD WINAPI GetMenuContextHelpId16( HMENU16 hMenu )
4797 return GetMenuContextHelpId( hMenu );
4800 /**********************************************************************
4801 * GetMenuContextHelpId (USER32.@)
4803 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4805 LPPOPUPMENU menu;
4807 TRACE("(0x%04x)\n", hMenu);
4809 if ((menu = MENU_GetMenu(hMenu)))
4811 return menu->dwContextHelpID;
4813 return 0;
4816 /**********************************************************************
4817 * MenuItemFromPoint (USER32.@)
4819 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4821 FIXME("(0x%04x,0x%04x,(%ld,%ld)):stub\n",
4822 hWnd, hMenu, ptScreen.x, ptScreen.y);
4823 return 0;
4827 /**********************************************************************
4828 * translate_accelerator
4830 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4831 BYTE fVirt, WORD key, WORD cmd )
4833 UINT mesg = 0;
4835 if (wParam != key) return FALSE;
4837 if (message == WM_CHAR)
4839 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4841 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4842 goto found;
4845 else
4847 if(fVirt & FVIRTKEY)
4849 INT mask = 0;
4850 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4851 wParam, 0xff & HIWORD(lParam));
4852 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4853 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4854 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4855 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4856 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4858 else
4860 if (!(lParam & 0x01000000)) /* no special_key */
4862 if ((fVirt & FALT) && (lParam & 0x20000000))
4863 { /* ^^ ALT pressed */
4864 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4865 goto found;
4870 return FALSE;
4872 found:
4873 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4874 mesg = 1;
4875 else if (GetCapture())
4876 mesg = 2;
4877 else if (!IsWindowEnabled(hWnd))
4878 mesg = 3;
4879 else
4881 HMENU hMenu, hSubMenu, hSysMenu;
4882 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4884 hMenu = (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4885 hSysMenu = get_win_sys_menu( hWnd );
4887 /* find menu item and ask application to initialize it */
4888 /* 1. in the system menu */
4889 hSubMenu = hSysMenu;
4890 nPos = cmd;
4891 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4893 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4894 if(hSubMenu != hSysMenu)
4896 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4897 TRACE_(accel)("hSysMenu = %04x, hSubMenu = %04x, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4898 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4900 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4902 else /* 2. in the window's menu */
4904 hSubMenu = hMenu;
4905 nPos = cmd;
4906 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4908 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4909 if(hSubMenu != hMenu)
4911 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4912 TRACE_(accel)("hMenu = %04x, hSubMenu = %04x, nPos = %d\n", hMenu, hSubMenu, nPos);
4913 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4915 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4919 if (uSysStat != (UINT)-1)
4921 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4922 mesg=4;
4923 else
4924 mesg=WM_SYSCOMMAND;
4926 else
4928 if (uStat != (UINT)-1)
4930 if (IsIconic(hWnd))
4931 mesg=5;
4932 else
4934 if (uStat & (MF_DISABLED|MF_GRAYED))
4935 mesg=6;
4936 else
4937 mesg=WM_COMMAND;
4940 else
4941 mesg=WM_COMMAND;
4945 if( mesg==WM_COMMAND )
4947 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4948 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
4950 else if( mesg==WM_SYSCOMMAND )
4952 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4953 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
4955 else
4957 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4958 * #0: unknown (please report!)
4959 * #1: for WM_KEYUP,WM_SYSKEYUP
4960 * #2: mouse is captured
4961 * #3: window is disabled
4962 * #4: it's a disabled system menu option
4963 * #5: it's a menu option, but window is iconic
4964 * #6: it's a menu option, but disabled
4966 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4967 if(mesg==0)
4968 ERR_(accel)(" unknown reason - please report!");
4970 return TRUE;
4973 /**********************************************************************
4974 * TranslateAccelerator (USER32.@)
4975 * TranslateAcceleratorA (USER32.@)
4976 * TranslateAcceleratorW (USER32.@)
4978 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
4980 /* YES, Accel16! */
4981 LPACCEL16 lpAccelTbl;
4982 int i;
4984 if (msg == NULL)
4986 WARN_(accel)("msg null; should hang here to be win compatible\n");
4987 return 0;
4989 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
4991 WARN_(accel)("invalid accel handle=%x\n", hAccel);
4992 return 0;
4994 if ((msg->message != WM_KEYDOWN &&
4995 msg->message != WM_KEYUP &&
4996 msg->message != WM_SYSKEYDOWN &&
4997 msg->message != WM_SYSKEYUP &&
4998 msg->message != WM_CHAR)) return 0;
5000 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5001 "msg->hwnd=%04x, msg->message=%04x, wParam=%08x, lParam=%lx\n",
5002 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5004 i = 0;
5007 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5008 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5009 return 1;
5010 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5011 WARN_(accel)("couldn't translate accelerator key\n");
5012 return 0;