Avoid referencing potentially freed token.
[wine/dcerpc.git] / controls / menu.c
blob8be53a19e836aeaef209939becf84af6576f6197
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 "wownt32.h"
43 #include "wine/server.h"
44 #include "wine/unicode.h"
45 #include "win.h"
46 #include "controls.h"
47 #include "nonclient.h"
48 #include "user.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_PTR 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 (HBRUSH)(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 type = MENU_ITEM_TYPE(flags);
220 DPRINTF( "{ ID=0x%x", mp->wID);
221 if (flags & MF_POPUP)
222 DPRINTF( ", Sub=%p", mp->hSubMenu);
223 if (flags) {
224 int count = 0;
225 DPRINTF( ", Type=");
226 if (type == MFT_STRING)
227 /* Nothing */ ;
228 else if (type == MFT_SEPARATOR)
229 MENUOUT("sep");
230 else if (type == MFT_OWNERDRAW)
231 MENUOUT("own");
232 else if (type == MFT_BITMAP)
233 MENUOUT("bit");
234 else
235 MENUOUT("???");
236 flags -= type;
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=%p", mp->hCheckBit);
265 if (mp->hUnCheckBit)
266 DPRINTF( ", Unc=%p", mp->hUnCheckBit);
268 if (type == 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=%p, 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 %p.\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 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
361 if ((hMenu = CreateMenu()))
363 POPUPMENU *menu = MENU_GetMenu(hMenu);
364 menu->wFlags = MF_SYSMENU;
365 menu->hWnd = WIN_GetFullHandle( hWnd );
366 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
368 if (hPopupMenu == (HMENU)(-1))
369 hPopupMenu = MENU_CopySysPopup();
370 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
372 if (hPopupMenu)
374 InsertMenuA( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
375 (UINT_PTR)hPopupMenu, NULL );
377 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
378 menu->items[0].fState = 0;
379 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
381 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
382 return hMenu;
384 DestroyMenu( hMenu );
386 ERR("failed to load system menu!\n");
387 return 0;
391 /***********************************************************************
392 * MENU_Init
394 * Menus initialisation.
396 BOOL MENU_Init()
398 HBITMAP hBitmap;
399 NONCLIENTMETRICSA ncm;
401 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
402 0x55, 0, 0xAA, 0,
403 0x55, 0, 0xAA, 0,
404 0x55, 0, 0xAA, 0 };
406 /* Load menu bitmaps */
407 hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
408 /* Load system buttons bitmaps */
409 hBmpSysMenu = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_CLOSE));
411 if (hStdMnArrow)
413 BITMAP bm;
414 GetObjectA( hStdMnArrow, sizeof(bm), &bm );
415 arrow_bitmap_width = bm.bmWidth;
416 arrow_bitmap_height = bm.bmHeight;
417 } else
418 return FALSE;
420 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
421 return FALSE;
423 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
424 return FALSE;
426 DeleteObject( hBitmap );
427 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
428 return FALSE;
430 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
431 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0)))
432 return FALSE;
434 if (!(hMenuFont = CreateFontIndirectA( &ncm.lfMenuFont )))
435 return FALSE;
437 ncm.lfMenuFont.lfWeight += 300;
438 if ( ncm.lfMenuFont.lfWeight > 1000)
439 ncm.lfMenuFont.lfWeight = 1000;
441 if (!(hMenuFontBold = CreateFontIndirectA( &ncm.lfMenuFont )))
442 return FALSE;
444 return TRUE;
447 /***********************************************************************
448 * MENU_InitSysMenuPopup
450 * Grey the appropriate items in System menu.
452 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
454 BOOL gray;
456 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
457 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
458 gray = ((style & WS_MAXIMIZE) != 0);
459 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
460 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
461 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
462 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
463 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
464 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
465 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
466 gray = (clsStyle & CS_NOCLOSE) != 0;
468 /* The menu item must keep its state if it's disabled */
469 if(gray)
470 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
474 /******************************************************************************
476 * UINT MENU_GetStartOfNextColumn(
477 * HMENU hMenu )
479 *****************************************************************************/
481 static UINT MENU_GetStartOfNextColumn(
482 HMENU hMenu )
484 POPUPMENU *menu = MENU_GetMenu(hMenu);
485 UINT i;
487 if(!menu)
488 return NO_SELECTED_ITEM;
490 i = menu->FocusedItem + 1;
491 if( i == NO_SELECTED_ITEM )
492 return i;
494 for( ; i < menu->nItems; ++i ) {
495 if (menu->items[i].fType & MF_MENUBARBREAK)
496 return i;
499 return NO_SELECTED_ITEM;
503 /******************************************************************************
505 * UINT MENU_GetStartOfPrevColumn(
506 * HMENU hMenu )
508 *****************************************************************************/
510 static UINT MENU_GetStartOfPrevColumn(
511 HMENU hMenu )
513 POPUPMENU *menu = MENU_GetMenu(hMenu);
514 UINT i;
516 if( !menu )
517 return NO_SELECTED_ITEM;
519 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
520 return NO_SELECTED_ITEM;
522 /* Find the start of the column */
524 for(i = menu->FocusedItem; i != 0 &&
525 !(menu->items[i].fType & MF_MENUBARBREAK);
526 --i); /* empty */
528 if(i == 0)
529 return NO_SELECTED_ITEM;
531 for(--i; i != 0; --i) {
532 if (menu->items[i].fType & MF_MENUBARBREAK)
533 break;
536 TRACE("ret %d.\n", i );
538 return i;
543 /***********************************************************************
544 * MENU_FindItem
546 * Find a menu item. Return a pointer on the item, and modifies *hmenu
547 * in case the item was in a sub-menu.
549 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
551 POPUPMENU *menu;
552 UINT i;
554 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
555 if (wFlags & MF_BYPOSITION)
557 if (*nPos >= menu->nItems) return NULL;
558 return &menu->items[*nPos];
560 else
562 MENUITEM *item = menu->items;
563 for (i = 0; i < menu->nItems; i++, item++)
565 if (item->wID == *nPos)
567 *nPos = i;
568 return item;
570 else if (item->fType & MF_POPUP)
572 HMENU hsubmenu = item->hSubMenu;
573 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
574 if (subitem)
576 *hmenu = hsubmenu;
577 return subitem;
582 return NULL;
585 /***********************************************************************
586 * MENU_FindSubMenu
588 * Find a Sub menu. Return the position of the submenu, and modifies
589 * *hmenu in case it is found in another sub-menu.
590 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
592 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
594 POPUPMENU *menu;
595 UINT i;
596 MENUITEM *item;
597 if (((*hmenu)==(HMENU)0xffff) ||
598 (!(menu = MENU_GetMenu(*hmenu))))
599 return NO_SELECTED_ITEM;
600 item = menu->items;
601 for (i = 0; i < menu->nItems; i++, item++) {
602 if(!(item->fType & MF_POPUP)) continue;
603 if (item->hSubMenu == hSubTarget) {
604 return i;
606 else {
607 HMENU hsubmenu = item->hSubMenu;
608 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
609 if (pos != NO_SELECTED_ITEM) {
610 *hmenu = hsubmenu;
611 return pos;
615 return NO_SELECTED_ITEM;
618 /***********************************************************************
619 * MENU_FreeItemData
621 static void MENU_FreeItemData( MENUITEM* item )
623 /* delete text */
624 if (IS_STRING_ITEM(item->fType) && item->text)
625 HeapFree( GetProcessHeap(), 0, item->text );
628 /***********************************************************************
629 * MENU_FindItemByCoords
631 * Find the item at the specified coordinates (screen coords). Does
632 * not work for child windows and therefore should not be called for
633 * an arbitrary system menu.
635 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
636 POINT pt, UINT *pos )
638 MENUITEM *item;
639 UINT i;
640 RECT wrect;
642 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
643 pt.x -= wrect.left;pt.y -= wrect.top;
644 item = menu->items;
645 for (i = 0; i < menu->nItems; i++, item++)
647 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
648 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
650 if (pos) *pos = i;
651 return item;
654 return NULL;
658 /***********************************************************************
659 * MENU_FindItemByKey
661 * Find the menu item selected by a key press.
662 * Return item id, -1 if none, -2 if we should close the menu.
664 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
665 UINT key, BOOL forceMenuChar )
667 TRACE("\tlooking for '%c' in [%p]\n", (char)key, hmenu );
669 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
671 if (hmenu)
673 POPUPMENU *menu = MENU_GetMenu( hmenu );
674 MENUITEM *item = menu->items;
675 LONG menuchar;
677 if( !forceMenuChar )
679 UINT i;
681 key = toupper(key);
682 for (i = 0; i < menu->nItems; i++, item++)
684 if (IS_STRING_ITEM(item->fType) && item->text)
686 WCHAR *p = item->text - 2;
689 p = strchrW (p + 2, '&');
691 while (p != NULL && p [1] == '&');
692 if (p && (toupper(p[1]) == key)) return i;
696 menuchar = SendMessageA( hwndOwner, WM_MENUCHAR,
697 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
698 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
699 if (HIWORD(menuchar) == 1) return (UINT)(-2);
701 return (UINT)(-1);
705 /***********************************************************************
706 * MENU_GetBitmapItemSize
708 * Get the size of a bitmap item.
710 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
712 BITMAP bm;
713 HBITMAP bmp = (HBITMAP)id;
715 size->cx = size->cy = 0;
717 /* check if there is a magic menu item associated with this item */
718 if (id && IS_MAGIC_ITEM( id ))
720 switch(LOWORD(id))
722 case (INT_PTR)HBMMENU_SYSTEM:
723 if (data)
725 bmp = (HBITMAP)data;
726 break;
728 /* fall through */
729 case (INT_PTR)HBMMENU_MBAR_RESTORE:
730 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
731 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
732 case (INT_PTR)HBMMENU_MBAR_CLOSE:
733 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
734 size->cx = GetSystemMetrics( SM_CXSIZE );
735 size->cy = GetSystemMetrics( SM_CYSIZE );
736 return;
737 case (INT_PTR)HBMMENU_CALLBACK:
738 case (INT_PTR)HBMMENU_POPUP_CLOSE:
739 case (INT_PTR)HBMMENU_POPUP_RESTORE:
740 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
741 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
742 default:
743 FIXME("Magic 0x%08x not implemented\n", id);
744 return;
747 if (GetObjectA(bmp, sizeof(bm), &bm ))
749 size->cx = bm.bmWidth;
750 size->cy = bm.bmHeight;
754 /***********************************************************************
755 * MENU_DrawBitmapItem
757 * Draw a bitmap item.
759 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar )
761 BITMAP bm;
762 DWORD rop;
763 HDC hdcMem;
764 HBITMAP bmp = (HBITMAP)lpitem->text;
765 int w = rect->right - rect->left;
766 int h = rect->bottom - rect->top;
767 int bmp_xoffset = 0;
768 int left, top;
770 /* Check if there is a magic menu item associated with this item */
771 if (lpitem->text && IS_MAGIC_ITEM(lpitem->text))
773 UINT flags = 0;
774 RECT r;
776 switch(LOWORD(lpitem->text))
778 case (INT_PTR)HBMMENU_SYSTEM:
779 if (lpitem->dwItemData)
781 bmp = (HBITMAP)lpitem->dwItemData;
782 if (!GetObjectA( bmp, sizeof(bm), &bm )) return;
784 else
786 bmp = hBmpSysMenu;
787 if (!GetObjectA( bmp, sizeof(bm), &bm )) return;
788 /* only use right half of the bitmap */
789 bmp_xoffset = bm.bmWidth / 2;
790 bm.bmWidth -= bmp_xoffset;
792 goto got_bitmap;
793 case (INT_PTR)HBMMENU_MBAR_RESTORE:
794 flags = DFCS_CAPTIONRESTORE;
795 break;
796 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
797 flags = DFCS_CAPTIONMIN;
798 break;
799 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
800 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
801 break;
802 case (INT_PTR)HBMMENU_MBAR_CLOSE:
803 flags = DFCS_CAPTIONCLOSE;
804 break;
805 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
806 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
807 break;
808 case (INT_PTR)HBMMENU_CALLBACK:
809 case (INT_PTR)HBMMENU_POPUP_CLOSE:
810 case (INT_PTR)HBMMENU_POPUP_RESTORE:
811 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
812 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
813 default:
814 FIXME("Magic 0x%08x not implemented\n", LOWORD(lpitem->text));
815 return;
817 r = *rect;
818 InflateRect( &r, -1, -1 );
819 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
820 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
821 return;
824 if (!bmp || !GetObjectA( bmp, sizeof(bm), &bm )) return;
826 got_bitmap:
827 hdcMem = CreateCompatibleDC( hdc );
828 SelectObject( hdcMem, bmp );
830 /* handle fontsize > bitmap_height */
831 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
832 left=rect->left;
833 if (TWEAK_WineLook == WIN95_LOOK) {
834 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
835 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
836 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
837 } else {
838 left++;
839 w-=2;
840 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text) && (!menuBar)) ? MERGEPAINT : SRCCOPY;
842 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
843 DeleteDC( hdcMem );
847 /***********************************************************************
848 * MENU_CalcItemSize
850 * Calculate the size of the menu item and store it in lpitem->rect.
852 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
853 INT orgX, INT orgY, BOOL menuBar )
855 WCHAR *p;
856 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
858 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
859 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
860 (menuBar ? " (MenuBar)" : ""));
862 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
864 if (lpitem->fType & MF_OWNERDRAW)
867 ** Experimentation under Windows reveals that an owner-drawn
868 ** menu is expected to return the size of the content part of
869 ** the menu item, not including the checkmark nor the submenu
870 ** arrow. Windows adds those values itself and returns the
871 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
873 MEASUREITEMSTRUCT mis;
874 mis.CtlType = ODT_MENU;
875 mis.CtlID = 0;
876 mis.itemID = lpitem->wID;
877 mis.itemData = (DWORD)lpitem->dwItemData;
878 mis.itemHeight = 0;
879 mis.itemWidth = 0;
880 SendMessageA( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
881 lpitem->rect.right += mis.itemWidth;
883 if (menuBar)
885 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
888 /* under at least win95 you seem to be given a standard
889 height for the menu and the height value is ignored */
891 if (TWEAK_WineLook == WIN31_LOOK)
892 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
893 else
894 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
896 else
897 lpitem->rect.bottom += mis.itemHeight;
899 TRACE("id=%04x size=%dx%d\n",
900 lpitem->wID, mis.itemWidth, mis.itemHeight);
901 /* Fall through to get check/arrow width calculation. */
904 if (lpitem->fType & MF_SEPARATOR)
906 lpitem->rect.bottom += SEPARATOR_HEIGHT;
907 return;
910 if (!menuBar)
912 lpitem->rect.right += 2 * check_bitmap_width;
913 if (lpitem->fType & MF_POPUP)
914 lpitem->rect.right += arrow_bitmap_width;
917 if (lpitem->fType & MF_OWNERDRAW)
918 return;
920 if (IS_BITMAP_ITEM(lpitem->fType))
922 SIZE size;
924 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
925 lpitem->rect.right += size.cx;
926 lpitem->rect.bottom += size.cy;
927 if (TWEAK_WineLook == WIN98_LOOK)
929 /* Leave space for the sunken border */
930 lpitem->rect.right += 2;
931 lpitem->rect.bottom += 2;
936 /* it must be a text item - unless it's the system menu */
937 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
938 { SIZE size;
940 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
942 lpitem->rect.right += size.cx;
943 if (TWEAK_WineLook == WIN31_LOOK)
944 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
945 else
946 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
947 lpitem->xTab = 0;
949 if (menuBar)
951 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
953 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
955 /* Item contains a tab (only meaningful in popup menus) */
956 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
957 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
958 lpitem->rect.right += MENU_TAB_SPACE;
960 else
962 if (strchrW( lpitem->text, '\b' ))
963 lpitem->rect.right += MENU_TAB_SPACE;
964 lpitem->xTab = lpitem->rect.right - check_bitmap_width
965 - arrow_bitmap_width;
968 TRACE("(%d,%d)-(%d,%d)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
972 /***********************************************************************
973 * MENU_PopupMenuCalcSize
975 * Calculate the size of a popup menu.
977 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
979 MENUITEM *lpitem;
980 HDC hdc;
981 int start, i;
982 int orgX, orgY, maxX, maxTab, maxTabWidth;
984 lppop->Width = lppop->Height = 0;
985 if (lppop->nItems == 0) return;
986 hdc = GetDC( 0 );
988 SelectObject( hdc, hMenuFont);
990 start = 0;
991 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
993 while (start < lppop->nItems)
995 lpitem = &lppop->items[start];
996 orgX = maxX;
997 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
999 maxTab = maxTabWidth = 0;
1001 /* Parse items until column break or end of menu */
1002 for (i = start; i < lppop->nItems; i++, lpitem++)
1004 if ((i != start) &&
1005 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1007 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
1009 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1010 maxX = max( maxX, lpitem->rect.right );
1011 orgY = lpitem->rect.bottom;
1012 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1014 maxTab = max( maxTab, lpitem->xTab );
1015 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1019 /* Finish the column (set all items to the largest width found) */
1020 maxX = max( maxX, maxTab + maxTabWidth );
1021 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1023 lpitem->rect.right = maxX;
1024 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1025 lpitem->xTab = maxTab;
1028 lppop->Height = max( lppop->Height, orgY );
1031 lppop->Width = maxX;
1033 /* space for 3d border */
1034 if(TWEAK_WineLook > WIN31_LOOK)
1036 lppop->Height += 2;
1037 lppop->Width += 2;
1040 ReleaseDC( 0, hdc );
1044 /***********************************************************************
1045 * MENU_MenuBarCalcSize
1047 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1048 * height is off by 1 pixel which causes lengthy window relocations when
1049 * active document window is maximized/restored.
1051 * Calculate the size of the menu bar.
1053 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1054 LPPOPUPMENU lppop, HWND hwndOwner )
1056 MENUITEM *lpitem;
1057 int start, i, orgX, orgY, maxY, helpPos;
1059 if ((lprect == NULL) || (lppop == NULL)) return;
1060 if (lppop->nItems == 0) return;
1061 TRACE("left=%d top=%d right=%d bottom=%d\n",
1062 lprect->left, lprect->top, lprect->right, lprect->bottom);
1063 lppop->Width = lprect->right - lprect->left;
1064 lppop->Height = 0;
1065 maxY = lprect->top+1;
1066 start = 0;
1067 helpPos = -1;
1068 while (start < lppop->nItems)
1070 lpitem = &lppop->items[start];
1071 orgX = lprect->left;
1072 orgY = maxY;
1074 /* Parse items until line break or end of menu */
1075 for (i = start; i < lppop->nItems; i++, lpitem++)
1077 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1078 if ((i != start) &&
1079 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1081 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1082 orgX, orgY );
1083 debug_print_menuitem (" item: ", lpitem, "");
1084 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
1086 if (lpitem->rect.right > lprect->right)
1088 if (i != start) break;
1089 else lpitem->rect.right = lprect->right;
1091 maxY = max( maxY, lpitem->rect.bottom );
1092 orgX = lpitem->rect.right;
1095 /* Finish the line (set all items to the largest height found) */
1096 while (start < i) lppop->items[start++].rect.bottom = maxY;
1099 lprect->bottom = maxY;
1100 lppop->Height = lprect->bottom - lprect->top;
1102 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1103 /* the last item (if several lines, only move the last line) */
1104 lpitem = &lppop->items[lppop->nItems-1];
1105 orgY = lpitem->rect.top;
1106 orgX = lprect->right;
1107 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1108 if ( (helpPos==-1) || (helpPos>i) )
1109 break; /* done */
1110 if (lpitem->rect.top != orgY) break; /* Other line */
1111 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1112 lpitem->rect.left += orgX - lpitem->rect.right;
1113 lpitem->rect.right = orgX;
1114 orgX = lpitem->rect.left;
1118 /***********************************************************************
1119 * MENU_DrawMenuItem
1121 * Draw a single menu item.
1123 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1124 UINT height, BOOL menuBar, UINT odaction )
1126 RECT rect;
1128 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1130 if (lpitem->fType & MF_SYSMENU)
1132 if( !IsIconic(hwnd) ) {
1133 if (TWEAK_WineLook > WIN31_LOOK)
1134 NC_DrawSysButton95( hwnd, hdc,
1135 lpitem->fState &
1136 (MF_HILITE | MF_MOUSESELECT) );
1137 else
1138 NC_DrawSysButton( hwnd, hdc,
1139 lpitem->fState &
1140 (MF_HILITE | MF_MOUSESELECT) );
1143 return;
1146 if (lpitem->fType & MF_OWNERDRAW)
1149 ** Experimentation under Windows reveals that an owner-drawn
1150 ** menu is given the rectangle which includes the space it requested
1151 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1152 ** and a popup-menu arrow. This is the value of lpitem->rect.
1153 ** Windows will leave all drawing to the application except for
1154 ** the popup-menu arrow. Windows always draws that itself, after
1155 ** the menu owner has finished drawing.
1157 DRAWITEMSTRUCT dis;
1159 dis.CtlType = ODT_MENU;
1160 dis.CtlID = 0;
1161 dis.itemID = lpitem->wID;
1162 dis.itemData = (DWORD)lpitem->dwItemData;
1163 dis.itemState = 0;
1164 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1165 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1166 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1167 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1168 dis.hwndItem = (HWND)hmenu;
1169 dis.hDC = hdc;
1170 dis.rcItem = lpitem->rect;
1171 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1172 "hwndItem=%p, hdc=%p, rcItem={%d,%d,%d,%d}\n", hwndOwner,
1173 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1174 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1175 dis.rcItem.bottom);
1176 SendMessageA( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1177 /* Fall through to draw popup-menu arrow */
1180 TRACE("rect={%d,%d,%d,%d}\n", lpitem->rect.left, lpitem->rect.top,
1181 lpitem->rect.right,lpitem->rect.bottom);
1183 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1185 rect = lpitem->rect;
1187 if (!(lpitem->fType & MF_OWNERDRAW))
1189 if (lpitem->fState & MF_HILITE)
1191 if(TWEAK_WineLook == WIN98_LOOK)
1193 if(menuBar)
1194 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1195 else
1196 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1198 else /* Not Win98 Look */
1200 if(!IS_BITMAP_ITEM(lpitem->fType))
1201 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1204 else
1205 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1208 SetBkMode( hdc, TRANSPARENT );
1210 if (!(lpitem->fType & MF_OWNERDRAW))
1212 /* vertical separator */
1213 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1215 if (TWEAK_WineLook > WIN31_LOOK)
1217 RECT rc = rect;
1218 rc.top = 3;
1219 rc.bottom = height - 3;
1220 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1222 else
1224 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1225 MoveToEx( hdc, rect.left, 0, NULL );
1226 LineTo( hdc, rect.left, height );
1230 /* horizontal separator */
1231 if (lpitem->fType & MF_SEPARATOR)
1233 if (TWEAK_WineLook > WIN31_LOOK)
1235 RECT rc = rect;
1236 rc.left++;
1237 rc.right--;
1238 rc.top += SEPARATOR_HEIGHT / 2;
1239 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1241 else
1243 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1244 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1245 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1247 return;
1251 /* Setup colors */
1253 if (lpitem->fState & MF_HILITE)
1255 if(TWEAK_WineLook == WIN98_LOOK)
1257 if(menuBar) {
1258 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1259 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1260 } else {
1261 if(lpitem->fState & MF_GRAYED)
1262 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1263 else
1264 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1265 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1268 else /* Not Win98 Look */
1270 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1271 if(!IS_BITMAP_ITEM(lpitem->fType))
1272 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1275 else
1277 if (lpitem->fState & MF_GRAYED)
1278 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1279 else
1280 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1281 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1284 /* helper lines for debugging */
1285 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1286 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1287 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1288 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1291 if (!menuBar)
1293 INT y = rect.top + rect.bottom;
1294 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1295 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1297 if (!(lpitem->fType & MF_OWNERDRAW))
1299 /* Draw the check mark
1301 * FIXME:
1302 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1304 HBITMAP bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1305 if (bm) /* we have a custom bitmap */
1307 HDC hdcMem = CreateCompatibleDC( hdc );
1308 SelectObject( hdcMem, bm );
1309 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1310 check_bitmap_width, check_bitmap_height,
1311 hdcMem, 0, 0, SRCCOPY );
1312 DeleteDC( hdcMem );
1314 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1316 RECT r;
1317 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1318 HDC hdcMem = CreateCompatibleDC( hdc );
1319 SelectObject( hdcMem, bm );
1320 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1321 DrawFrameControl( hdcMem, &r, DFC_MENU,
1322 (lpitem->fType & MFT_RADIOCHECK) ?
1323 DFCS_MENUBULLET : DFCS_MENUCHECK );
1324 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1325 hdcMem, 0, 0, SRCCOPY );
1326 DeleteDC( hdcMem );
1327 DeleteObject( bm );
1331 /* Draw the popup-menu arrow */
1332 if (lpitem->fType & MF_POPUP)
1334 HDC hdcMem = CreateCompatibleDC( hdc );
1335 HBITMAP hOrigBitmap;
1337 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1338 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1339 (y - arrow_bitmap_height) / 2,
1340 arrow_bitmap_width, arrow_bitmap_height,
1341 hdcMem, 0, 0, SRCCOPY );
1342 SelectObject( hdcMem, hOrigBitmap );
1343 DeleteDC( hdcMem );
1346 rect.left += check_bitmap_width;
1347 rect.right -= arrow_bitmap_width;
1350 /* Done for owner-drawn */
1351 if (lpitem->fType & MF_OWNERDRAW)
1352 return;
1354 /* Draw the item text or bitmap */
1355 if (IS_BITMAP_ITEM(lpitem->fType))
1357 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar );
1358 return;
1361 /* No bitmap - process text if present */
1362 else if (IS_STRING_ITEM(lpitem->fType))
1364 register int i;
1365 HFONT hfontOld = 0;
1367 UINT uFormat = (menuBar) ?
1368 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1369 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1371 if ( lpitem->fState & MFS_DEFAULT )
1373 hfontOld = SelectObject( hdc, hMenuFontBold);
1376 if (menuBar)
1378 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1379 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1382 for (i = 0; lpitem->text[i]; i++)
1383 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1384 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 (!menuBar && 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=%p dc=%p menu=%p\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("(%p, %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, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1549 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1550 LineTo( hDC, lprect->right, lprect->bottom );
1552 else
1554 SelectObject( hDC, SYSCOLOR_GetPen(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=%p hmenu=%p 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, (HINSTANCE)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=%p menu=%p 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);
1668 if (!top_popup) top_popup = lppop->hWnd;
1670 SelectObject( hdc, hMenuFont);
1672 /* Clear previous highlighted item */
1673 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1675 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1676 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1677 lppop->Height, !(lppop->wFlags & MF_POPUP),
1678 ODA_SELECT );
1681 /* Highlight new item (if any) */
1682 lppop->FocusedItem = wIndex;
1683 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1685 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1686 lppop->items[wIndex].fState |= MF_HILITE;
1687 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1688 &lppop->items[wIndex], lppop->Height,
1689 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1691 if (sendMenuSelect)
1693 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1694 SendMessageA( hwndOwner, WM_MENUSELECT,
1695 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1696 ip->fType | ip->fState | MF_MOUSESELECT |
1697 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1700 else if (sendMenuSelect) {
1701 if(topmenu){
1702 int pos;
1703 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1704 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1705 MENUITEM *ip = &ptm->items[pos];
1706 SendMessageA( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1707 ip->fType | ip->fState | MF_MOUSESELECT |
1708 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1712 ReleaseDC( lppop->hWnd, hdc );
1716 /***********************************************************************
1717 * MENU_MoveSelection
1719 * Moves currently selected item according to the offset parameter.
1720 * If there is no selection then it should select the last item if
1721 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1723 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1725 INT i;
1726 POPUPMENU *menu;
1728 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1730 menu = MENU_GetMenu( hmenu );
1731 if ((!menu) || (!menu->items)) return;
1733 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1735 if( menu->nItems == 1 ) return; else
1736 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1737 ; i += offset)
1738 if (!(menu->items[i].fType & MF_SEPARATOR))
1740 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1741 return;
1745 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1746 i >= 0 && i < menu->nItems ; i += offset)
1747 if (!(menu->items[i].fType & MF_SEPARATOR))
1749 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1750 return;
1755 /**********************************************************************
1756 * MENU_SetItemData
1758 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1759 * ModifyMenu().
1761 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1762 LPCWSTR str )
1764 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1766 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1767 TRACE("flags=%x str=%p\n", flags, str);
1769 if (IS_STRING_ITEM(flags))
1771 if (!str)
1773 flags |= MF_SEPARATOR;
1774 item->text = NULL;
1776 else
1778 LPWSTR text;
1779 /* Item beginning with a backspace is a help item */
1780 if (*str == '\b')
1782 flags |= MF_HELP;
1783 str++;
1785 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1786 return FALSE;
1787 strcpyW( text, str );
1788 item->text = text;
1791 else if (IS_BITMAP_ITEM(flags))
1792 item->text = (LPWSTR)HBITMAP_32(LOWORD(str));
1793 else item->text = NULL;
1795 if (flags & MF_OWNERDRAW)
1796 item->dwItemData = (DWORD)str;
1797 else
1798 item->dwItemData = 0;
1800 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1801 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1803 if (flags & MF_POPUP)
1805 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1806 if (menu) menu->wFlags |= MF_POPUP;
1807 else
1809 item->wID = 0;
1810 item->hSubMenu = 0;
1811 item->fType = 0;
1812 item->fState = 0;
1813 return FALSE;
1817 item->wID = id;
1818 if (flags & MF_POPUP) item->hSubMenu = (HMENU)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 (allocate) 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 * NOTE: flags is equivalent to the mtOption field
1897 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1899 WORD flags, id = 0;
1900 LPCSTR str;
1904 flags = GET_WORD(res);
1905 res += sizeof(WORD);
1906 if (!(flags & MF_POPUP))
1908 id = GET_WORD(res);
1909 res += sizeof(WORD);
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 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1981 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1982 mii.wID, mii.fType);
1983 mii.fType |= MF_SEPARATOR;
1985 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1986 } while (!(resinfo & MF_END));
1987 return res;
1991 /***********************************************************************
1992 * MENU_GetSubPopup
1994 * Return the handle of the selected sub-popup menu (if any).
1996 static HMENU MENU_GetSubPopup( HMENU hmenu )
1998 POPUPMENU *menu;
1999 MENUITEM *item;
2001 menu = MENU_GetMenu( hmenu );
2003 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2005 item = &menu->items[menu->FocusedItem];
2006 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2007 return item->hSubMenu;
2008 return 0;
2012 /***********************************************************************
2013 * MENU_HideSubPopups
2015 * Hide the sub-popup menus of this menu.
2017 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2018 BOOL sendMenuSelect )
2020 POPUPMENU *menu = MENU_GetMenu( hmenu );
2022 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2024 if (menu && top_popup)
2026 HMENU hsubmenu;
2027 POPUPMENU *submenu;
2028 MENUITEM *item;
2030 if (menu->FocusedItem != NO_SELECTED_ITEM)
2032 item = &menu->items[menu->FocusedItem];
2033 if (!(item->fType & MF_POPUP) ||
2034 !(item->fState & MF_MOUSESELECT)) return;
2035 item->fState &= ~MF_MOUSESELECT;
2036 hsubmenu = item->hSubMenu;
2037 } else return;
2039 submenu = MENU_GetMenu( hsubmenu );
2040 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2041 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2042 DestroyWindow( submenu->hWnd );
2043 submenu->hWnd = 0;
2048 /***********************************************************************
2049 * MENU_ShowSubPopup
2051 * Display the sub-menu of the selected item of this menu.
2052 * Return the handle of the submenu, or hmenu if no submenu to display.
2054 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2055 BOOL selectFirst, UINT wFlags )
2057 RECT rect;
2058 POPUPMENU *menu;
2059 MENUITEM *item;
2060 HDC hdc;
2062 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2064 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2066 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2068 item = &menu->items[menu->FocusedItem];
2069 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2070 return hmenu;
2072 /* message must be sent before using item,
2073 because nearly everything may be changed by the application ! */
2075 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2076 if (!(wFlags & TPM_NONOTIFY))
2077 SendMessageA( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2078 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2080 item = &menu->items[menu->FocusedItem];
2081 rect = item->rect;
2083 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2084 if (!(item->fState & MF_HILITE))
2086 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2087 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2089 SelectObject( hdc, hMenuFont);
2091 item->fState |= MF_HILITE;
2092 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2093 ReleaseDC( menu->hWnd, hdc );
2095 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2096 item->rect = rect;
2098 item->fState |= MF_MOUSESELECT;
2100 if (IS_SYSTEM_MENU(menu))
2102 MENU_InitSysMenuPopup(item->hSubMenu,
2103 GetWindowLongA( menu->hWnd, GWL_STYLE ),
2104 GetClassLongA( menu->hWnd, GCL_STYLE));
2106 NC_GetSysPopupPos( menu->hWnd, &rect );
2107 rect.top = rect.bottom;
2108 rect.right = GetSystemMetrics(SM_CXSIZE);
2109 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2111 else
2113 GetWindowRect( menu->hWnd, &rect );
2114 if (menu->wFlags & MF_POPUP)
2116 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2117 rect.top += item->rect.top;
2118 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2119 rect.bottom = item->rect.top - item->rect.bottom;
2121 else
2123 rect.left += item->rect.left;
2124 rect.top += item->rect.bottom;
2125 rect.right = item->rect.right - item->rect.left;
2126 rect.bottom = item->rect.bottom - item->rect.top;
2130 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2131 rect.left, rect.top, rect.right, rect.bottom );
2132 if (selectFirst)
2133 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2134 return item->hSubMenu;
2139 /**********************************************************************
2140 * MENU_IsMenuActive
2142 BOOL MENU_IsMenuActive(void)
2144 return (top_popup != 0);
2147 /***********************************************************************
2148 * MENU_PtMenu
2150 * Walks menu chain trying to find a menu pt maps to.
2152 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2154 POPUPMENU *menu = MENU_GetMenu( hMenu );
2155 UINT item = menu->FocusedItem;
2156 HMENU ret;
2158 /* try subpopup first (if any) */
2159 ret = (item != NO_SELECTED_ITEM &&
2160 (menu->items[item].fType & MF_POPUP) &&
2161 (menu->items[item].fState & MF_MOUSESELECT))
2162 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2164 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2166 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2167 if( menu->wFlags & MF_POPUP )
2169 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2171 else if (ht == HTSYSMENU)
2172 ret = get_win_sys_menu( menu->hWnd );
2173 else if (ht == HTMENU)
2174 ret = GetMenu( menu->hWnd );
2176 return ret;
2179 /***********************************************************************
2180 * MENU_ExecFocusedItem
2182 * Execute a menu item (for instance when user pressed Enter).
2183 * Return the wID of the executed item. Otherwise, -1 indicating
2184 * that no menu item was executed;
2185 * Have to receive the flags for the TrackPopupMenu options to avoid
2186 * sending unwanted message.
2189 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2191 MENUITEM *item;
2192 POPUPMENU *menu = MENU_GetMenu( hMenu );
2194 TRACE("%p hmenu=%p\n", pmt, hMenu);
2196 if (!menu || !menu->nItems ||
2197 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2199 item = &menu->items[menu->FocusedItem];
2201 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2203 if (!(item->fType & MF_POPUP))
2205 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2207 /* If TPM_RETURNCMD is set you return the id, but
2208 do not send a message to the owner */
2209 if(!(wFlags & TPM_RETURNCMD))
2211 if( menu->wFlags & MF_SYSMENU )
2212 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2213 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2214 else
2215 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2217 return item->wID;
2220 else
2221 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2223 return -1;
2226 /***********************************************************************
2227 * MENU_SwitchTracking
2229 * Helper function for menu navigation routines.
2231 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2233 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2234 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2236 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2238 if( pmt->hTopMenu != hPtMenu &&
2239 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2241 /* both are top level menus (system and menu-bar) */
2242 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2243 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2244 pmt->hTopMenu = hPtMenu;
2246 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2247 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2251 /***********************************************************************
2252 * MENU_ButtonDown
2254 * Return TRUE if we can go on with menu tracking.
2256 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2258 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2260 if (hPtMenu)
2262 UINT id = 0;
2263 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2264 MENUITEM *item;
2266 if( IS_SYSTEM_MENU(ptmenu) )
2267 item = ptmenu->items;
2268 else
2269 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2271 if( item )
2273 if( ptmenu->FocusedItem != id )
2274 MENU_SwitchTracking( pmt, hPtMenu, id );
2276 /* If the popup menu is not already "popped" */
2277 if(!(item->fState & MF_MOUSESELECT ))
2279 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2281 /* In win31, a newly popped menu always remains opened for the next buttonup */
2282 if(TWEAK_WineLook == WIN31_LOOK)
2283 ptmenu->bTimeToHide = FALSE;
2286 return TRUE;
2288 /* Else the click was on the menu bar, finish the tracking */
2290 return FALSE;
2293 /***********************************************************************
2294 * MENU_ButtonUp
2296 * Return the value of MENU_ExecFocusedItem if
2297 * the selected item was not a popup. Else open the popup.
2298 * A -1 return value indicates that we go on with menu tracking.
2301 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2303 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2305 if (hPtMenu)
2307 UINT id = 0;
2308 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2309 MENUITEM *item;
2311 if( IS_SYSTEM_MENU(ptmenu) )
2312 item = ptmenu->items;
2313 else
2314 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2316 if( item && (ptmenu->FocusedItem == id ))
2318 if( !(item->fType & MF_POPUP) )
2319 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2321 /* If we are dealing with the top-level menu */
2322 /* and this is a click on an already "popped" item: */
2323 /* Stop the menu tracking and close the opened submenus */
2324 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2325 return 0;
2327 ptmenu->bTimeToHide = TRUE;
2329 return -1;
2333 /***********************************************************************
2334 * MENU_MouseMove
2336 * Return TRUE if we can go on with menu tracking.
2338 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2340 UINT id = NO_SELECTED_ITEM;
2341 POPUPMENU *ptmenu = NULL;
2343 if( hPtMenu )
2345 ptmenu = MENU_GetMenu( hPtMenu );
2346 if( IS_SYSTEM_MENU(ptmenu) )
2347 id = 0;
2348 else
2349 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2352 if( id == NO_SELECTED_ITEM )
2354 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2355 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2358 else if( ptmenu->FocusedItem != id )
2360 MENU_SwitchTracking( pmt, hPtMenu, id );
2361 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2363 return TRUE;
2367 /***********************************************************************
2368 * MENU_SetCapture
2370 static void MENU_SetCapture( HWND hwnd )
2372 HWND previous = 0;
2374 SERVER_START_REQ( set_capture_window )
2376 req->handle = hwnd;
2377 req->flags = CAPTURE_MENU;
2378 if (!wine_server_call_err( req ))
2380 previous = reply->previous;
2381 hwnd = reply->full_handle;
2384 SERVER_END_REQ;
2386 if (previous && previous != hwnd)
2387 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2391 /***********************************************************************
2392 * MENU_DoNextMenu
2394 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2396 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2398 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2400 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2401 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2403 MDINEXTMENU next_menu;
2404 HMENU hNewMenu;
2405 HWND hNewWnd;
2406 UINT id = 0;
2408 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2409 next_menu.hmenuNext = 0;
2410 next_menu.hwndNext = 0;
2411 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2413 TRACE("%p [%p] -> %p [%p]\n",
2414 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2416 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2418 DWORD style = GetWindowLongA( pmt->hOwnerWnd, GWL_STYLE );
2419 hNewWnd = pmt->hOwnerWnd;
2420 if( IS_SYSTEM_MENU(menu) )
2422 /* switch to the menu bar */
2424 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2426 if( vk == VK_LEFT )
2428 menu = MENU_GetMenu( hNewMenu );
2429 id = menu->nItems - 1;
2432 else if (style & WS_SYSMENU )
2434 /* switch to the system menu */
2435 hNewMenu = get_win_sys_menu( hNewWnd );
2437 else return FALSE;
2439 else /* application returned a new menu to switch to */
2441 hNewMenu = next_menu.hmenuNext;
2442 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2444 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2446 DWORD style = GetWindowLongA( hNewWnd, GWL_STYLE );
2448 if (style & WS_SYSMENU &&
2449 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2451 /* get the real system menu */
2452 hNewMenu = get_win_sys_menu(hNewWnd);
2454 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2456 /* FIXME: Not sure what to do here;
2457 * perhaps try to track hNewMenu as a popup? */
2459 TRACE(" -- got confused.\n");
2460 return FALSE;
2463 else return FALSE;
2466 if( hNewMenu != pmt->hTopMenu )
2468 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2469 FALSE, 0 );
2470 if( pmt->hCurrentMenu != pmt->hTopMenu )
2471 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2474 if( hNewWnd != pmt->hOwnerWnd )
2476 pmt->hOwnerWnd = hNewWnd;
2477 MENU_SetCapture( pmt->hOwnerWnd );
2480 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2481 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2483 return TRUE;
2485 return FALSE;
2488 /***********************************************************************
2489 * MENU_SuspendPopup
2491 * The idea is not to show the popup if the next input message is
2492 * going to hide it anyway.
2494 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2496 MSG msg;
2498 msg.hwnd = pmt->hOwnerWnd;
2500 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2501 pmt->trackFlags |= TF_SKIPREMOVE;
2503 switch( uMsg )
2505 case WM_KEYDOWN:
2506 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2507 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2509 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2510 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2511 if( msg.message == WM_KEYDOWN &&
2512 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2514 pmt->trackFlags |= TF_SUSPENDPOPUP;
2515 return TRUE;
2518 break;
2521 /* failures go through this */
2522 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2523 return FALSE;
2526 /***********************************************************************
2527 * MENU_KeyEscape
2529 * Handle a VK_ESCAPE key event in a menu.
2531 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2533 BOOL bEndMenu = TRUE;
2535 if (pmt->hCurrentMenu != pmt->hTopMenu)
2537 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2539 if (menu->wFlags & MF_POPUP)
2541 HMENU hmenutmp, hmenuprev;
2543 hmenuprev = hmenutmp = pmt->hTopMenu;
2545 /* close topmost popup */
2546 while (hmenutmp != pmt->hCurrentMenu)
2548 hmenuprev = hmenutmp;
2549 hmenutmp = MENU_GetSubPopup( hmenuprev );
2552 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2553 pmt->hCurrentMenu = hmenuprev;
2554 bEndMenu = FALSE;
2558 return bEndMenu;
2561 /***********************************************************************
2562 * MENU_KeyLeft
2564 * Handle a VK_LEFT key event in a menu.
2566 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2568 POPUPMENU *menu;
2569 HMENU hmenutmp, hmenuprev;
2570 UINT prevcol;
2572 hmenuprev = hmenutmp = pmt->hTopMenu;
2573 menu = MENU_GetMenu( hmenutmp );
2575 /* Try to move 1 column left (if possible) */
2576 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2577 NO_SELECTED_ITEM ) {
2579 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2580 prevcol, TRUE, 0 );
2581 return;
2584 /* close topmost popup */
2585 while (hmenutmp != pmt->hCurrentMenu)
2587 hmenuprev = hmenutmp;
2588 hmenutmp = MENU_GetSubPopup( hmenuprev );
2591 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2592 pmt->hCurrentMenu = hmenuprev;
2594 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2596 /* move menu bar selection if no more popups are left */
2598 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2599 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2601 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2603 /* A sublevel menu was displayed - display the next one
2604 * unless there is another displacement coming up */
2606 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2607 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2608 pmt->hTopMenu, TRUE, wFlags);
2614 /***********************************************************************
2615 * MENU_KeyRight
2617 * Handle a VK_RIGHT key event in a menu.
2619 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2621 HMENU hmenutmp;
2622 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2623 UINT nextcol;
2625 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2626 pmt->hCurrentMenu,
2627 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2628 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2630 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2632 /* If already displaying a popup, try to display sub-popup */
2634 hmenutmp = pmt->hCurrentMenu;
2635 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2637 /* if subpopup was displayed then we are done */
2638 if (hmenutmp != pmt->hCurrentMenu) return;
2641 /* Check to see if there's another column */
2642 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2643 NO_SELECTED_ITEM ) {
2644 TRACE("Going to %d.\n", nextcol );
2645 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2646 nextcol, TRUE, 0 );
2647 return;
2650 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2652 if( pmt->hCurrentMenu != pmt->hTopMenu )
2654 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2655 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2656 } else hmenutmp = 0;
2658 /* try to move to the next item */
2659 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2660 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2662 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2663 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2664 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2665 pmt->hTopMenu, TRUE, wFlags);
2669 /***********************************************************************
2670 * MENU_TrackMenu
2672 * Menu tracking code.
2674 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2675 HWND hwnd, const RECT *lprect )
2677 MSG msg;
2678 POPUPMENU *menu;
2679 BOOL fRemove;
2680 INT executedMenuId = -1;
2681 MTRACKER mt;
2682 BOOL enterIdleSent = FALSE;
2684 mt.trackFlags = 0;
2685 mt.hCurrentMenu = hmenu;
2686 mt.hTopMenu = hmenu;
2687 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2688 mt.pt.x = x;
2689 mt.pt.y = y;
2691 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%d,%d)-(%d,%d)\n",
2692 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2693 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2695 fEndMenu = FALSE;
2696 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2698 if (wFlags & TPM_BUTTONDOWN)
2700 /* Get the result in order to start the tracking or not */
2701 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2702 fEndMenu = !fRemove;
2705 MENU_SetCapture( mt.hOwnerWnd );
2707 while (!fEndMenu)
2709 menu = MENU_GetMenu( mt.hCurrentMenu );
2710 if (!menu) /* sometimes happens if I do a window manager close */
2711 break;
2713 /* we have to keep the message in the queue until it's
2714 * clear that menu loop is not over yet. */
2716 for (;;)
2718 if (PeekMessageA( &msg, 0, 0, 0, PM_NOREMOVE ))
2720 if (!CallMsgFilterA( &msg, MSGF_MENU )) break;
2721 /* remove the message from the queue */
2722 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2724 else
2726 if (!enterIdleSent)
2728 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2729 enterIdleSent = TRUE;
2730 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2732 WaitMessage();
2736 /* check if EndMenu() tried to cancel us, by posting this message */
2737 if(msg.message == WM_CANCELMODE)
2739 /* we are now out of the loop */
2740 fEndMenu = TRUE;
2742 /* remove the message from the queue */
2743 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2745 /* break out of internal loop, ala ESCAPE */
2746 break;
2749 TranslateMessage( &msg );
2750 mt.pt = msg.pt;
2752 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2753 enterIdleSent=FALSE;
2755 fRemove = FALSE;
2756 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2759 * Use the mouse coordinates in lParam instead of those in the MSG
2760 * struct to properly handle synthetic messages. They are already
2761 * in screen coordinates.
2763 mt.pt.x = SLOWORD(msg.lParam);
2764 mt.pt.y = SHIWORD(msg.lParam);
2766 /* Find a menu for this mouse event */
2767 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2769 switch(msg.message)
2771 /* no WM_NC... messages in captured state */
2773 case WM_RBUTTONDBLCLK:
2774 case WM_RBUTTONDOWN:
2775 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2776 /* fall through */
2777 case WM_LBUTTONDBLCLK:
2778 case WM_LBUTTONDOWN:
2779 /* If the message belongs to the menu, removes it from the queue */
2780 /* Else, end menu tracking */
2781 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2782 fEndMenu = !fRemove;
2783 break;
2785 case WM_RBUTTONUP:
2786 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2787 /* fall through */
2788 case WM_LBUTTONUP:
2789 /* Check if a menu was selected by the mouse */
2790 if (hmenu)
2792 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2794 /* End the loop if executedMenuId is an item ID */
2795 /* or if the job was done (executedMenuId = 0). */
2796 fEndMenu = fRemove = (executedMenuId != -1);
2798 /* No menu was selected by the mouse */
2799 /* if the function was called by TrackPopupMenu, continue
2800 with the menu tracking. If not, stop it */
2801 else
2802 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2804 break;
2806 case WM_MOUSEMOVE:
2807 /* In win95 winelook, the selected menu item must be changed every time the
2808 mouse moves. In Win31 winelook, the mouse button has to be held down */
2810 if ( hmenu && ((TWEAK_WineLook > WIN31_LOOK) ||
2811 ( (msg.wParam & MK_LBUTTON) ||
2812 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON)))) )
2814 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2816 } /* switch(msg.message) - mouse */
2818 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2820 fRemove = TRUE; /* Keyboard messages are always removed */
2821 switch(msg.message)
2823 case WM_KEYDOWN:
2824 switch(msg.wParam)
2826 case VK_HOME:
2827 case VK_END:
2828 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2829 NO_SELECTED_ITEM, FALSE, 0 );
2830 /* fall through */
2831 case VK_UP:
2832 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2833 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2834 break;
2836 case VK_DOWN: /* If on menu bar, pull-down the menu */
2838 menu = MENU_GetMenu( mt.hCurrentMenu );
2839 if (!(menu->wFlags & MF_POPUP))
2840 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2841 else /* otherwise try to move selection */
2842 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2843 break;
2845 case VK_LEFT:
2846 MENU_KeyLeft( &mt, wFlags );
2847 break;
2849 case VK_RIGHT:
2850 MENU_KeyRight( &mt, wFlags );
2851 break;
2853 case VK_ESCAPE:
2854 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2855 break;
2857 case VK_F1:
2859 HELPINFO hi;
2860 hi.cbSize = sizeof(HELPINFO);
2861 hi.iContextType = HELPINFO_MENUITEM;
2862 if (menu->FocusedItem == NO_SELECTED_ITEM)
2863 hi.iCtrlId = 0;
2864 else
2865 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2866 hi.hItemHandle = hmenu;
2867 hi.dwContextId = menu->dwContextHelpID;
2868 hi.MousePos = msg.pt;
2869 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2870 break;
2873 default:
2874 break;
2876 break; /* WM_KEYDOWN */
2878 case WM_SYSKEYDOWN:
2879 switch(msg.wParam)
2881 case VK_MENU:
2882 fEndMenu = TRUE;
2883 break;
2886 break; /* WM_SYSKEYDOWN */
2888 case WM_CHAR:
2890 UINT pos;
2892 if (msg.wParam == '\r' || msg.wParam == ' ')
2894 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2895 fEndMenu = (executedMenuId != -1);
2897 break;
2900 /* Hack to avoid control chars. */
2901 /* We will find a better way real soon... */
2902 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2904 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2905 LOWORD(msg.wParam), FALSE );
2906 if (pos == (UINT)-2) fEndMenu = TRUE;
2907 else if (pos == (UINT)-1) MessageBeep(0);
2908 else
2910 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2911 TRUE, 0 );
2912 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2913 fEndMenu = (executedMenuId != -1);
2916 break;
2917 } /* switch(msg.message) - kbd */
2919 else
2921 DispatchMessageA( &msg );
2924 if (!fEndMenu) fRemove = TRUE;
2926 /* finally remove message from the queue */
2928 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2929 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2930 else mt.trackFlags &= ~TF_SKIPREMOVE;
2933 MENU_SetCapture(0); /* release the capture */
2935 /* If dropdown is still painted and the close box is clicked on
2936 then the menu will be destroyed as part of the DispatchMessage above.
2937 This will then invalidate the menu handle in mt.hTopMenu. We should
2938 check for this first. */
2939 if( IsMenu( mt.hTopMenu ) )
2941 menu = MENU_GetMenu( mt.hTopMenu );
2943 if( IsWindow( mt.hOwnerWnd ) )
2945 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2947 if (menu && menu->wFlags & MF_POPUP)
2949 DestroyWindow( menu->hWnd );
2950 menu->hWnd = 0;
2952 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2953 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2956 /* Reset the variable for hiding menu */
2957 if( menu ) menu->bTimeToHide = FALSE;
2960 /* The return value is only used by TrackPopupMenu */
2961 return ((executedMenuId != -1) ? executedMenuId : 0);
2964 /***********************************************************************
2965 * MENU_InitTracking
2967 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2969 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2971 HideCaret(0);
2973 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2974 if (!(wFlags & TPM_NONOTIFY))
2975 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2977 SendMessageA( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2979 if (!(wFlags & TPM_NONOTIFY))
2981 POPUPMENU *menu;
2982 SendMessageA( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2983 if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
2984 { /* app changed/recreated menu bar entries in WM_INITMENU
2985 Recalculate menu sizes else clicks will not work */
2986 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2987 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2991 return TRUE;
2993 /***********************************************************************
2994 * MENU_ExitTracking
2996 static BOOL MENU_ExitTracking(HWND hWnd)
2998 TRACE("hwnd=%p\n", hWnd);
3000 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
3001 ShowCaret(0);
3002 return TRUE;
3005 /***********************************************************************
3006 * MENU_TrackMouseMenuBar
3008 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3010 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3012 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3013 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3015 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
3017 if (IsMenu(hMenu))
3019 /* map point to parent client coordinates */
3020 HWND parent = GetAncestor( hWnd, GA_PARENT );
3021 if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );
3023 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3024 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3025 MENU_ExitTracking(hWnd);
3030 /***********************************************************************
3031 * MENU_TrackKbdMenuBar
3033 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3035 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, INT vkey)
3037 UINT uItem = NO_SELECTED_ITEM;
3038 HMENU hTrackMenu;
3039 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3041 /* find window that has a menu */
3043 while (GetWindowLongA( hwnd, GWL_STYLE ) & WS_CHILD)
3044 if (!(hwnd = GetParent( hwnd ))) return;
3046 /* check if we have to track a system menu */
3048 hTrackMenu = GetMenu( hwnd );
3049 if (!hTrackMenu || IsIconic(hwnd) || vkey == VK_SPACE )
3051 if (!(GetWindowLongA( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3052 hTrackMenu = get_win_sys_menu( hwnd );
3053 uItem = 0;
3054 wParam |= HTSYSMENU; /* prevent item lookup */
3057 if (!IsMenu( hTrackMenu )) return;
3059 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3061 if( vkey && vkey != VK_SPACE )
3063 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, vkey, (wParam & HTSYSMENU) );
3064 if( uItem >= (UINT)(-2) )
3066 if( uItem == (UINT)(-1) ) MessageBeep(0);
3067 hTrackMenu = 0;
3071 if( hTrackMenu )
3073 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3075 if( uItem == NO_SELECTED_ITEM )
3076 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3077 else if( vkey )
3078 PostMessageA( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3080 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3082 MENU_ExitTracking( hwnd );
3086 /**********************************************************************
3087 * TrackPopupMenu (USER32.@)
3089 * Like the win32 API, the function return the command ID only if the
3090 * flag TPM_RETURNCMD is on.
3093 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3094 INT nReserved, HWND hWnd, const RECT *lpRect )
3096 BOOL ret = FALSE;
3098 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3100 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3101 if (!(wFlags & TPM_NONOTIFY))
3102 SendMessageA( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3104 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3105 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3106 MENU_ExitTracking(hWnd);
3108 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3109 ret = 1;
3111 return ret;
3114 /**********************************************************************
3115 * TrackPopupMenuEx (USER32.@)
3117 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3118 HWND hWnd, LPTPMPARAMS lpTpm )
3120 FIXME("not fully implemented\n" );
3121 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3122 lpTpm ? &lpTpm->rcExclude : NULL );
3125 /***********************************************************************
3126 * PopupMenuWndProc
3128 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3130 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3132 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3134 switch(message)
3136 case WM_CREATE:
3138 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3139 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3140 return 0;
3143 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3144 return MA_NOACTIVATE;
3146 case WM_PAINT:
3148 PAINTSTRUCT ps;
3149 BeginPaint( hwnd, &ps );
3150 MENU_DrawPopupMenu( hwnd, ps.hdc,
3151 (HMENU)GetWindowLongA( hwnd, 0 ) );
3152 EndPaint( hwnd, &ps );
3153 return 0;
3155 case WM_ERASEBKGND:
3156 return 1;
3158 case WM_DESTROY:
3159 /* zero out global pointer in case resident popup window was destroyed. */
3160 if (hwnd == top_popup) top_popup = 0;
3161 break;
3163 case WM_SHOWWINDOW:
3165 if( wParam )
3167 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3169 else
3170 SetWindowLongW( hwnd, 0, 0 );
3171 break;
3173 case MM_SETMENUHANDLE:
3174 SetWindowLongW( hwnd, 0, wParam );
3175 break;
3177 case MM_GETMENUHANDLE:
3178 return GetWindowLongW( hwnd, 0 );
3180 default:
3181 return DefWindowProcW( hwnd, message, wParam, lParam );
3183 return 0;
3187 /***********************************************************************
3188 * MENU_GetMenuBarHeight
3190 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3192 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3193 INT orgX, INT orgY )
3195 HDC hdc;
3196 RECT rectBar;
3197 LPPOPUPMENU lppop;
3199 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3201 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3203 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3204 SelectObject( hdc, hMenuFont);
3205 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3206 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3207 ReleaseDC( hwnd, hdc );
3208 return lppop->Height;
3212 /*******************************************************************
3213 * ChangeMenuA (USER32.@)
3215 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3216 UINT id, UINT flags )
3218 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3219 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3220 id, data );
3221 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3222 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3223 id, data );
3224 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3225 flags & MF_BYPOSITION ? pos : id,
3226 flags & ~MF_REMOVE );
3227 /* Default: MF_INSERT */
3228 return InsertMenuA( hMenu, pos, flags, id, data );
3232 /*******************************************************************
3233 * ChangeMenuW (USER32.@)
3235 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3236 UINT id, UINT flags )
3238 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3239 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3240 id, data );
3241 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3242 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3243 id, data );
3244 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3245 flags & MF_BYPOSITION ? pos : id,
3246 flags & ~MF_REMOVE );
3247 /* Default: MF_INSERT */
3248 return InsertMenuW( hMenu, pos, flags, id, data );
3252 /*******************************************************************
3253 * CheckMenuItem (USER32.@)
3255 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3257 MENUITEM *item;
3258 DWORD ret;
3260 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3261 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3262 ret = item->fState & MF_CHECKED;
3263 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3264 else item->fState &= ~MF_CHECKED;
3265 return ret;
3269 /**********************************************************************
3270 * EnableMenuItem (USER32.@)
3272 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3274 UINT oldflags;
3275 MENUITEM *item;
3276 POPUPMENU *menu;
3278 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3280 /* Get the Popupmenu to access the owner menu */
3281 if (!(menu = MENU_GetMenu(hMenu)))
3282 return (UINT)-1;
3284 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3285 return (UINT)-1;
3287 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3288 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3290 /* In win95 if the close item in the system menu change update the close button */
3291 if (TWEAK_WineLook == WIN95_LOOK)
3292 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3294 if (menu->hSysMenuOwner != 0)
3296 POPUPMENU* parentMenu;
3298 /* Get the parent menu to access*/
3299 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3300 return (UINT)-1;
3302 /* Refresh the frame to reflect the change*/
3303 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3304 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3308 return oldflags;
3312 /*******************************************************************
3313 * GetMenuStringA (USER32.@)
3315 INT WINAPI GetMenuStringA(
3316 HMENU hMenu, /* [in] menuhandle */
3317 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3318 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3319 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3320 UINT wFlags /* [in] MF_ flags */
3322 MENUITEM *item;
3324 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3325 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3326 if (!IS_STRING_ITEM(item->fType)) return 0;
3327 if (!str || !nMaxSiz) return strlenW(item->text);
3328 str[0] = '\0';
3329 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3330 str[nMaxSiz-1] = 0;
3331 TRACE("returning '%s'\n", str );
3332 return strlen(str);
3336 /*******************************************************************
3337 * GetMenuStringW (USER32.@)
3339 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3340 LPWSTR str, INT nMaxSiz, UINT wFlags )
3342 MENUITEM *item;
3344 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3345 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3346 if (!IS_STRING_ITEM(item->fType)) return 0;
3347 if (!str || !nMaxSiz) return strlenW(item->text);
3348 str[0] = '\0';
3349 lstrcpynW( str, item->text, nMaxSiz );
3350 return strlenW(str);
3354 /**********************************************************************
3355 * HiliteMenuItem (USER32.@)
3357 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3358 UINT wHilite )
3360 LPPOPUPMENU menu;
3361 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3362 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3363 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3364 if (menu->FocusedItem == wItemID) return TRUE;
3365 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3366 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3367 return TRUE;
3371 /**********************************************************************
3372 * GetMenuState (USER32.@)
3374 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3376 MENUITEM *item;
3377 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3378 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3379 debug_print_menuitem (" item: ", item, "");
3380 if (item->fType & MF_POPUP)
3382 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3383 if (!menu) return -1;
3384 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3386 else
3388 /* We used to (from way back then) mask the result to 0xff. */
3389 /* I don't know why and it seems wrong as the documented */
3390 /* return flag MF_SEPARATOR is outside that mask. */
3391 return (item->fType | item->fState);
3396 /**********************************************************************
3397 * GetMenuItemCount (USER32.@)
3399 INT WINAPI GetMenuItemCount( HMENU hMenu )
3401 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3402 if (!menu) return -1;
3403 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3404 return menu->nItems;
3408 /**********************************************************************
3409 * GetMenuItemID (USER32.@)
3411 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3413 MENUITEM * lpmi;
3415 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3416 if (lpmi->fType & MF_POPUP) return -1;
3417 return lpmi->wID;
3422 /*******************************************************************
3423 * InsertMenuW (USER32.@)
3425 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3426 UINT_PTR id, LPCWSTR str )
3428 MENUITEM *item;
3430 if (IS_STRING_ITEM(flags) && str)
3431 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3432 hMenu, pos, flags, id, debugstr_w(str) );
3433 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3434 hMenu, pos, flags, id, (DWORD)str );
3436 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3438 if (!(MENU_SetItemData( item, flags, id, str )))
3440 RemoveMenu( hMenu, pos, flags );
3441 return FALSE;
3444 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3445 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3447 item->hCheckBit = item->hUnCheckBit = 0;
3448 return TRUE;
3452 /*******************************************************************
3453 * InsertMenuA (USER32.@)
3455 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3456 UINT_PTR id, LPCSTR str )
3458 BOOL ret = FALSE;
3460 if (IS_STRING_ITEM(flags) && str)
3462 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3463 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3464 if (newstr)
3466 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3467 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3468 HeapFree( GetProcessHeap(), 0, newstr );
3470 return ret;
3472 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3476 /*******************************************************************
3477 * AppendMenuA (USER32.@)
3479 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3480 UINT_PTR id, LPCSTR data )
3482 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3486 /*******************************************************************
3487 * AppendMenuW (USER32.@)
3489 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3490 UINT_PTR id, LPCWSTR data )
3492 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3496 /**********************************************************************
3497 * RemoveMenu (USER32.@)
3499 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3501 LPPOPUPMENU menu;
3502 MENUITEM *item;
3504 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3505 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3506 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3508 /* Remove item */
3510 MENU_FreeItemData( item );
3512 if (--menu->nItems == 0)
3514 HeapFree( GetProcessHeap(), 0, menu->items );
3515 menu->items = NULL;
3517 else
3519 while(nPos < menu->nItems)
3521 *item = *(item+1);
3522 item++;
3523 nPos++;
3525 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3526 menu->nItems * sizeof(MENUITEM) );
3528 return TRUE;
3532 /**********************************************************************
3533 * DeleteMenu (USER32.@)
3535 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3537 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3538 if (!item) return FALSE;
3539 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3540 /* nPos is now the position of the item */
3541 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3542 return TRUE;
3546 /*******************************************************************
3547 * ModifyMenuW (USER32.@)
3549 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3550 UINT_PTR id, LPCWSTR str )
3552 MENUITEM *item;
3554 if (IS_STRING_ITEM(flags))
3556 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3557 if (!str) return FALSE;
3559 else
3561 TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3564 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3565 return MENU_SetItemData( item, flags, id, str );
3569 /*******************************************************************
3570 * ModifyMenuA (USER32.@)
3572 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3573 UINT_PTR id, LPCSTR str )
3575 BOOL ret = FALSE;
3577 if (IS_STRING_ITEM(flags) && str)
3579 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3580 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3581 if (newstr)
3583 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3584 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3585 HeapFree( GetProcessHeap(), 0, newstr );
3587 return ret;
3589 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3593 /**********************************************************************
3594 * CreatePopupMenu (USER32.@)
3596 HMENU WINAPI CreatePopupMenu(void)
3598 HMENU hmenu;
3599 POPUPMENU *menu;
3601 if (!(hmenu = CreateMenu())) return 0;
3602 menu = MENU_GetMenu( hmenu );
3603 menu->wFlags |= MF_POPUP;
3604 menu->bTimeToHide = FALSE;
3605 return hmenu;
3609 /**********************************************************************
3610 * GetMenuCheckMarkDimensions (USER.417)
3611 * GetMenuCheckMarkDimensions (USER32.@)
3613 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3615 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3619 /**********************************************************************
3620 * SetMenuItemBitmaps (USER32.@)
3622 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3623 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3625 MENUITEM *item;
3626 TRACE("(%p, %04x, %04x, %p, %p)\n",
3627 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3628 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3630 if (!hNewCheck && !hNewUnCheck)
3632 item->fState &= ~MF_USECHECKBITMAPS;
3634 else /* Install new bitmaps */
3636 item->hCheckBit = hNewCheck;
3637 item->hUnCheckBit = hNewUnCheck;
3638 item->fState |= MF_USECHECKBITMAPS;
3640 return TRUE;
3644 /**********************************************************************
3645 * CreateMenu (USER32.@)
3647 HMENU WINAPI CreateMenu(void)
3649 HMENU hMenu;
3650 LPPOPUPMENU menu;
3651 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3652 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3654 ZeroMemory(menu, sizeof(POPUPMENU));
3655 menu->wMagic = MENU_MAGIC;
3656 menu->FocusedItem = NO_SELECTED_ITEM;
3657 menu->bTimeToHide = FALSE;
3659 TRACE("return %p\n", hMenu );
3661 return hMenu;
3665 /**********************************************************************
3666 * DestroyMenu (USER32.@)
3668 BOOL WINAPI DestroyMenu( HMENU hMenu )
3670 TRACE("(%p)\n", hMenu);
3672 /* Silently ignore attempts to destroy default system popup */
3674 if (hMenu && hMenu != MENU_DefSysPopup)
3676 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3678 if (!lppop) return FALSE;
3680 lppop->wMagic = 0; /* Mark it as destroyed */
3682 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3684 DestroyWindow( lppop->hWnd );
3685 lppop->hWnd = 0;
3688 if (lppop->items) /* recursively destroy submenus */
3690 int i;
3691 MENUITEM *item = lppop->items;
3692 for (i = lppop->nItems; i > 0; i--, item++)
3694 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3695 MENU_FreeItemData( item );
3697 HeapFree( GetProcessHeap(), 0, lppop->items );
3699 USER_HEAP_FREE( hMenu );
3701 return (hMenu != MENU_DefSysPopup);
3705 /**********************************************************************
3706 * GetSystemMenu (USER32.@)
3708 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3710 WND *wndPtr = WIN_FindWndPtr( hWnd );
3711 HMENU retvalue = 0;
3713 if (wndPtr)
3715 if( wndPtr->hSysMenu )
3717 if( bRevert )
3719 DestroyMenu(wndPtr->hSysMenu);
3720 wndPtr->hSysMenu = 0;
3722 else
3724 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3725 if( menu )
3727 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3728 menu->items[0].hSubMenu = MENU_CopySysPopup();
3730 else
3732 WARN("Current sys-menu (%p) of wnd %p is broken\n",
3733 wndPtr->hSysMenu, hWnd);
3734 wndPtr->hSysMenu = 0;
3739 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3740 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3742 if( wndPtr->hSysMenu )
3744 POPUPMENU *menu;
3745 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3747 /* Store the dummy sysmenu handle to facilitate the refresh */
3748 /* of the close button if the SC_CLOSE item change */
3749 menu = MENU_GetMenu(retvalue);
3750 if ( menu )
3751 menu->hSysMenuOwner = wndPtr->hSysMenu;
3753 WIN_ReleaseWndPtr(wndPtr);
3755 return bRevert ? 0 : retvalue;
3759 /*******************************************************************
3760 * SetSystemMenu (USER32.@)
3762 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3764 WND *wndPtr = WIN_FindWndPtr(hwnd);
3766 if (wndPtr)
3768 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3769 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3770 WIN_ReleaseWndPtr(wndPtr);
3771 return TRUE;
3773 return FALSE;
3777 /**********************************************************************
3778 * GetMenu (USER32.@)
3780 HMENU WINAPI GetMenu( HWND hWnd )
3782 HMENU retvalue = (HMENU)GetWindowLongA( hWnd, GWL_ID );
3783 TRACE("for %p returning %p\n", hWnd, retvalue);
3784 return retvalue;
3788 /**********************************************************************
3789 * SetMenu (USER32.@)
3791 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3793 TRACE("(%p, %p);\n", hWnd, hMenu);
3795 if (hMenu && !IsMenu(hMenu))
3797 WARN("hMenu %p is not a menu handle\n", hMenu);
3798 return FALSE;
3800 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3802 hWnd = WIN_GetFullHandle( hWnd );
3803 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
3805 if (hMenu != 0)
3807 LPPOPUPMENU lpmenu;
3809 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3811 lpmenu->hWnd = hWnd;
3812 lpmenu->Height = 0; /* Make sure we recalculate the size */
3814 SetWindowLongA( hWnd, GWL_ID, (LONG_PTR)hMenu );
3816 if (IsWindowVisible(hWnd))
3817 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3818 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3819 return TRUE;
3824 /**********************************************************************
3825 * GetSubMenu (USER32.@)
3827 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3829 MENUITEM * lpmi;
3831 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3832 if (!(lpmi->fType & MF_POPUP)) return 0;
3833 return lpmi->hSubMenu;
3837 /**********************************************************************
3838 * DrawMenuBar (USER32.@)
3840 BOOL WINAPI DrawMenuBar( HWND hWnd )
3842 LPPOPUPMENU lppop;
3843 HMENU hMenu = GetMenu(hWnd);
3845 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3846 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3848 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3849 lppop->hwndOwner = hWnd;
3850 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3851 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3852 return TRUE;
3855 /***********************************************************************
3856 * DrawMenuBarTemp (USER32.@)
3858 * UNDOCUMENTED !!
3860 * called by W98SE desk.cpl Control Panel Applet
3862 * Not 100% sure about the param names, but close.
3864 DWORD WINAPI DrawMenuBarTemp(HWND someHWND, HDC someHDC, LPRECT someRECT, HMENU someHMENU, HFONT someFONT)
3866 FIXME("(%p, %p, %p, %p, %p): stub\n", someHWND, someHDC, someRECT, someHMENU, someFONT);
3867 return 0;
3870 /***********************************************************************
3871 * EndMenu (USER.187)
3872 * EndMenu (USER32.@)
3874 void WINAPI EndMenu(void)
3876 /* if we are in the menu code, and it is active */
3877 if (!fEndMenu && top_popup)
3879 /* terminate the menu handling code */
3880 fEndMenu = TRUE;
3882 /* needs to be posted to wakeup the internal menu handler */
3883 /* which will now terminate the menu, in the event that */
3884 /* the main window was minimized, or lost focus, so we */
3885 /* don't end up with an orphaned menu */
3886 PostMessageA( top_popup, WM_CANCELMODE, 0, 0);
3891 /***********************************************************************
3892 * LookupMenuHandle (USER.217)
3894 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3896 HMENU hmenu32 = HMENU_32(hmenu);
3897 UINT id32 = id;
3898 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3899 else return HMENU_16(hmenu32);
3903 /**********************************************************************
3904 * LoadMenu (USER.150)
3906 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3908 HRSRC16 hRsrc;
3909 HGLOBAL16 handle;
3910 HMENU16 hMenu;
3912 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3913 if (!name) return 0;
3915 instance = GetExePtr( instance );
3916 if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
3917 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3918 hMenu = LoadMenuIndirect16(LockResource16(handle));
3919 FreeResource16( handle );
3920 return hMenu;
3924 /*****************************************************************
3925 * LoadMenuA (USER32.@)
3927 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3929 HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
3930 if (!hrsrc) return 0;
3931 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3935 /*****************************************************************
3936 * LoadMenuW (USER32.@)
3938 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3940 HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
3941 if (!hrsrc) return 0;
3942 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
3946 /**********************************************************************
3947 * LoadMenuIndirect (USER.220)
3949 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3951 HMENU hMenu;
3952 WORD version, offset;
3953 LPCSTR p = (LPCSTR)template;
3955 TRACE("(%p)\n", template );
3956 version = GET_WORD(p);
3957 p += sizeof(WORD);
3958 if (version)
3960 WARN("version must be 0 for Win16\n" );
3961 return 0;
3963 offset = GET_WORD(p);
3964 p += sizeof(WORD) + offset;
3965 if (!(hMenu = CreateMenu())) return 0;
3966 if (!MENU_ParseResource( p, hMenu, FALSE ))
3968 DestroyMenu( hMenu );
3969 return 0;
3971 return HMENU_16(hMenu);
3975 /**********************************************************************
3976 * LoadMenuIndirectA (USER32.@)
3978 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
3980 HMENU hMenu;
3981 WORD version, offset;
3982 LPCSTR p = (LPCSTR)template;
3984 version = GET_WORD(p);
3985 p += sizeof(WORD);
3986 TRACE("%p, ver %d\n", template, version );
3987 switch (version)
3989 case 0: /* standard format is version of 0 */
3990 offset = GET_WORD(p);
3991 p += sizeof(WORD) + offset;
3992 if (!(hMenu = CreateMenu())) return 0;
3993 if (!MENU_ParseResource( p, hMenu, TRUE ))
3995 DestroyMenu( hMenu );
3996 return 0;
3998 return hMenu;
3999 case 1: /* extended format is version of 1 */
4000 offset = GET_WORD(p);
4001 p += sizeof(WORD) + offset;
4002 if (!(hMenu = CreateMenu())) return 0;
4003 if (!MENUEX_ParseResource( p, hMenu))
4005 DestroyMenu( hMenu );
4006 return 0;
4008 return hMenu;
4009 default:
4010 ERR("version %d not supported.\n", version);
4011 return 0;
4016 /**********************************************************************
4017 * LoadMenuIndirectW (USER32.@)
4019 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4021 /* FIXME: is there anything different between A and W? */
4022 return LoadMenuIndirectA( template );
4026 /**********************************************************************
4027 * IsMenu (USER32.@)
4029 BOOL WINAPI IsMenu(HMENU hmenu)
4031 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4032 return menu != NULL;
4035 /**********************************************************************
4036 * GetMenuItemInfo_common
4039 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4040 LPMENUITEMINFOW lpmii, BOOL unicode)
4042 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4044 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4046 if (!menu)
4047 return FALSE;
4049 if (lpmii->fMask & MIIM_TYPE) {
4050 lpmii->fType = menu->fType;
4051 switch (MENU_ITEM_TYPE(menu->fType)) {
4052 case MF_STRING:
4053 break; /* will be done below */
4054 case MF_OWNERDRAW:
4055 case MF_BITMAP:
4056 lpmii->dwTypeData = menu->text;
4057 /* fall through */
4058 default:
4059 lpmii->cch = 0;
4063 /* copy the text string */
4064 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4065 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4067 int len;
4068 if (unicode)
4070 len = strlenW(menu->text);
4071 if(lpmii->dwTypeData && lpmii->cch)
4072 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4074 else
4076 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4077 if(lpmii->dwTypeData && lpmii->cch)
4078 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4079 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4080 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4082 /* if we've copied a substring we return its length */
4083 if(lpmii->dwTypeData && lpmii->cch)
4085 if (lpmii->cch <= len) lpmii->cch--;
4087 else /* return length of string */
4088 lpmii->cch = len;
4091 if (lpmii->fMask & MIIM_FTYPE)
4092 lpmii->fType = menu->fType;
4094 if (lpmii->fMask & MIIM_BITMAP)
4095 lpmii->hbmpItem = menu->hbmpItem;
4097 if (lpmii->fMask & MIIM_STATE)
4098 lpmii->fState = menu->fState;
4100 if (lpmii->fMask & MIIM_ID)
4101 lpmii->wID = menu->wID;
4103 if (lpmii->fMask & MIIM_SUBMENU)
4104 lpmii->hSubMenu = menu->hSubMenu;
4106 if (lpmii->fMask & MIIM_CHECKMARKS) {
4107 lpmii->hbmpChecked = menu->hCheckBit;
4108 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4110 if (lpmii->fMask & MIIM_DATA)
4111 lpmii->dwItemData = menu->dwItemData;
4113 return TRUE;
4116 /**********************************************************************
4117 * GetMenuItemInfoA (USER32.@)
4119 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4120 LPMENUITEMINFOA lpmii)
4122 return GetMenuItemInfo_common (hmenu, item, bypos,
4123 (LPMENUITEMINFOW)lpmii, FALSE);
4126 /**********************************************************************
4127 * GetMenuItemInfoW (USER32.@)
4129 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4130 LPMENUITEMINFOW lpmii)
4132 return GetMenuItemInfo_common (hmenu, item, bypos,
4133 lpmii, TRUE);
4137 /* set a menu item text from a ASCII or Unicode string */
4138 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4140 if (!text)
4142 menu->text = NULL;
4143 menu->fType |= MF_SEPARATOR;
4145 else if (unicode)
4147 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4148 strcpyW( menu->text, text );
4150 else
4152 LPCSTR str = (LPCSTR)text;
4153 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4154 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4155 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4160 /**********************************************************************
4161 * SetMenuItemInfo_common
4164 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4165 const MENUITEMINFOW *lpmii,
4166 BOOL unicode)
4168 if (!menu) return FALSE;
4170 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4172 if (lpmii->fMask & MIIM_TYPE ) {
4173 /* Get rid of old string. */
4174 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4175 HeapFree(GetProcessHeap(), 0, menu->text);
4176 menu->text = NULL;
4179 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4180 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4181 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4183 menu->text = lpmii->dwTypeData;
4185 if (IS_STRING_ITEM(menu->fType))
4186 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4189 if (lpmii->fMask & MIIM_FTYPE ) {
4190 /* free the string when the type is changing */
4191 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4192 HeapFree(GetProcessHeap(), 0, menu->text);
4193 menu->text = NULL;
4195 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4196 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4197 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4198 menu->fType |= MF_SEPARATOR;
4201 if (lpmii->fMask & MIIM_STRING ) {
4202 /* free the string when used */
4203 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4204 HeapFree(GetProcessHeap(), 0, menu->text);
4205 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4209 if (lpmii->fMask & MIIM_STATE)
4211 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4212 menu->fState = lpmii->fState;
4215 if (lpmii->fMask & MIIM_ID)
4216 menu->wID = lpmii->wID;
4218 if (lpmii->fMask & MIIM_SUBMENU) {
4219 menu->hSubMenu = lpmii->hSubMenu;
4220 if (menu->hSubMenu) {
4221 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4222 if (subMenu) {
4223 subMenu->wFlags |= MF_POPUP;
4224 menu->fType |= MF_POPUP;
4226 else
4227 /* FIXME: Return an error ? */
4228 menu->fType &= ~MF_POPUP;
4230 else
4231 menu->fType &= ~MF_POPUP;
4234 if (lpmii->fMask & MIIM_CHECKMARKS)
4236 if (lpmii->fType & MFT_RADIOCHECK)
4237 menu->fType |= MFT_RADIOCHECK;
4239 menu->hCheckBit = lpmii->hbmpChecked;
4240 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4242 if (lpmii->fMask & MIIM_DATA)
4243 menu->dwItemData = lpmii->dwItemData;
4245 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4246 return TRUE;
4249 /**********************************************************************
4250 * SetMenuItemInfoA (USER32.@)
4252 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4253 const MENUITEMINFOA *lpmii)
4255 if ((lpmii->fType & (MF_HILITE|MF_POPUP)) || (lpmii->fState)) {
4256 /* QuickTime does pass invalid data into SetMenuItemInfo.
4257 * do some of the checks Windows does.
4259 WARN("Bad masks for type (0x%08x) or state (0x%08x)\n",
4260 lpmii->fType,lpmii->fState );
4261 return FALSE;
4264 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4265 (const MENUITEMINFOW *)lpmii, FALSE);
4268 /**********************************************************************
4269 * SetMenuItemInfoW (USER32.@)
4271 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4272 const MENUITEMINFOW *lpmii)
4274 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4275 lpmii, TRUE);
4278 /**********************************************************************
4279 * SetMenuDefaultItem (USER32.@)
4282 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4284 UINT i;
4285 POPUPMENU *menu;
4286 MENUITEM *item;
4288 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4290 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4292 /* reset all default-item flags */
4293 item = menu->items;
4294 for (i = 0; i < menu->nItems; i++, item++)
4296 item->fState &= ~MFS_DEFAULT;
4299 /* no default item */
4300 if ( -1 == uItem)
4302 return TRUE;
4305 item = menu->items;
4306 if ( bypos )
4308 if ( uItem >= menu->nItems ) return FALSE;
4309 item[uItem].fState |= MFS_DEFAULT;
4310 return TRUE;
4312 else
4314 for (i = 0; i < menu->nItems; i++, item++)
4316 if (item->wID == uItem)
4318 item->fState |= MFS_DEFAULT;
4319 return TRUE;
4324 return FALSE;
4327 /**********************************************************************
4328 * GetMenuDefaultItem (USER32.@)
4330 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4332 POPUPMENU *menu;
4333 MENUITEM * item;
4334 UINT i = 0;
4336 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4338 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4340 /* find default item */
4341 item = menu->items;
4343 /* empty menu */
4344 if (! item) return -1;
4346 while ( !( item->fState & MFS_DEFAULT ) )
4348 i++; item++;
4349 if (i >= menu->nItems ) return -1;
4352 /* default: don't return disabled items */
4353 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4355 /* search rekursiv when needed */
4356 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4358 UINT ret;
4359 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4360 if ( -1 != ret ) return ret;
4362 /* when item not found in submenu, return the popup item */
4364 return ( bypos ) ? i : item->wID;
4369 /**********************************************************************
4370 * InsertMenuItemA (USER32.@)
4372 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4373 const MENUITEMINFOA *lpmii)
4375 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4376 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4380 /**********************************************************************
4381 * InsertMenuItemW (USER32.@)
4383 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4384 const MENUITEMINFOW *lpmii)
4386 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4387 return SetMenuItemInfo_common(item, lpmii, TRUE);
4390 /**********************************************************************
4391 * CheckMenuRadioItem (USER32.@)
4394 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4395 UINT first, UINT last, UINT check,
4396 UINT bypos)
4398 MENUITEM *mifirst, *milast, *micheck;
4399 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4401 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4403 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4404 milast = MENU_FindItem (&mlast, &last, bypos);
4405 micheck = MENU_FindItem (&mcheck, &check, bypos);
4407 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4408 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4409 micheck > milast || micheck < mifirst)
4410 return FALSE;
4412 while (mifirst <= milast)
4414 if (mifirst == micheck)
4416 mifirst->fType |= MFT_RADIOCHECK;
4417 mifirst->fState |= MFS_CHECKED;
4418 } else {
4419 mifirst->fType &= ~MFT_RADIOCHECK;
4420 mifirst->fState &= ~MFS_CHECKED;
4422 mifirst++;
4425 return TRUE;
4429 /**********************************************************************
4430 * GetMenuItemRect (USER32.@)
4432 * ATTENTION: Here, the returned values in rect are the screen
4433 * coordinates of the item just like if the menu was
4434 * always on the upper left side of the application.
4437 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4438 LPRECT rect)
4440 POPUPMENU *itemMenu;
4441 MENUITEM *item;
4442 HWND referenceHwnd;
4444 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4446 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4447 referenceHwnd = hwnd;
4449 if(!hwnd)
4451 itemMenu = MENU_GetMenu(hMenu);
4452 if (itemMenu == NULL)
4453 return FALSE;
4455 if(itemMenu->hWnd == 0)
4456 return FALSE;
4457 referenceHwnd = itemMenu->hWnd;
4460 if ((rect == NULL) || (item == NULL))
4461 return FALSE;
4463 *rect = item->rect;
4465 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4467 return TRUE;
4471 /**********************************************************************
4472 * SetMenuInfo (USER32.@)
4474 * FIXME
4475 * MIM_APPLYTOSUBMENUS
4476 * actually use the items to draw the menu
4478 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4480 POPUPMENU *menu;
4482 TRACE("(%p %p)\n", hMenu, lpmi);
4484 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4487 if (lpmi->fMask & MIM_BACKGROUND)
4488 menu->hbrBack = lpmi->hbrBack;
4490 if (lpmi->fMask & MIM_HELPID)
4491 menu->dwContextHelpID = lpmi->dwContextHelpID;
4493 if (lpmi->fMask & MIM_MAXHEIGHT)
4494 menu->cyMax = lpmi->cyMax;
4496 if (lpmi->fMask & MIM_MENUDATA)
4497 menu->dwMenuData = lpmi->dwMenuData;
4499 if (lpmi->fMask & MIM_STYLE)
4500 menu->dwStyle = lpmi->dwStyle;
4502 return TRUE;
4504 return FALSE;
4507 /**********************************************************************
4508 * GetMenuInfo (USER32.@)
4510 * NOTES
4511 * win98/NT5.0
4514 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4515 { POPUPMENU *menu;
4517 TRACE("(%p %p)\n", hMenu, lpmi);
4519 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4522 if (lpmi->fMask & MIM_BACKGROUND)
4523 lpmi->hbrBack = menu->hbrBack;
4525 if (lpmi->fMask & MIM_HELPID)
4526 lpmi->dwContextHelpID = menu->dwContextHelpID;
4528 if (lpmi->fMask & MIM_MAXHEIGHT)
4529 lpmi->cyMax = menu->cyMax;
4531 if (lpmi->fMask & MIM_MENUDATA)
4532 lpmi->dwMenuData = menu->dwMenuData;
4534 if (lpmi->fMask & MIM_STYLE)
4535 lpmi->dwStyle = menu->dwStyle;
4537 return TRUE;
4539 return FALSE;
4543 /**********************************************************************
4544 * SetMenuContextHelpId (USER32.@)
4546 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4548 LPPOPUPMENU menu;
4550 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4552 if ((menu = MENU_GetMenu(hMenu)))
4554 menu->dwContextHelpID = dwContextHelpID;
4555 return TRUE;
4557 return FALSE;
4561 /**********************************************************************
4562 * GetMenuContextHelpId (USER32.@)
4564 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4566 LPPOPUPMENU menu;
4568 TRACE("(%p)\n", hMenu);
4570 if ((menu = MENU_GetMenu(hMenu)))
4572 return menu->dwContextHelpID;
4574 return 0;
4577 /**********************************************************************
4578 * MenuItemFromPoint (USER32.@)
4580 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4582 POPUPMENU *menu = MENU_GetMenu(hMenu);
4583 UINT pos;
4584 MENUITEM *item;
4586 /*FIXME: Do we have to handle hWnd here? */
4587 item = MENU_FindItemByCoords(menu, ptScreen, &pos);
4589 return pos;
4593 /**********************************************************************
4594 * translate_accelerator
4596 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4597 BYTE fVirt, WORD key, WORD cmd )
4599 UINT mesg = 0;
4601 if (wParam != key) return FALSE;
4603 if (message == WM_CHAR)
4605 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4607 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4608 goto found;
4611 else
4613 if(fVirt & FVIRTKEY)
4615 INT mask = 0;
4616 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4617 wParam, 0xff & HIWORD(lParam));
4618 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4619 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4620 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4621 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4622 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4624 else
4626 if (!(lParam & 0x01000000)) /* no special_key */
4628 if ((fVirt & FALT) && (lParam & 0x20000000))
4629 { /* ^^ ALT pressed */
4630 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4631 goto found;
4636 return FALSE;
4638 found:
4639 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4640 mesg = 1;
4641 else if (GetCapture())
4642 mesg = 2;
4643 else if (!IsWindowEnabled(hWnd))
4644 mesg = 3;
4645 else
4647 HMENU hMenu, hSubMenu, hSysMenu;
4648 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4650 hMenu = (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4651 hSysMenu = get_win_sys_menu( hWnd );
4653 /* find menu item and ask application to initialize it */
4654 /* 1. in the system menu */
4655 hSubMenu = hSysMenu;
4656 nPos = cmd;
4657 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4659 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4660 if(hSubMenu != hSysMenu)
4662 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4663 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4664 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4666 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4668 else /* 2. in the window's menu */
4670 hSubMenu = hMenu;
4671 nPos = cmd;
4672 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4674 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4675 if(hSubMenu != hMenu)
4677 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4678 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4679 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4681 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4685 if (uSysStat != (UINT)-1)
4687 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4688 mesg=4;
4689 else
4690 mesg=WM_SYSCOMMAND;
4692 else
4694 if (uStat != (UINT)-1)
4696 if (IsIconic(hWnd))
4697 mesg=5;
4698 else
4700 if (uStat & (MF_DISABLED|MF_GRAYED))
4701 mesg=6;
4702 else
4703 mesg=WM_COMMAND;
4706 else
4707 mesg=WM_COMMAND;
4711 if( mesg==WM_COMMAND )
4713 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4714 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
4716 else if( mesg==WM_SYSCOMMAND )
4718 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4719 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
4721 else
4723 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4724 * #0: unknown (please report!)
4725 * #1: for WM_KEYUP,WM_SYSKEYUP
4726 * #2: mouse is captured
4727 * #3: window is disabled
4728 * #4: it's a disabled system menu option
4729 * #5: it's a menu option, but window is iconic
4730 * #6: it's a menu option, but disabled
4732 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4733 if(mesg==0)
4734 ERR_(accel)(" unknown reason - please report!");
4736 return TRUE;
4739 /**********************************************************************
4740 * TranslateAccelerator (USER32.@)
4741 * TranslateAcceleratorA (USER32.@)
4742 * TranslateAcceleratorW (USER32.@)
4744 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
4746 /* YES, Accel16! */
4747 LPACCEL16 lpAccelTbl;
4748 int i;
4750 if (msg == NULL)
4752 WARN_(accel)("msg null; should hang here to be win compatible\n");
4753 return 0;
4755 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4757 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4758 return 0;
4760 if ((msg->message != WM_KEYDOWN &&
4761 msg->message != WM_KEYUP &&
4762 msg->message != WM_SYSKEYDOWN &&
4763 msg->message != WM_SYSKEYUP &&
4764 msg->message != WM_CHAR)) return 0;
4766 TRACE_(accel)("TranslateAccelerators hAccel=%p, hWnd=%p,"
4767 "msg->hwnd=%p, msg->message=%04x, wParam=%08x, lParam=%lx\n",
4768 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4770 i = 0;
4773 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4774 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4775 return 1;
4776 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4777 WARN_(accel)("couldn't translate accelerator key\n");
4778 return 0;