Convert HACCEL to a void*.
[wine/multimedia.git] / controls / menu.c
blob2646605b845eded741553b49c2a22045e793e543
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Note: the style MF_MOUSESELECT is used to mark popup items that
25 * have been selected, i.e. their popup menu is currently displayed.
26 * This is probably not the meaning this style has in MS-Windows.
29 #include "config.h"
30 #include "wine/port.h"
32 #include <assert.h>
33 #include <ctype.h>
34 #include <stdlib.h>
35 #include <string.h>
37 #include "windef.h"
38 #include "winnls.h"
39 #include "wingdi.h"
40 #include "wine/winbase16.h"
41 #include "wine/winuser16.h"
42 #include "wine/unicode.h"
43 #include "win.h"
44 #include "controls.h"
45 #include "nonclient.h"
46 #include "user.h"
47 #include "message.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(menu);
52 WINE_DECLARE_DEBUG_CHANNEL(accel);
54 /* internal popup menu window messages */
56 #define MM_SETMENUHANDLE (WM_USER + 0)
57 #define MM_GETMENUHANDLE (WM_USER + 1)
59 /* Menu item structure */
60 typedef struct {
61 /* ----------- MENUITEMINFO Stuff ----------- */
62 UINT fType; /* Item type. */
63 UINT fState; /* Item state. */
64 UINT wID; /* Item id. */
65 HMENU hSubMenu; /* Pop-up menu. */
66 HBITMAP hCheckBit; /* Bitmap when checked. */
67 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
68 LPWSTR text; /* Item text or bitmap handle. */
69 DWORD dwItemData; /* Application defined. */
70 DWORD dwTypeData; /* depends on fMask */
71 HBITMAP hbmpItem; /* bitmap in win98 style menus */
72 /* ----------- Wine stuff ----------- */
73 RECT rect; /* Item area (relative to menu window) */
74 UINT xTab; /* X position of text after Tab */
75 } MENUITEM;
77 /* Popup menu structure */
78 typedef struct {
79 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
80 WORD wMagic; /* Magic number */
81 WORD Width; /* Width of the whole menu */
82 WORD Height; /* Height of the whole menu */
83 UINT nItems; /* Number of items in the menu */
84 HWND hWnd; /* Window containing the menu */
85 MENUITEM *items; /* Array of menu items */
86 UINT FocusedItem; /* Currently focused item */
87 HWND hwndOwner; /* window receiving the messages for ownerdraw */
88 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
89 /* ------------ MENUINFO members ------ */
90 DWORD dwStyle; /* Extended mennu style */
91 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
92 HBRUSH hbrBack; /* brush for menu background */
93 DWORD dwContextHelpID;
94 DWORD dwMenuData; /* application defined value */
95 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
96 } POPUPMENU, *LPPOPUPMENU;
98 /* internal flags for menu tracking */
100 #define TF_ENDMENU 0x0001
101 #define TF_SUSPENDPOPUP 0x0002
102 #define TF_SKIPREMOVE 0x0004
104 typedef struct
106 UINT trackFlags;
107 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
108 HMENU hTopMenu; /* initial menu */
109 HWND hOwnerWnd; /* where notifications are sent */
110 POINT pt;
111 } MTRACKER;
113 #define MENU_MAGIC 0x554d /* 'MU' */
115 #define ITEM_PREV -1
116 #define ITEM_NEXT 1
118 /* Internal MENU_TrackMenu() flags */
119 #define TPM_INTERNAL 0xF0000000
120 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
121 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
122 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
124 /* popup menu shade thickness */
125 #define POPUP_XSHADE 4
126 #define POPUP_YSHADE 4
128 /* Space between 2 menu bar items */
129 #define MENU_BAR_ITEMS_SPACE 12
131 /* Minimum width of a tab character */
132 #define MENU_TAB_SPACE 8
134 /* Height of a separator item */
135 #define SEPARATOR_HEIGHT 5
137 /* (other menu->FocusedItem values give the position of the focused item) */
138 #define NO_SELECTED_ITEM 0xffff
140 #define MENU_ITEM_TYPE(flags) \
141 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
143 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
144 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
145 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
147 #define IS_SYSTEM_MENU(menu) \
148 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
150 #define IS_SYSTEM_POPUP(menu) \
151 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
153 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
154 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
155 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
156 MF_POPUP | MF_SYSMENU | MF_HELP)
157 #define STATE_MASK (~TYPE_MASK)
159 /* Dimension of the menu bitmaps */
160 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
162 static HBITMAP hStdMnArrow = 0;
163 static HBITMAP hBmpSysMenu = 0;
165 static HBRUSH hShadeBrush = 0;
166 static HFONT hMenuFont = 0;
167 static HFONT hMenuFontBold = 0;
169 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
171 /* Use global popup window because there's no way 2 menus can
172 * be tracked at the same time. */
173 static HWND top_popup;
175 /* Flag set by EndMenu() to force an exit from menu tracking */
176 static BOOL fEndMenu = FALSE;
178 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
181 /*********************************************************************
182 * menu class descriptor
184 const struct builtin_class_descr MENU_builtin_class =
186 POPUPMENU_CLASS_ATOM, /* name */
187 CS_GLOBALCLASS | CS_SAVEBITS, /* style */
188 NULL, /* procA (winproc is Unicode only) */
189 PopupMenuWndProc, /* procW */
190 sizeof(HMENU), /* extra */
191 IDC_ARROWA, /* cursor */
192 COLOR_MENU+1 /* brush */
196 /***********************************************************************
197 * debug_print_menuitem
199 * Print a menuitem in readable form.
202 #define debug_print_menuitem(pre, mp, post) \
203 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
205 #define MENUOUT(text) \
206 DPRINTF("%s%s", (count++ ? "," : ""), (text))
208 #define MENUFLAG(bit,text) \
209 do { \
210 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
211 } while (0)
213 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
214 const char *postfix)
216 TRACE("%s ", prefix);
217 if (mp) {
218 UINT flags = mp->fType;
219 int typ = MENU_ITEM_TYPE(flags);
220 DPRINTF( "{ ID=0x%x", mp->wID);
221 if (flags & MF_POPUP)
222 DPRINTF( ", Sub=0x%x", mp->hSubMenu);
223 if (flags) {
224 int count = 0;
225 DPRINTF( ", Typ=");
226 if (typ == MFT_STRING)
227 /* Nothing */ ;
228 else if (typ == MFT_SEPARATOR)
229 MENUOUT("sep");
230 else if (typ == MFT_OWNERDRAW)
231 MENUOUT("own");
232 else if (typ == MFT_BITMAP)
233 MENUOUT("bit");
234 else
235 MENUOUT("???");
236 flags -= typ;
238 MENUFLAG(MF_POPUP, "pop");
239 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
240 MENUFLAG(MFT_MENUBREAK, "brk");
241 MENUFLAG(MFT_RADIOCHECK, "radio");
242 MENUFLAG(MFT_RIGHTORDER, "rorder");
243 MENUFLAG(MF_SYSMENU, "sys");
244 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
246 if (flags)
247 DPRINTF( "+0x%x", flags);
249 flags = mp->fState;
250 if (flags) {
251 int count = 0;
252 DPRINTF( ", State=");
253 MENUFLAG(MFS_GRAYED, "grey");
254 MENUFLAG(MFS_DEFAULT, "default");
255 MENUFLAG(MFS_DISABLED, "dis");
256 MENUFLAG(MFS_CHECKED, "check");
257 MENUFLAG(MFS_HILITE, "hi");
258 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
259 MENUFLAG(MF_MOUSESELECT, "mouse");
260 if (flags)
261 DPRINTF( "+0x%x", flags);
263 if (mp->hCheckBit)
264 DPRINTF( ", Chk=0x%x", mp->hCheckBit);
265 if (mp->hUnCheckBit)
266 DPRINTF( ", Unc=0x%x", mp->hUnCheckBit);
268 if (typ == MFT_STRING) {
269 if (mp->text)
270 DPRINTF( ", Text=%s", debugstr_w(mp->text));
271 else
272 DPRINTF( ", Text=Null");
273 } else if (mp->text == NULL)
274 /* Nothing */ ;
275 else
276 DPRINTF( ", Text=%p", mp->text);
277 if (mp->dwItemData)
278 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
279 DPRINTF( " }");
280 } else {
281 DPRINTF( "NULL");
284 DPRINTF(" %s\n", postfix);
287 #undef MENUOUT
288 #undef MENUFLAG
291 /***********************************************************************
292 * MENU_GetMenu
294 * Validate the given menu handle and returns the menu structure pointer.
296 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
298 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
299 if (!menu || menu->wMagic != MENU_MAGIC)
301 WARN("invalid menu handle=%x, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
302 menu = NULL;
304 return menu;
307 /***********************************************************************
308 * get_win_sys_menu
310 * Get the system menu of a window
312 static HMENU get_win_sys_menu( HWND hwnd )
314 HMENU ret = 0;
315 WND *win = WIN_FindWndPtr( hwnd );
316 if (win)
318 ret = win->hSysMenu;
319 WIN_ReleaseWndPtr( win );
321 return ret;
324 /***********************************************************************
325 * MENU_CopySysPopup
327 * Return the default system menu.
329 static HMENU MENU_CopySysPopup(void)
331 HMENU hMenu = LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
333 if( hMenu ) {
334 POPUPMENU* menu = MENU_GetMenu(hMenu);
335 menu->wFlags |= MF_SYSMENU | MF_POPUP;
336 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
338 else
339 ERR("Unable to load default system menu\n" );
341 TRACE("returning %x.\n", hMenu );
343 return hMenu;
347 /**********************************************************************
348 * MENU_GetSysMenu
350 * Create a copy of the system menu. System menu in Windows is
351 * a special menu bar with the single entry - system menu popup.
352 * This popup is presented to the outside world as a "system menu".
353 * However, the real system menu handle is sometimes seen in the
354 * WM_MENUSELECT parameters (and Word 6 likes it this way).
356 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
358 HMENU hMenu;
360 if ((hMenu = CreateMenu()))
362 POPUPMENU *menu = MENU_GetMenu(hMenu);
363 menu->wFlags = MF_SYSMENU;
364 menu->hWnd = WIN_GetFullHandle( hWnd );
366 if (hPopupMenu == (HMENU)(-1))
367 hPopupMenu = MENU_CopySysPopup();
368 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
370 if (hPopupMenu)
372 InsertMenuA( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION, hPopupMenu, NULL );
374 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
375 menu->items[0].fState = 0;
376 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
378 TRACE("GetSysMenu hMenu=%04x (%04x)\n", hMenu, hPopupMenu );
379 return hMenu;
381 DestroyMenu( hMenu );
383 ERR("failed to load system menu!\n");
384 return 0;
388 /***********************************************************************
389 * MENU_Init
391 * Menus initialisation.
393 BOOL MENU_Init()
395 HBITMAP hBitmap;
396 NONCLIENTMETRICSA ncm;
398 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
399 0x55, 0, 0xAA, 0,
400 0x55, 0, 0xAA, 0,
401 0x55, 0, 0xAA, 0 };
403 /* Load menu bitmaps */
404 hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
405 /* Load system buttons bitmaps */
406 hBmpSysMenu = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_CLOSE));
408 if (hStdMnArrow)
410 BITMAP bm;
411 GetObjectA( hStdMnArrow, sizeof(bm), &bm );
412 arrow_bitmap_width = bm.bmWidth;
413 arrow_bitmap_height = bm.bmHeight;
414 } else
415 return FALSE;
417 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
418 return FALSE;
420 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
421 return FALSE;
423 DeleteObject( hBitmap );
424 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
425 return FALSE;
427 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
428 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0)))
429 return FALSE;
431 if (!(hMenuFont = CreateFontIndirectA( &ncm.lfMenuFont )))
432 return FALSE;
434 ncm.lfMenuFont.lfWeight += 300;
435 if ( ncm.lfMenuFont.lfWeight > 1000)
436 ncm.lfMenuFont.lfWeight = 1000;
438 if (!(hMenuFontBold = CreateFontIndirectA( &ncm.lfMenuFont )))
439 return FALSE;
441 return TRUE;
444 /***********************************************************************
445 * MENU_InitSysMenuPopup
447 * Grey the appropriate items in System menu.
449 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
451 BOOL gray;
453 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
454 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
455 gray = ((style & WS_MAXIMIZE) != 0);
456 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
457 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
458 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
459 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
460 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
461 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
462 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
463 gray = (clsStyle & CS_NOCLOSE) != 0;
465 /* The menu item must keep its state if it's disabled */
466 if(gray)
467 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
471 /******************************************************************************
473 * UINT MENU_GetStartOfNextColumn(
474 * HMENU hMenu )
476 *****************************************************************************/
478 static UINT MENU_GetStartOfNextColumn(
479 HMENU hMenu )
481 POPUPMENU *menu = MENU_GetMenu(hMenu);
482 UINT i;
484 if(!menu)
485 return NO_SELECTED_ITEM;
487 i = menu->FocusedItem + 1;
488 if( i == NO_SELECTED_ITEM )
489 return i;
491 for( ; i < menu->nItems; ++i ) {
492 if (menu->items[i].fType & MF_MENUBARBREAK)
493 return i;
496 return NO_SELECTED_ITEM;
500 /******************************************************************************
502 * UINT MENU_GetStartOfPrevColumn(
503 * HMENU hMenu )
505 *****************************************************************************/
507 static UINT MENU_GetStartOfPrevColumn(
508 HMENU hMenu )
510 POPUPMENU *menu = MENU_GetMenu(hMenu);
511 UINT i;
513 if( !menu )
514 return NO_SELECTED_ITEM;
516 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
517 return NO_SELECTED_ITEM;
519 /* Find the start of the column */
521 for(i = menu->FocusedItem; i != 0 &&
522 !(menu->items[i].fType & MF_MENUBARBREAK);
523 --i); /* empty */
525 if(i == 0)
526 return NO_SELECTED_ITEM;
528 for(--i; i != 0; --i) {
529 if (menu->items[i].fType & MF_MENUBARBREAK)
530 break;
533 TRACE("ret %d.\n", i );
535 return i;
540 /***********************************************************************
541 * MENU_FindItem
543 * Find a menu item. Return a pointer on the item, and modifies *hmenu
544 * in case the item was in a sub-menu.
546 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
548 POPUPMENU *menu;
549 UINT i;
551 if (((*hmenu)==0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
552 if (wFlags & MF_BYPOSITION)
554 if (*nPos >= menu->nItems) return NULL;
555 return &menu->items[*nPos];
557 else
559 MENUITEM *item = menu->items;
560 for (i = 0; i < menu->nItems; i++, item++)
562 if (item->wID == *nPos)
564 *nPos = i;
565 return item;
567 else if (item->fType & MF_POPUP)
569 HMENU hsubmenu = item->hSubMenu;
570 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
571 if (subitem)
573 *hmenu = hsubmenu;
574 return subitem;
579 return NULL;
582 /***********************************************************************
583 * MENU_FindSubMenu
585 * Find a Sub menu. Return the position of the submenu, and modifies
586 * *hmenu in case it is found in another sub-menu.
587 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
589 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
591 POPUPMENU *menu;
592 UINT i;
593 MENUITEM *item;
594 if (((*hmenu)==0xffff) ||
595 (!(menu = MENU_GetMenu(*hmenu))))
596 return NO_SELECTED_ITEM;
597 item = menu->items;
598 for (i = 0; i < menu->nItems; i++, item++) {
599 if(!(item->fType & MF_POPUP)) continue;
600 if (item->hSubMenu == hSubTarget) {
601 return i;
603 else {
604 HMENU hsubmenu = item->hSubMenu;
605 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
606 if (pos != NO_SELECTED_ITEM) {
607 *hmenu = hsubmenu;
608 return pos;
612 return NO_SELECTED_ITEM;
615 /***********************************************************************
616 * MENU_FreeItemData
618 static void MENU_FreeItemData( MENUITEM* item )
620 /* delete text */
621 if (IS_STRING_ITEM(item->fType) && item->text)
622 HeapFree( GetProcessHeap(), 0, item->text );
625 /***********************************************************************
626 * MENU_FindItemByCoords
628 * Find the item at the specified coordinates (screen coords). Does
629 * not work for child windows and therefore should not be called for
630 * an arbitrary system menu.
632 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
633 POINT pt, UINT *pos )
635 MENUITEM *item;
636 UINT i;
637 RECT wrect;
639 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
640 pt.x -= wrect.left;pt.y -= wrect.top;
641 item = menu->items;
642 for (i = 0; i < menu->nItems; i++, item++)
644 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
645 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
647 if (pos) *pos = i;
648 return item;
651 return NULL;
655 /***********************************************************************
656 * MENU_FindItemByKey
658 * Find the menu item selected by a key press.
659 * Return item id, -1 if none, -2 if we should close the menu.
661 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
662 UINT key, BOOL forceMenuChar )
664 TRACE("\tlooking for '%c' in [%04x]\n", (char)key, (UINT16)hmenu );
666 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
668 if (hmenu)
670 POPUPMENU *menu = MENU_GetMenu( hmenu );
671 MENUITEM *item = menu->items;
672 LONG menuchar;
674 if( !forceMenuChar )
676 UINT i;
678 key = toupper(key);
679 for (i = 0; i < menu->nItems; i++, item++)
681 if (item->text && (IS_STRING_ITEM(item->fType)))
683 WCHAR *p = item->text - 2;
686 p = strchrW (p + 2, '&');
688 while (p != NULL && p [1] == '&');
689 if (p && (toupper(p[1]) == key)) return i;
693 menuchar = SendMessageA( hwndOwner, WM_MENUCHAR,
694 MAKEWPARAM( key, menu->wFlags ), hmenu );
695 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
696 if (HIWORD(menuchar) == 1) return (UINT)(-2);
698 return (UINT)(-1);
702 /***********************************************************************
703 * MENU_GetBitmapItemSize
705 * Get the size of a bitmap item.
707 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
709 BITMAP bm;
710 HBITMAP bmp = (HBITMAP)id;
712 size->cx = size->cy = 0;
714 /* check if there is a magic menu item associated with this item */
715 if (id && IS_MAGIC_ITEM( id ))
717 switch(LOWORD(id))
719 case HBMMENU_SYSTEM:
720 if (data)
722 bmp = (HBITMAP)data;
723 break;
725 /* fall through */
726 case HBMMENU_MBAR_RESTORE:
727 case HBMMENU_MBAR_MINIMIZE:
728 case HBMMENU_MBAR_MINIMIZE_D:
729 case HBMMENU_MBAR_CLOSE:
730 case HBMMENU_MBAR_CLOSE_D:
731 size->cx = GetSystemMetrics( SM_CXSIZE );
732 size->cy = GetSystemMetrics( SM_CYSIZE );
733 return;
734 case HBMMENU_CALLBACK:
735 case HBMMENU_POPUP_CLOSE:
736 case HBMMENU_POPUP_RESTORE:
737 case HBMMENU_POPUP_MAXIMIZE:
738 case HBMMENU_POPUP_MINIMIZE:
739 default:
740 FIXME("Magic 0x%08x not implemented\n", id);
741 return;
744 if (GetObjectA(bmp, sizeof(bm), &bm ))
746 size->cx = bm.bmWidth;
747 size->cy = bm.bmHeight;
751 /***********************************************************************
752 * MENU_DrawBitmapItem
754 * Draw a bitmap item.
756 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar )
758 BITMAP bm;
759 DWORD rop;
760 HDC hdcMem;
761 HBITMAP bmp = (HBITMAP)lpitem->text;
762 int w = rect->right - rect->left;
763 int h = rect->bottom - rect->top;
764 int bmp_xoffset = 0;
765 int left, top;
767 /* Check if there is a magic menu item associated with this item */
768 if (lpitem->text && IS_MAGIC_ITEM(lpitem->text))
770 UINT flags = 0;
771 RECT r;
773 switch(LOWORD(lpitem->text))
775 case HBMMENU_SYSTEM:
776 if (lpitem->dwItemData)
778 bmp = (HBITMAP)lpitem->dwItemData;
779 if (!GetObjectA( bmp, sizeof(bm), &bm )) return;
781 else
783 bmp = hBmpSysMenu;
784 if (!GetObjectA( bmp, sizeof(bm), &bm )) return;
785 /* only use right half of the bitmap */
786 bmp_xoffset = bm.bmWidth / 2;
787 bm.bmWidth -= bmp_xoffset;
789 goto got_bitmap;
790 case HBMMENU_MBAR_RESTORE:
791 flags = DFCS_CAPTIONRESTORE;
792 break;
793 case HBMMENU_MBAR_MINIMIZE:
794 flags = DFCS_CAPTIONMIN;
795 break;
796 case HBMMENU_MBAR_MINIMIZE_D:
797 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
798 break;
799 case HBMMENU_MBAR_CLOSE:
800 flags = DFCS_CAPTIONCLOSE;
801 break;
802 case HBMMENU_MBAR_CLOSE_D:
803 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
804 break;
805 case HBMMENU_CALLBACK:
806 case HBMMENU_POPUP_CLOSE:
807 case HBMMENU_POPUP_RESTORE:
808 case HBMMENU_POPUP_MAXIMIZE:
809 case HBMMENU_POPUP_MINIMIZE:
810 default:
811 FIXME("Magic 0x%08x not implemented\n", LOWORD(lpitem->text));
812 return;
814 r = *rect;
815 InflateRect( &r, -1, -1 );
816 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
817 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
818 return;
821 if (!bmp || !GetObjectA( bmp, sizeof(bm), &bm )) return;
823 got_bitmap:
824 hdcMem = CreateCompatibleDC( hdc );
825 SelectObject( hdcMem, bmp );
827 /* handle fontsize > bitmap_height */
828 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
829 left=rect->left;
830 if (TWEAK_WineLook == WIN95_LOOK) {
831 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
832 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
833 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
834 } else {
835 left++;
836 w-=2;
837 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text) && (!menuBar)) ? MERGEPAINT : SRCCOPY;
839 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
840 DeleteDC( hdcMem );
844 /***********************************************************************
845 * MENU_CalcItemSize
847 * Calculate the size of the menu item and store it in lpitem->rect.
849 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
850 INT orgX, INT orgY, BOOL menuBar )
852 WCHAR *p;
853 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
855 TRACE("dc=0x%04x owner=0x%04x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
856 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
857 (menuBar ? " (MenuBar)" : ""));
859 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
861 if (lpitem->fType & MF_OWNERDRAW)
864 ** Experimentation under Windows reveals that an owner-drawn
865 ** menu is expected to return the size of the content part of
866 ** the menu item, not including the checkmark nor the submenu
867 ** arrow. Windows adds those values itself and returns the
868 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
870 MEASUREITEMSTRUCT mis;
871 mis.CtlType = ODT_MENU;
872 mis.CtlID = 0;
873 mis.itemID = lpitem->wID;
874 mis.itemData = (DWORD)lpitem->dwItemData;
875 mis.itemHeight = 0;
876 mis.itemWidth = 0;
877 SendMessageA( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
878 lpitem->rect.right += mis.itemWidth;
880 if (menuBar)
882 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
885 /* under at least win95 you seem to be given a standard
886 height for the menu and the height value is ignored */
888 if (TWEAK_WineLook == WIN31_LOOK)
889 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
890 else
891 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
893 else
894 lpitem->rect.bottom += mis.itemHeight;
896 TRACE("id=%04x size=%dx%d\n",
897 lpitem->wID, mis.itemWidth, mis.itemHeight);
898 /* Fall through to get check/arrow width calculation. */
901 if (lpitem->fType & MF_SEPARATOR)
903 lpitem->rect.bottom += SEPARATOR_HEIGHT;
904 return;
907 if (!menuBar)
909 lpitem->rect.right += 2 * check_bitmap_width;
910 if (lpitem->fType & MF_POPUP)
911 lpitem->rect.right += arrow_bitmap_width;
914 if (lpitem->fType & MF_OWNERDRAW)
915 return;
917 if (IS_BITMAP_ITEM(lpitem->fType))
919 SIZE size;
921 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
922 lpitem->rect.right += size.cx;
923 lpitem->rect.bottom += size.cy;
924 if (TWEAK_WineLook == WIN98_LOOK)
926 /* Leave space for the sunken border */
927 lpitem->rect.right += 2;
928 lpitem->rect.bottom += 2;
933 /* it must be a text item - unless it's the system menu */
934 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
935 { SIZE size;
937 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
939 lpitem->rect.right += size.cx;
940 if (TWEAK_WineLook == WIN31_LOOK)
941 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
942 else
943 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
944 lpitem->xTab = 0;
946 if (menuBar)
948 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
950 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
952 /* Item contains a tab (only meaningful in popup menus) */
953 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
954 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
955 lpitem->rect.right += MENU_TAB_SPACE;
957 else
959 if (strchrW( lpitem->text, '\b' ))
960 lpitem->rect.right += MENU_TAB_SPACE;
961 lpitem->xTab = lpitem->rect.right - check_bitmap_width
962 - arrow_bitmap_width;
965 TRACE("(%d,%d)-(%d,%d)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
969 /***********************************************************************
970 * MENU_PopupMenuCalcSize
972 * Calculate the size of a popup menu.
974 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
976 MENUITEM *lpitem;
977 HDC hdc;
978 int start, i;
979 int orgX, orgY, maxX, maxTab, maxTabWidth;
981 lppop->Width = lppop->Height = 0;
982 if (lppop->nItems == 0) return;
983 hdc = GetDC( 0 );
985 SelectObject( hdc, hMenuFont);
987 start = 0;
988 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
990 while (start < lppop->nItems)
992 lpitem = &lppop->items[start];
993 orgX = maxX;
994 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
996 maxTab = maxTabWidth = 0;
998 /* Parse items until column break or end of menu */
999 for (i = start; i < lppop->nItems; i++, lpitem++)
1001 if ((i != start) &&
1002 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1004 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
1006 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1007 maxX = max( maxX, lpitem->rect.right );
1008 orgY = lpitem->rect.bottom;
1009 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1011 maxTab = max( maxTab, lpitem->xTab );
1012 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1016 /* Finish the column (set all items to the largest width found) */
1017 maxX = max( maxX, maxTab + maxTabWidth );
1018 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1020 lpitem->rect.right = maxX;
1021 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1022 lpitem->xTab = maxTab;
1025 lppop->Height = max( lppop->Height, orgY );
1028 lppop->Width = maxX;
1030 /* space for 3d border */
1031 if(TWEAK_WineLook > WIN31_LOOK)
1033 lppop->Height += 2;
1034 lppop->Width += 2;
1037 ReleaseDC( 0, hdc );
1041 /***********************************************************************
1042 * MENU_MenuBarCalcSize
1044 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1045 * height is off by 1 pixel which causes lengthy window relocations when
1046 * active document window is maximized/restored.
1048 * Calculate the size of the menu bar.
1050 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1051 LPPOPUPMENU lppop, HWND hwndOwner )
1053 MENUITEM *lpitem;
1054 int start, i, orgX, orgY, maxY, helpPos;
1056 if ((lprect == NULL) || (lppop == NULL)) return;
1057 if (lppop->nItems == 0) return;
1058 TRACE("left=%d top=%d right=%d bottom=%d\n",
1059 lprect->left, lprect->top, lprect->right, lprect->bottom);
1060 lppop->Width = lprect->right - lprect->left;
1061 lppop->Height = 0;
1062 maxY = lprect->top+1;
1063 start = 0;
1064 helpPos = -1;
1065 while (start < lppop->nItems)
1067 lpitem = &lppop->items[start];
1068 orgX = lprect->left;
1069 orgY = maxY;
1071 /* Parse items until line break or end of menu */
1072 for (i = start; i < lppop->nItems; i++, lpitem++)
1074 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1075 if ((i != start) &&
1076 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1078 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1079 orgX, orgY );
1080 debug_print_menuitem (" item: ", lpitem, "");
1081 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
1083 if (lpitem->rect.right > lprect->right)
1085 if (i != start) break;
1086 else lpitem->rect.right = lprect->right;
1088 maxY = max( maxY, lpitem->rect.bottom );
1089 orgX = lpitem->rect.right;
1092 /* Finish the line (set all items to the largest height found) */
1093 while (start < i) lppop->items[start++].rect.bottom = maxY;
1096 lprect->bottom = maxY;
1097 lppop->Height = lprect->bottom - lprect->top;
1099 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1100 /* the last item (if several lines, only move the last line) */
1101 lpitem = &lppop->items[lppop->nItems-1];
1102 orgY = lpitem->rect.top;
1103 orgX = lprect->right;
1104 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1105 if ( (helpPos==-1) || (helpPos>i) )
1106 break; /* done */
1107 if (lpitem->rect.top != orgY) break; /* Other line */
1108 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1109 lpitem->rect.left += orgX - lpitem->rect.right;
1110 lpitem->rect.right = orgX;
1111 orgX = lpitem->rect.left;
1115 /***********************************************************************
1116 * MENU_DrawMenuItem
1118 * Draw a single menu item.
1120 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1121 UINT height, BOOL menuBar, UINT odaction )
1123 RECT rect;
1125 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1127 if (lpitem->fType & MF_SYSMENU)
1129 if( !IsIconic(hwnd) ) {
1130 if (TWEAK_WineLook > WIN31_LOOK)
1131 NC_DrawSysButton95( hwnd, hdc,
1132 lpitem->fState &
1133 (MF_HILITE | MF_MOUSESELECT) );
1134 else
1135 NC_DrawSysButton( hwnd, hdc,
1136 lpitem->fState &
1137 (MF_HILITE | MF_MOUSESELECT) );
1140 return;
1143 if (lpitem->fType & MF_OWNERDRAW)
1146 ** Experimentation under Windows reveals that an owner-drawn
1147 ** menu is given the rectangle which includes the space it requested
1148 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1149 ** and a popup-menu arrow. This is the value of lpitem->rect.
1150 ** Windows will leave all drawing to the application except for
1151 ** the popup-menu arrow. Windows always draws that itself, after
1152 ** the menu owner has finished drawing.
1154 DRAWITEMSTRUCT dis;
1156 dis.CtlType = ODT_MENU;
1157 dis.CtlID = 0;
1158 dis.itemID = lpitem->wID;
1159 dis.itemData = (DWORD)lpitem->dwItemData;
1160 dis.itemState = 0;
1161 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1162 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED;
1163 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1164 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1165 dis.hwndItem = (HWND)hmenu;
1166 dis.hDC = hdc;
1167 dis.rcItem = lpitem->rect;
1168 TRACE("Ownerdraw: owner=%04x itemID=%d, itemState=%d, itemAction=%d, "
1169 "hwndItem=%04x, hdc=%04x, rcItem={%d,%d,%d,%d}\n", hwndOwner,
1170 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1171 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1172 dis.rcItem.bottom);
1173 SendMessageA( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1174 /* Fall through to draw popup-menu arrow */
1177 TRACE("rect={%d,%d,%d,%d}\n", lpitem->rect.left, lpitem->rect.top,
1178 lpitem->rect.right,lpitem->rect.bottom);
1180 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1182 rect = lpitem->rect;
1184 if (!(lpitem->fType & MF_OWNERDRAW))
1186 if (lpitem->fState & MF_HILITE)
1188 if(TWEAK_WineLook == WIN98_LOOK)
1190 if(menuBar)
1191 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1192 else
1193 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1195 else /* Not Win98 Look */
1197 if(!IS_BITMAP_ITEM(lpitem->fType))
1198 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1201 else
1202 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1205 SetBkMode( hdc, TRANSPARENT );
1207 if (!(lpitem->fType & MF_OWNERDRAW))
1209 /* vertical separator */
1210 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1212 if (TWEAK_WineLook > WIN31_LOOK)
1214 RECT rc = rect;
1215 rc.top = 3;
1216 rc.bottom = height - 3;
1217 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1219 else
1221 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1222 MoveToEx( hdc, rect.left, 0, NULL );
1223 LineTo( hdc, rect.left, height );
1227 /* horizontal separator */
1228 if (lpitem->fType & MF_SEPARATOR)
1230 if (TWEAK_WineLook > WIN31_LOOK)
1232 RECT rc = rect;
1233 rc.left++;
1234 rc.right--;
1235 rc.top += SEPARATOR_HEIGHT / 2;
1236 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1238 else
1240 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1241 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1242 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1244 return;
1248 /* Setup colors */
1250 if (lpitem->fState & MF_HILITE)
1252 if(TWEAK_WineLook == WIN98_LOOK)
1254 if(menuBar) {
1255 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1256 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1257 } else {
1258 if(lpitem->fState & MF_GRAYED)
1259 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1260 else
1261 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1262 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1265 else /* Not Win98 Look */
1267 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1268 if(!IS_BITMAP_ITEM(lpitem->fType))
1269 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1272 else
1274 if (lpitem->fState & MF_GRAYED)
1275 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1276 else
1277 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1278 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1281 /* helper lines for debugging */
1282 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1283 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1284 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1285 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1288 if (!menuBar)
1290 INT y = rect.top + rect.bottom;
1291 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1292 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1294 if (!(lpitem->fType & MF_OWNERDRAW))
1296 /* Draw the check mark
1298 * FIXME:
1299 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1301 HBITMAP bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1302 if (bm) /* we have a custom bitmap */
1304 HDC hdcMem = CreateCompatibleDC( hdc );
1305 SelectObject( hdcMem, bm );
1306 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1307 check_bitmap_width, check_bitmap_height,
1308 hdcMem, 0, 0, SRCCOPY );
1309 DeleteDC( hdcMem );
1311 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1313 RECT r;
1314 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1315 HDC hdcMem = CreateCompatibleDC( hdc );
1316 SelectObject( hdcMem, bm );
1317 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1318 DrawFrameControl( hdcMem, &r, DFC_MENU,
1319 (lpitem->fType & MFT_RADIOCHECK) ?
1320 DFCS_MENUBULLET : DFCS_MENUCHECK );
1321 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1322 hdcMem, 0, 0, SRCCOPY );
1323 DeleteDC( hdcMem );
1324 DeleteObject( bm );
1328 /* Draw the popup-menu arrow */
1329 if (lpitem->fType & MF_POPUP)
1331 HDC hdcMem = CreateCompatibleDC( hdc );
1332 HBITMAP hOrigBitmap;
1334 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1335 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1336 (y - arrow_bitmap_height) / 2,
1337 arrow_bitmap_width, arrow_bitmap_height,
1338 hdcMem, 0, 0, SRCCOPY );
1339 SelectObject( hdcMem, hOrigBitmap );
1340 DeleteDC( hdcMem );
1343 rect.left += check_bitmap_width;
1344 rect.right -= arrow_bitmap_width;
1347 /* Done for owner-drawn */
1348 if (lpitem->fType & MF_OWNERDRAW)
1349 return;
1351 /* Draw the item text or bitmap */
1352 if (IS_BITMAP_ITEM(lpitem->fType))
1354 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar );
1355 return;
1358 /* No bitmap - process text if present */
1359 else if (IS_STRING_ITEM(lpitem->fType))
1361 register int i;
1362 HFONT hfontOld = 0;
1364 UINT uFormat = (menuBar) ?
1365 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1366 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1368 if ( lpitem->fState & MFS_DEFAULT )
1370 hfontOld = SelectObject( hdc, hMenuFontBold);
1373 if (menuBar)
1375 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1376 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1379 for (i = 0; lpitem->text[i]; i++)
1380 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1381 break;
1383 if( (TWEAK_WineLook != WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1385 if (!(lpitem->fState & MF_HILITE) )
1387 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1388 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1389 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1390 --rect.left; --rect.top; --rect.right; --rect.bottom;
1392 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1395 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1397 /* paint the shortcut text */
1398 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1400 if (lpitem->text[i] == '\t')
1402 rect.left = lpitem->xTab;
1403 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1405 else
1407 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1410 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1412 if (!(lpitem->fState & MF_HILITE) )
1414 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1415 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1416 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1417 --rect.left; --rect.top; --rect.right; --rect.bottom;
1419 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1421 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1424 if (hfontOld)
1425 SelectObject (hdc, hfontOld);
1430 /***********************************************************************
1431 * MENU_DrawPopupMenu
1433 * Paint a popup menu.
1435 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1437 HBRUSH hPrevBrush = 0;
1438 RECT rect;
1440 TRACE("wnd=0x%04x dc=0x%04x menu=0x%04x\n", hwnd, hdc, hmenu);
1442 GetClientRect( hwnd, &rect );
1444 if(TWEAK_WineLook == WIN31_LOOK)
1446 rect.bottom -= POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1447 rect.right -= POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER);
1450 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1451 && (SelectObject( hdc, hMenuFont)))
1453 HPEN hPrevPen;
1455 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1457 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1458 if( hPrevPen )
1460 INT ropPrev, i;
1461 POPUPMENU *menu;
1463 /* draw 3-d shade */
1464 if(TWEAK_WineLook == WIN31_LOOK) {
1465 SelectObject( hdc, hShadeBrush );
1466 SetBkMode( hdc, TRANSPARENT );
1467 ropPrev = SetROP2( hdc, R2_MASKPEN );
1469 i = rect.right; /* why SetBrushOrg() doesn't? */
1470 PatBlt( hdc, i & 0xfffffffe,
1471 rect.top + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER),
1472 i%2 + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1473 rect.bottom - rect.top, 0x00a000c9 );
1474 i = rect.bottom;
1475 PatBlt( hdc, rect.left + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1476 i & 0xfffffffe,rect.right - rect.left,
1477 i%2 + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER), 0x00a000c9 );
1478 SelectObject( hdc, hPrevPen );
1479 SelectObject( hdc, hPrevBrush );
1480 SetROP2( hdc, ropPrev );
1482 else
1483 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1485 /* draw menu items */
1487 menu = MENU_GetMenu( hmenu );
1488 if (menu && menu->nItems)
1490 MENUITEM *item;
1491 UINT u;
1493 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1494 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1495 menu->Height, FALSE, ODA_DRAWENTIRE );
1498 } else
1500 SelectObject( hdc, hPrevBrush );
1505 /***********************************************************************
1506 * MENU_DrawMenuBar
1508 * Paint a menu bar. Returns the height of the menu bar.
1509 * called from [windows/nonclient.c]
1511 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1512 BOOL suppress_draw)
1514 LPPOPUPMENU lppop;
1515 UINT i,retvalue;
1516 HFONT hfontOld = 0;
1517 HMENU hMenu = GetMenu(hwnd);
1519 lppop = MENU_GetMenu( hMenu );
1520 if (lppop == NULL || lprect == NULL)
1522 retvalue = GetSystemMetrics(SM_CYMENU);
1523 goto END;
1526 TRACE("(%04x, %p, %p)\n", hDC, lprect, lppop);
1528 hfontOld = SelectObject( hDC, hMenuFont);
1530 if (lppop->Height == 0)
1531 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1533 lprect->bottom = lprect->top + lppop->Height;
1535 if (suppress_draw)
1537 retvalue = lppop->Height;
1538 goto END;
1541 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
1543 if (TWEAK_WineLook == WIN31_LOOK)
1545 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1546 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1547 LineTo( hDC, lprect->right, lprect->bottom );
1549 else
1551 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
1552 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1553 LineTo( hDC, lprect->right, lprect->bottom );
1556 if (lppop->nItems == 0)
1558 retvalue = GetSystemMetrics(SM_CYMENU);
1559 goto END;
1562 for (i = 0; i < lppop->nItems; i++)
1564 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
1565 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
1567 retvalue = lppop->Height;
1569 END:
1570 if (hfontOld) SelectObject (hDC, hfontOld);
1571 return retvalue;
1575 /***********************************************************************
1576 * MENU_ShowPopup
1578 * Display a popup menu.
1580 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1581 INT x, INT y, INT xanchor, INT yanchor )
1583 POPUPMENU *menu;
1584 UINT width, height;
1586 TRACE("owner=0x%04x hmenu=0x%04x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1587 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1589 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1590 if (menu->FocusedItem != NO_SELECTED_ITEM)
1592 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1593 menu->FocusedItem = NO_SELECTED_ITEM;
1596 /* store the owner for DrawItem */
1597 menu->hwndOwner = hwndOwner;
1600 MENU_PopupMenuCalcSize( menu, hwndOwner );
1602 /* adjust popup menu pos so that it fits within the desktop */
1604 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1605 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1607 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1609 if( xanchor )
1610 x -= width - xanchor;
1611 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1612 x = GetSystemMetrics(SM_CXSCREEN) - width;
1614 if( x < 0 ) x = 0;
1616 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1618 if( yanchor )
1619 y -= height + yanchor;
1620 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1621 y = GetSystemMetrics(SM_CYSCREEN) - height;
1623 if( y < 0 ) y = 0;
1625 if( TWEAK_WineLook == WIN31_LOOK )
1627 width += POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER); /* add space for shading */
1628 height += POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1631 /* NOTE: In Windows, top menu popup is not owned. */
1632 menu->hWnd = CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1633 WS_POPUP, x, y, width, height,
1634 hwndOwner, 0, GetWindowLongA(hwndOwner,GWL_HINSTANCE),
1635 (LPVOID)hmenu );
1636 if( !menu->hWnd ) return FALSE;
1637 if (!top_popup) top_popup = menu->hWnd;
1639 /* Display the window */
1641 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1642 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1643 UpdateWindow( menu->hWnd );
1644 return TRUE;
1648 /***********************************************************************
1649 * MENU_SelectItem
1651 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1652 BOOL sendMenuSelect, HMENU topmenu )
1654 LPPOPUPMENU lppop;
1655 HDC hdc;
1657 TRACE("owner=0x%04x menu=0x%04x index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1659 lppop = MENU_GetMenu( hmenu );
1660 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1662 if (lppop->FocusedItem == wIndex) return;
1663 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1664 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1665 if (!top_popup) top_popup = lppop->hWnd;
1667 SelectObject( hdc, hMenuFont);
1669 /* Clear previous highlighted item */
1670 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1672 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1673 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1674 lppop->Height, !(lppop->wFlags & MF_POPUP),
1675 ODA_SELECT );
1678 /* Highlight new item (if any) */
1679 lppop->FocusedItem = wIndex;
1680 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1682 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1683 lppop->items[wIndex].fState |= MF_HILITE;
1684 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1685 &lppop->items[wIndex], lppop->Height,
1686 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1688 if (sendMenuSelect)
1690 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1691 SendMessageA( hwndOwner, WM_MENUSELECT,
1692 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1693 ip->fType | ip->fState | MF_MOUSESELECT |
1694 (lppop->wFlags & MF_SYSMENU)), hmenu);
1697 else if (sendMenuSelect) {
1698 if(topmenu){
1699 int pos;
1700 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1701 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1702 MENUITEM *ip = &ptm->items[pos];
1703 SendMessageA( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1704 ip->fType | ip->fState | MF_MOUSESELECT |
1705 (ptm->wFlags & MF_SYSMENU)), topmenu);
1709 ReleaseDC( lppop->hWnd, hdc );
1713 /***********************************************************************
1714 * MENU_MoveSelection
1716 * Moves currently selected item according to the offset parameter.
1717 * If there is no selection then it should select the last item if
1718 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1720 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1722 INT i;
1723 POPUPMENU *menu;
1725 TRACE("hwnd=0x%04x hmenu=0x%04x off=0x%04x\n", hwndOwner, hmenu, offset);
1727 menu = MENU_GetMenu( hmenu );
1728 if ((!menu) || (!menu->items)) return;
1730 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1732 if( menu->nItems == 1 ) return; else
1733 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1734 ; i += offset)
1735 if (!(menu->items[i].fType & MF_SEPARATOR))
1737 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1738 return;
1742 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1743 i >= 0 && i < menu->nItems ; i += offset)
1744 if (!(menu->items[i].fType & MF_SEPARATOR))
1746 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1747 return;
1752 /**********************************************************************
1753 * MENU_SetItemData
1755 * Set an item flags, id and text ptr. Called by InsertMenu() and
1756 * ModifyMenu().
1758 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT id,
1759 LPCWSTR str )
1761 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1763 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1764 TRACE("flags=%x str=%p\n", flags, str);
1766 if (IS_STRING_ITEM(flags))
1768 if (!str)
1770 flags |= MF_SEPARATOR;
1771 item->text = NULL;
1773 else
1775 LPWSTR text;
1776 /* Item beginning with a backspace is a help item */
1777 if (*str == '\b')
1779 flags |= MF_HELP;
1780 str++;
1782 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1783 return FALSE;
1784 strcpyW( text, str );
1785 item->text = text;
1788 else if (IS_BITMAP_ITEM(flags))
1789 item->text = (LPWSTR)(HBITMAP)LOWORD(str);
1790 else item->text = NULL;
1792 if (flags & MF_OWNERDRAW)
1793 item->dwItemData = (DWORD)str;
1794 else
1795 item->dwItemData = 0;
1797 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != id) )
1798 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1800 if (flags & MF_POPUP)
1802 POPUPMENU *menu = MENU_GetMenu((UINT16)id);
1803 if (menu) menu->wFlags |= MF_POPUP;
1804 else
1806 item->wID = 0;
1807 item->hSubMenu = 0;
1808 item->fType = 0;
1809 item->fState = 0;
1810 return FALSE;
1814 item->wID = id;
1815 if (flags & MF_POPUP)
1816 item->hSubMenu = id;
1818 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1819 flags |= MF_POPUP; /* keep popup */
1821 item->fType = flags & TYPE_MASK;
1822 item->fState = (flags & STATE_MASK) &
1823 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1826 /* Don't call SetRectEmpty here! */
1829 if (prevText) HeapFree( GetProcessHeap(), 0, prevText );
1831 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1832 return TRUE;
1836 /**********************************************************************
1837 * MENU_InsertItem
1839 * Insert a new item into a menu.
1841 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1843 MENUITEM *newItems;
1844 POPUPMENU *menu;
1846 if (!(menu = MENU_GetMenu(hMenu)))
1847 return NULL;
1849 /* Find where to insert new item */
1851 if (flags & MF_BYPOSITION) {
1852 if (pos > menu->nItems)
1853 pos = menu->nItems;
1854 } else {
1855 if (!MENU_FindItem( &hMenu, &pos, flags ))
1856 pos = menu->nItems;
1857 else {
1858 if (!(menu = MENU_GetMenu( hMenu )))
1859 return NULL;
1863 /* Create new items array */
1865 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1866 if (!newItems)
1868 WARN("allocation failed\n" );
1869 return NULL;
1871 if (menu->nItems > 0)
1873 /* Copy the old array into the new one */
1874 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1875 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1876 (menu->nItems-pos)*sizeof(MENUITEM) );
1877 HeapFree( GetProcessHeap(), 0, menu->items );
1879 menu->items = newItems;
1880 menu->nItems++;
1881 memset( &newItems[pos], 0, sizeof(*newItems) );
1882 menu->Height = 0; /* force size recalculate */
1883 return &newItems[pos];
1887 /**********************************************************************
1888 * MENU_ParseResource
1890 * Parse a standard menu resource and add items to the menu.
1891 * Return a pointer to the end of the resource.
1893 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1895 WORD flags, id = 0;
1896 LPCSTR str;
1900 flags = GET_WORD(res);
1901 res += sizeof(WORD);
1902 if (!(flags & MF_POPUP))
1904 id = GET_WORD(res);
1905 res += sizeof(WORD);
1907 if (!IS_STRING_ITEM(flags))
1908 ERR("not a string item %04x\n", flags );
1909 str = res;
1910 if (!unicode) res += strlen(str) + 1;
1911 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1912 if (flags & MF_POPUP)
1914 HMENU hSubMenu = CreatePopupMenu();
1915 if (!hSubMenu) return NULL;
1916 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1917 return NULL;
1918 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1919 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1921 else /* Not a popup */
1923 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1924 else AppendMenuW( hMenu, flags, id,
1925 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1927 } while (!(flags & MF_END));
1928 return res;
1932 /**********************************************************************
1933 * MENUEX_ParseResource
1935 * Parse an extended menu resource and add items to the menu.
1936 * Return a pointer to the end of the resource.
1938 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1940 WORD resinfo;
1941 do {
1942 MENUITEMINFOW mii;
1944 mii.cbSize = sizeof(mii);
1945 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1946 mii.fType = GET_DWORD(res);
1947 res += sizeof(DWORD);
1948 mii.fState = GET_DWORD(res);
1949 res += sizeof(DWORD);
1950 mii.wID = GET_DWORD(res);
1951 res += sizeof(DWORD);
1952 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1953 res += sizeof(WORD);
1954 /* Align the text on a word boundary. */
1955 res += (~((int)res - 1)) & 1;
1956 mii.dwTypeData = (LPWSTR) res;
1957 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1958 /* Align the following fields on a dword boundary. */
1959 res += (~((int)res - 1)) & 3;
1961 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1962 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1964 if (resinfo & 1) { /* Pop-up? */
1965 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1966 res += sizeof(DWORD);
1967 mii.hSubMenu = CreatePopupMenu();
1968 if (!mii.hSubMenu)
1969 return NULL;
1970 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1971 DestroyMenu(mii.hSubMenu);
1972 return NULL;
1974 mii.fMask |= MIIM_SUBMENU;
1975 mii.fType |= MF_POPUP;
1977 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1979 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1980 mii.wID, mii.fType);
1981 mii.fType |= MF_SEPARATOR;
1983 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1984 } while (!(resinfo & MF_END));
1985 return res;
1989 /***********************************************************************
1990 * MENU_GetSubPopup
1992 * Return the handle of the selected sub-popup menu (if any).
1994 static HMENU MENU_GetSubPopup( HMENU hmenu )
1996 POPUPMENU *menu;
1997 MENUITEM *item;
1999 menu = MENU_GetMenu( hmenu );
2001 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2003 item = &menu->items[menu->FocusedItem];
2004 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2005 return item->hSubMenu;
2006 return 0;
2010 /***********************************************************************
2011 * MENU_HideSubPopups
2013 * Hide the sub-popup menus of this menu.
2015 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2016 BOOL sendMenuSelect )
2018 POPUPMENU *menu = MENU_GetMenu( hmenu );
2020 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2022 if (menu && top_popup)
2024 HMENU hsubmenu;
2025 POPUPMENU *submenu;
2026 MENUITEM *item;
2028 if (menu->FocusedItem != NO_SELECTED_ITEM)
2030 item = &menu->items[menu->FocusedItem];
2031 if (!(item->fType & MF_POPUP) ||
2032 !(item->fState & MF_MOUSESELECT)) return;
2033 item->fState &= ~MF_MOUSESELECT;
2034 hsubmenu = item->hSubMenu;
2035 } else return;
2037 submenu = MENU_GetMenu( hsubmenu );
2038 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2039 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2040 DestroyWindow( submenu->hWnd );
2041 submenu->hWnd = 0;
2046 /***********************************************************************
2047 * MENU_ShowSubPopup
2049 * Display the sub-menu of the selected item of this menu.
2050 * Return the handle of the submenu, or hmenu if no submenu to display.
2052 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2053 BOOL selectFirst, UINT wFlags )
2055 RECT rect;
2056 POPUPMENU *menu;
2057 MENUITEM *item;
2058 HDC hdc;
2060 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, selectFirst);
2062 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2064 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2066 item = &menu->items[menu->FocusedItem];
2067 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2068 return hmenu;
2070 /* message must be sent before using item,
2071 because nearly everything may be changed by the application ! */
2073 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2074 if (!(wFlags & TPM_NONOTIFY))
2075 SendMessageA( hwndOwner, WM_INITMENUPOPUP, item->hSubMenu,
2076 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2078 item = &menu->items[menu->FocusedItem];
2079 rect = item->rect;
2081 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2082 if (!(item->fState & MF_HILITE))
2084 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2085 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2087 SelectObject( hdc, hMenuFont);
2089 item->fState |= MF_HILITE;
2090 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2091 ReleaseDC( menu->hWnd, hdc );
2093 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2094 item->rect = rect;
2096 item->fState |= MF_MOUSESELECT;
2098 if (IS_SYSTEM_MENU(menu))
2100 MENU_InitSysMenuPopup(item->hSubMenu,
2101 GetWindowLongA( menu->hWnd, GWL_STYLE ),
2102 GetClassLongA( menu->hWnd, GCL_STYLE));
2104 NC_GetSysPopupPos( menu->hWnd, &rect );
2105 rect.top = rect.bottom;
2106 rect.right = GetSystemMetrics(SM_CXSIZE);
2107 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2109 else
2111 GetWindowRect( menu->hWnd, &rect );
2112 if (menu->wFlags & MF_POPUP)
2114 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2115 rect.top += item->rect.top;
2116 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2117 rect.bottom = item->rect.top - item->rect.bottom;
2119 else
2121 rect.left += item->rect.left;
2122 rect.top += item->rect.bottom;
2123 rect.right = item->rect.right - item->rect.left;
2124 rect.bottom = item->rect.bottom - item->rect.top;
2128 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2129 rect.left, rect.top, rect.right, rect.bottom );
2130 if (selectFirst)
2131 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2132 return item->hSubMenu;
2137 /**********************************************************************
2138 * MENU_IsMenuActive
2140 BOOL MENU_IsMenuActive(void)
2142 return (top_popup != 0);
2145 /***********************************************************************
2146 * MENU_PtMenu
2148 * Walks menu chain trying to find a menu pt maps to.
2150 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2152 POPUPMENU *menu = MENU_GetMenu( hMenu );
2153 UINT item = menu->FocusedItem;
2154 HMENU ret;
2156 /* try subpopup first (if any) */
2157 ret = (item != NO_SELECTED_ITEM &&
2158 (menu->items[item].fType & MF_POPUP) &&
2159 (menu->items[item].fState & MF_MOUSESELECT))
2160 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2162 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2164 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2165 if( menu->wFlags & MF_POPUP )
2167 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2169 else if (ht == HTSYSMENU)
2170 ret = get_win_sys_menu( menu->hWnd );
2171 else if (ht == HTMENU)
2172 ret = GetMenu( menu->hWnd );
2174 return ret;
2177 /***********************************************************************
2178 * MENU_ExecFocusedItem
2180 * Execute a menu item (for instance when user pressed Enter).
2181 * Return the wID of the executed item. Otherwise, -1 indicating
2182 * that no menu item was executed;
2183 * Have to receive the flags for the TrackPopupMenu options to avoid
2184 * sending unwanted message.
2187 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2189 MENUITEM *item;
2190 POPUPMENU *menu = MENU_GetMenu( hMenu );
2192 TRACE("%p hmenu=0x%04x\n", pmt, hMenu);
2194 if (!menu || !menu->nItems ||
2195 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2197 item = &menu->items[menu->FocusedItem];
2199 TRACE("%08x %08x %08x\n",
2200 hMenu, item->wID, item->hSubMenu);
2202 if (!(item->fType & MF_POPUP))
2204 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2206 /* If TPM_RETURNCMD is set you return the id, but
2207 do not send a message to the owner */
2208 if(!(wFlags & TPM_RETURNCMD))
2210 if( menu->wFlags & MF_SYSMENU )
2211 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2212 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2213 else
2214 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2216 return item->wID;
2219 else
2220 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2222 return -1;
2225 /***********************************************************************
2226 * MENU_SwitchTracking
2228 * Helper function for menu navigation routines.
2230 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2232 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2233 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2235 TRACE("%p hmenu=0x%04x 0x%04x\n", pmt, hPtMenu, id);
2237 if( pmt->hTopMenu != hPtMenu &&
2238 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2240 /* both are top level menus (system and menu-bar) */
2241 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2242 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2243 pmt->hTopMenu = hPtMenu;
2245 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2246 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2250 /***********************************************************************
2251 * MENU_ButtonDown
2253 * Return TRUE if we can go on with menu tracking.
2255 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2257 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2259 if (hPtMenu)
2261 UINT id = 0;
2262 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2263 MENUITEM *item;
2265 if( IS_SYSTEM_MENU(ptmenu) )
2266 item = ptmenu->items;
2267 else
2268 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2270 if( item )
2272 if( ptmenu->FocusedItem != id )
2273 MENU_SwitchTracking( pmt, hPtMenu, id );
2275 /* If the popup menu is not already "popped" */
2276 if(!(item->fState & MF_MOUSESELECT ))
2278 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2280 /* In win31, a newly popped menu always remains opened for the next buttonup */
2281 if(TWEAK_WineLook == WIN31_LOOK)
2282 ptmenu->bTimeToHide = FALSE;
2285 return TRUE;
2287 /* Else the click was on the menu bar, finish the tracking */
2289 return FALSE;
2292 /***********************************************************************
2293 * MENU_ButtonUp
2295 * Return the value of MENU_ExecFocusedItem if
2296 * the selected item was not a popup. Else open the popup.
2297 * A -1 return value indicates that we go on with menu tracking.
2300 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2302 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2304 if (hPtMenu)
2306 UINT id = 0;
2307 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2308 MENUITEM *item;
2310 if( IS_SYSTEM_MENU(ptmenu) )
2311 item = ptmenu->items;
2312 else
2313 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2315 if( item && (ptmenu->FocusedItem == id ))
2317 if( !(item->fType & MF_POPUP) )
2318 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2320 /* If we are dealing with the top-level menu */
2321 /* and this is a click on an already "popped" item: */
2322 /* Stop the menu tracking and close the opened submenus */
2323 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2324 return 0;
2326 ptmenu->bTimeToHide = TRUE;
2328 return -1;
2332 /***********************************************************************
2333 * MENU_MouseMove
2335 * Return TRUE if we can go on with menu tracking.
2337 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2339 UINT id = NO_SELECTED_ITEM;
2340 POPUPMENU *ptmenu = NULL;
2342 if( hPtMenu )
2344 ptmenu = MENU_GetMenu( hPtMenu );
2345 if( IS_SYSTEM_MENU(ptmenu) )
2346 id = 0;
2347 else
2348 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2351 if( id == NO_SELECTED_ITEM )
2353 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2354 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2357 else if( ptmenu->FocusedItem != id )
2359 MENU_SwitchTracking( pmt, hPtMenu, id );
2360 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2362 return TRUE;
2366 /***********************************************************************
2367 * MENU_DoNextMenu
2369 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2371 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2373 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2375 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2376 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2378 MDINEXTMENU next_menu;
2379 HMENU hNewMenu;
2380 HWND hNewWnd;
2381 UINT id = 0;
2383 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2384 next_menu.hmenuNext = 0;
2385 next_menu.hwndNext = 0;
2386 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2388 TRACE("%04x [%04x] -> %04x [%04x]\n",
2389 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2391 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2393 DWORD style = GetWindowLongA( pmt->hOwnerWnd, GWL_STYLE );
2394 hNewWnd = pmt->hOwnerWnd;
2395 if( IS_SYSTEM_MENU(menu) )
2397 /* switch to the menu bar */
2399 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2401 if( vk == VK_LEFT )
2403 menu = MENU_GetMenu( hNewMenu );
2404 id = menu->nItems - 1;
2407 else if (style & WS_SYSMENU )
2409 /* switch to the system menu */
2410 hNewMenu = get_win_sys_menu( hNewWnd );
2412 else return FALSE;
2414 else /* application returned a new menu to switch to */
2416 hNewMenu = next_menu.hmenuNext;
2417 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2419 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2421 DWORD style = GetWindowLongA( hNewWnd, GWL_STYLE );
2423 if (style & WS_SYSMENU &&
2424 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2426 /* get the real system menu */
2427 hNewMenu = get_win_sys_menu(hNewWnd);
2429 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2431 /* FIXME: Not sure what to do here;
2432 * perhaps try to track hNewMenu as a popup? */
2434 TRACE(" -- got confused.\n");
2435 return FALSE;
2438 else return FALSE;
2441 if( hNewMenu != pmt->hTopMenu )
2443 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2444 FALSE, 0 );
2445 if( pmt->hCurrentMenu != pmt->hTopMenu )
2446 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2449 if( hNewWnd != pmt->hOwnerWnd )
2451 ReleaseCapture();
2452 pmt->hOwnerWnd = hNewWnd;
2453 EVENT_Capture( pmt->hOwnerWnd, HTMENU );
2456 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2457 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2459 return TRUE;
2461 return FALSE;
2464 /***********************************************************************
2465 * MENU_SuspendPopup
2467 * The idea is not to show the popup if the next input message is
2468 * going to hide it anyway.
2470 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2472 MSG msg;
2474 msg.hwnd = pmt->hOwnerWnd;
2476 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2477 pmt->trackFlags |= TF_SKIPREMOVE;
2479 switch( uMsg )
2481 case WM_KEYDOWN:
2482 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2483 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2485 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2486 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2487 if( msg.message == WM_KEYDOWN &&
2488 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2490 pmt->trackFlags |= TF_SUSPENDPOPUP;
2491 return TRUE;
2494 break;
2497 /* failures go through this */
2498 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2499 return FALSE;
2502 /***********************************************************************
2503 * MENU_KeyEscape
2505 * Handle a VK_ESCAPE key event in a menu.
2507 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2509 BOOL bEndMenu = TRUE;
2511 if (pmt->hCurrentMenu != pmt->hTopMenu)
2513 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2515 if (menu->wFlags & MF_POPUP)
2517 HMENU hmenutmp, hmenuprev;
2519 hmenuprev = hmenutmp = pmt->hTopMenu;
2521 /* close topmost popup */
2522 while (hmenutmp != pmt->hCurrentMenu)
2524 hmenuprev = hmenutmp;
2525 hmenutmp = MENU_GetSubPopup( hmenuprev );
2528 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2529 pmt->hCurrentMenu = hmenuprev;
2530 bEndMenu = FALSE;
2534 return bEndMenu;
2537 /***********************************************************************
2538 * MENU_KeyLeft
2540 * Handle a VK_LEFT key event in a menu.
2542 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2544 POPUPMENU *menu;
2545 HMENU hmenutmp, hmenuprev;
2546 UINT prevcol;
2548 hmenuprev = hmenutmp = pmt->hTopMenu;
2549 menu = MENU_GetMenu( hmenutmp );
2551 /* Try to move 1 column left (if possible) */
2552 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2553 NO_SELECTED_ITEM ) {
2555 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2556 prevcol, TRUE, 0 );
2557 return;
2560 /* close topmost popup */
2561 while (hmenutmp != pmt->hCurrentMenu)
2563 hmenuprev = hmenutmp;
2564 hmenutmp = MENU_GetSubPopup( hmenuprev );
2567 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2568 pmt->hCurrentMenu = hmenuprev;
2570 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2572 /* move menu bar selection if no more popups are left */
2574 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2575 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2577 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2579 /* A sublevel menu was displayed - display the next one
2580 * unless there is another displacement coming up */
2582 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2583 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2584 pmt->hTopMenu, TRUE, wFlags);
2590 /***********************************************************************
2591 * MENU_KeyRight
2593 * Handle a VK_RIGHT key event in a menu.
2595 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2597 HMENU hmenutmp;
2598 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2599 UINT nextcol;
2601 TRACE("MENU_KeyRight called, cur %x (%s), top %x (%s).\n",
2602 pmt->hCurrentMenu,
2603 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->
2604 items[0].text),
2605 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2607 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2609 /* If already displaying a popup, try to display sub-popup */
2611 hmenutmp = pmt->hCurrentMenu;
2612 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2614 /* if subpopup was displayed then we are done */
2615 if (hmenutmp != pmt->hCurrentMenu) return;
2618 /* Check to see if there's another column */
2619 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2620 NO_SELECTED_ITEM ) {
2621 TRACE("Going to %d.\n", nextcol );
2622 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2623 nextcol, TRUE, 0 );
2624 return;
2627 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2629 if( pmt->hCurrentMenu != pmt->hTopMenu )
2631 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2632 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2633 } else hmenutmp = 0;
2635 /* try to move to the next item */
2636 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2637 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2639 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2640 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2641 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2642 pmt->hTopMenu, TRUE, wFlags);
2646 /***********************************************************************
2647 * MENU_TrackMenu
2649 * Menu tracking code.
2651 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2652 HWND hwnd, const RECT *lprect )
2654 MSG msg;
2655 POPUPMENU *menu;
2656 BOOL fRemove;
2657 INT executedMenuId = -1;
2658 MTRACKER mt;
2659 BOOL enterIdleSent = FALSE;
2661 mt.trackFlags = 0;
2662 mt.hCurrentMenu = hmenu;
2663 mt.hTopMenu = hmenu;
2664 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2665 mt.pt.x = x;
2666 mt.pt.y = y;
2668 TRACE("hmenu=0x%04x flags=0x%08x (%d,%d) hwnd=0x%04x (%d,%d)-(%d,%d)\n",
2669 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2670 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2672 fEndMenu = FALSE;
2673 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2675 if (wFlags & TPM_BUTTONDOWN)
2677 /* Get the result in order to start the tracking or not */
2678 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2679 fEndMenu = !fRemove;
2682 EVENT_Capture( mt.hOwnerWnd, HTMENU );
2684 while (!fEndMenu)
2686 menu = MENU_GetMenu( mt.hCurrentMenu );
2687 if (!menu) /* sometimes happens if I do a window manager close */
2688 break;
2690 /* we have to keep the message in the queue until it's
2691 * clear that menu loop is not over yet. */
2693 for (;;)
2695 if (PeekMessageA( &msg, 0, 0, 0, PM_NOREMOVE ))
2697 if (!CallMsgFilterA( &msg, MSGF_MENU )) break;
2698 /* remove the message from the queue */
2699 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2701 else
2703 if (!enterIdleSent)
2705 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2706 enterIdleSent = TRUE;
2707 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2709 WaitMessage();
2713 /* check if EndMenu() tried to cancel us, by posting this message */
2714 if(msg.message == WM_CANCELMODE)
2716 /* we are now out of the loop */
2717 fEndMenu = TRUE;
2719 /* remove the message from the queue */
2720 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2722 /* break out of internal loop, ala ESCAPE */
2723 break;
2726 TranslateMessage( &msg );
2727 mt.pt = msg.pt;
2729 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2730 enterIdleSent=FALSE;
2732 fRemove = FALSE;
2733 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2736 * use the mouse coordinates in lParam instead of those in the MSG
2737 * struct to properly handle synthetic messages. lParam coords are
2738 * relative to client area, so they must be converted; since they can
2739 * be negative, we must use SLOWORD/SHIWORD instead of LOWORD/HIWORD.
2741 mt.pt.x = SLOWORD(msg.lParam);
2742 mt.pt.y = SHIWORD(msg.lParam);
2743 ClientToScreen(msg.hwnd,&mt.pt);
2745 /* Find a menu for this mouse event */
2746 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2748 switch(msg.message)
2750 /* no WM_NC... messages in captured state */
2752 case WM_RBUTTONDBLCLK:
2753 case WM_RBUTTONDOWN:
2754 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2755 /* fall through */
2756 case WM_LBUTTONDBLCLK:
2757 case WM_LBUTTONDOWN:
2758 /* If the message belongs to the menu, removes it from the queue */
2759 /* Else, end menu tracking */
2760 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2761 fEndMenu = !fRemove;
2762 break;
2764 case WM_RBUTTONUP:
2765 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2766 /* fall through */
2767 case WM_LBUTTONUP:
2768 /* Check if a menu was selected by the mouse */
2769 if (hmenu)
2771 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2773 /* End the loop if executedMenuId is an item ID */
2774 /* or if the job was done (executedMenuId = 0). */
2775 fEndMenu = fRemove = (executedMenuId != -1);
2777 /* No menu was selected by the mouse */
2778 /* if the function was called by TrackPopupMenu, continue
2779 with the menu tracking. If not, stop it */
2780 else
2781 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2783 break;
2785 case WM_MOUSEMOVE:
2786 /* In win95 winelook, the selected menu item must be changed every time the
2787 mouse moves. In Win31 winelook, the mouse button has to be held down */
2789 if ( hmenu && ((TWEAK_WineLook > WIN31_LOOK) ||
2790 ( (msg.wParam & MK_LBUTTON) ||
2791 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON)))) )
2793 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2795 } /* switch(msg.message) - mouse */
2797 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2799 fRemove = TRUE; /* Keyboard messages are always removed */
2800 switch(msg.message)
2802 case WM_KEYDOWN:
2803 switch(msg.wParam)
2805 case VK_HOME:
2806 case VK_END:
2807 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2808 NO_SELECTED_ITEM, FALSE, 0 );
2809 /* fall through */
2810 case VK_UP:
2811 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2812 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2813 break;
2815 case VK_DOWN: /* If on menu bar, pull-down the menu */
2817 menu = MENU_GetMenu( mt.hCurrentMenu );
2818 if (!(menu->wFlags & MF_POPUP))
2819 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2820 else /* otherwise try to move selection */
2821 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2822 break;
2824 case VK_LEFT:
2825 MENU_KeyLeft( &mt, wFlags );
2826 break;
2828 case VK_RIGHT:
2829 MENU_KeyRight( &mt, wFlags );
2830 break;
2832 case VK_ESCAPE:
2833 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2834 break;
2836 case VK_F1:
2838 HELPINFO hi;
2839 hi.cbSize = sizeof(HELPINFO);
2840 hi.iContextType = HELPINFO_MENUITEM;
2841 if (menu->FocusedItem == NO_SELECTED_ITEM)
2842 hi.iCtrlId = 0;
2843 else
2844 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2845 hi.hItemHandle = hmenu;
2846 hi.dwContextId = menu->dwContextHelpID;
2847 hi.MousePos = msg.pt;
2848 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2849 break;
2852 default:
2853 break;
2855 break; /* WM_KEYDOWN */
2857 case WM_SYSKEYDOWN:
2858 switch(msg.wParam)
2860 case VK_MENU:
2861 fEndMenu = TRUE;
2862 break;
2865 break; /* WM_SYSKEYDOWN */
2867 case WM_CHAR:
2869 UINT pos;
2871 if (msg.wParam == '\r' || msg.wParam == ' ')
2873 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2874 fEndMenu = (executedMenuId != -1);
2876 break;
2879 /* Hack to avoid control chars. */
2880 /* We will find a better way real soon... */
2881 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2883 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2884 LOWORD(msg.wParam), FALSE );
2885 if (pos == (UINT)-2) fEndMenu = TRUE;
2886 else if (pos == (UINT)-1) MessageBeep(0);
2887 else
2889 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2890 TRUE, 0 );
2891 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2892 fEndMenu = (executedMenuId != -1);
2895 break;
2896 } /* switch(msg.message) - kbd */
2898 else
2900 DispatchMessageA( &msg );
2903 if (!fEndMenu) fRemove = TRUE;
2905 /* finally remove message from the queue */
2907 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2908 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2909 else mt.trackFlags &= ~TF_SKIPREMOVE;
2912 ReleaseCapture();
2914 /* If dropdown is still painted and the close box is clicked on
2915 then the menu will be destroyed as part of the DispatchMessage above.
2916 This will then invalidate the menu handle in mt.hTopMenu. We should
2917 check for this first. */
2918 if( IsMenu( mt.hTopMenu ) )
2920 menu = MENU_GetMenu( mt.hTopMenu );
2922 if( IsWindow( mt.hOwnerWnd ) )
2924 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2926 if (menu && menu->wFlags & MF_POPUP)
2928 DestroyWindow( menu->hWnd );
2929 menu->hWnd = 0;
2931 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2932 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2935 /* Reset the variable for hiding menu */
2936 if( menu ) menu->bTimeToHide = FALSE;
2939 /* The return value is only used by TrackPopupMenu */
2940 return ((executedMenuId != -1) ? executedMenuId : 0);
2943 /***********************************************************************
2944 * MENU_InitTracking
2946 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2948 TRACE("hwnd=0x%04x hmenu=0x%04x\n", hWnd, hMenu);
2950 HideCaret(0);
2952 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2953 if (!(wFlags & TPM_NONOTIFY))
2954 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2956 SendMessageA( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2958 if (!(wFlags & TPM_NONOTIFY))
2960 POPUPMENU *menu;
2961 SendMessageA( hWnd, WM_INITMENU, hMenu, 0 );
2962 if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
2963 { /* app changed/recreated menu bar entries in WM_INITMENU
2964 Recalculate menu sizes else clicks will not work */
2965 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2966 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2970 return TRUE;
2972 /***********************************************************************
2973 * MENU_ExitTracking
2975 static BOOL MENU_ExitTracking(HWND hWnd)
2977 TRACE("hwnd=0x%04x\n", hWnd);
2979 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
2980 ShowCaret(0);
2981 return TRUE;
2984 /***********************************************************************
2985 * MENU_TrackMouseMenuBar
2987 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2989 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2991 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2992 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2994 TRACE("wnd=%x ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2996 if (IsMenu(hMenu))
2998 /* map point to parent client coordinates */
2999 HWND parent = GetAncestor( hWnd, GA_PARENT );
3000 if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );
3002 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3003 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3004 MENU_ExitTracking(hWnd);
3009 /***********************************************************************
3010 * MENU_TrackKbdMenuBar
3012 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3014 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, INT vkey)
3016 UINT uItem = NO_SELECTED_ITEM;
3017 HMENU hTrackMenu;
3018 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3020 /* find window that has a menu */
3022 while (GetWindowLongA( hwnd, GWL_STYLE ) & WS_CHILD)
3023 if (!(hwnd = GetParent( hwnd ))) return;
3025 /* check if we have to track a system menu */
3027 hTrackMenu = GetMenu( hwnd );
3028 if (!hTrackMenu || IsIconic(hwnd) || vkey == VK_SPACE )
3030 if (!(GetWindowLongA( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3031 hTrackMenu = get_win_sys_menu( hwnd );
3032 uItem = 0;
3033 wParam |= HTSYSMENU; /* prevent item lookup */
3036 if (!IsMenu( hTrackMenu )) return;
3038 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3040 if( vkey && vkey != VK_SPACE )
3042 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, vkey, (wParam & HTSYSMENU) );
3043 if( uItem >= (UINT)(-2) )
3045 if( uItem == (UINT)(-1) ) MessageBeep(0);
3046 hTrackMenu = 0;
3050 if( hTrackMenu )
3052 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3054 if( uItem == NO_SELECTED_ITEM )
3055 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3056 else if( vkey )
3057 PostMessageA( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3059 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3061 MENU_ExitTracking( hwnd );
3065 /**********************************************************************
3066 * TrackPopupMenu (USER32.@)
3068 * Like the win32 API, the function return the command ID only if the
3069 * flag TPM_RETURNCMD is on.
3072 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3073 INT nReserved, HWND hWnd, const RECT *lpRect )
3075 BOOL ret = FALSE;
3077 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3079 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3080 if (!(wFlags & TPM_NONOTIFY))
3081 SendMessageA( hWnd, WM_INITMENUPOPUP, hMenu, 0);
3083 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3084 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3085 MENU_ExitTracking(hWnd);
3087 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3088 ret = 1;
3090 return ret;
3093 /**********************************************************************
3094 * TrackPopupMenuEx (USER32.@)
3096 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3097 HWND hWnd, LPTPMPARAMS lpTpm )
3099 FIXME("not fully implemented\n" );
3100 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3101 lpTpm ? &lpTpm->rcExclude : NULL );
3104 /***********************************************************************
3105 * PopupMenuWndProc
3107 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3109 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3111 TRACE("hwnd=0x%04x msg=0x%04x wp=0x%04x lp=0x%08lx\n",
3112 hwnd, message, wParam, lParam);
3114 switch(message)
3116 case WM_CREATE:
3118 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3119 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3120 return 0;
3123 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3124 return MA_NOACTIVATE;
3126 case WM_PAINT:
3128 PAINTSTRUCT ps;
3129 BeginPaint( hwnd, &ps );
3130 MENU_DrawPopupMenu( hwnd, ps.hdc,
3131 (HMENU)GetWindowLongA( hwnd, 0 ) );
3132 EndPaint( hwnd, &ps );
3133 return 0;
3135 case WM_ERASEBKGND:
3136 return 1;
3138 case WM_DESTROY:
3139 /* zero out global pointer in case resident popup window was destroyed. */
3140 if (hwnd == top_popup) top_popup = 0;
3141 break;
3143 case WM_SHOWWINDOW:
3145 if( wParam )
3147 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3149 else
3150 SetWindowLongW( hwnd, 0, 0 );
3151 break;
3153 case MM_SETMENUHANDLE:
3154 SetWindowLongW( hwnd, 0, wParam );
3155 break;
3157 case MM_GETMENUHANDLE:
3158 return GetWindowLongW( hwnd, 0 );
3160 default:
3161 return DefWindowProcW( hwnd, message, wParam, lParam );
3163 return 0;
3167 /***********************************************************************
3168 * MENU_GetMenuBarHeight
3170 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3172 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3173 INT orgX, INT orgY )
3175 HDC hdc;
3176 RECT rectBar;
3177 LPPOPUPMENU lppop;
3179 TRACE("HWND 0x%x, width %d, at (%d, %d).\n",
3180 hwnd, menubarWidth, orgX, orgY );
3182 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3184 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3185 SelectObject( hdc, hMenuFont);
3186 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3187 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3188 ReleaseDC( hwnd, hdc );
3189 return lppop->Height;
3193 /*******************************************************************
3194 * ChangeMenu (USER.153)
3196 BOOL16 WINAPI ChangeMenu16( HMENU16 hMenu, UINT16 pos, SEGPTR data,
3197 UINT16 id, UINT16 flags )
3199 TRACE("menu=%04x pos=%d data=%08lx id=%04x flags=%04x\n",
3200 hMenu, pos, (DWORD)data, id, flags );
3201 if (flags & MF_APPEND) return AppendMenu16( hMenu, flags & ~MF_APPEND,
3202 id, data );
3204 /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
3205 /* for MF_DELETE. We should check the parameters for all others */
3206 /* MF_* actions also (anybody got a doc on ChangeMenu?). */
3208 if (flags & MF_DELETE) return DeleteMenu16(hMenu, pos, flags & ~MF_DELETE);
3209 if (flags & MF_CHANGE) return ModifyMenu16(hMenu, pos, flags & ~MF_CHANGE,
3210 id, data );
3211 if (flags & MF_REMOVE) return RemoveMenu16(hMenu,
3212 flags & MF_BYPOSITION ? pos : id,
3213 flags & ~MF_REMOVE );
3214 /* Default: MF_INSERT */
3215 return InsertMenu16( hMenu, pos, flags, id, data );
3219 /*******************************************************************
3220 * ChangeMenuA (USER32.@)
3222 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3223 UINT id, UINT flags )
3225 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3226 hMenu, pos, (DWORD)data, id, flags );
3227 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3228 id, data );
3229 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3230 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3231 id, data );
3232 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3233 flags & MF_BYPOSITION ? pos : id,
3234 flags & ~MF_REMOVE );
3235 /* Default: MF_INSERT */
3236 return InsertMenuA( hMenu, pos, flags, id, data );
3240 /*******************************************************************
3241 * ChangeMenuW (USER32.@)
3243 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3244 UINT id, UINT flags )
3246 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3247 hMenu, pos, (DWORD)data, id, flags );
3248 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3249 id, data );
3250 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3251 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3252 id, data );
3253 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3254 flags & MF_BYPOSITION ? pos : id,
3255 flags & ~MF_REMOVE );
3256 /* Default: MF_INSERT */
3257 return InsertMenuW( hMenu, pos, flags, id, data );
3261 /*******************************************************************
3262 * CheckMenuItem (USER.154)
3264 BOOL16 WINAPI CheckMenuItem16( HMENU16 hMenu, UINT16 id, UINT16 flags )
3266 return (BOOL16)CheckMenuItem( hMenu, id, flags );
3270 /*******************************************************************
3271 * CheckMenuItem (USER32.@)
3273 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3275 MENUITEM *item;
3276 DWORD ret;
3278 TRACE("menu=%04x id=%04x flags=%04x\n", hMenu, id, flags );
3279 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3280 ret = item->fState & MF_CHECKED;
3281 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3282 else item->fState &= ~MF_CHECKED;
3283 return ret;
3287 /**********************************************************************
3288 * EnableMenuItem (USER.155)
3290 UINT16 WINAPI EnableMenuItem16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3292 return EnableMenuItem( hMenu, wItemID, wFlags );
3296 /**********************************************************************
3297 * EnableMenuItem (USER32.@)
3299 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3301 UINT oldflags;
3302 MENUITEM *item;
3303 POPUPMENU *menu;
3305 TRACE("(%04x, %04X, %04X) !\n",
3306 hMenu, wItemID, wFlags);
3308 /* Get the Popupmenu to access the owner menu */
3309 if (!(menu = MENU_GetMenu(hMenu)))
3310 return (UINT)-1;
3312 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3313 return (UINT)-1;
3315 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3316 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3318 /* In win95 if the close item in the system menu change update the close button */
3319 if (TWEAK_WineLook == WIN95_LOOK)
3320 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3322 if (menu->hSysMenuOwner != 0)
3324 POPUPMENU* parentMenu;
3326 /* Get the parent menu to access*/
3327 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3328 return (UINT)-1;
3330 /* Refresh the frame to reflect the change*/
3331 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3332 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3336 return oldflags;
3340 /*******************************************************************
3341 * GetMenuString (USER.161)
3343 INT16 WINAPI GetMenuString16( HMENU16 hMenu, UINT16 wItemID,
3344 LPSTR str, INT16 nMaxSiz, UINT16 wFlags )
3346 return GetMenuStringA( hMenu, wItemID, str, nMaxSiz, wFlags );
3350 /*******************************************************************
3351 * GetMenuStringA (USER32.@)
3353 INT WINAPI GetMenuStringA(
3354 HMENU hMenu, /* [in] menuhandle */
3355 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3356 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3357 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3358 UINT wFlags /* [in] MF_ flags */
3360 MENUITEM *item;
3362 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3363 hMenu, wItemID, str, nMaxSiz, wFlags );
3364 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3365 if (!IS_STRING_ITEM(item->fType)) return 0;
3366 if (!str || !nMaxSiz) return strlenW(item->text);
3367 str[0] = '\0';
3368 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3369 str[nMaxSiz-1] = 0;
3370 TRACE("returning '%s'\n", str );
3371 return strlen(str);
3375 /*******************************************************************
3376 * GetMenuStringW (USER32.@)
3378 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3379 LPWSTR str, INT nMaxSiz, UINT wFlags )
3381 MENUITEM *item;
3383 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3384 hMenu, wItemID, str, nMaxSiz, wFlags );
3385 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3386 if (!IS_STRING_ITEM(item->fType)) return 0;
3387 if (!str || !nMaxSiz) return strlenW(item->text);
3388 str[0] = '\0';
3389 lstrcpynW( str, item->text, nMaxSiz );
3390 return strlenW(str);
3394 /**********************************************************************
3395 * HiliteMenuItem (USER32.@)
3397 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3398 UINT wHilite )
3400 LPPOPUPMENU menu;
3401 TRACE("(%04x, %04x, %04x, %04x);\n",
3402 hWnd, hMenu, wItemID, wHilite);
3403 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3404 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3405 if (menu->FocusedItem == wItemID) return TRUE;
3406 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3407 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3408 return TRUE;
3412 /**********************************************************************
3413 * GetMenuState (USER.250)
3415 UINT16 WINAPI GetMenuState16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3417 return GetMenuState( hMenu, wItemID, wFlags );
3421 /**********************************************************************
3422 * GetMenuState (USER32.@)
3424 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3426 MENUITEM *item;
3427 TRACE("(menu=%04x, id=%04x, flags=%04x);\n",
3428 hMenu, wItemID, wFlags);
3429 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3430 debug_print_menuitem (" item: ", item, "");
3431 if (item->fType & MF_POPUP)
3433 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3434 if (!menu) return -1;
3435 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3437 else
3439 /* We used to (from way back then) mask the result to 0xff. */
3440 /* I don't know why and it seems wrong as the documented */
3441 /* return flag MF_SEPARATOR is outside that mask. */
3442 return (item->fType | item->fState);
3447 /**********************************************************************
3448 * GetMenuItemCount (USER.263)
3450 INT16 WINAPI GetMenuItemCount16( HMENU16 hMenu )
3452 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3453 if (!menu) return -1;
3454 TRACE("(%04x) returning %d\n",
3455 hMenu, menu->nItems );
3456 return menu->nItems;
3460 /**********************************************************************
3461 * GetMenuItemCount (USER32.@)
3463 INT WINAPI GetMenuItemCount( HMENU hMenu )
3465 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3466 if (!menu) return -1;
3467 TRACE("(%04x) returning %d\n",
3468 hMenu, menu->nItems );
3469 return menu->nItems;
3472 /**********************************************************************
3473 * GetMenuItemID (USER.264)
3475 UINT16 WINAPI GetMenuItemID16( HMENU16 hMenu, INT16 nPos )
3477 return (UINT16) GetMenuItemID (hMenu, nPos);
3480 /**********************************************************************
3481 * GetMenuItemID (USER32.@)
3483 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3485 MENUITEM * lpmi;
3487 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3488 if (lpmi->fType & MF_POPUP) return -1;
3489 return lpmi->wID;
3493 /*******************************************************************
3494 * InsertMenu (USER.410)
3496 BOOL16 WINAPI InsertMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3497 UINT16 id, SEGPTR data )
3499 UINT pos32 = (UINT)pos;
3500 if ((pos == (UINT16)-1) && (flags & MF_BYPOSITION)) pos32 = (UINT)-1;
3501 if (IS_STRING_ITEM(flags) && data)
3502 return InsertMenuA( hMenu, pos32, flags, id, MapSL(data) );
3503 return InsertMenuA( hMenu, pos32, flags, id, (LPSTR)data );
3507 /*******************************************************************
3508 * InsertMenuW (USER32.@)
3510 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3511 UINT id, LPCWSTR str )
3513 MENUITEM *item;
3515 if (IS_STRING_ITEM(flags) && str)
3516 TRACE("hMenu %04x, pos %d, flags %08x, "
3517 "id %04x, str %s\n",
3518 hMenu, pos, flags, id, debugstr_w(str) );
3519 else TRACE("hMenu %04x, pos %d, flags %08x, "
3520 "id %04x, str %08lx (not a string)\n",
3521 hMenu, pos, flags, id, (DWORD)str );
3523 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3525 if (!(MENU_SetItemData( item, flags, id, str )))
3527 RemoveMenu( hMenu, pos, flags );
3528 return FALSE;
3531 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3532 (MENU_GetMenu((HMENU16)id))->wFlags |= MF_POPUP;
3534 item->hCheckBit = item->hUnCheckBit = 0;
3535 return TRUE;
3539 /*******************************************************************
3540 * InsertMenuA (USER32.@)
3542 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3543 UINT id, LPCSTR str )
3545 BOOL ret = FALSE;
3547 if (IS_STRING_ITEM(flags) && str)
3549 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3550 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3551 if (newstr)
3553 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3554 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3555 HeapFree( GetProcessHeap(), 0, newstr );
3557 return ret;
3559 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3563 /*******************************************************************
3564 * AppendMenu (USER.411)
3566 BOOL16 WINAPI AppendMenu16(HMENU16 hMenu, UINT16 flags, UINT16 id, SEGPTR data)
3568 return InsertMenu16( hMenu, -1, flags | MF_BYPOSITION, id, data );
3572 /*******************************************************************
3573 * AppendMenuA (USER32.@)
3575 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3576 UINT id, LPCSTR data )
3578 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3582 /*******************************************************************
3583 * AppendMenuW (USER32.@)
3585 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3586 UINT id, LPCWSTR data )
3588 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3592 /**********************************************************************
3593 * RemoveMenu (USER.412)
3595 BOOL16 WINAPI RemoveMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3597 return RemoveMenu( hMenu, nPos, wFlags );
3601 /**********************************************************************
3602 * RemoveMenu (USER32.@)
3604 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3606 LPPOPUPMENU menu;
3607 MENUITEM *item;
3609 TRACE("(menu=%04x pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3610 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3611 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3613 /* Remove item */
3615 MENU_FreeItemData( item );
3617 if (--menu->nItems == 0)
3619 HeapFree( GetProcessHeap(), 0, menu->items );
3620 menu->items = NULL;
3622 else
3624 while(nPos < menu->nItems)
3626 *item = *(item+1);
3627 item++;
3628 nPos++;
3630 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3631 menu->nItems * sizeof(MENUITEM) );
3633 return TRUE;
3637 /**********************************************************************
3638 * DeleteMenu (USER.413)
3640 BOOL16 WINAPI DeleteMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3642 return DeleteMenu( hMenu, nPos, wFlags );
3646 /**********************************************************************
3647 * DeleteMenu (USER32.@)
3649 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3651 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3652 if (!item) return FALSE;
3653 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3654 /* nPos is now the position of the item */
3655 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3656 return TRUE;
3660 /*******************************************************************
3661 * ModifyMenu (USER.414)
3663 BOOL16 WINAPI ModifyMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3664 UINT16 id, SEGPTR data )
3666 if (IS_STRING_ITEM(flags))
3667 return ModifyMenuA( hMenu, pos, flags, id, MapSL(data) );
3668 return ModifyMenuA( hMenu, pos, flags, id, (LPSTR)data );
3672 /*******************************************************************
3673 * ModifyMenuW (USER32.@)
3675 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3676 UINT id, LPCWSTR str )
3678 MENUITEM *item;
3680 if (IS_STRING_ITEM(flags))
3682 TRACE("%04x %d %04x %04x %s\n",
3683 hMenu, pos, flags, id, debugstr_w(str) );
3684 if (!str) return FALSE;
3686 else
3688 TRACE("%04x %d %04x %04x %08lx\n",
3689 hMenu, pos, flags, id, (DWORD)str );
3692 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3693 return MENU_SetItemData( item, flags, id, str );
3697 /*******************************************************************
3698 * ModifyMenuA (USER32.@)
3700 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3701 UINT id, LPCSTR str )
3703 BOOL ret = FALSE;
3705 if (IS_STRING_ITEM(flags) && str)
3707 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3708 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3709 if (newstr)
3711 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3712 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3713 HeapFree( GetProcessHeap(), 0, newstr );
3715 return ret;
3717 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3721 /**********************************************************************
3722 * CreatePopupMenu (USER.415)
3724 HMENU16 WINAPI CreatePopupMenu16(void)
3726 return CreatePopupMenu();
3730 /**********************************************************************
3731 * CreatePopupMenu (USER32.@)
3733 HMENU WINAPI CreatePopupMenu(void)
3735 HMENU hmenu;
3736 POPUPMENU *menu;
3738 if (!(hmenu = CreateMenu())) return 0;
3739 menu = MENU_GetMenu( hmenu );
3740 menu->wFlags |= MF_POPUP;
3741 menu->bTimeToHide = FALSE;
3742 return hmenu;
3746 /**********************************************************************
3747 * GetMenuCheckMarkDimensions (USER.417)
3748 * GetMenuCheckMarkDimensions (USER32.@)
3750 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3752 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3756 /**********************************************************************
3757 * SetMenuItemBitmaps (USER.418)
3759 BOOL16 WINAPI SetMenuItemBitmaps16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags,
3760 HBITMAP16 hNewUnCheck, HBITMAP16 hNewCheck)
3762 return SetMenuItemBitmaps( hMenu, nPos, wFlags, hNewUnCheck, hNewCheck );
3766 /**********************************************************************
3767 * SetMenuItemBitmaps (USER32.@)
3769 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3770 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3772 MENUITEM *item;
3773 TRACE("(%04x, %04x, %04x, %04x, %04x)\n",
3774 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3775 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3777 if (!hNewCheck && !hNewUnCheck)
3779 item->fState &= ~MF_USECHECKBITMAPS;
3781 else /* Install new bitmaps */
3783 item->hCheckBit = hNewCheck;
3784 item->hUnCheckBit = hNewUnCheck;
3785 item->fState |= MF_USECHECKBITMAPS;
3787 return TRUE;
3791 /**********************************************************************
3792 * CreateMenu (USER.151)
3794 HMENU16 WINAPI CreateMenu16(void)
3796 return CreateMenu();
3800 /**********************************************************************
3801 * CreateMenu (USER32.@)
3803 HMENU WINAPI CreateMenu(void)
3805 HMENU hMenu;
3806 LPPOPUPMENU menu;
3807 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3808 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3810 ZeroMemory(menu, sizeof(POPUPMENU));
3811 menu->wMagic = MENU_MAGIC;
3812 menu->FocusedItem = NO_SELECTED_ITEM;
3813 menu->bTimeToHide = FALSE;
3815 TRACE("return %04x\n", hMenu );
3817 return hMenu;
3821 /**********************************************************************
3822 * DestroyMenu (USER.152)
3824 BOOL16 WINAPI DestroyMenu16( HMENU16 hMenu )
3826 return DestroyMenu( hMenu );
3830 /**********************************************************************
3831 * DestroyMenu (USER32.@)
3833 BOOL WINAPI DestroyMenu( HMENU hMenu )
3835 TRACE("(%04x)\n", hMenu);
3837 /* Silently ignore attempts to destroy default system popup */
3839 if (hMenu && hMenu != MENU_DefSysPopup)
3841 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3843 if (!lppop) return FALSE;
3845 lppop->wMagic = 0; /* Mark it as destroyed */
3847 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3849 DestroyWindow( lppop->hWnd );
3850 lppop->hWnd = 0;
3853 if (lppop->items) /* recursively destroy submenus */
3855 int i;
3856 MENUITEM *item = lppop->items;
3857 for (i = lppop->nItems; i > 0; i--, item++)
3859 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3860 MENU_FreeItemData( item );
3862 HeapFree( GetProcessHeap(), 0, lppop->items );
3864 USER_HEAP_FREE( hMenu );
3866 return (hMenu != MENU_DefSysPopup);
3870 /**********************************************************************
3871 * GetSystemMenu (USER32.@)
3873 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3875 WND *wndPtr = WIN_FindWndPtr( hWnd );
3876 HMENU retvalue = 0;
3878 if (wndPtr)
3880 if( wndPtr->hSysMenu )
3882 if( bRevert )
3884 DestroyMenu(wndPtr->hSysMenu);
3885 wndPtr->hSysMenu = 0;
3887 else
3889 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3890 if( menu )
3892 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3893 menu->items[0].hSubMenu = MENU_CopySysPopup();
3895 else
3897 WARN("Current sys-menu (%04x) of wnd %04x is broken\n",
3898 wndPtr->hSysMenu, hWnd);
3899 wndPtr->hSysMenu = 0;
3904 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3905 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3907 if( wndPtr->hSysMenu )
3909 POPUPMENU *menu;
3910 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3912 /* Store the dummy sysmenu handle to facilitate the refresh */
3913 /* of the close button if the SC_CLOSE item change */
3914 menu = MENU_GetMenu(retvalue);
3915 if ( menu )
3916 menu->hSysMenuOwner = wndPtr->hSysMenu;
3918 WIN_ReleaseWndPtr(wndPtr);
3920 return bRevert ? 0 : retvalue;
3924 /*******************************************************************
3925 * SetSystemMenu (USER32.@)
3927 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3929 WND *wndPtr = WIN_FindWndPtr(hwnd);
3931 if (wndPtr)
3933 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3934 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3935 WIN_ReleaseWndPtr(wndPtr);
3936 return TRUE;
3938 return FALSE;
3942 /**********************************************************************
3943 * GetMenu (USER32.@)
3945 HMENU WINAPI GetMenu( HWND hWnd )
3947 HMENU retvalue = (HMENU)GetWindowLongA( hWnd, GWL_ID );
3948 TRACE("for %04x returning %04x\n", hWnd, retvalue);
3949 return retvalue;
3953 /**********************************************************************
3954 * SetMenu (USER32.@)
3956 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3958 TRACE("(%04x, %04x);\n", hWnd, hMenu);
3960 if (hMenu && !IsMenu(hMenu))
3962 WARN("hMenu %x is not a menu handle\n", hMenu);
3963 return FALSE;
3965 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3967 hWnd = WIN_GetFullHandle( hWnd );
3968 if (GetCapture() == hWnd) ReleaseCapture();
3970 if (hMenu != 0)
3972 LPPOPUPMENU lpmenu;
3974 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3976 lpmenu->hWnd = hWnd;
3977 lpmenu->Height = 0; /* Make sure we recalculate the size */
3979 SetWindowLongA( hWnd, GWL_ID, hMenu );
3981 if (IsWindowVisible(hWnd))
3982 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3983 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3984 return TRUE;
3989 /**********************************************************************
3990 * GetSubMenu (USER.159)
3992 HMENU16 WINAPI GetSubMenu16( HMENU16 hMenu, INT16 nPos )
3994 return GetSubMenu( hMenu, nPos );
3998 /**********************************************************************
3999 * GetSubMenu (USER32.@)
4001 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4003 MENUITEM * lpmi;
4005 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
4006 if (!(lpmi->fType & MF_POPUP)) return 0;
4007 return lpmi->hSubMenu;
4011 /**********************************************************************
4012 * DrawMenuBar (USER32.@)
4014 BOOL WINAPI DrawMenuBar( HWND hWnd )
4016 LPPOPUPMENU lppop;
4017 HMENU hMenu = GetMenu(hWnd);
4019 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
4020 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4022 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4023 lppop->hwndOwner = hWnd;
4024 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4025 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4026 return TRUE;
4029 /***********************************************************************
4030 * DrawMenuBarTemp (USER32.@)
4032 * UNDOCUMENTED !!
4034 * called by W98SE desk.cpl Control Panel Applet
4036 * Not 100% sure about the param names, but close.
4038 DWORD WINAPI DrawMenuBarTemp(HWND someHWND, HDC someHDC, LPRECT someRECT, HMENU someHMENU, HFONT someFONT)
4040 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, 0x%08x): stub\n", someHWND, someHDC, someRECT, someHMENU, someFONT);
4041 return 0;
4044 /***********************************************************************
4045 * EndMenu (USER.187)
4046 * EndMenu (USER32.@)
4048 void WINAPI EndMenu(void)
4050 /* if we are in the menu code, and it is active */
4051 if (!fEndMenu && top_popup)
4053 /* terminate the menu handling code */
4054 fEndMenu = TRUE;
4056 /* needs to be posted to wakeup the internal menu handler */
4057 /* which will now terminate the menu, in the event that */
4058 /* the main window was minimized, or lost focus, so we */
4059 /* don't end up with an orphaned menu */
4060 PostMessageA( top_popup, WM_CANCELMODE, 0, 0);
4065 /***********************************************************************
4066 * LookupMenuHandle (USER.217)
4068 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4070 HMENU hmenu32 = hmenu;
4071 UINT id32 = id;
4072 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4073 else return hmenu32;
4077 /**********************************************************************
4078 * LoadMenu (USER.150)
4080 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4082 HRSRC16 hRsrc;
4083 HGLOBAL16 handle;
4084 HMENU16 hMenu;
4086 TRACE("(%04x,%s)\n", instance, debugstr_a(name) );
4088 if (HIWORD(name))
4090 if (name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4093 if (!name) return 0;
4095 /* check for Win32 module */
4096 if (HIWORD(instance)) return LoadMenuA( instance, name );
4097 instance = GetExePtr( instance );
4099 if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
4100 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4101 hMenu = LoadMenuIndirect16(LockResource16(handle));
4102 FreeResource16( handle );
4103 return hMenu;
4107 /*****************************************************************
4108 * LoadMenuA (USER32.@)
4110 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4112 HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
4113 if (!hrsrc) return 0;
4114 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4118 /*****************************************************************
4119 * LoadMenuW (USER32.@)
4121 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4123 HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
4124 if (!hrsrc) return 0;
4125 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4129 /**********************************************************************
4130 * LoadMenuIndirect (USER.220)
4132 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4134 HMENU16 hMenu;
4135 WORD version, offset;
4136 LPCSTR p = (LPCSTR)template;
4138 TRACE("(%p)\n", template );
4139 version = GET_WORD(p);
4140 p += sizeof(WORD);
4141 if (version)
4143 WARN("version must be 0 for Win16\n" );
4144 return 0;
4146 offset = GET_WORD(p);
4147 p += sizeof(WORD) + offset;
4148 if (!(hMenu = CreateMenu())) return 0;
4149 if (!MENU_ParseResource( p, hMenu, FALSE ))
4151 DestroyMenu( hMenu );
4152 return 0;
4154 return hMenu;
4158 /**********************************************************************
4159 * LoadMenuIndirectA (USER32.@)
4161 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4163 HMENU16 hMenu;
4164 WORD version, offset;
4165 LPCSTR p = (LPCSTR)template;
4167 TRACE("%p\n", template );
4168 version = GET_WORD(p);
4169 p += sizeof(WORD);
4170 switch (version)
4172 case 0:
4173 offset = GET_WORD(p);
4174 p += sizeof(WORD) + offset;
4175 if (!(hMenu = CreateMenu())) return 0;
4176 if (!MENU_ParseResource( p, hMenu, TRUE ))
4178 DestroyMenu( hMenu );
4179 return 0;
4181 return hMenu;
4182 case 1:
4183 offset = GET_WORD(p);
4184 p += sizeof(WORD) + offset;
4185 if (!(hMenu = CreateMenu())) return 0;
4186 if (!MENUEX_ParseResource( p, hMenu))
4188 DestroyMenu( hMenu );
4189 return 0;
4191 return hMenu;
4192 default:
4193 ERR("version %d not supported.\n", version);
4194 return 0;
4199 /**********************************************************************
4200 * LoadMenuIndirectW (USER32.@)
4202 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4204 /* FIXME: is there anything different between A and W? */
4205 return LoadMenuIndirectA( template );
4209 /**********************************************************************
4210 * IsMenu (USER.358)
4212 BOOL16 WINAPI IsMenu16( HMENU16 hmenu )
4214 return IsMenu( hmenu );
4218 /**********************************************************************
4219 * IsMenu (USER32.@)
4221 BOOL WINAPI IsMenu(HMENU hmenu)
4223 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4224 return menu != NULL;
4227 /**********************************************************************
4228 * GetMenuItemInfo_common
4231 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4232 LPMENUITEMINFOW lpmii, BOOL unicode)
4234 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4236 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4238 if (!menu)
4239 return FALSE;
4241 if (lpmii->fMask & MIIM_TYPE) {
4242 lpmii->fType = menu->fType;
4243 switch (MENU_ITEM_TYPE(menu->fType)) {
4244 case MF_STRING:
4245 break; /* will be done below */
4246 case MF_OWNERDRAW:
4247 case MF_BITMAP:
4248 lpmii->dwTypeData = menu->text;
4249 /* fall through */
4250 default:
4251 lpmii->cch = 0;
4255 /* copy the text string */
4256 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4257 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4259 int len;
4260 if (unicode)
4262 len = strlenW(menu->text);
4263 if(lpmii->dwTypeData && lpmii->cch)
4264 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4266 else
4268 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4269 if(lpmii->dwTypeData && lpmii->cch)
4270 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4271 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4272 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4274 /* if we've copied a substring we return its length */
4275 if(lpmii->dwTypeData && lpmii->cch)
4277 if (lpmii->cch <= len) lpmii->cch--;
4279 else /* return length of string */
4280 lpmii->cch = len;
4283 if (lpmii->fMask & MIIM_FTYPE)
4284 lpmii->fType = menu->fType;
4286 if (lpmii->fMask & MIIM_BITMAP)
4287 lpmii->hbmpItem = menu->hbmpItem;
4289 if (lpmii->fMask & MIIM_STATE)
4290 lpmii->fState = menu->fState;
4292 if (lpmii->fMask & MIIM_ID)
4293 lpmii->wID = menu->wID;
4295 if (lpmii->fMask & MIIM_SUBMENU)
4296 lpmii->hSubMenu = menu->hSubMenu;
4298 if (lpmii->fMask & MIIM_CHECKMARKS) {
4299 lpmii->hbmpChecked = menu->hCheckBit;
4300 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4302 if (lpmii->fMask & MIIM_DATA)
4303 lpmii->dwItemData = menu->dwItemData;
4305 return TRUE;
4308 /**********************************************************************
4309 * GetMenuItemInfoA (USER32.@)
4311 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4312 LPMENUITEMINFOA lpmii)
4314 return GetMenuItemInfo_common (hmenu, item, bypos,
4315 (LPMENUITEMINFOW)lpmii, FALSE);
4318 /**********************************************************************
4319 * GetMenuItemInfoW (USER32.@)
4321 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4322 LPMENUITEMINFOW lpmii)
4324 return GetMenuItemInfo_common (hmenu, item, bypos,
4325 lpmii, TRUE);
4329 /* set a menu item text from a ASCII or Unicode string */
4330 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4332 if (!text)
4334 menu->text = NULL;
4335 menu->fType |= MF_SEPARATOR;
4337 else if (unicode)
4339 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4340 strcpyW( menu->text, text );
4342 else
4344 LPCSTR str = (LPCSTR)text;
4345 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4346 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4347 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4352 /**********************************************************************
4353 * SetMenuItemInfo_common
4356 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4357 const MENUITEMINFOW *lpmii,
4358 BOOL unicode)
4360 if (!menu) return FALSE;
4362 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4364 if (lpmii->fMask & MIIM_TYPE ) {
4365 /* Get rid of old string. */
4366 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4367 HeapFree(GetProcessHeap(), 0, menu->text);
4368 menu->text = NULL;
4371 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4372 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4373 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4375 menu->text = lpmii->dwTypeData;
4377 if (IS_STRING_ITEM(menu->fType))
4378 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4381 if (lpmii->fMask & MIIM_FTYPE ) {
4382 /* free the string when the type is changing */
4383 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4384 HeapFree(GetProcessHeap(), 0, menu->text);
4385 menu->text = NULL;
4387 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4388 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4389 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4390 menu->fType |= MF_SEPARATOR;
4393 if (lpmii->fMask & MIIM_STRING ) {
4394 /* free the string when used */
4395 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4396 HeapFree(GetProcessHeap(), 0, menu->text);
4397 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4401 if (lpmii->fMask & MIIM_STATE)
4403 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4404 menu->fState = lpmii->fState;
4407 if (lpmii->fMask & MIIM_ID)
4408 menu->wID = lpmii->wID;
4410 if (lpmii->fMask & MIIM_SUBMENU) {
4411 menu->hSubMenu = lpmii->hSubMenu;
4412 if (menu->hSubMenu) {
4413 POPUPMENU *subMenu = MENU_GetMenu((UINT16)menu->hSubMenu);
4414 if (subMenu) {
4415 subMenu->wFlags |= MF_POPUP;
4416 menu->fType |= MF_POPUP;
4418 else
4419 /* FIXME: Return an error ? */
4420 menu->fType &= ~MF_POPUP;
4422 else
4423 menu->fType &= ~MF_POPUP;
4426 if (lpmii->fMask & MIIM_CHECKMARKS)
4428 if (lpmii->fType & MFT_RADIOCHECK)
4429 menu->fType |= MFT_RADIOCHECK;
4431 menu->hCheckBit = lpmii->hbmpChecked;
4432 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4434 if (lpmii->fMask & MIIM_DATA)
4435 menu->dwItemData = lpmii->dwItemData;
4437 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4438 return TRUE;
4441 /**********************************************************************
4442 * SetMenuItemInfoA (USER32.@)
4444 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4445 const MENUITEMINFOA *lpmii)
4447 if ((lpmii->fType & (MF_HILITE|MF_POPUP)) || (lpmii->fState)) {
4448 /* QuickTime does pass invalid data into SetMenuItemInfo.
4449 * do some of the checks Windows does.
4451 WARN("Bad masks for type (0x%08x) or state (0x%08x)\n",
4452 lpmii->fType,lpmii->fState );
4453 return FALSE;
4456 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4457 (const MENUITEMINFOW *)lpmii, FALSE);
4460 /**********************************************************************
4461 * SetMenuItemInfoW (USER32.@)
4463 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4464 const MENUITEMINFOW *lpmii)
4466 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4467 lpmii, TRUE);
4470 /**********************************************************************
4471 * SetMenuDefaultItem (USER32.@)
4474 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4476 UINT i;
4477 POPUPMENU *menu;
4478 MENUITEM *item;
4480 TRACE("(0x%x,%d,%d)\n", hmenu, uItem, bypos);
4482 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4484 /* reset all default-item flags */
4485 item = menu->items;
4486 for (i = 0; i < menu->nItems; i++, item++)
4488 item->fState &= ~MFS_DEFAULT;
4491 /* no default item */
4492 if ( -1 == uItem)
4494 return TRUE;
4497 item = menu->items;
4498 if ( bypos )
4500 if ( uItem >= menu->nItems ) return FALSE;
4501 item[uItem].fState |= MFS_DEFAULT;
4502 return TRUE;
4504 else
4506 for (i = 0; i < menu->nItems; i++, item++)
4508 if (item->wID == uItem)
4510 item->fState |= MFS_DEFAULT;
4511 return TRUE;
4516 return FALSE;
4519 /**********************************************************************
4520 * GetMenuDefaultItem (USER32.@)
4522 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4524 POPUPMENU *menu;
4525 MENUITEM * item;
4526 UINT i = 0;
4528 TRACE("(0x%x,%d,%d)\n", hmenu, bypos, flags);
4530 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4532 /* find default item */
4533 item = menu->items;
4535 /* empty menu */
4536 if (! item) return -1;
4538 while ( !( item->fState & MFS_DEFAULT ) )
4540 i++; item++;
4541 if (i >= menu->nItems ) return -1;
4544 /* default: don't return disabled items */
4545 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4547 /* search rekursiv when needed */
4548 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4550 UINT ret;
4551 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4552 if ( -1 != ret ) return ret;
4554 /* when item not found in submenu, return the popup item */
4556 return ( bypos ) ? i : item->wID;
4560 /*******************************************************************
4561 * InsertMenuItem (USER.441)
4563 * FIXME: untested
4565 BOOL16 WINAPI InsertMenuItem16( HMENU16 hmenu, UINT16 pos, BOOL16 byposition,
4566 const MENUITEMINFO16 *mii )
4568 MENUITEMINFOA miia;
4570 miia.cbSize = sizeof(miia);
4571 miia.fMask = mii->fMask;
4572 miia.dwTypeData = (LPSTR)mii->dwTypeData;
4573 miia.fType = mii->fType;
4574 miia.fState = mii->fState;
4575 miia.wID = mii->wID;
4576 miia.hSubMenu = mii->hSubMenu;
4577 miia.hbmpChecked = mii->hbmpChecked;
4578 miia.hbmpUnchecked = mii->hbmpUnchecked;
4579 miia.dwItemData = mii->dwItemData;
4580 miia.cch = mii->cch;
4581 if (IS_STRING_ITEM(miia.fType))
4582 miia.dwTypeData = MapSL(mii->dwTypeData);
4583 return InsertMenuItemA( hmenu, pos, byposition, &miia );
4587 /**********************************************************************
4588 * InsertMenuItemA (USER32.@)
4590 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4591 const MENUITEMINFOA *lpmii)
4593 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4594 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4598 /**********************************************************************
4599 * InsertMenuItemW (USER32.@)
4601 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4602 const MENUITEMINFOW *lpmii)
4604 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4605 return SetMenuItemInfo_common(item, lpmii, TRUE);
4608 /**********************************************************************
4609 * CheckMenuRadioItem (USER32.@)
4612 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4613 UINT first, UINT last, UINT check,
4614 UINT bypos)
4616 MENUITEM *mifirst, *milast, *micheck;
4617 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4619 TRACE("ox%x: %d-%d, check %d, bypos=%d\n",
4620 hMenu, first, last, check, bypos);
4622 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4623 milast = MENU_FindItem (&mlast, &last, bypos);
4624 micheck = MENU_FindItem (&mcheck, &check, bypos);
4626 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4627 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4628 micheck > milast || micheck < mifirst)
4629 return FALSE;
4631 while (mifirst <= milast)
4633 if (mifirst == micheck)
4635 mifirst->fType |= MFT_RADIOCHECK;
4636 mifirst->fState |= MFS_CHECKED;
4637 } else {
4638 mifirst->fType &= ~MFT_RADIOCHECK;
4639 mifirst->fState &= ~MFS_CHECKED;
4641 mifirst++;
4644 return TRUE;
4647 /**********************************************************************
4648 * CheckMenuRadioItem (USER.666)
4650 BOOL16 WINAPI CheckMenuRadioItem16(HMENU16 hMenu,
4651 UINT16 first, UINT16 last, UINT16 check,
4652 BOOL16 bypos)
4654 return CheckMenuRadioItem (hMenu, first, last, check, bypos);
4657 /**********************************************************************
4658 * GetMenuItemRect (USER32.@)
4660 * ATTENTION: Here, the returned values in rect are the screen
4661 * coordinates of the item just like if the menu was
4662 * always on the upper left side of the application.
4665 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4666 LPRECT rect)
4668 POPUPMENU *itemMenu;
4669 MENUITEM *item;
4670 HWND referenceHwnd;
4672 TRACE("(0x%x,0x%x,%d,%p)\n", hwnd, hMenu, uItem, rect);
4674 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4675 referenceHwnd = hwnd;
4677 if(!hwnd)
4679 itemMenu = MENU_GetMenu(hMenu);
4680 if (itemMenu == NULL)
4681 return FALSE;
4683 if(itemMenu->hWnd == 0)
4684 return FALSE;
4685 referenceHwnd = itemMenu->hWnd;
4688 if ((rect == NULL) || (item == NULL))
4689 return FALSE;
4691 *rect = item->rect;
4693 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4695 return TRUE;
4699 /**********************************************************************
4700 * SetMenuInfo (USER32.@)
4702 * FIXME
4703 * MIM_APPLYTOSUBMENUS
4704 * actually use the items to draw the menu
4706 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4708 POPUPMENU *menu;
4710 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4712 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4715 if (lpmi->fMask & MIM_BACKGROUND)
4716 menu->hbrBack = lpmi->hbrBack;
4718 if (lpmi->fMask & MIM_HELPID)
4719 menu->dwContextHelpID = lpmi->dwContextHelpID;
4721 if (lpmi->fMask & MIM_MAXHEIGHT)
4722 menu->cyMax = lpmi->cyMax;
4724 if (lpmi->fMask & MIM_MENUDATA)
4725 menu->dwMenuData = lpmi->dwMenuData;
4727 if (lpmi->fMask & MIM_STYLE)
4728 menu->dwStyle = lpmi->dwStyle;
4730 return TRUE;
4732 return FALSE;
4735 /**********************************************************************
4736 * GetMenuInfo (USER32.@)
4738 * NOTES
4739 * win98/NT5.0
4742 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4743 { POPUPMENU *menu;
4745 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4747 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4750 if (lpmi->fMask & MIM_BACKGROUND)
4751 lpmi->hbrBack = menu->hbrBack;
4753 if (lpmi->fMask & MIM_HELPID)
4754 lpmi->dwContextHelpID = menu->dwContextHelpID;
4756 if (lpmi->fMask & MIM_MAXHEIGHT)
4757 lpmi->cyMax = menu->cyMax;
4759 if (lpmi->fMask & MIM_MENUDATA)
4760 lpmi->dwMenuData = menu->dwMenuData;
4762 if (lpmi->fMask & MIM_STYLE)
4763 lpmi->dwStyle = menu->dwStyle;
4765 return TRUE;
4767 return FALSE;
4770 /**********************************************************************
4771 * SetMenuContextHelpId (USER.384)
4773 BOOL16 WINAPI SetMenuContextHelpId16( HMENU16 hMenu, DWORD dwContextHelpID)
4775 return SetMenuContextHelpId( hMenu, dwContextHelpID );
4779 /**********************************************************************
4780 * SetMenuContextHelpId (USER32.@)
4782 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4784 LPPOPUPMENU menu;
4786 TRACE("(0x%04x 0x%08lx)\n", hMenu, dwContextHelpID);
4788 if ((menu = MENU_GetMenu(hMenu)))
4790 menu->dwContextHelpID = dwContextHelpID;
4791 return TRUE;
4793 return FALSE;
4796 /**********************************************************************
4797 * GetMenuContextHelpId (USER.385)
4799 DWORD WINAPI GetMenuContextHelpId16( HMENU16 hMenu )
4801 return GetMenuContextHelpId( hMenu );
4804 /**********************************************************************
4805 * GetMenuContextHelpId (USER32.@)
4807 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4809 LPPOPUPMENU menu;
4811 TRACE("(0x%04x)\n", hMenu);
4813 if ((menu = MENU_GetMenu(hMenu)))
4815 return menu->dwContextHelpID;
4817 return 0;
4820 /**********************************************************************
4821 * MenuItemFromPoint (USER32.@)
4823 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4825 POPUPMENU *menu = MENU_GetMenu(hMenu);
4826 UINT pos;
4827 MENUITEM *item;
4829 /*FIXME: Do we have to handle hWnd here? */
4830 item = MENU_FindItemByCoords(menu, ptScreen, &pos);
4832 return pos;
4836 /**********************************************************************
4837 * translate_accelerator
4839 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4840 BYTE fVirt, WORD key, WORD cmd )
4842 UINT mesg = 0;
4844 if (wParam != key) return FALSE;
4846 if (message == WM_CHAR)
4848 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4850 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4851 goto found;
4854 else
4856 if(fVirt & FVIRTKEY)
4858 INT mask = 0;
4859 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4860 wParam, 0xff & HIWORD(lParam));
4861 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4862 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4863 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4864 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4865 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4867 else
4869 if (!(lParam & 0x01000000)) /* no special_key */
4871 if ((fVirt & FALT) && (lParam & 0x20000000))
4872 { /* ^^ ALT pressed */
4873 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4874 goto found;
4879 return FALSE;
4881 found:
4882 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4883 mesg = 1;
4884 else if (GetCapture())
4885 mesg = 2;
4886 else if (!IsWindowEnabled(hWnd))
4887 mesg = 3;
4888 else
4890 HMENU hMenu, hSubMenu, hSysMenu;
4891 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4893 hMenu = (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4894 hSysMenu = get_win_sys_menu( hWnd );
4896 /* find menu item and ask application to initialize it */
4897 /* 1. in the system menu */
4898 hSubMenu = hSysMenu;
4899 nPos = cmd;
4900 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4902 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4903 if(hSubMenu != hSysMenu)
4905 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4906 TRACE_(accel)("hSysMenu = %04x, hSubMenu = %04x, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4907 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4909 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4911 else /* 2. in the window's menu */
4913 hSubMenu = hMenu;
4914 nPos = cmd;
4915 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4917 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4918 if(hSubMenu != hMenu)
4920 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4921 TRACE_(accel)("hMenu = %04x, hSubMenu = %04x, nPos = %d\n", hMenu, hSubMenu, nPos);
4922 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4924 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4928 if (uSysStat != (UINT)-1)
4930 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4931 mesg=4;
4932 else
4933 mesg=WM_SYSCOMMAND;
4935 else
4937 if (uStat != (UINT)-1)
4939 if (IsIconic(hWnd))
4940 mesg=5;
4941 else
4943 if (uStat & (MF_DISABLED|MF_GRAYED))
4944 mesg=6;
4945 else
4946 mesg=WM_COMMAND;
4949 else
4950 mesg=WM_COMMAND;
4954 if( mesg==WM_COMMAND )
4956 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4957 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
4959 else if( mesg==WM_SYSCOMMAND )
4961 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4962 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
4964 else
4966 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4967 * #0: unknown (please report!)
4968 * #1: for WM_KEYUP,WM_SYSKEYUP
4969 * #2: mouse is captured
4970 * #3: window is disabled
4971 * #4: it's a disabled system menu option
4972 * #5: it's a menu option, but window is iconic
4973 * #6: it's a menu option, but disabled
4975 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4976 if(mesg==0)
4977 ERR_(accel)(" unknown reason - please report!");
4979 return TRUE;
4982 /**********************************************************************
4983 * TranslateAccelerator (USER32.@)
4984 * TranslateAcceleratorA (USER32.@)
4985 * TranslateAcceleratorW (USER32.@)
4987 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
4989 /* YES, Accel16! */
4990 LPACCEL16 lpAccelTbl;
4991 int i;
4993 if (msg == NULL)
4995 WARN_(accel)("msg null; should hang here to be win compatible\n");
4996 return 0;
4998 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5000 WARN_(accel)("invalid accel handle=%x\n", hAccel);
5001 return 0;
5003 if ((msg->message != WM_KEYDOWN &&
5004 msg->message != WM_KEYUP &&
5005 msg->message != WM_SYSKEYDOWN &&
5006 msg->message != WM_SYSKEYUP &&
5007 msg->message != WM_CHAR)) return 0;
5009 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5010 "msg->hwnd=%04x, msg->message=%04x, wParam=%08x, lParam=%lx\n",
5011 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5013 i = 0;
5016 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5017 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5018 return 1;
5019 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5020 WARN_(accel)("couldn't translate accelerator key\n");
5021 return 0;