fget(w)s(): Don't drop characters when buffer is not large enough.
[wine/multimedia.git] / controls / menu.c
blob4bfbf0e6407c0bcad19bf63c7857d3ccb5bd7696
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 <stdarg.h>
35 #include <stdlib.h>
36 #include <string.h>
38 #include "windef.h"
39 #include "winbase.h"
40 #include "wingdi.h"
41 #include "winnls.h"
42 #include "wine/winbase16.h"
43 #include "wine/winuser16.h"
44 #include "wownt32.h"
45 #include "wine/server.h"
46 #include "wine/unicode.h"
47 #include "win.h"
48 #include "controls.h"
49 #include "nonclient.h"
50 #include "user.h"
51 #include "wine/debug.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(menu);
54 WINE_DECLARE_DEBUG_CHANNEL(accel);
56 /* internal popup menu window messages */
58 #define MM_SETMENUHANDLE (WM_USER + 0)
59 #define MM_GETMENUHANDLE (WM_USER + 1)
61 /* Menu item structure */
62 typedef struct {
63 /* ----------- MENUITEMINFO Stuff ----------- */
64 UINT fType; /* Item type. */
65 UINT fState; /* Item state. */
66 UINT_PTR wID; /* Item id. */
67 HMENU hSubMenu; /* Pop-up menu. */
68 HBITMAP hCheckBit; /* Bitmap when checked. */
69 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
70 LPWSTR text; /* Item text or bitmap handle. */
71 DWORD dwItemData; /* Application defined. */
72 DWORD dwTypeData; /* depends on fMask */
73 HBITMAP hbmpItem; /* bitmap in win98 style menus */
74 /* ----------- Wine stuff ----------- */
75 RECT rect; /* Item area (relative to menu window) */
76 UINT xTab; /* X position of text after Tab */
77 } MENUITEM;
79 /* Popup menu structure */
80 typedef struct {
81 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
82 WORD wMagic; /* Magic number */
83 WORD Width; /* Width of the whole menu */
84 WORD Height; /* Height of the whole menu */
85 UINT nItems; /* Number of items in the menu */
86 HWND hWnd; /* Window containing the menu */
87 MENUITEM *items; /* Array of menu items */
88 UINT FocusedItem; /* Currently focused item */
89 HWND hwndOwner; /* window receiving the messages for ownerdraw */
90 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
91 /* ------------ MENUINFO members ------ */
92 DWORD dwStyle; /* Extended mennu style */
93 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
94 HBRUSH hbrBack; /* brush for menu background */
95 DWORD dwContextHelpID;
96 DWORD dwMenuData; /* application defined value */
97 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
98 } POPUPMENU, *LPPOPUPMENU;
100 /* internal flags for menu tracking */
102 #define TF_ENDMENU 0x0001
103 #define TF_SUSPENDPOPUP 0x0002
104 #define TF_SKIPREMOVE 0x0004
106 typedef struct
108 UINT trackFlags;
109 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
110 HMENU hTopMenu; /* initial menu */
111 HWND hOwnerWnd; /* where notifications are sent */
112 POINT pt;
113 } MTRACKER;
115 #define MENU_MAGIC 0x554d /* 'MU' */
117 #define ITEM_PREV -1
118 #define ITEM_NEXT 1
120 /* Internal MENU_TrackMenu() flags */
121 #define TPM_INTERNAL 0xF0000000
122 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
123 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
124 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
126 /* popup menu shade thickness */
127 #define POPUP_XSHADE 4
128 #define POPUP_YSHADE 4
130 /* Space between 2 menu bar items */
131 #define MENU_BAR_ITEMS_SPACE 12
133 /* Minimum width of a tab character */
134 #define MENU_TAB_SPACE 8
136 /* Height of a separator item */
137 #define SEPARATOR_HEIGHT 5
139 /* (other menu->FocusedItem values give the position of the focused item) */
140 #define NO_SELECTED_ITEM 0xffff
142 #define MENU_ITEM_TYPE(flags) \
143 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
145 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
146 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
147 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
149 #define IS_SYSTEM_MENU(menu) \
150 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
152 #define IS_SYSTEM_POPUP(menu) \
153 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
155 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
156 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
157 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
158 MF_POPUP | MF_SYSMENU | MF_HELP)
159 #define STATE_MASK (~TYPE_MASK)
161 /* Dimension of the menu bitmaps */
162 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
164 static HBITMAP hStdMnArrow = 0;
165 static HBITMAP hBmpSysMenu = 0;
167 static HBRUSH hShadeBrush = 0;
168 static HFONT hMenuFont = 0;
169 static HFONT hMenuFontBold = 0;
171 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
173 /* Use global popup window because there's no way 2 menus can
174 * be tracked at the same time. */
175 static HWND top_popup;
177 /* Flag set by EndMenu() to force an exit from menu tracking */
178 static BOOL fEndMenu = FALSE;
180 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
182 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
184 /*********************************************************************
185 * menu class descriptor
187 const struct builtin_class_descr MENU_builtin_class =
189 POPUPMENU_CLASS_ATOM, /* name */
190 CS_GLOBALCLASS | CS_SAVEBITS, /* style */
191 NULL, /* procA (winproc is Unicode only) */
192 PopupMenuWndProc, /* procW */
193 sizeof(HMENU), /* extra */
194 IDC_ARROW, /* cursor */
195 (HBRUSH)(COLOR_MENU+1) /* brush */
199 /***********************************************************************
200 * debug_print_menuitem
202 * Print a menuitem in readable form.
205 #define debug_print_menuitem(pre, mp, post) \
206 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
208 #define MENUOUT(text) \
209 DPRINTF("%s%s", (count++ ? "," : ""), (text))
211 #define MENUFLAG(bit,text) \
212 do { \
213 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
214 } while (0)
216 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
217 const char *postfix)
219 TRACE("%s ", prefix);
220 if (mp) {
221 UINT flags = mp->fType;
222 int type = MENU_ITEM_TYPE(flags);
223 DPRINTF( "{ ID=0x%x", mp->wID);
224 if (flags & MF_POPUP)
225 DPRINTF( ", Sub=%p", mp->hSubMenu);
226 if (flags) {
227 int count = 0;
228 DPRINTF( ", Type=");
229 if (type == MFT_STRING)
230 /* Nothing */ ;
231 else if (type == MFT_SEPARATOR)
232 MENUOUT("sep");
233 else if (type == MFT_OWNERDRAW)
234 MENUOUT("own");
235 else if (type == MFT_BITMAP)
236 MENUOUT("bit");
237 else
238 MENUOUT("???");
239 flags -= type;
241 MENUFLAG(MF_POPUP, "pop");
242 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
243 MENUFLAG(MFT_MENUBREAK, "brk");
244 MENUFLAG(MFT_RADIOCHECK, "radio");
245 MENUFLAG(MFT_RIGHTORDER, "rorder");
246 MENUFLAG(MF_SYSMENU, "sys");
247 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
249 if (flags)
250 DPRINTF( "+0x%x", flags);
252 flags = mp->fState;
253 if (flags) {
254 int count = 0;
255 DPRINTF( ", State=");
256 MENUFLAG(MFS_GRAYED, "grey");
257 MENUFLAG(MFS_DEFAULT, "default");
258 MENUFLAG(MFS_DISABLED, "dis");
259 MENUFLAG(MFS_CHECKED, "check");
260 MENUFLAG(MFS_HILITE, "hi");
261 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
262 MENUFLAG(MF_MOUSESELECT, "mouse");
263 if (flags)
264 DPRINTF( "+0x%x", flags);
266 if (mp->hCheckBit)
267 DPRINTF( ", Chk=%p", mp->hCheckBit);
268 if (mp->hUnCheckBit)
269 DPRINTF( ", Unc=%p", mp->hUnCheckBit);
271 if (type == MFT_STRING) {
272 if (mp->text)
273 DPRINTF( ", Text=%s", debugstr_w(mp->text));
274 else
275 DPRINTF( ", Text=Null");
276 } else if (mp->text == NULL)
277 /* Nothing */ ;
278 else
279 DPRINTF( ", Text=%p", mp->text);
280 if (mp->dwItemData)
281 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
282 DPRINTF( " }");
283 } else {
284 DPRINTF( "NULL");
287 DPRINTF(" %s\n", postfix);
290 #undef MENUOUT
291 #undef MENUFLAG
294 /***********************************************************************
295 * MENU_GetMenu
297 * Validate the given menu handle and returns the menu structure pointer.
299 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
301 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
302 if (!menu || menu->wMagic != MENU_MAGIC)
304 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
305 menu = NULL;
307 return menu;
310 /***********************************************************************
311 * get_win_sys_menu
313 * Get the system menu of a window
315 static HMENU get_win_sys_menu( HWND hwnd )
317 HMENU ret = 0;
318 WND *win = WIN_FindWndPtr( hwnd );
319 if (win)
321 ret = win->hSysMenu;
322 WIN_ReleaseWndPtr( win );
324 return ret;
327 /***********************************************************************
328 * MENU_CopySysPopup
330 * Return the default system menu.
332 static HMENU MENU_CopySysPopup(void)
334 HMENU hMenu = LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
336 if( hMenu ) {
337 POPUPMENU* menu = MENU_GetMenu(hMenu);
338 menu->wFlags |= MF_SYSMENU | MF_POPUP;
339 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
341 else
342 ERR("Unable to load default system menu\n" );
344 TRACE("returning %p.\n", hMenu );
346 return hMenu;
350 /**********************************************************************
351 * MENU_GetSysMenu
353 * Create a copy of the system menu. System menu in Windows is
354 * a special menu bar with the single entry - system menu popup.
355 * This popup is presented to the outside world as a "system menu".
356 * However, the real system menu handle is sometimes seen in the
357 * WM_MENUSELECT parameters (and Word 6 likes it this way).
359 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
361 HMENU hMenu;
363 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
364 if ((hMenu = CreateMenu()))
366 POPUPMENU *menu = MENU_GetMenu(hMenu);
367 menu->wFlags = MF_SYSMENU;
368 menu->hWnd = WIN_GetFullHandle( hWnd );
369 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
371 if (hPopupMenu == (HMENU)(-1))
372 hPopupMenu = MENU_CopySysPopup();
373 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
375 if (hPopupMenu)
377 InsertMenuA( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
378 (UINT_PTR)hPopupMenu, NULL );
380 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
381 menu->items[0].fState = 0;
382 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
384 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
385 return hMenu;
387 DestroyMenu( hMenu );
389 ERR("failed to load system menu!\n");
390 return 0;
394 /***********************************************************************
395 * MENU_Init
397 * Menus initialisation.
399 BOOL MENU_Init()
401 HBITMAP hBitmap;
402 NONCLIENTMETRICSA ncm;
404 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
405 0x55, 0, 0xAA, 0,
406 0x55, 0, 0xAA, 0,
407 0x55, 0, 0xAA, 0 };
409 /* Load menu bitmaps */
410 hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
411 /* Load system buttons bitmaps */
412 hBmpSysMenu = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_CLOSE));
414 if (hStdMnArrow)
416 BITMAP bm;
417 GetObjectA( hStdMnArrow, sizeof(bm), &bm );
418 arrow_bitmap_width = bm.bmWidth;
419 arrow_bitmap_height = bm.bmHeight;
420 } else
421 return FALSE;
423 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
424 return FALSE;
426 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
427 return FALSE;
429 DeleteObject( hBitmap );
430 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
431 return FALSE;
433 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
434 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0)))
435 return FALSE;
437 if (!(hMenuFont = CreateFontIndirectA( &ncm.lfMenuFont )))
438 return FALSE;
440 ncm.lfMenuFont.lfWeight += 300;
441 if ( ncm.lfMenuFont.lfWeight > 1000)
442 ncm.lfMenuFont.lfWeight = 1000;
444 if (!(hMenuFontBold = CreateFontIndirectA( &ncm.lfMenuFont )))
445 return FALSE;
447 return TRUE;
450 /***********************************************************************
451 * MENU_InitSysMenuPopup
453 * Grey the appropriate items in System menu.
455 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
457 BOOL gray;
459 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
460 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
461 gray = ((style & WS_MAXIMIZE) != 0);
462 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
463 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
464 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
465 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
466 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
467 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
468 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
469 gray = (clsStyle & CS_NOCLOSE) != 0;
471 /* The menu item must keep its state if it's disabled */
472 if(gray)
473 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
477 /******************************************************************************
479 * UINT MENU_GetStartOfNextColumn(
480 * HMENU hMenu )
482 *****************************************************************************/
484 static UINT MENU_GetStartOfNextColumn(
485 HMENU hMenu )
487 POPUPMENU *menu = MENU_GetMenu(hMenu);
488 UINT i;
490 if(!menu)
491 return NO_SELECTED_ITEM;
493 i = menu->FocusedItem + 1;
494 if( i == NO_SELECTED_ITEM )
495 return i;
497 for( ; i < menu->nItems; ++i ) {
498 if (menu->items[i].fType & MF_MENUBARBREAK)
499 return i;
502 return NO_SELECTED_ITEM;
506 /******************************************************************************
508 * UINT MENU_GetStartOfPrevColumn(
509 * HMENU hMenu )
511 *****************************************************************************/
513 static UINT MENU_GetStartOfPrevColumn(
514 HMENU hMenu )
516 POPUPMENU *menu = MENU_GetMenu(hMenu);
517 UINT i;
519 if( !menu )
520 return NO_SELECTED_ITEM;
522 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
523 return NO_SELECTED_ITEM;
525 /* Find the start of the column */
527 for(i = menu->FocusedItem; i != 0 &&
528 !(menu->items[i].fType & MF_MENUBARBREAK);
529 --i); /* empty */
531 if(i == 0)
532 return NO_SELECTED_ITEM;
534 for(--i; i != 0; --i) {
535 if (menu->items[i].fType & MF_MENUBARBREAK)
536 break;
539 TRACE("ret %d.\n", i );
541 return i;
546 /***********************************************************************
547 * MENU_FindItem
549 * Find a menu item. Return a pointer on the item, and modifies *hmenu
550 * in case the item was in a sub-menu.
552 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
554 POPUPMENU *menu;
555 UINT i;
557 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
558 if (wFlags & MF_BYPOSITION)
560 if (*nPos >= menu->nItems) return NULL;
561 return &menu->items[*nPos];
563 else
565 MENUITEM *item = menu->items;
566 for (i = 0; i < menu->nItems; i++, item++)
568 if (item->wID == *nPos)
570 *nPos = i;
571 return item;
573 else if (item->fType & MF_POPUP)
575 HMENU hsubmenu = item->hSubMenu;
576 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
577 if (subitem)
579 *hmenu = hsubmenu;
580 return subitem;
585 return NULL;
588 /***********************************************************************
589 * MENU_FindSubMenu
591 * Find a Sub menu. Return the position of the submenu, and modifies
592 * *hmenu in case it is found in another sub-menu.
593 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
595 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
597 POPUPMENU *menu;
598 UINT i;
599 MENUITEM *item;
600 if (((*hmenu)==(HMENU)0xffff) ||
601 (!(menu = MENU_GetMenu(*hmenu))))
602 return NO_SELECTED_ITEM;
603 item = menu->items;
604 for (i = 0; i < menu->nItems; i++, item++) {
605 if(!(item->fType & MF_POPUP)) continue;
606 if (item->hSubMenu == hSubTarget) {
607 return i;
609 else {
610 HMENU hsubmenu = item->hSubMenu;
611 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
612 if (pos != NO_SELECTED_ITEM) {
613 *hmenu = hsubmenu;
614 return pos;
618 return NO_SELECTED_ITEM;
621 /***********************************************************************
622 * MENU_FreeItemData
624 static void MENU_FreeItemData( MENUITEM* item )
626 /* delete text */
627 if (IS_STRING_ITEM(item->fType) && item->text)
628 HeapFree( GetProcessHeap(), 0, item->text );
631 /***********************************************************************
632 * MENU_FindItemByCoords
634 * Find the item at the specified coordinates (screen coords). Does
635 * not work for child windows and therefore should not be called for
636 * an arbitrary system menu.
638 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
639 POINT pt, UINT *pos )
641 MENUITEM *item;
642 UINT i;
643 RECT wrect;
645 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
646 pt.x -= wrect.left;pt.y -= wrect.top;
647 item = menu->items;
648 for (i = 0; i < menu->nItems; i++, item++)
650 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
651 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
653 if (pos) *pos = i;
654 return item;
657 return NULL;
661 /***********************************************************************
662 * MENU_FindItemByKey
664 * Find the menu item selected by a key press.
665 * Return item id, -1 if none, -2 if we should close the menu.
667 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
668 UINT key, BOOL forceMenuChar )
670 TRACE("\tlooking for '%c' in [%p]\n", (char)key, hmenu );
672 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
674 if (hmenu)
676 POPUPMENU *menu = MENU_GetMenu( hmenu );
677 MENUITEM *item = menu->items;
678 LONG menuchar;
680 if( !forceMenuChar )
682 UINT i;
684 key = toupper(key);
685 for (i = 0; i < menu->nItems; i++, item++)
687 if (IS_STRING_ITEM(item->fType) && item->text)
689 WCHAR *p = item->text - 2;
692 p = strchrW (p + 2, '&');
694 while (p != NULL && p [1] == '&');
695 if (p && (toupper(p[1]) == key)) return i;
699 menuchar = SendMessageA( hwndOwner, WM_MENUCHAR,
700 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
701 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
702 if (HIWORD(menuchar) == 1) return (UINT)(-2);
704 return (UINT)(-1);
708 /***********************************************************************
709 * MENU_GetBitmapItemSize
711 * Get the size of a bitmap item.
713 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
715 BITMAP bm;
716 HBITMAP bmp = (HBITMAP)id;
718 size->cx = size->cy = 0;
720 /* check if there is a magic menu item associated with this item */
721 if (id && IS_MAGIC_ITEM( id ))
723 switch(LOWORD(id))
725 case (INT_PTR)HBMMENU_SYSTEM:
726 if (data)
728 bmp = (HBITMAP)data;
729 break;
731 /* fall through */
732 case (INT_PTR)HBMMENU_MBAR_RESTORE:
733 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
734 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
735 case (INT_PTR)HBMMENU_MBAR_CLOSE:
736 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
737 size->cx = GetSystemMetrics( SM_CXSIZE );
738 size->cy = GetSystemMetrics( SM_CYSIZE );
739 return;
740 case (INT_PTR)HBMMENU_CALLBACK:
741 case (INT_PTR)HBMMENU_POPUP_CLOSE:
742 case (INT_PTR)HBMMENU_POPUP_RESTORE:
743 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
744 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
745 default:
746 FIXME("Magic 0x%08x not implemented\n", id);
747 return;
750 if (GetObjectA(bmp, sizeof(bm), &bm ))
752 size->cx = bm.bmWidth;
753 size->cy = bm.bmHeight;
757 /***********************************************************************
758 * MENU_DrawBitmapItem
760 * Draw a bitmap item.
762 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar )
764 BITMAP bm;
765 DWORD rop;
766 HDC hdcMem;
767 HBITMAP bmp = (HBITMAP)lpitem->text;
768 int w = rect->right - rect->left;
769 int h = rect->bottom - rect->top;
770 int bmp_xoffset = 0;
771 int left, top;
773 /* Check if there is a magic menu item associated with this item */
774 if (lpitem->text && IS_MAGIC_ITEM(lpitem->text))
776 UINT flags = 0;
777 RECT r;
779 switch(LOWORD(lpitem->text))
781 case (INT_PTR)HBMMENU_SYSTEM:
782 if (lpitem->dwItemData)
784 bmp = (HBITMAP)lpitem->dwItemData;
785 if (!GetObjectA( bmp, sizeof(bm), &bm )) return;
787 else
789 bmp = hBmpSysMenu;
790 if (!GetObjectA( bmp, sizeof(bm), &bm )) return;
791 /* only use right half of the bitmap */
792 bmp_xoffset = bm.bmWidth / 2;
793 bm.bmWidth -= bmp_xoffset;
795 goto got_bitmap;
796 case (INT_PTR)HBMMENU_MBAR_RESTORE:
797 flags = DFCS_CAPTIONRESTORE;
798 break;
799 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
800 flags = DFCS_CAPTIONMIN;
801 break;
802 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
803 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
804 break;
805 case (INT_PTR)HBMMENU_MBAR_CLOSE:
806 flags = DFCS_CAPTIONCLOSE;
807 break;
808 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
809 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
810 break;
811 case (INT_PTR)HBMMENU_CALLBACK:
812 case (INT_PTR)HBMMENU_POPUP_CLOSE:
813 case (INT_PTR)HBMMENU_POPUP_RESTORE:
814 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
815 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
816 default:
817 FIXME("Magic 0x%08x not implemented\n", LOWORD(lpitem->text));
818 return;
820 r = *rect;
821 InflateRect( &r, -1, -1 );
822 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
823 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
824 return;
827 if (!bmp || !GetObjectA( bmp, sizeof(bm), &bm )) return;
829 got_bitmap:
830 hdcMem = CreateCompatibleDC( hdc );
831 SelectObject( hdcMem, bmp );
833 /* handle fontsize > bitmap_height */
834 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
835 left=rect->left;
836 if (TWEAK_WineLook == WIN95_LOOK) {
837 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
838 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
839 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
840 } else {
841 left++;
842 w-=2;
843 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text) && (!menuBar)) ? MERGEPAINT : SRCCOPY;
845 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
846 DeleteDC( hdcMem );
850 /***********************************************************************
851 * MENU_CalcItemSize
853 * Calculate the size of the menu item and store it in lpitem->rect.
855 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
856 INT orgX, INT orgY, BOOL menuBar )
858 WCHAR *p;
859 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
861 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
862 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
863 (menuBar ? " (MenuBar)" : ""));
865 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
867 if (lpitem->fType & MF_OWNERDRAW)
870 ** Experimentation under Windows reveals that an owner-drawn
871 ** menu is expected to return the size of the content part of
872 ** the menu item, not including the checkmark nor the submenu
873 ** arrow. Windows adds those values itself and returns the
874 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
876 MEASUREITEMSTRUCT mis;
877 mis.CtlType = ODT_MENU;
878 mis.CtlID = 0;
879 mis.itemID = lpitem->wID;
880 mis.itemData = (DWORD)lpitem->dwItemData;
881 mis.itemHeight = 0;
882 mis.itemWidth = 0;
883 SendMessageA( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
884 lpitem->rect.right += mis.itemWidth;
886 if (menuBar)
888 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
891 /* under at least win95 you seem to be given a standard
892 height for the menu and the height value is ignored */
894 if (TWEAK_WineLook == WIN31_LOOK)
895 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
896 else
897 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
899 else
900 lpitem->rect.bottom += mis.itemHeight;
902 TRACE("id=%04x size=%dx%d\n",
903 lpitem->wID, mis.itemWidth, mis.itemHeight);
904 /* Fall through to get check/arrow width calculation. */
907 if (lpitem->fType & MF_SEPARATOR)
909 lpitem->rect.bottom += SEPARATOR_HEIGHT;
910 return;
913 if (!menuBar)
915 lpitem->rect.right += 2 * check_bitmap_width;
916 if (lpitem->fType & MF_POPUP)
917 lpitem->rect.right += arrow_bitmap_width;
920 if (lpitem->fType & MF_OWNERDRAW)
921 return;
923 if (IS_BITMAP_ITEM(lpitem->fType))
925 SIZE size;
927 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
928 lpitem->rect.right += size.cx;
929 lpitem->rect.bottom += size.cy;
930 if (TWEAK_WineLook == WIN98_LOOK)
932 /* Leave space for the sunken border */
933 lpitem->rect.right += 2;
934 lpitem->rect.bottom += 2;
939 /* it must be a text item - unless it's the system menu */
940 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
941 { SIZE size;
943 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
945 lpitem->rect.right += size.cx;
946 if (TWEAK_WineLook == WIN31_LOOK)
947 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
948 else
949 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
950 lpitem->xTab = 0;
952 if (menuBar)
954 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
956 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
958 /* Item contains a tab (only meaningful in popup menus) */
959 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
960 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
961 lpitem->rect.right += MENU_TAB_SPACE;
963 else
965 if (strchrW( lpitem->text, '\b' ))
966 lpitem->rect.right += MENU_TAB_SPACE;
967 lpitem->xTab = lpitem->rect.right - check_bitmap_width
968 - arrow_bitmap_width;
971 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
975 /***********************************************************************
976 * MENU_PopupMenuCalcSize
978 * Calculate the size of a popup menu.
980 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
982 MENUITEM *lpitem;
983 HDC hdc;
984 int start, i;
985 int orgX, orgY, maxX, maxTab, maxTabWidth;
987 lppop->Width = lppop->Height = 0;
988 if (lppop->nItems == 0) return;
989 hdc = GetDC( 0 );
991 SelectObject( hdc, hMenuFont);
993 start = 0;
994 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
996 while (start < lppop->nItems)
998 lpitem = &lppop->items[start];
999 orgX = maxX;
1000 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
1002 maxTab = maxTabWidth = 0;
1004 /* Parse items until column break or end of menu */
1005 for (i = start; i < lppop->nItems; i++, lpitem++)
1007 if ((i != start) &&
1008 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1010 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
1012 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1013 maxX = max( maxX, lpitem->rect.right );
1014 orgY = lpitem->rect.bottom;
1015 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1017 maxTab = max( maxTab, lpitem->xTab );
1018 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1022 /* Finish the column (set all items to the largest width found) */
1023 maxX = max( maxX, maxTab + maxTabWidth );
1024 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1026 lpitem->rect.right = maxX;
1027 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1028 lpitem->xTab = maxTab;
1031 lppop->Height = max( lppop->Height, orgY );
1034 lppop->Width = maxX;
1036 /* space for 3d border */
1037 if(TWEAK_WineLook > WIN31_LOOK)
1039 lppop->Height += 2;
1040 lppop->Width += 2;
1043 ReleaseDC( 0, hdc );
1047 /***********************************************************************
1048 * MENU_MenuBarCalcSize
1050 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1051 * height is off by 1 pixel which causes lengthy window relocations when
1052 * active document window is maximized/restored.
1054 * Calculate the size of the menu bar.
1056 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1057 LPPOPUPMENU lppop, HWND hwndOwner )
1059 MENUITEM *lpitem;
1060 int start, i, orgX, orgY, maxY, helpPos;
1062 if ((lprect == NULL) || (lppop == NULL)) return;
1063 if (lppop->nItems == 0) return;
1064 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1065 lprect->left, lprect->top, lprect->right, lprect->bottom);
1066 lppop->Width = lprect->right - lprect->left;
1067 lppop->Height = 0;
1068 maxY = lprect->top+1;
1069 start = 0;
1070 helpPos = -1;
1071 while (start < lppop->nItems)
1073 lpitem = &lppop->items[start];
1074 orgX = lprect->left;
1075 orgY = maxY;
1077 /* Parse items until line break or end of menu */
1078 for (i = start; i < lppop->nItems; i++, lpitem++)
1080 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1081 if ((i != start) &&
1082 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1084 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1085 orgX, orgY );
1086 debug_print_menuitem (" item: ", lpitem, "");
1087 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
1089 if (lpitem->rect.right > lprect->right)
1091 if (i != start) break;
1092 else lpitem->rect.right = lprect->right;
1094 maxY = max( maxY, lpitem->rect.bottom );
1095 orgX = lpitem->rect.right;
1098 /* Finish the line (set all items to the largest height found) */
1099 while (start < i) lppop->items[start++].rect.bottom = maxY;
1102 lprect->bottom = maxY;
1103 lppop->Height = lprect->bottom - lprect->top;
1105 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1106 /* the last item (if several lines, only move the last line) */
1107 lpitem = &lppop->items[lppop->nItems-1];
1108 orgY = lpitem->rect.top;
1109 orgX = lprect->right;
1110 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1111 if ( (helpPos==-1) || (helpPos>i) )
1112 break; /* done */
1113 if (lpitem->rect.top != orgY) break; /* Other line */
1114 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1115 lpitem->rect.left += orgX - lpitem->rect.right;
1116 lpitem->rect.right = orgX;
1117 orgX = lpitem->rect.left;
1121 /***********************************************************************
1122 * MENU_DrawMenuItem
1124 * Draw a single menu item.
1126 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1127 UINT height, BOOL menuBar, UINT odaction )
1129 RECT rect;
1131 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1133 if (lpitem->fType & MF_SYSMENU)
1135 if( !IsIconic(hwnd) ) {
1136 if (TWEAK_WineLook > WIN31_LOOK)
1137 NC_DrawSysButton95( hwnd, hdc,
1138 lpitem->fState &
1139 (MF_HILITE | MF_MOUSESELECT) );
1140 else
1141 NC_DrawSysButton( hwnd, hdc,
1142 lpitem->fState &
1143 (MF_HILITE | MF_MOUSESELECT) );
1146 return;
1149 if (lpitem->fType & MF_OWNERDRAW)
1152 ** Experimentation under Windows reveals that an owner-drawn
1153 ** menu is given the rectangle which includes the space it requested
1154 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1155 ** and a popup-menu arrow. This is the value of lpitem->rect.
1156 ** Windows will leave all drawing to the application except for
1157 ** the popup-menu arrow. Windows always draws that itself, after
1158 ** the menu owner has finished drawing.
1160 DRAWITEMSTRUCT dis;
1162 dis.CtlType = ODT_MENU;
1163 dis.CtlID = 0;
1164 dis.itemID = lpitem->wID;
1165 dis.itemData = (DWORD)lpitem->dwItemData;
1166 dis.itemState = 0;
1167 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1168 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1169 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1170 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1171 dis.hwndItem = (HWND)hmenu;
1172 dis.hDC = hdc;
1173 dis.rcItem = lpitem->rect;
1174 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1175 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner,
1176 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1177 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1178 dis.rcItem.bottom);
1179 SendMessageA( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1180 /* Fall through to draw popup-menu arrow */
1183 TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem->rect.left, lpitem->rect.top,
1184 lpitem->rect.right,lpitem->rect.bottom);
1186 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1188 rect = lpitem->rect;
1190 if (!(lpitem->fType & MF_OWNERDRAW))
1192 if (lpitem->fState & MF_HILITE)
1194 if(TWEAK_WineLook == WIN98_LOOK)
1196 if(menuBar)
1197 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1198 else
1199 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1201 else /* Not Win98 Look */
1203 if(!IS_BITMAP_ITEM(lpitem->fType))
1204 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1207 else
1208 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1211 SetBkMode( hdc, TRANSPARENT );
1213 if (!(lpitem->fType & MF_OWNERDRAW))
1215 /* vertical separator */
1216 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1218 if (TWEAK_WineLook > WIN31_LOOK)
1220 RECT rc = rect;
1221 rc.top = 3;
1222 rc.bottom = height - 3;
1223 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1225 else
1227 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1228 MoveToEx( hdc, rect.left, 0, NULL );
1229 LineTo( hdc, rect.left, height );
1233 /* horizontal separator */
1234 if (lpitem->fType & MF_SEPARATOR)
1236 if (TWEAK_WineLook > WIN31_LOOK)
1238 RECT rc = rect;
1239 rc.left++;
1240 rc.right--;
1241 rc.top += SEPARATOR_HEIGHT / 2;
1242 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1244 else
1246 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1247 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1248 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1250 return;
1254 /* Setup colors */
1256 if (lpitem->fState & MF_HILITE)
1258 if(TWEAK_WineLook == WIN98_LOOK)
1260 if(menuBar) {
1261 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1262 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1263 } else {
1264 if(lpitem->fState & MF_GRAYED)
1265 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1266 else
1267 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1268 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1271 else /* Not Win98 Look */
1273 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1274 if(!IS_BITMAP_ITEM(lpitem->fType))
1275 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1278 else
1280 if (lpitem->fState & MF_GRAYED)
1281 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1282 else
1283 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1284 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1287 /* helper lines for debugging */
1288 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1289 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1290 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1291 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1294 if (!menuBar)
1296 INT y = rect.top + rect.bottom;
1297 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1298 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1300 if (!(lpitem->fType & MF_OWNERDRAW))
1302 /* Draw the check mark
1304 * FIXME:
1305 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1307 HBITMAP bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1308 if (bm) /* we have a custom bitmap */
1310 HDC hdcMem = CreateCompatibleDC( hdc );
1311 SelectObject( hdcMem, bm );
1312 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1313 check_bitmap_width, check_bitmap_height,
1314 hdcMem, 0, 0, SRCCOPY );
1315 DeleteDC( hdcMem );
1317 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1319 RECT r;
1320 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1321 HDC hdcMem = CreateCompatibleDC( hdc );
1322 SelectObject( hdcMem, bm );
1323 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1324 DrawFrameControl( hdcMem, &r, DFC_MENU,
1325 (lpitem->fType & MFT_RADIOCHECK) ?
1326 DFCS_MENUBULLET : DFCS_MENUCHECK );
1327 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1328 hdcMem, 0, 0, SRCCOPY );
1329 DeleteDC( hdcMem );
1330 DeleteObject( bm );
1334 /* Draw the popup-menu arrow */
1335 if (lpitem->fType & MF_POPUP)
1337 HDC hdcMem = CreateCompatibleDC( hdc );
1338 HBITMAP hOrigBitmap;
1340 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1341 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1342 (y - arrow_bitmap_height) / 2,
1343 arrow_bitmap_width, arrow_bitmap_height,
1344 hdcMem, 0, 0, SRCCOPY );
1345 SelectObject( hdcMem, hOrigBitmap );
1346 DeleteDC( hdcMem );
1349 rect.left += check_bitmap_width;
1350 rect.right -= arrow_bitmap_width;
1353 /* Done for owner-drawn */
1354 if (lpitem->fType & MF_OWNERDRAW)
1355 return;
1357 /* Draw the item text or bitmap */
1358 if (IS_BITMAP_ITEM(lpitem->fType))
1360 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar );
1361 return;
1364 /* No bitmap - process text if present */
1365 else if (IS_STRING_ITEM(lpitem->fType))
1367 register int i;
1368 HFONT hfontOld = 0;
1370 UINT uFormat = (menuBar) ?
1371 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1372 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1374 if ( lpitem->fState & MFS_DEFAULT )
1376 hfontOld = SelectObject( hdc, hMenuFontBold);
1379 if (menuBar)
1381 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1382 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1385 for (i = 0; lpitem->text[i]; i++)
1386 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1387 break;
1389 if( (TWEAK_WineLook != WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1391 if (!(lpitem->fState & MF_HILITE) )
1393 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1394 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1395 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1396 --rect.left; --rect.top; --rect.right; --rect.bottom;
1398 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1401 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1403 /* paint the shortcut text */
1404 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1406 if (lpitem->text[i] == '\t')
1408 rect.left = lpitem->xTab;
1409 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1411 else
1413 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1416 if( (TWEAK_WineLook != WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1418 if (!(lpitem->fState & MF_HILITE) )
1420 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1421 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1422 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1423 --rect.left; --rect.top; --rect.right; --rect.bottom;
1425 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1427 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1430 if (hfontOld)
1431 SelectObject (hdc, hfontOld);
1436 /***********************************************************************
1437 * MENU_DrawPopupMenu
1439 * Paint a popup menu.
1441 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1443 HBRUSH hPrevBrush = 0;
1444 RECT rect;
1446 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1448 GetClientRect( hwnd, &rect );
1450 if(TWEAK_WineLook == WIN31_LOOK)
1452 rect.bottom -= POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1453 rect.right -= POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER);
1456 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1457 && (SelectObject( hdc, hMenuFont)))
1459 HPEN hPrevPen;
1461 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1463 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1464 if( hPrevPen )
1466 INT ropPrev, i;
1467 POPUPMENU *menu;
1469 /* draw 3-d shade */
1470 if(TWEAK_WineLook == WIN31_LOOK) {
1471 SelectObject( hdc, hShadeBrush );
1472 SetBkMode( hdc, TRANSPARENT );
1473 ropPrev = SetROP2( hdc, R2_MASKPEN );
1475 i = rect.right; /* why SetBrushOrg() doesn't? */
1476 PatBlt( hdc, i & 0xfffffffe,
1477 rect.top + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER),
1478 i%2 + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1479 rect.bottom - rect.top, 0x00a000c9 );
1480 i = rect.bottom;
1481 PatBlt( hdc, rect.left + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1482 i & 0xfffffffe,rect.right - rect.left,
1483 i%2 + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER), 0x00a000c9 );
1484 SelectObject( hdc, hPrevPen );
1485 SelectObject( hdc, hPrevBrush );
1486 SetROP2( hdc, ropPrev );
1488 else
1489 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1491 /* draw menu items */
1493 menu = MENU_GetMenu( hmenu );
1494 if (menu && menu->nItems)
1496 MENUITEM *item;
1497 UINT u;
1499 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1500 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1501 menu->Height, FALSE, ODA_DRAWENTIRE );
1504 } else
1506 SelectObject( hdc, hPrevBrush );
1511 /***********************************************************************
1512 * MENU_DrawMenuBar
1514 * Paint a menu bar. Returns the height of the menu bar.
1515 * called from [windows/nonclient.c]
1517 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1518 BOOL suppress_draw)
1520 LPPOPUPMENU lppop;
1521 HFONT hfontOld = 0;
1522 HMENU hMenu = GetMenu(hwnd);
1524 lppop = MENU_GetMenu( hMenu );
1525 if (lppop == NULL || lprect == NULL)
1527 return GetSystemMetrics(SM_CYMENU);
1530 if (suppress_draw)
1532 hfontOld = SelectObject( hDC, hMenuFont);
1534 if (lppop->Height == 0)
1535 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1537 lprect->bottom = lprect->top + lppop->Height;
1539 if (hfontOld) SelectObject( hDC, hfontOld);
1540 return lppop->Height;
1542 else
1543 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1547 /***********************************************************************
1548 * MENU_ShowPopup
1550 * Display a popup menu.
1552 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1553 INT x, INT y, INT xanchor, INT yanchor )
1555 POPUPMENU *menu;
1556 UINT width, height;
1558 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1559 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1561 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1562 if (menu->FocusedItem != NO_SELECTED_ITEM)
1564 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1565 menu->FocusedItem = NO_SELECTED_ITEM;
1568 /* store the owner for DrawItem */
1569 menu->hwndOwner = hwndOwner;
1572 MENU_PopupMenuCalcSize( menu, hwndOwner );
1574 /* adjust popup menu pos so that it fits within the desktop */
1576 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1577 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1579 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1581 if( xanchor )
1582 x -= width - xanchor;
1583 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1584 x = GetSystemMetrics(SM_CXSCREEN) - width;
1586 if( x < 0 ) x = 0;
1588 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1590 if( yanchor )
1591 y -= height + yanchor;
1592 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1593 y = GetSystemMetrics(SM_CYSCREEN) - height;
1595 if( y < 0 ) y = 0;
1597 if( TWEAK_WineLook == WIN31_LOOK )
1599 width += POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER); /* add space for shading */
1600 height += POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1603 /* NOTE: In Windows, top menu popup is not owned. */
1604 menu->hWnd = CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1605 WS_POPUP, x, y, width, height,
1606 hwndOwner, 0, (HINSTANCE)GetWindowLongA(hwndOwner,GWL_HINSTANCE),
1607 (LPVOID)hmenu );
1608 if( !menu->hWnd ) return FALSE;
1609 if (!top_popup) top_popup = menu->hWnd;
1611 /* Display the window */
1613 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1614 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1615 UpdateWindow( menu->hWnd );
1616 return TRUE;
1620 /***********************************************************************
1621 * MENU_SelectItem
1623 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1624 BOOL sendMenuSelect, HMENU topmenu )
1626 LPPOPUPMENU lppop;
1627 HDC hdc;
1629 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1631 lppop = MENU_GetMenu( hmenu );
1632 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1634 if (lppop->FocusedItem == wIndex) return;
1635 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1636 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1637 if (!top_popup) top_popup = lppop->hWnd;
1639 SelectObject( hdc, hMenuFont);
1641 /* Clear previous highlighted item */
1642 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1644 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1645 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1646 lppop->Height, !(lppop->wFlags & MF_POPUP),
1647 ODA_SELECT );
1650 /* Highlight new item (if any) */
1651 lppop->FocusedItem = wIndex;
1652 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1654 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1655 lppop->items[wIndex].fState |= MF_HILITE;
1656 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1657 &lppop->items[wIndex], lppop->Height,
1658 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1660 if (sendMenuSelect)
1662 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1663 SendMessageA( hwndOwner, WM_MENUSELECT,
1664 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1665 ip->fType | ip->fState | MF_MOUSESELECT |
1666 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1669 else if (sendMenuSelect) {
1670 if(topmenu){
1671 int pos;
1672 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1673 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1674 MENUITEM *ip = &ptm->items[pos];
1675 SendMessageA( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1676 ip->fType | ip->fState | MF_MOUSESELECT |
1677 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1681 ReleaseDC( lppop->hWnd, hdc );
1685 /***********************************************************************
1686 * MENU_MoveSelection
1688 * Moves currently selected item according to the offset parameter.
1689 * If there is no selection then it should select the last item if
1690 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1692 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1694 INT i;
1695 POPUPMENU *menu;
1697 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1699 menu = MENU_GetMenu( hmenu );
1700 if ((!menu) || (!menu->items)) return;
1702 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1704 if( menu->nItems == 1 ) return; else
1705 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1706 ; i += offset)
1707 if (!(menu->items[i].fType & MF_SEPARATOR))
1709 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1710 return;
1714 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1715 i >= 0 && i < menu->nItems ; i += offset)
1716 if (!(menu->items[i].fType & MF_SEPARATOR))
1718 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1719 return;
1724 /**********************************************************************
1725 * MENU_SetItemData
1727 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1728 * ModifyMenu().
1730 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1731 LPCWSTR str )
1733 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1735 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1736 TRACE("flags=%x str=%p\n", flags, str);
1738 if (IS_STRING_ITEM(flags))
1740 if (!str)
1742 flags |= MF_SEPARATOR;
1743 item->text = NULL;
1745 else
1747 LPWSTR text;
1748 /* Item beginning with a backspace is a help item */
1749 if (*str == '\b')
1751 flags |= MF_HELP;
1752 str++;
1754 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1755 return FALSE;
1756 strcpyW( text, str );
1757 item->text = text;
1760 else if (IS_BITMAP_ITEM(flags))
1761 item->text = (LPWSTR)HBITMAP_32(LOWORD(str));
1762 else item->text = NULL;
1764 if (flags & MF_OWNERDRAW)
1765 item->dwItemData = (DWORD)str;
1766 else
1767 item->dwItemData = 0;
1769 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1770 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1772 if (flags & MF_POPUP)
1774 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1775 if (menu) menu->wFlags |= MF_POPUP;
1776 else
1778 item->wID = 0;
1779 item->hSubMenu = 0;
1780 item->fType = 0;
1781 item->fState = 0;
1782 return FALSE;
1786 item->wID = id;
1787 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
1789 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1790 flags |= MF_POPUP; /* keep popup */
1792 item->fType = flags & TYPE_MASK;
1793 item->fState = (flags & STATE_MASK) &
1794 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1797 /* Don't call SetRectEmpty here! */
1800 if (prevText) HeapFree( GetProcessHeap(), 0, prevText );
1802 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1803 return TRUE;
1807 /**********************************************************************
1808 * MENU_InsertItem
1810 * Insert (allocate) a new item into a menu.
1812 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1814 MENUITEM *newItems;
1815 POPUPMENU *menu;
1817 if (!(menu = MENU_GetMenu(hMenu)))
1818 return NULL;
1820 /* Find where to insert new item */
1822 if (flags & MF_BYPOSITION) {
1823 if (pos > menu->nItems)
1824 pos = menu->nItems;
1825 } else {
1826 if (!MENU_FindItem( &hMenu, &pos, flags ))
1827 pos = menu->nItems;
1828 else {
1829 if (!(menu = MENU_GetMenu( hMenu )))
1830 return NULL;
1834 /* Create new items array */
1836 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1837 if (!newItems)
1839 WARN("allocation failed\n" );
1840 return NULL;
1842 if (menu->nItems > 0)
1844 /* Copy the old array into the new one */
1845 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1846 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1847 (menu->nItems-pos)*sizeof(MENUITEM) );
1848 HeapFree( GetProcessHeap(), 0, menu->items );
1850 menu->items = newItems;
1851 menu->nItems++;
1852 memset( &newItems[pos], 0, sizeof(*newItems) );
1853 menu->Height = 0; /* force size recalculate */
1854 return &newItems[pos];
1858 /**********************************************************************
1859 * MENU_ParseResource
1861 * Parse a standard menu resource and add items to the menu.
1862 * Return a pointer to the end of the resource.
1864 * NOTE: flags is equivalent to the mtOption field
1866 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1868 WORD flags, id = 0;
1869 LPCSTR str;
1873 flags = GET_WORD(res);
1874 res += sizeof(WORD);
1875 if (!(flags & MF_POPUP))
1877 id = GET_WORD(res);
1878 res += sizeof(WORD);
1880 str = res;
1881 if (!unicode) res += strlen(str) + 1;
1882 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1883 if (flags & MF_POPUP)
1885 HMENU hSubMenu = CreatePopupMenu();
1886 if (!hSubMenu) return NULL;
1887 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1888 return NULL;
1889 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1890 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1892 else /* Not a popup */
1894 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1895 else AppendMenuW( hMenu, flags, id,
1896 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1898 } while (!(flags & MF_END));
1899 return res;
1903 /**********************************************************************
1904 * MENUEX_ParseResource
1906 * Parse an extended menu resource and add items to the menu.
1907 * Return a pointer to the end of the resource.
1909 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1911 WORD resinfo;
1912 do {
1913 MENUITEMINFOW mii;
1915 mii.cbSize = sizeof(mii);
1916 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1917 mii.fType = GET_DWORD(res);
1918 res += sizeof(DWORD);
1919 mii.fState = GET_DWORD(res);
1920 res += sizeof(DWORD);
1921 mii.wID = GET_DWORD(res);
1922 res += sizeof(DWORD);
1923 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1924 res += sizeof(WORD);
1925 /* Align the text on a word boundary. */
1926 res += (~((int)res - 1)) & 1;
1927 mii.dwTypeData = (LPWSTR) res;
1928 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1929 /* Align the following fields on a dword boundary. */
1930 res += (~((int)res - 1)) & 3;
1932 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1933 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1935 if (resinfo & 1) { /* Pop-up? */
1936 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1937 res += sizeof(DWORD);
1938 mii.hSubMenu = CreatePopupMenu();
1939 if (!mii.hSubMenu)
1940 return NULL;
1941 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1942 DestroyMenu(mii.hSubMenu);
1943 return NULL;
1945 mii.fMask |= MIIM_SUBMENU;
1946 mii.fType |= MF_POPUP;
1948 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1950 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1951 mii.wID, mii.fType);
1952 mii.fType |= MF_SEPARATOR;
1954 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1955 } while (!(resinfo & MF_END));
1956 return res;
1960 /***********************************************************************
1961 * MENU_GetSubPopup
1963 * Return the handle of the selected sub-popup menu (if any).
1965 static HMENU MENU_GetSubPopup( HMENU hmenu )
1967 POPUPMENU *menu;
1968 MENUITEM *item;
1970 menu = MENU_GetMenu( hmenu );
1972 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1974 item = &menu->items[menu->FocusedItem];
1975 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1976 return item->hSubMenu;
1977 return 0;
1981 /***********************************************************************
1982 * MENU_HideSubPopups
1984 * Hide the sub-popup menus of this menu.
1986 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1987 BOOL sendMenuSelect )
1989 POPUPMENU *menu = MENU_GetMenu( hmenu );
1991 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1993 if (menu && top_popup)
1995 HMENU hsubmenu;
1996 POPUPMENU *submenu;
1997 MENUITEM *item;
1999 if (menu->FocusedItem != NO_SELECTED_ITEM)
2001 item = &menu->items[menu->FocusedItem];
2002 if (!(item->fType & MF_POPUP) ||
2003 !(item->fState & MF_MOUSESELECT)) return;
2004 item->fState &= ~MF_MOUSESELECT;
2005 hsubmenu = item->hSubMenu;
2006 } else return;
2008 submenu = MENU_GetMenu( hsubmenu );
2009 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2010 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2011 DestroyWindow( submenu->hWnd );
2012 submenu->hWnd = 0;
2017 /***********************************************************************
2018 * MENU_ShowSubPopup
2020 * Display the sub-menu of the selected item of this menu.
2021 * Return the handle of the submenu, or hmenu if no submenu to display.
2023 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2024 BOOL selectFirst, UINT wFlags )
2026 RECT rect;
2027 POPUPMENU *menu;
2028 MENUITEM *item;
2029 HDC hdc;
2031 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2033 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2035 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2037 item = &menu->items[menu->FocusedItem];
2038 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2039 return hmenu;
2041 /* message must be sent before using item,
2042 because nearly everything may be changed by the application ! */
2044 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2045 if (!(wFlags & TPM_NONOTIFY))
2046 SendMessageA( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2047 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2049 item = &menu->items[menu->FocusedItem];
2050 rect = item->rect;
2052 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2053 if (!(item->fState & MF_HILITE))
2055 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2056 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2058 SelectObject( hdc, hMenuFont);
2060 item->fState |= MF_HILITE;
2061 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2062 ReleaseDC( menu->hWnd, hdc );
2064 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2065 item->rect = rect;
2067 item->fState |= MF_MOUSESELECT;
2069 if (IS_SYSTEM_MENU(menu))
2071 MENU_InitSysMenuPopup(item->hSubMenu,
2072 GetWindowLongA( menu->hWnd, GWL_STYLE ),
2073 GetClassLongA( menu->hWnd, GCL_STYLE));
2075 NC_GetSysPopupPos( menu->hWnd, &rect );
2076 rect.top = rect.bottom;
2077 rect.right = GetSystemMetrics(SM_CXSIZE);
2078 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2080 else
2082 GetWindowRect( menu->hWnd, &rect );
2083 if (menu->wFlags & MF_POPUP)
2085 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2086 rect.top += item->rect.top;
2087 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2088 rect.bottom = item->rect.top - item->rect.bottom;
2090 else
2092 rect.left += item->rect.left;
2093 rect.top += item->rect.bottom;
2094 rect.right = item->rect.right - item->rect.left;
2095 rect.bottom = item->rect.bottom - item->rect.top;
2099 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2100 rect.left, rect.top, rect.right, rect.bottom );
2101 if (selectFirst)
2102 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2103 return item->hSubMenu;
2108 /**********************************************************************
2109 * MENU_IsMenuActive
2111 BOOL MENU_IsMenuActive(void)
2113 return (top_popup != 0);
2116 /***********************************************************************
2117 * MENU_PtMenu
2119 * Walks menu chain trying to find a menu pt maps to.
2121 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2123 POPUPMENU *menu = MENU_GetMenu( hMenu );
2124 UINT item = menu->FocusedItem;
2125 HMENU ret;
2127 /* try subpopup first (if any) */
2128 ret = (item != NO_SELECTED_ITEM &&
2129 (menu->items[item].fType & MF_POPUP) &&
2130 (menu->items[item].fState & MF_MOUSESELECT))
2131 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2133 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2135 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2136 if( menu->wFlags & MF_POPUP )
2138 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2140 else if (ht == HTSYSMENU)
2141 ret = get_win_sys_menu( menu->hWnd );
2142 else if (ht == HTMENU)
2143 ret = GetMenu( menu->hWnd );
2145 return ret;
2148 /***********************************************************************
2149 * MENU_ExecFocusedItem
2151 * Execute a menu item (for instance when user pressed Enter).
2152 * Return the wID of the executed item. Otherwise, -1 indicating
2153 * that no menu item was executed;
2154 * Have to receive the flags for the TrackPopupMenu options to avoid
2155 * sending unwanted message.
2158 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2160 MENUITEM *item;
2161 POPUPMENU *menu = MENU_GetMenu( hMenu );
2163 TRACE("%p hmenu=%p\n", pmt, hMenu);
2165 if (!menu || !menu->nItems ||
2166 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2168 item = &menu->items[menu->FocusedItem];
2170 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2172 if (!(item->fType & MF_POPUP))
2174 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2176 /* If TPM_RETURNCMD is set you return the id, but
2177 do not send a message to the owner */
2178 if(!(wFlags & TPM_RETURNCMD))
2180 if( menu->wFlags & MF_SYSMENU )
2181 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2182 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2183 else
2184 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2186 return item->wID;
2189 else
2190 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2192 return -1;
2195 /***********************************************************************
2196 * MENU_SwitchTracking
2198 * Helper function for menu navigation routines.
2200 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2202 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2203 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2205 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2207 if( pmt->hTopMenu != hPtMenu &&
2208 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2210 /* both are top level menus (system and menu-bar) */
2211 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2212 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2213 pmt->hTopMenu = hPtMenu;
2215 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2216 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2220 /***********************************************************************
2221 * MENU_ButtonDown
2223 * Return TRUE if we can go on with menu tracking.
2225 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2227 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2229 if (hPtMenu)
2231 UINT id = 0;
2232 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2233 MENUITEM *item;
2235 if( IS_SYSTEM_MENU(ptmenu) )
2236 item = ptmenu->items;
2237 else
2238 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2240 if( item )
2242 if( ptmenu->FocusedItem != id )
2243 MENU_SwitchTracking( pmt, hPtMenu, id );
2245 /* If the popup menu is not already "popped" */
2246 if(!(item->fState & MF_MOUSESELECT ))
2248 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2250 /* In win31, a newly popped menu always remains opened for the next buttonup */
2251 if(TWEAK_WineLook == WIN31_LOOK)
2252 ptmenu->bTimeToHide = FALSE;
2255 return TRUE;
2257 /* Else the click was on the menu bar, finish the tracking */
2259 return FALSE;
2262 /***********************************************************************
2263 * MENU_ButtonUp
2265 * Return the value of MENU_ExecFocusedItem if
2266 * the selected item was not a popup. Else open the popup.
2267 * A -1 return value indicates that we go on with menu tracking.
2270 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2272 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2274 if (hPtMenu)
2276 UINT id = 0;
2277 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2278 MENUITEM *item;
2280 if( IS_SYSTEM_MENU(ptmenu) )
2281 item = ptmenu->items;
2282 else
2283 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2285 if( item && (ptmenu->FocusedItem == id ))
2287 if( !(item->fType & MF_POPUP) )
2288 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2290 /* If we are dealing with the top-level menu */
2291 /* and this is a click on an already "popped" item: */
2292 /* Stop the menu tracking and close the opened submenus */
2293 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2294 return 0;
2296 ptmenu->bTimeToHide = TRUE;
2298 return -1;
2302 /***********************************************************************
2303 * MENU_MouseMove
2305 * Return TRUE if we can go on with menu tracking.
2307 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2309 UINT id = NO_SELECTED_ITEM;
2310 POPUPMENU *ptmenu = NULL;
2312 if( hPtMenu )
2314 ptmenu = MENU_GetMenu( hPtMenu );
2315 if( IS_SYSTEM_MENU(ptmenu) )
2316 id = 0;
2317 else
2318 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2321 if( id == NO_SELECTED_ITEM )
2323 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2324 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2327 else if( ptmenu->FocusedItem != id )
2329 MENU_SwitchTracking( pmt, hPtMenu, id );
2330 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2332 return TRUE;
2336 /***********************************************************************
2337 * MENU_SetCapture
2339 static void MENU_SetCapture( HWND hwnd )
2341 HWND previous = 0;
2343 SERVER_START_REQ( set_capture_window )
2345 req->handle = hwnd;
2346 req->flags = CAPTURE_MENU;
2347 if (!wine_server_call_err( req ))
2349 previous = reply->previous;
2350 hwnd = reply->full_handle;
2353 SERVER_END_REQ;
2355 if (previous && previous != hwnd)
2356 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2360 /***********************************************************************
2361 * MENU_DoNextMenu
2363 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2365 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2367 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2369 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2370 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2372 MDINEXTMENU next_menu;
2373 HMENU hNewMenu;
2374 HWND hNewWnd;
2375 UINT id = 0;
2377 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2378 next_menu.hmenuNext = 0;
2379 next_menu.hwndNext = 0;
2380 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2382 TRACE("%p [%p] -> %p [%p]\n",
2383 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2385 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2387 DWORD style = GetWindowLongA( pmt->hOwnerWnd, GWL_STYLE );
2388 hNewWnd = pmt->hOwnerWnd;
2389 if( IS_SYSTEM_MENU(menu) )
2391 /* switch to the menu bar */
2393 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2395 if( vk == VK_LEFT )
2397 menu = MENU_GetMenu( hNewMenu );
2398 id = menu->nItems - 1;
2401 else if (style & WS_SYSMENU )
2403 /* switch to the system menu */
2404 hNewMenu = get_win_sys_menu( hNewWnd );
2406 else return FALSE;
2408 else /* application returned a new menu to switch to */
2410 hNewMenu = next_menu.hmenuNext;
2411 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2413 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2415 DWORD style = GetWindowLongA( hNewWnd, GWL_STYLE );
2417 if (style & WS_SYSMENU &&
2418 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2420 /* get the real system menu */
2421 hNewMenu = get_win_sys_menu(hNewWnd);
2423 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2425 /* FIXME: Not sure what to do here;
2426 * perhaps try to track hNewMenu as a popup? */
2428 TRACE(" -- got confused.\n");
2429 return FALSE;
2432 else return FALSE;
2435 if( hNewMenu != pmt->hTopMenu )
2437 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2438 FALSE, 0 );
2439 if( pmt->hCurrentMenu != pmt->hTopMenu )
2440 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2443 if( hNewWnd != pmt->hOwnerWnd )
2445 pmt->hOwnerWnd = hNewWnd;
2446 MENU_SetCapture( pmt->hOwnerWnd );
2449 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2450 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2452 return TRUE;
2454 return FALSE;
2457 /***********************************************************************
2458 * MENU_SuspendPopup
2460 * The idea is not to show the popup if the next input message is
2461 * going to hide it anyway.
2463 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2465 MSG msg;
2467 msg.hwnd = pmt->hOwnerWnd;
2469 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2470 pmt->trackFlags |= TF_SKIPREMOVE;
2472 switch( uMsg )
2474 case WM_KEYDOWN:
2475 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2476 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2478 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2479 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2480 if( msg.message == WM_KEYDOWN &&
2481 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2483 pmt->trackFlags |= TF_SUSPENDPOPUP;
2484 return TRUE;
2487 break;
2490 /* failures go through this */
2491 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2492 return FALSE;
2495 /***********************************************************************
2496 * MENU_KeyEscape
2498 * Handle a VK_ESCAPE key event in a menu.
2500 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2502 BOOL bEndMenu = TRUE;
2504 if (pmt->hCurrentMenu != pmt->hTopMenu)
2506 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2508 if (menu->wFlags & MF_POPUP)
2510 HMENU hmenutmp, hmenuprev;
2512 hmenuprev = hmenutmp = pmt->hTopMenu;
2514 /* close topmost popup */
2515 while (hmenutmp != pmt->hCurrentMenu)
2517 hmenuprev = hmenutmp;
2518 hmenutmp = MENU_GetSubPopup( hmenuprev );
2521 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2522 pmt->hCurrentMenu = hmenuprev;
2523 bEndMenu = FALSE;
2527 return bEndMenu;
2530 /***********************************************************************
2531 * MENU_KeyLeft
2533 * Handle a VK_LEFT key event in a menu.
2535 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2537 POPUPMENU *menu;
2538 HMENU hmenutmp, hmenuprev;
2539 UINT prevcol;
2541 hmenuprev = hmenutmp = pmt->hTopMenu;
2542 menu = MENU_GetMenu( hmenutmp );
2544 /* Try to move 1 column left (if possible) */
2545 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2546 NO_SELECTED_ITEM ) {
2548 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2549 prevcol, TRUE, 0 );
2550 return;
2553 /* close topmost popup */
2554 while (hmenutmp != pmt->hCurrentMenu)
2556 hmenuprev = hmenutmp;
2557 hmenutmp = MENU_GetSubPopup( hmenuprev );
2560 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2561 pmt->hCurrentMenu = hmenuprev;
2563 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2565 /* move menu bar selection if no more popups are left */
2567 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2568 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2570 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2572 /* A sublevel menu was displayed - display the next one
2573 * unless there is another displacement coming up */
2575 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2576 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2577 pmt->hTopMenu, TRUE, wFlags);
2583 /***********************************************************************
2584 * MENU_KeyRight
2586 * Handle a VK_RIGHT key event in a menu.
2588 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2590 HMENU hmenutmp;
2591 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2592 UINT nextcol;
2594 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2595 pmt->hCurrentMenu,
2596 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2597 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2599 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2601 /* If already displaying a popup, try to display sub-popup */
2603 hmenutmp = pmt->hCurrentMenu;
2604 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2606 /* if subpopup was displayed then we are done */
2607 if (hmenutmp != pmt->hCurrentMenu) return;
2610 /* Check to see if there's another column */
2611 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2612 NO_SELECTED_ITEM ) {
2613 TRACE("Going to %d.\n", nextcol );
2614 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2615 nextcol, TRUE, 0 );
2616 return;
2619 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2621 if( pmt->hCurrentMenu != pmt->hTopMenu )
2623 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2624 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2625 } else hmenutmp = 0;
2627 /* try to move to the next item */
2628 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2629 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2631 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2632 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2633 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2634 pmt->hTopMenu, TRUE, wFlags);
2638 /***********************************************************************
2639 * MENU_TrackMenu
2641 * Menu tracking code.
2643 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2644 HWND hwnd, const RECT *lprect )
2646 MSG msg;
2647 POPUPMENU *menu;
2648 BOOL fRemove;
2649 INT executedMenuId = -1;
2650 MTRACKER mt;
2651 BOOL enterIdleSent = FALSE;
2653 mt.trackFlags = 0;
2654 mt.hCurrentMenu = hmenu;
2655 mt.hTopMenu = hmenu;
2656 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2657 mt.pt.x = x;
2658 mt.pt.y = y;
2660 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2661 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2662 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2664 fEndMenu = FALSE;
2665 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2667 if (wFlags & TPM_BUTTONDOWN)
2669 /* Get the result in order to start the tracking or not */
2670 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2671 fEndMenu = !fRemove;
2674 MENU_SetCapture( mt.hOwnerWnd );
2676 while (!fEndMenu)
2678 menu = MENU_GetMenu( mt.hCurrentMenu );
2679 if (!menu) /* sometimes happens if I do a window manager close */
2680 break;
2682 /* we have to keep the message in the queue until it's
2683 * clear that menu loop is not over yet. */
2685 for (;;)
2687 if (PeekMessageA( &msg, 0, 0, 0, PM_NOREMOVE ))
2689 if (!CallMsgFilterA( &msg, MSGF_MENU )) break;
2690 /* remove the message from the queue */
2691 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2693 else
2695 if (!enterIdleSent)
2697 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2698 enterIdleSent = TRUE;
2699 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2701 WaitMessage();
2705 /* check if EndMenu() tried to cancel us, by posting this message */
2706 if(msg.message == WM_CANCELMODE)
2708 /* we are now out of the loop */
2709 fEndMenu = TRUE;
2711 /* remove the message from the queue */
2712 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2714 /* break out of internal loop, ala ESCAPE */
2715 break;
2718 TranslateMessage( &msg );
2719 mt.pt = msg.pt;
2721 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2722 enterIdleSent=FALSE;
2724 fRemove = FALSE;
2725 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2728 * Use the mouse coordinates in lParam instead of those in the MSG
2729 * struct to properly handle synthetic messages. They are already
2730 * in screen coordinates.
2732 mt.pt.x = (short)LOWORD(msg.lParam);
2733 mt.pt.y = (short)HIWORD(msg.lParam);
2735 /* Find a menu for this mouse event */
2736 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2738 switch(msg.message)
2740 /* no WM_NC... messages in captured state */
2742 case WM_RBUTTONDBLCLK:
2743 case WM_RBUTTONDOWN:
2744 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2745 /* fall through */
2746 case WM_LBUTTONDBLCLK:
2747 case WM_LBUTTONDOWN:
2748 /* If the message belongs to the menu, removes it from the queue */
2749 /* Else, end menu tracking */
2750 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2751 fEndMenu = !fRemove;
2752 break;
2754 case WM_RBUTTONUP:
2755 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2756 /* fall through */
2757 case WM_LBUTTONUP:
2758 /* Check if a menu was selected by the mouse */
2759 if (hmenu)
2761 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2763 /* End the loop if executedMenuId is an item ID */
2764 /* or if the job was done (executedMenuId = 0). */
2765 fEndMenu = fRemove = (executedMenuId != -1);
2767 /* No menu was selected by the mouse */
2768 /* if the function was called by TrackPopupMenu, continue
2769 with the menu tracking. If not, stop it */
2770 else
2771 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2773 break;
2775 case WM_MOUSEMOVE:
2776 /* In win95 winelook, the selected menu item must be changed every time the
2777 mouse moves. In Win31 winelook, the mouse button has to be held down */
2779 if ( hmenu && ((TWEAK_WineLook > WIN31_LOOK) ||
2780 ( (msg.wParam & MK_LBUTTON) ||
2781 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON)))) )
2783 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2785 } /* switch(msg.message) - mouse */
2787 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2789 fRemove = TRUE; /* Keyboard messages are always removed */
2790 switch(msg.message)
2792 case WM_KEYDOWN:
2793 switch(msg.wParam)
2795 case VK_HOME:
2796 case VK_END:
2797 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2798 NO_SELECTED_ITEM, FALSE, 0 );
2799 /* fall through */
2800 case VK_UP:
2801 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2802 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2803 break;
2805 case VK_DOWN: /* If on menu bar, pull-down the menu */
2807 menu = MENU_GetMenu( mt.hCurrentMenu );
2808 if (!(menu->wFlags & MF_POPUP))
2809 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2810 else /* otherwise try to move selection */
2811 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2812 break;
2814 case VK_LEFT:
2815 MENU_KeyLeft( &mt, wFlags );
2816 break;
2818 case VK_RIGHT:
2819 MENU_KeyRight( &mt, wFlags );
2820 break;
2822 case VK_ESCAPE:
2823 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2824 break;
2826 case VK_F1:
2828 HELPINFO hi;
2829 hi.cbSize = sizeof(HELPINFO);
2830 hi.iContextType = HELPINFO_MENUITEM;
2831 if (menu->FocusedItem == NO_SELECTED_ITEM)
2832 hi.iCtrlId = 0;
2833 else
2834 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2835 hi.hItemHandle = hmenu;
2836 hi.dwContextId = menu->dwContextHelpID;
2837 hi.MousePos = msg.pt;
2838 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2839 break;
2842 default:
2843 break;
2845 break; /* WM_KEYDOWN */
2847 case WM_SYSKEYDOWN:
2848 switch(msg.wParam)
2850 case VK_MENU:
2851 fEndMenu = TRUE;
2852 break;
2855 break; /* WM_SYSKEYDOWN */
2857 case WM_CHAR:
2859 UINT pos;
2861 if (msg.wParam == '\r' || msg.wParam == ' ')
2863 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2864 fEndMenu = (executedMenuId != -1);
2866 break;
2869 /* Hack to avoid control chars. */
2870 /* We will find a better way real soon... */
2871 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2873 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2874 LOWORD(msg.wParam), FALSE );
2875 if (pos == (UINT)-2) fEndMenu = TRUE;
2876 else if (pos == (UINT)-1) MessageBeep(0);
2877 else
2879 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2880 TRUE, 0 );
2881 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2882 fEndMenu = (executedMenuId != -1);
2885 break;
2886 } /* switch(msg.message) - kbd */
2888 else
2890 DispatchMessageA( &msg );
2893 if (!fEndMenu) fRemove = TRUE;
2895 /* finally remove message from the queue */
2897 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2898 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2899 else mt.trackFlags &= ~TF_SKIPREMOVE;
2902 MENU_SetCapture(0); /* release the capture */
2904 /* If dropdown is still painted and the close box is clicked on
2905 then the menu will be destroyed as part of the DispatchMessage above.
2906 This will then invalidate the menu handle in mt.hTopMenu. We should
2907 check for this first. */
2908 if( IsMenu( mt.hTopMenu ) )
2910 menu = MENU_GetMenu( mt.hTopMenu );
2912 if( IsWindow( mt.hOwnerWnd ) )
2914 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2916 if (menu && menu->wFlags & MF_POPUP)
2918 DestroyWindow( menu->hWnd );
2919 menu->hWnd = 0;
2921 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2922 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2925 /* Reset the variable for hiding menu */
2926 if( menu ) menu->bTimeToHide = FALSE;
2929 /* The return value is only used by TrackPopupMenu */
2930 return ((executedMenuId != -1) ? executedMenuId : 0);
2933 /***********************************************************************
2934 * MENU_InitTracking
2936 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2938 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2940 HideCaret(0);
2942 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2943 if (!(wFlags & TPM_NONOTIFY))
2944 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2946 SendMessageA( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2948 if (!(wFlags & TPM_NONOTIFY))
2950 POPUPMENU *menu;
2951 SendMessageA( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2952 if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
2953 { /* app changed/recreated menu bar entries in WM_INITMENU
2954 Recalculate menu sizes else clicks will not work */
2955 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2956 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2960 return TRUE;
2962 /***********************************************************************
2963 * MENU_ExitTracking
2965 static BOOL MENU_ExitTracking(HWND hWnd)
2967 TRACE("hwnd=%p\n", hWnd);
2969 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
2970 ShowCaret(0);
2971 return TRUE;
2974 /***********************************************************************
2975 * MENU_TrackMouseMenuBar
2977 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2979 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2981 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2982 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2984 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2986 if (IsMenu(hMenu))
2988 /* map point to parent client coordinates */
2989 HWND parent = GetAncestor( hWnd, GA_PARENT );
2990 if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );
2992 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2993 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2994 MENU_ExitTracking(hWnd);
2999 /***********************************************************************
3000 * MENU_TrackKbdMenuBar
3002 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3004 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, INT vkey)
3006 UINT uItem = NO_SELECTED_ITEM;
3007 HMENU hTrackMenu;
3008 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3010 /* find window that has a menu */
3012 while (GetWindowLongA( hwnd, GWL_STYLE ) & WS_CHILD)
3013 if (!(hwnd = GetParent( hwnd ))) return;
3015 /* check if we have to track a system menu */
3017 hTrackMenu = GetMenu( hwnd );
3018 if (!hTrackMenu || IsIconic(hwnd) || vkey == VK_SPACE )
3020 if (!(GetWindowLongA( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3021 if (GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_MANAGED) return;
3022 hTrackMenu = get_win_sys_menu( hwnd );
3023 uItem = 0;
3024 wParam |= HTSYSMENU; /* prevent item lookup */
3027 if (!IsMenu( hTrackMenu )) return;
3029 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3031 if( vkey && vkey != VK_SPACE )
3033 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, vkey, (wParam & HTSYSMENU) );
3034 if( uItem >= (UINT)(-2) )
3036 if( uItem == (UINT)(-1) ) MessageBeep(0);
3037 hTrackMenu = 0;
3041 if( hTrackMenu )
3043 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3045 if( uItem == NO_SELECTED_ITEM )
3046 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3047 else if( vkey )
3048 PostMessageA( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3050 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3052 MENU_ExitTracking( hwnd );
3056 /**********************************************************************
3057 * TrackPopupMenu (USER32.@)
3059 * Like the win32 API, the function return the command ID only if the
3060 * flag TPM_RETURNCMD is on.
3063 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3064 INT nReserved, HWND hWnd, const RECT *lpRect )
3066 BOOL ret = FALSE;
3068 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3070 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3071 if (!(wFlags & TPM_NONOTIFY))
3072 SendMessageA( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3074 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3075 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3076 MENU_ExitTracking(hWnd);
3078 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3079 ret = 1;
3081 return ret;
3084 /**********************************************************************
3085 * TrackPopupMenuEx (USER32.@)
3087 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3088 HWND hWnd, LPTPMPARAMS lpTpm )
3090 FIXME("not fully implemented\n" );
3091 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3092 lpTpm ? &lpTpm->rcExclude : NULL );
3095 /***********************************************************************
3096 * PopupMenuWndProc
3098 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3100 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3102 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3104 switch(message)
3106 case WM_CREATE:
3108 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3109 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3110 return 0;
3113 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3114 return MA_NOACTIVATE;
3116 case WM_PAINT:
3118 PAINTSTRUCT ps;
3119 BeginPaint( hwnd, &ps );
3120 MENU_DrawPopupMenu( hwnd, ps.hdc,
3121 (HMENU)GetWindowLongA( hwnd, 0 ) );
3122 EndPaint( hwnd, &ps );
3123 return 0;
3125 case WM_ERASEBKGND:
3126 return 1;
3128 case WM_DESTROY:
3129 /* zero out global pointer in case resident popup window was destroyed. */
3130 if (hwnd == top_popup) top_popup = 0;
3131 break;
3133 case WM_SHOWWINDOW:
3135 if( wParam )
3137 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3139 else
3140 SetWindowLongW( hwnd, 0, 0 );
3141 break;
3143 case MM_SETMENUHANDLE:
3144 SetWindowLongW( hwnd, 0, wParam );
3145 break;
3147 case MM_GETMENUHANDLE:
3148 return GetWindowLongW( hwnd, 0 );
3150 default:
3151 return DefWindowProcW( hwnd, message, wParam, lParam );
3153 return 0;
3157 /***********************************************************************
3158 * MENU_GetMenuBarHeight
3160 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3162 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3163 INT orgX, INT orgY )
3165 HDC hdc;
3166 RECT rectBar;
3167 LPPOPUPMENU lppop;
3169 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3171 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3173 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3174 SelectObject( hdc, hMenuFont);
3175 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3176 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3177 ReleaseDC( hwnd, hdc );
3178 return lppop->Height;
3182 /*******************************************************************
3183 * ChangeMenuA (USER32.@)
3185 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3186 UINT id, UINT flags )
3188 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3189 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3190 id, data );
3191 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3192 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3193 id, data );
3194 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3195 flags & MF_BYPOSITION ? pos : id,
3196 flags & ~MF_REMOVE );
3197 /* Default: MF_INSERT */
3198 return InsertMenuA( hMenu, pos, flags, id, data );
3202 /*******************************************************************
3203 * ChangeMenuW (USER32.@)
3205 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3206 UINT id, UINT flags )
3208 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3209 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3210 id, data );
3211 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3212 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3213 id, data );
3214 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3215 flags & MF_BYPOSITION ? pos : id,
3216 flags & ~MF_REMOVE );
3217 /* Default: MF_INSERT */
3218 return InsertMenuW( hMenu, pos, flags, id, data );
3222 /*******************************************************************
3223 * CheckMenuItem (USER32.@)
3225 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3227 MENUITEM *item;
3228 DWORD ret;
3230 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3231 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3232 ret = item->fState & MF_CHECKED;
3233 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3234 else item->fState &= ~MF_CHECKED;
3235 return ret;
3239 /**********************************************************************
3240 * EnableMenuItem (USER32.@)
3242 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3244 UINT oldflags;
3245 MENUITEM *item;
3246 POPUPMENU *menu;
3248 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3250 /* Get the Popupmenu to access the owner menu */
3251 if (!(menu = MENU_GetMenu(hMenu)))
3252 return (UINT)-1;
3254 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3255 return (UINT)-1;
3257 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3258 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3260 /* In win95 if the close item in the system menu change update the close button */
3261 if (TWEAK_WineLook == WIN95_LOOK)
3262 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3264 if (menu->hSysMenuOwner != 0)
3266 POPUPMENU* parentMenu;
3268 /* Get the parent menu to access*/
3269 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3270 return (UINT)-1;
3272 /* Refresh the frame to reflect the change*/
3273 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3274 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3278 return oldflags;
3282 /*******************************************************************
3283 * GetMenuStringA (USER32.@)
3285 INT WINAPI GetMenuStringA(
3286 HMENU hMenu, /* [in] menuhandle */
3287 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3288 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3289 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3290 UINT wFlags /* [in] MF_ flags */
3292 MENUITEM *item;
3294 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3295 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3296 if (!IS_STRING_ITEM(item->fType)) return 0;
3297 if (!str || !nMaxSiz) return strlenW(item->text);
3298 str[0] = '\0';
3299 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3300 str[nMaxSiz-1] = 0;
3301 TRACE("returning '%s'\n", str );
3302 return strlen(str);
3306 /*******************************************************************
3307 * GetMenuStringW (USER32.@)
3309 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3310 LPWSTR str, INT nMaxSiz, UINT wFlags )
3312 MENUITEM *item;
3314 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3315 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3316 if (!IS_STRING_ITEM(item->fType)) return 0;
3317 if (!str || !nMaxSiz) return strlenW(item->text);
3318 str[0] = '\0';
3319 lstrcpynW( str, item->text, nMaxSiz );
3320 return strlenW(str);
3324 /**********************************************************************
3325 * HiliteMenuItem (USER32.@)
3327 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3328 UINT wHilite )
3330 LPPOPUPMENU menu;
3331 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3332 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3333 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3334 if (menu->FocusedItem == wItemID) return TRUE;
3335 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3336 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3337 return TRUE;
3341 /**********************************************************************
3342 * GetMenuState (USER32.@)
3344 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3346 MENUITEM *item;
3347 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3348 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3349 debug_print_menuitem (" item: ", item, "");
3350 if (item->fType & MF_POPUP)
3352 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3353 if (!menu) return -1;
3354 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3356 else
3358 /* We used to (from way back then) mask the result to 0xff. */
3359 /* I don't know why and it seems wrong as the documented */
3360 /* return flag MF_SEPARATOR is outside that mask. */
3361 return (item->fType | item->fState);
3366 /**********************************************************************
3367 * GetMenuItemCount (USER32.@)
3369 INT WINAPI GetMenuItemCount( HMENU hMenu )
3371 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3372 if (!menu) return -1;
3373 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3374 return menu->nItems;
3378 /**********************************************************************
3379 * GetMenuItemID (USER32.@)
3381 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3383 MENUITEM * lpmi;
3385 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3386 if (lpmi->fType & MF_POPUP) return -1;
3387 return lpmi->wID;
3392 /*******************************************************************
3393 * InsertMenuW (USER32.@)
3395 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3396 UINT_PTR id, LPCWSTR str )
3398 MENUITEM *item;
3400 if (IS_STRING_ITEM(flags) && str)
3401 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3402 hMenu, pos, flags, id, debugstr_w(str) );
3403 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3404 hMenu, pos, flags, id, (DWORD)str );
3406 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3408 if (!(MENU_SetItemData( item, flags, id, str )))
3410 RemoveMenu( hMenu, pos, flags );
3411 return FALSE;
3414 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3415 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3417 item->hCheckBit = item->hUnCheckBit = 0;
3418 return TRUE;
3422 /*******************************************************************
3423 * InsertMenuA (USER32.@)
3425 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3426 UINT_PTR id, LPCSTR str )
3428 BOOL ret = FALSE;
3430 if (IS_STRING_ITEM(flags) && str)
3432 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3433 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3434 if (newstr)
3436 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3437 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3438 HeapFree( GetProcessHeap(), 0, newstr );
3440 return ret;
3442 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3446 /*******************************************************************
3447 * AppendMenuA (USER32.@)
3449 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3450 UINT_PTR id, LPCSTR data )
3452 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3456 /*******************************************************************
3457 * AppendMenuW (USER32.@)
3459 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3460 UINT_PTR id, LPCWSTR data )
3462 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3466 /**********************************************************************
3467 * RemoveMenu (USER32.@)
3469 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3471 LPPOPUPMENU menu;
3472 MENUITEM *item;
3474 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3475 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3476 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3478 /* Remove item */
3480 MENU_FreeItemData( item );
3482 if (--menu->nItems == 0)
3484 HeapFree( GetProcessHeap(), 0, menu->items );
3485 menu->items = NULL;
3487 else
3489 while(nPos < menu->nItems)
3491 *item = *(item+1);
3492 item++;
3493 nPos++;
3495 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3496 menu->nItems * sizeof(MENUITEM) );
3498 return TRUE;
3502 /**********************************************************************
3503 * DeleteMenu (USER32.@)
3505 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3507 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3508 if (!item) return FALSE;
3509 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3510 /* nPos is now the position of the item */
3511 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3512 return TRUE;
3516 /*******************************************************************
3517 * ModifyMenuW (USER32.@)
3519 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3520 UINT_PTR id, LPCWSTR str )
3522 MENUITEM *item;
3524 if (IS_STRING_ITEM(flags))
3526 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3528 else
3530 TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3533 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3534 return MENU_SetItemData( item, flags, id, str );
3538 /*******************************************************************
3539 * ModifyMenuA (USER32.@)
3541 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3542 UINT_PTR id, LPCSTR str )
3544 BOOL ret = FALSE;
3546 if (IS_STRING_ITEM(flags) && str)
3548 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3549 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3550 if (newstr)
3552 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3553 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3554 HeapFree( GetProcessHeap(), 0, newstr );
3556 return ret;
3558 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3562 /**********************************************************************
3563 * CreatePopupMenu (USER32.@)
3565 HMENU WINAPI CreatePopupMenu(void)
3567 HMENU hmenu;
3568 POPUPMENU *menu;
3570 if (!(hmenu = CreateMenu())) return 0;
3571 menu = MENU_GetMenu( hmenu );
3572 menu->wFlags |= MF_POPUP;
3573 menu->bTimeToHide = FALSE;
3574 return hmenu;
3578 /**********************************************************************
3579 * GetMenuCheckMarkDimensions (USER.417)
3580 * GetMenuCheckMarkDimensions (USER32.@)
3582 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3584 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3588 /**********************************************************************
3589 * SetMenuItemBitmaps (USER32.@)
3591 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3592 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3594 MENUITEM *item;
3595 TRACE("(%p, %04x, %04x, %p, %p)\n",
3596 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3597 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3599 if (!hNewCheck && !hNewUnCheck)
3601 item->fState &= ~MF_USECHECKBITMAPS;
3603 else /* Install new bitmaps */
3605 item->hCheckBit = hNewCheck;
3606 item->hUnCheckBit = hNewUnCheck;
3607 item->fState |= MF_USECHECKBITMAPS;
3609 return TRUE;
3613 /**********************************************************************
3614 * CreateMenu (USER32.@)
3616 HMENU WINAPI CreateMenu(void)
3618 HMENU hMenu;
3619 LPPOPUPMENU menu;
3620 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3621 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3623 ZeroMemory(menu, sizeof(POPUPMENU));
3624 menu->wMagic = MENU_MAGIC;
3625 menu->FocusedItem = NO_SELECTED_ITEM;
3626 menu->bTimeToHide = FALSE;
3628 TRACE("return %p\n", hMenu );
3630 return hMenu;
3634 /**********************************************************************
3635 * DestroyMenu (USER32.@)
3637 BOOL WINAPI DestroyMenu( HMENU hMenu )
3639 TRACE("(%p)\n", hMenu);
3641 /* Silently ignore attempts to destroy default system popup */
3643 if (hMenu && hMenu != MENU_DefSysPopup)
3645 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3647 if (!lppop) return FALSE;
3649 lppop->wMagic = 0; /* Mark it as destroyed */
3651 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3653 DestroyWindow( lppop->hWnd );
3654 lppop->hWnd = 0;
3657 if (lppop->items) /* recursively destroy submenus */
3659 int i;
3660 MENUITEM *item = lppop->items;
3661 for (i = lppop->nItems; i > 0; i--, item++)
3663 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3664 MENU_FreeItemData( item );
3666 HeapFree( GetProcessHeap(), 0, lppop->items );
3668 USER_HEAP_FREE( hMenu );
3670 return (hMenu != MENU_DefSysPopup);
3674 /**********************************************************************
3675 * GetSystemMenu (USER32.@)
3677 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3679 WND *wndPtr = WIN_FindWndPtr( hWnd );
3680 HMENU retvalue = 0;
3682 if (wndPtr)
3684 if( wndPtr->hSysMenu )
3686 if( bRevert )
3688 DestroyMenu(wndPtr->hSysMenu);
3689 wndPtr->hSysMenu = 0;
3691 else
3693 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3694 if( menu )
3696 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3697 menu->items[0].hSubMenu = MENU_CopySysPopup();
3699 else
3701 WARN("Current sys-menu (%p) of wnd %p is broken\n",
3702 wndPtr->hSysMenu, hWnd);
3703 wndPtr->hSysMenu = 0;
3708 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3709 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3711 if( wndPtr->hSysMenu )
3713 POPUPMENU *menu;
3714 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3716 /* Store the dummy sysmenu handle to facilitate the refresh */
3717 /* of the close button if the SC_CLOSE item change */
3718 menu = MENU_GetMenu(retvalue);
3719 if ( menu )
3720 menu->hSysMenuOwner = wndPtr->hSysMenu;
3722 WIN_ReleaseWndPtr(wndPtr);
3724 return bRevert ? 0 : retvalue;
3728 /*******************************************************************
3729 * SetSystemMenu (USER32.@)
3731 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3733 WND *wndPtr = WIN_FindWndPtr(hwnd);
3735 if (wndPtr)
3737 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3738 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3739 WIN_ReleaseWndPtr(wndPtr);
3740 return TRUE;
3742 return FALSE;
3746 /**********************************************************************
3747 * GetMenu (USER32.@)
3749 HMENU WINAPI GetMenu( HWND hWnd )
3751 HMENU retvalue = (HMENU)GetWindowLongA( hWnd, GWL_ID );
3752 TRACE("for %p returning %p\n", hWnd, retvalue);
3753 return retvalue;
3757 /**********************************************************************
3758 * SetMenu (USER32.@)
3760 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3762 TRACE("(%p, %p);\n", hWnd, hMenu);
3764 if (hMenu && !IsMenu(hMenu))
3766 WARN("hMenu %p is not a menu handle\n", hMenu);
3767 return FALSE;
3769 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3771 hWnd = WIN_GetFullHandle( hWnd );
3772 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
3774 if (hMenu != 0)
3776 LPPOPUPMENU lpmenu;
3778 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3780 lpmenu->hWnd = hWnd;
3781 lpmenu->Height = 0; /* Make sure we recalculate the size */
3783 SetWindowLongA( hWnd, GWL_ID, (LONG_PTR)hMenu );
3785 if (IsWindowVisible(hWnd))
3786 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3787 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3788 return TRUE;
3793 /**********************************************************************
3794 * GetSubMenu (USER32.@)
3796 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3798 MENUITEM * lpmi;
3800 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3801 if (!(lpmi->fType & MF_POPUP)) return 0;
3802 return lpmi->hSubMenu;
3806 /**********************************************************************
3807 * DrawMenuBar (USER32.@)
3809 BOOL WINAPI DrawMenuBar( HWND hWnd )
3811 LPPOPUPMENU lppop;
3812 HMENU hMenu = GetMenu(hWnd);
3814 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3815 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3817 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3818 lppop->hwndOwner = hWnd;
3819 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3820 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3821 return TRUE;
3824 /***********************************************************************
3825 * DrawMenuBarTemp (USER32.@)
3827 * UNDOCUMENTED !!
3829 * called by W98SE desk.cpl Control Panel Applet
3831 * Not 100% sure about the param names, but close.
3833 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3835 LPPOPUPMENU lppop;
3836 UINT i,retvalue;
3837 HFONT hfontOld = 0;
3839 if (!hMenu)
3840 hMenu = GetMenu(hwnd);
3842 if (!hFont)
3843 hFont = hMenuFont;
3845 lppop = MENU_GetMenu( hMenu );
3846 if (lppop == NULL || lprect == NULL)
3848 retvalue = GetSystemMetrics(SM_CYMENU);
3849 goto END;
3852 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3854 hfontOld = SelectObject( hDC, hFont);
3856 if (lppop->Height == 0)
3857 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3859 lprect->bottom = lprect->top + lppop->Height;
3861 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
3863 if (TWEAK_WineLook == WIN31_LOOK)
3865 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
3866 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3867 LineTo( hDC, lprect->right, lprect->bottom );
3869 else
3871 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3872 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3873 LineTo( hDC, lprect->right, lprect->bottom );
3876 if (lppop->nItems == 0)
3878 retvalue = GetSystemMetrics(SM_CYMENU);
3879 goto END;
3882 for (i = 0; i < lppop->nItems; i++)
3884 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3885 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3887 retvalue = lppop->Height;
3889 END:
3890 if (hfontOld) SelectObject (hDC, hfontOld);
3891 return retvalue;
3894 /***********************************************************************
3895 * EndMenu (USER.187)
3896 * EndMenu (USER32.@)
3898 void WINAPI EndMenu(void)
3900 /* if we are in the menu code, and it is active */
3901 if (!fEndMenu && top_popup)
3903 /* terminate the menu handling code */
3904 fEndMenu = TRUE;
3906 /* needs to be posted to wakeup the internal menu handler */
3907 /* which will now terminate the menu, in the event that */
3908 /* the main window was minimized, or lost focus, so we */
3909 /* don't end up with an orphaned menu */
3910 PostMessageA( top_popup, WM_CANCELMODE, 0, 0);
3915 /***********************************************************************
3916 * LookupMenuHandle (USER.217)
3918 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3920 HMENU hmenu32 = HMENU_32(hmenu);
3921 UINT id32 = id;
3922 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3923 else return HMENU_16(hmenu32);
3927 /**********************************************************************
3928 * LoadMenu (USER.150)
3930 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3932 HRSRC16 hRsrc;
3933 HGLOBAL16 handle;
3934 HMENU16 hMenu;
3936 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3937 if (!name) return 0;
3939 instance = GetExePtr( instance );
3940 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3941 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3942 hMenu = LoadMenuIndirect16(LockResource16(handle));
3943 FreeResource16( handle );
3944 return hMenu;
3948 /*****************************************************************
3949 * LoadMenuA (USER32.@)
3951 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3953 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
3954 if (!hrsrc) return 0;
3955 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3959 /*****************************************************************
3960 * LoadMenuW (USER32.@)
3962 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3964 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
3965 if (!hrsrc) return 0;
3966 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
3970 /**********************************************************************
3971 * LoadMenuIndirect (USER.220)
3973 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3975 HMENU hMenu;
3976 WORD version, offset;
3977 LPCSTR p = (LPCSTR)template;
3979 TRACE("(%p)\n", template );
3980 version = GET_WORD(p);
3981 p += sizeof(WORD);
3982 if (version)
3984 WARN("version must be 0 for Win16\n" );
3985 return 0;
3987 offset = GET_WORD(p);
3988 p += sizeof(WORD) + offset;
3989 if (!(hMenu = CreateMenu())) return 0;
3990 if (!MENU_ParseResource( p, hMenu, FALSE ))
3992 DestroyMenu( hMenu );
3993 return 0;
3995 return HMENU_16(hMenu);
3999 /**********************************************************************
4000 * LoadMenuIndirectW (USER32.@)
4002 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4004 HMENU hMenu;
4005 WORD version, offset;
4006 LPCSTR p = (LPCSTR)template;
4008 version = GET_WORD(p);
4009 p += sizeof(WORD);
4010 TRACE("%p, ver %d\n", template, version );
4011 switch (version)
4013 case 0: /* standard format is version of 0 */
4014 offset = GET_WORD(p);
4015 p += sizeof(WORD) + offset;
4016 if (!(hMenu = CreateMenu())) return 0;
4017 if (!MENU_ParseResource( p, hMenu, TRUE ))
4019 DestroyMenu( hMenu );
4020 return 0;
4022 return hMenu;
4023 case 1: /* extended format is version of 1 */
4024 offset = GET_WORD(p);
4025 p += sizeof(WORD) + offset;
4026 if (!(hMenu = CreateMenu())) return 0;
4027 if (!MENUEX_ParseResource( p, hMenu))
4029 DestroyMenu( hMenu );
4030 return 0;
4032 return hMenu;
4033 default:
4034 ERR("version %d not supported.\n", version);
4035 return 0;
4040 /**********************************************************************
4041 * LoadMenuIndirectA (USER32.@)
4043 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4045 return LoadMenuIndirectW( template );
4049 /**********************************************************************
4050 * IsMenu (USER32.@)
4052 BOOL WINAPI IsMenu(HMENU hmenu)
4054 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4055 return menu != NULL;
4058 /**********************************************************************
4059 * GetMenuItemInfo_common
4062 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4063 LPMENUITEMINFOW lpmii, BOOL unicode)
4065 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4067 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4069 if (!menu)
4070 return FALSE;
4072 if (lpmii->fMask & MIIM_TYPE) {
4073 lpmii->fType = menu->fType;
4074 switch (MENU_ITEM_TYPE(menu->fType)) {
4075 case MF_STRING:
4076 break; /* will be done below */
4077 case MF_OWNERDRAW:
4078 case MF_BITMAP:
4079 lpmii->dwTypeData = menu->text;
4080 /* fall through */
4081 default:
4082 lpmii->cch = 0;
4086 /* copy the text string */
4087 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4088 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4090 int len;
4091 if (unicode)
4093 len = strlenW(menu->text);
4094 if(lpmii->dwTypeData && lpmii->cch)
4095 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4097 else
4099 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4100 if(lpmii->dwTypeData && lpmii->cch)
4101 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4102 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4103 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4105 /* if we've copied a substring we return its length */
4106 if(lpmii->dwTypeData && lpmii->cch)
4108 if (lpmii->cch <= len) lpmii->cch--;
4110 else /* return length of string */
4111 lpmii->cch = len;
4114 if (lpmii->fMask & MIIM_FTYPE)
4115 lpmii->fType = menu->fType;
4117 if (lpmii->fMask & MIIM_BITMAP)
4118 lpmii->hbmpItem = menu->hbmpItem;
4120 if (lpmii->fMask & MIIM_STATE)
4121 lpmii->fState = menu->fState;
4123 if (lpmii->fMask & MIIM_ID)
4124 lpmii->wID = menu->wID;
4126 if (lpmii->fMask & MIIM_SUBMENU)
4127 lpmii->hSubMenu = menu->hSubMenu;
4129 if (lpmii->fMask & MIIM_CHECKMARKS) {
4130 lpmii->hbmpChecked = menu->hCheckBit;
4131 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4133 if (lpmii->fMask & MIIM_DATA)
4134 lpmii->dwItemData = menu->dwItemData;
4136 return TRUE;
4139 /**********************************************************************
4140 * GetMenuItemInfoA (USER32.@)
4142 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4143 LPMENUITEMINFOA lpmii)
4145 return GetMenuItemInfo_common (hmenu, item, bypos,
4146 (LPMENUITEMINFOW)lpmii, FALSE);
4149 /**********************************************************************
4150 * GetMenuItemInfoW (USER32.@)
4152 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4153 LPMENUITEMINFOW lpmii)
4155 return GetMenuItemInfo_common (hmenu, item, bypos,
4156 lpmii, TRUE);
4160 /* set a menu item text from a ASCII or Unicode string */
4161 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4163 if (!text)
4165 menu->text = NULL;
4166 menu->fType |= MF_SEPARATOR;
4168 else if (unicode)
4170 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4171 strcpyW( menu->text, text );
4173 else
4175 LPCSTR str = (LPCSTR)text;
4176 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4177 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4178 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4183 /**********************************************************************
4184 * SetMenuItemInfo_common
4187 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4188 const MENUITEMINFOW *lpmii,
4189 BOOL unicode)
4191 if (!menu) return FALSE;
4193 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4195 if (lpmii->fMask & MIIM_TYPE ) {
4196 /* Get rid of old string. */
4197 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4198 HeapFree(GetProcessHeap(), 0, menu->text);
4199 menu->text = NULL;
4202 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4203 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4204 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4206 menu->text = lpmii->dwTypeData;
4208 if (IS_STRING_ITEM(menu->fType))
4209 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4212 if (lpmii->fMask & MIIM_FTYPE ) {
4213 /* free the string when the type is changing */
4214 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4215 HeapFree(GetProcessHeap(), 0, menu->text);
4216 menu->text = NULL;
4218 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4219 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4220 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4221 menu->fType |= MF_SEPARATOR;
4224 if (lpmii->fMask & MIIM_STRING ) {
4225 /* free the string when used */
4226 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4227 HeapFree(GetProcessHeap(), 0, menu->text);
4228 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4232 if (lpmii->fMask & MIIM_STATE)
4234 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4235 menu->fState = lpmii->fState;
4238 if (lpmii->fMask & MIIM_ID)
4239 menu->wID = lpmii->wID;
4241 if (lpmii->fMask & MIIM_SUBMENU) {
4242 menu->hSubMenu = lpmii->hSubMenu;
4243 if (menu->hSubMenu) {
4244 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4245 if (subMenu) {
4246 subMenu->wFlags |= MF_POPUP;
4247 menu->fType |= MF_POPUP;
4249 else
4250 /* FIXME: Return an error ? */
4251 menu->fType &= ~MF_POPUP;
4253 else
4254 menu->fType &= ~MF_POPUP;
4257 if (lpmii->fMask & MIIM_CHECKMARKS)
4259 if (lpmii->fType & MFT_RADIOCHECK)
4260 menu->fType |= MFT_RADIOCHECK;
4262 menu->hCheckBit = lpmii->hbmpChecked;
4263 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4265 if (lpmii->fMask & MIIM_DATA)
4266 menu->dwItemData = lpmii->dwItemData;
4268 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4269 return TRUE;
4272 /**********************************************************************
4273 * SetMenuItemInfoA (USER32.@)
4275 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4276 const MENUITEMINFOA *lpmii)
4278 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4279 (const MENUITEMINFOW *)lpmii, FALSE);
4282 /**********************************************************************
4283 * SetMenuItemInfoW (USER32.@)
4285 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4286 const MENUITEMINFOW *lpmii)
4288 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4289 lpmii, TRUE);
4292 /**********************************************************************
4293 * SetMenuDefaultItem (USER32.@)
4296 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4298 UINT i;
4299 POPUPMENU *menu;
4300 MENUITEM *item;
4302 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4304 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4306 /* reset all default-item flags */
4307 item = menu->items;
4308 for (i = 0; i < menu->nItems; i++, item++)
4310 item->fState &= ~MFS_DEFAULT;
4313 /* no default item */
4314 if ( -1 == uItem)
4316 return TRUE;
4319 item = menu->items;
4320 if ( bypos )
4322 if ( uItem >= menu->nItems ) return FALSE;
4323 item[uItem].fState |= MFS_DEFAULT;
4324 return TRUE;
4326 else
4328 for (i = 0; i < menu->nItems; i++, item++)
4330 if (item->wID == uItem)
4332 item->fState |= MFS_DEFAULT;
4333 return TRUE;
4338 return FALSE;
4341 /**********************************************************************
4342 * GetMenuDefaultItem (USER32.@)
4344 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4346 POPUPMENU *menu;
4347 MENUITEM * item;
4348 UINT i = 0;
4350 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4352 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4354 /* find default item */
4355 item = menu->items;
4357 /* empty menu */
4358 if (! item) return -1;
4360 while ( !( item->fState & MFS_DEFAULT ) )
4362 i++; item++;
4363 if (i >= menu->nItems ) return -1;
4366 /* default: don't return disabled items */
4367 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4369 /* search rekursiv when needed */
4370 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4372 UINT ret;
4373 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4374 if ( -1 != ret ) return ret;
4376 /* when item not found in submenu, return the popup item */
4378 return ( bypos ) ? i : item->wID;
4383 /**********************************************************************
4384 * InsertMenuItemA (USER32.@)
4386 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4387 const MENUITEMINFOA *lpmii)
4389 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4390 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4394 /**********************************************************************
4395 * InsertMenuItemW (USER32.@)
4397 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4398 const MENUITEMINFOW *lpmii)
4400 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4401 return SetMenuItemInfo_common(item, lpmii, TRUE);
4404 /**********************************************************************
4405 * CheckMenuRadioItem (USER32.@)
4408 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4409 UINT first, UINT last, UINT check,
4410 UINT bypos)
4412 MENUITEM *mifirst, *milast, *micheck;
4413 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4415 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4417 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4418 milast = MENU_FindItem (&mlast, &last, bypos);
4419 micheck = MENU_FindItem (&mcheck, &check, bypos);
4421 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4422 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4423 micheck > milast || micheck < mifirst)
4424 return FALSE;
4426 while (mifirst <= milast)
4428 if (mifirst == micheck)
4430 mifirst->fType |= MFT_RADIOCHECK;
4431 mifirst->fState |= MFS_CHECKED;
4432 } else {
4433 mifirst->fType &= ~MFT_RADIOCHECK;
4434 mifirst->fState &= ~MFS_CHECKED;
4436 mifirst++;
4439 return TRUE;
4443 /**********************************************************************
4444 * GetMenuItemRect (USER32.@)
4446 * ATTENTION: Here, the returned values in rect are the screen
4447 * coordinates of the item just like if the menu was
4448 * always on the upper left side of the application.
4451 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4452 LPRECT rect)
4454 POPUPMENU *itemMenu;
4455 MENUITEM *item;
4456 HWND referenceHwnd;
4458 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4460 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4461 referenceHwnd = hwnd;
4463 if(!hwnd)
4465 itemMenu = MENU_GetMenu(hMenu);
4466 if (itemMenu == NULL)
4467 return FALSE;
4469 if(itemMenu->hWnd == 0)
4470 return FALSE;
4471 referenceHwnd = itemMenu->hWnd;
4474 if ((rect == NULL) || (item == NULL))
4475 return FALSE;
4477 *rect = item->rect;
4479 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4481 return TRUE;
4485 /**********************************************************************
4486 * SetMenuInfo (USER32.@)
4488 * FIXME
4489 * MIM_APPLYTOSUBMENUS
4490 * actually use the items to draw the menu
4492 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4494 POPUPMENU *menu;
4496 TRACE("(%p %p)\n", hMenu, lpmi);
4498 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4501 if (lpmi->fMask & MIM_BACKGROUND)
4502 menu->hbrBack = lpmi->hbrBack;
4504 if (lpmi->fMask & MIM_HELPID)
4505 menu->dwContextHelpID = lpmi->dwContextHelpID;
4507 if (lpmi->fMask & MIM_MAXHEIGHT)
4508 menu->cyMax = lpmi->cyMax;
4510 if (lpmi->fMask & MIM_MENUDATA)
4511 menu->dwMenuData = lpmi->dwMenuData;
4513 if (lpmi->fMask & MIM_STYLE)
4514 menu->dwStyle = lpmi->dwStyle;
4516 return TRUE;
4518 return FALSE;
4521 /**********************************************************************
4522 * GetMenuInfo (USER32.@)
4524 * NOTES
4525 * win98/NT5.0
4528 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4529 { POPUPMENU *menu;
4531 TRACE("(%p %p)\n", hMenu, lpmi);
4533 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4536 if (lpmi->fMask & MIM_BACKGROUND)
4537 lpmi->hbrBack = menu->hbrBack;
4539 if (lpmi->fMask & MIM_HELPID)
4540 lpmi->dwContextHelpID = menu->dwContextHelpID;
4542 if (lpmi->fMask & MIM_MAXHEIGHT)
4543 lpmi->cyMax = menu->cyMax;
4545 if (lpmi->fMask & MIM_MENUDATA)
4546 lpmi->dwMenuData = menu->dwMenuData;
4548 if (lpmi->fMask & MIM_STYLE)
4549 lpmi->dwStyle = menu->dwStyle;
4551 return TRUE;
4553 return FALSE;
4557 /**********************************************************************
4558 * SetMenuContextHelpId (USER32.@)
4560 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4562 LPPOPUPMENU menu;
4564 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4566 if ((menu = MENU_GetMenu(hMenu)))
4568 menu->dwContextHelpID = dwContextHelpID;
4569 return TRUE;
4571 return FALSE;
4575 /**********************************************************************
4576 * GetMenuContextHelpId (USER32.@)
4578 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4580 LPPOPUPMENU menu;
4582 TRACE("(%p)\n", hMenu);
4584 if ((menu = MENU_GetMenu(hMenu)))
4586 return menu->dwContextHelpID;
4588 return 0;
4591 /**********************************************************************
4592 * MenuItemFromPoint (USER32.@)
4594 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4596 POPUPMENU *menu = MENU_GetMenu(hMenu);
4597 UINT pos;
4598 MENUITEM *item;
4600 /*FIXME: Do we have to handle hWnd here? */
4601 item = MENU_FindItemByCoords(menu, ptScreen, &pos);
4603 return pos;
4607 /**********************************************************************
4608 * translate_accelerator
4610 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4611 BYTE fVirt, WORD key, WORD cmd )
4613 UINT mesg = 0;
4615 if (wParam != key) return FALSE;
4617 if (message == WM_CHAR)
4619 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4621 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4622 goto found;
4625 else
4627 if(fVirt & FVIRTKEY)
4629 INT mask = 0;
4630 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4631 wParam, 0xff & HIWORD(lParam));
4632 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4633 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4634 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4635 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4636 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4638 else
4640 if (!(lParam & 0x01000000)) /* no special_key */
4642 if ((fVirt & FALT) && (lParam & 0x20000000))
4643 { /* ^^ ALT pressed */
4644 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4645 goto found;
4650 return FALSE;
4652 found:
4653 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4654 mesg = 1;
4655 else if (GetCapture())
4656 mesg = 2;
4657 else if (!IsWindowEnabled(hWnd))
4658 mesg = 3;
4659 else
4661 HMENU hMenu, hSubMenu, hSysMenu;
4662 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4664 hMenu = (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4665 hSysMenu = get_win_sys_menu( hWnd );
4667 /* find menu item and ask application to initialize it */
4668 /* 1. in the system menu */
4669 hSubMenu = hSysMenu;
4670 nPos = cmd;
4671 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4673 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4674 if(hSubMenu != hSysMenu)
4676 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4677 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4678 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4680 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4682 else /* 2. in the window's menu */
4684 hSubMenu = hMenu;
4685 nPos = cmd;
4686 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4688 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4689 if(hSubMenu != hMenu)
4691 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4692 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4693 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4695 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4699 if (uSysStat != (UINT)-1)
4701 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4702 mesg=4;
4703 else
4704 mesg=WM_SYSCOMMAND;
4706 else
4708 if (uStat != (UINT)-1)
4710 if (IsIconic(hWnd))
4711 mesg=5;
4712 else
4714 if (uStat & (MF_DISABLED|MF_GRAYED))
4715 mesg=6;
4716 else
4717 mesg=WM_COMMAND;
4720 else
4721 mesg=WM_COMMAND;
4725 if( mesg==WM_COMMAND )
4727 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4728 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
4730 else if( mesg==WM_SYSCOMMAND )
4732 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4733 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
4735 else
4737 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4738 * #0: unknown (please report!)
4739 * #1: for WM_KEYUP,WM_SYSKEYUP
4740 * #2: mouse is captured
4741 * #3: window is disabled
4742 * #4: it's a disabled system menu option
4743 * #5: it's a menu option, but window is iconic
4744 * #6: it's a menu option, but disabled
4746 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4747 if(mesg==0)
4748 ERR_(accel)(" unknown reason - please report!");
4750 return TRUE;
4753 /**********************************************************************
4754 * TranslateAccelerator (USER32.@)
4755 * TranslateAcceleratorA (USER32.@)
4756 * TranslateAcceleratorW (USER32.@)
4758 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
4760 /* YES, Accel16! */
4761 LPACCEL16 lpAccelTbl;
4762 int i;
4764 if (msg == NULL)
4766 WARN_(accel)("msg null; should hang here to be win compatible\n");
4767 return 0;
4769 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4771 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4772 return 0;
4774 if ((msg->message != WM_KEYDOWN &&
4775 msg->message != WM_KEYUP &&
4776 msg->message != WM_SYSKEYDOWN &&
4777 msg->message != WM_SYSKEYUP &&
4778 msg->message != WM_CHAR)) return 0;
4780 TRACE_(accel)("TranslateAccelerators hAccel=%p, hWnd=%p,"
4781 "msg->hwnd=%p, msg->message=%04x, wParam=%08x, lParam=%lx\n",
4782 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4784 i = 0;
4787 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4788 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4789 return 1;
4790 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4791 WARN_(accel)("couldn't translate accelerator key\n");
4792 return 0;