Made it possible to install the winapi tools.
[wine/hacks.git] / controls / menu.c
blob0bc988b148a3c77e12f4082c9c39125d568313b5
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 */
9 /*
10 * Note: the style MF_MOUSESELECT is used to mark popup items that
11 * have been selected, i.e. their popup menu is currently displayed.
12 * This is probably not the meaning this style has in MS-Windows.
15 #include <assert.h>
16 #include <ctype.h>
17 #include <stdlib.h>
18 #include <string.h>
20 #include "windef.h"
21 #include "winnls.h"
22 #include "wingdi.h"
23 #include "wine/winbase16.h"
24 #include "wine/winuser16.h"
25 #include "wine/unicode.h"
26 #include "wine/port.h"
27 #include "win.h"
28 #include "controls.h"
29 #include "nonclient.h"
30 #include "user.h"
31 #include "message.h"
33 #include "debugtools.h"
35 DEFAULT_DEBUG_CHANNEL(menu);
36 DECLARE_DEBUG_CHANNEL(accel);
38 /* internal popup menu window messages */
40 #define MM_SETMENUHANDLE (WM_USER + 0)
41 #define MM_GETMENUHANDLE (WM_USER + 1)
43 /* Menu item structure */
44 typedef struct {
45 /* ----------- MENUITEMINFO Stuff ----------- */
46 UINT fType; /* Item type. */
47 UINT fState; /* Item state. */
48 UINT wID; /* Item id. */
49 HMENU hSubMenu; /* Pop-up menu. */
50 HBITMAP hCheckBit; /* Bitmap when checked. */
51 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
52 LPWSTR text; /* Item text or bitmap handle. */
53 DWORD dwItemData; /* Application defined. */
54 DWORD dwTypeData; /* depends on fMask */
55 HBITMAP hbmpItem; /* bitmap in win98 style menus */
56 /* ----------- Wine stuff ----------- */
57 RECT rect; /* Item area (relative to menu window) */
58 UINT xTab; /* X position of text after Tab */
59 } MENUITEM;
61 /* Popup menu structure */
62 typedef struct {
63 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
64 WORD wMagic; /* Magic number */
65 WORD Width; /* Width of the whole menu */
66 WORD Height; /* Height of the whole menu */
67 UINT nItems; /* Number of items in the menu */
68 HWND hWnd; /* Window containing the menu */
69 MENUITEM *items; /* Array of menu items */
70 UINT FocusedItem; /* Currently focused item */
71 HWND hwndOwner; /* window receiving the messages for ownerdraw */
72 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
73 /* ------------ MENUINFO members ------ */
74 DWORD dwStyle; /* Extended mennu style */
75 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
76 HBRUSH hbrBack; /* brush for menu background */
77 DWORD dwContextHelpID;
78 DWORD dwMenuData; /* application defined value */
79 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
80 } POPUPMENU, *LPPOPUPMENU;
82 /* internal flags for menu tracking */
84 #define TF_ENDMENU 0x0001
85 #define TF_SUSPENDPOPUP 0x0002
86 #define TF_SKIPREMOVE 0x0004
88 typedef struct
90 UINT trackFlags;
91 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
92 HMENU hTopMenu; /* initial menu */
93 HWND hOwnerWnd; /* where notifications are sent */
94 POINT pt;
95 } MTRACKER;
97 #define MENU_MAGIC 0x554d /* 'MU' */
99 #define ITEM_PREV -1
100 #define ITEM_NEXT 1
102 /* Internal MENU_TrackMenu() flags */
103 #define TPM_INTERNAL 0xF0000000
104 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
105 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
106 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
108 /* popup menu shade thickness */
109 #define POPUP_XSHADE 4
110 #define POPUP_YSHADE 4
112 /* Space between 2 menu bar items */
113 #define MENU_BAR_ITEMS_SPACE 12
115 /* Minimum width of a tab character */
116 #define MENU_TAB_SPACE 8
118 /* Height of a separator item */
119 #define SEPARATOR_HEIGHT 5
121 /* (other menu->FocusedItem values give the position of the focused item) */
122 #define NO_SELECTED_ITEM 0xffff
124 #define MENU_ITEM_TYPE(flags) \
125 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
127 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
128 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
129 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
131 #define IS_SYSTEM_MENU(menu) \
132 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
134 #define IS_SYSTEM_POPUP(menu) \
135 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
137 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
138 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
139 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
140 MF_POPUP | MF_SYSMENU | MF_HELP)
141 #define STATE_MASK (~TYPE_MASK)
143 /* Dimension of the menu bitmaps */
144 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
146 static HBITMAP hStdMnArrow = 0;
148 /* Minimze/restore/close buttons to be inserted in menubar */
149 static HBITMAP hBmpMinimize = 0;
150 static HBITMAP hBmpMinimizeD = 0;
151 static HBITMAP hBmpMaximize = 0;
152 static HBITMAP hBmpMaximizeD = 0;
153 static HBITMAP hBmpClose = 0;
154 static HBITMAP hBmpCloseD = 0;
157 static HBRUSH hShadeBrush = 0;
158 static HFONT hMenuFont = 0;
159 static HFONT hMenuFontBold = 0;
161 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
163 /* Use global popup window because there's no way 2 menus can
164 * be tracked at the same time. */
165 static HWND top_popup;
167 /* Flag set by EndMenu() to force an exit from menu tracking */
168 static BOOL fEndMenu = FALSE;
170 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
173 /*********************************************************************
174 * menu class descriptor
176 const struct builtin_class_descr MENU_builtin_class =
178 POPUPMENU_CLASS_ATOM, /* name */
179 CS_GLOBALCLASS | CS_SAVEBITS, /* style */
180 NULL, /* procA (winproc is Unicode only) */
181 PopupMenuWndProc, /* procW */
182 sizeof(HMENU), /* extra */
183 IDC_ARROWA, /* cursor */
184 COLOR_MENU+1 /* brush */
188 /***********************************************************************
189 * debug_print_menuitem
191 * Print a menuitem in readable form.
194 #define debug_print_menuitem(pre, mp, post) \
195 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
197 #define MENUOUT(text) \
198 DPRINTF("%s%s", (count++ ? "," : ""), (text))
200 #define MENUFLAG(bit,text) \
201 do { \
202 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
203 } while (0)
205 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
206 const char *postfix)
208 TRACE("%s ", prefix);
209 if (mp) {
210 UINT flags = mp->fType;
211 int typ = MENU_ITEM_TYPE(flags);
212 DPRINTF( "{ ID=0x%x", mp->wID);
213 if (flags & MF_POPUP)
214 DPRINTF( ", Sub=0x%x", mp->hSubMenu);
215 if (flags) {
216 int count = 0;
217 DPRINTF( ", Typ=");
218 if (typ == MFT_STRING)
219 /* Nothing */ ;
220 else if (typ == MFT_SEPARATOR)
221 MENUOUT("sep");
222 else if (typ == MFT_OWNERDRAW)
223 MENUOUT("own");
224 else if (typ == MFT_BITMAP)
225 MENUOUT("bit");
226 else
227 MENUOUT("???");
228 flags -= typ;
230 MENUFLAG(MF_POPUP, "pop");
231 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
232 MENUFLAG(MFT_MENUBREAK, "brk");
233 MENUFLAG(MFT_RADIOCHECK, "radio");
234 MENUFLAG(MFT_RIGHTORDER, "rorder");
235 MENUFLAG(MF_SYSMENU, "sys");
236 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
238 if (flags)
239 DPRINTF( "+0x%x", flags);
241 flags = mp->fState;
242 if (flags) {
243 int count = 0;
244 DPRINTF( ", State=");
245 MENUFLAG(MFS_GRAYED, "grey");
246 MENUFLAG(MFS_DEFAULT, "default");
247 MENUFLAG(MFS_DISABLED, "dis");
248 MENUFLAG(MFS_CHECKED, "check");
249 MENUFLAG(MFS_HILITE, "hi");
250 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
251 MENUFLAG(MF_MOUSESELECT, "mouse");
252 if (flags)
253 DPRINTF( "+0x%x", flags);
255 if (mp->hCheckBit)
256 DPRINTF( ", Chk=0x%x", mp->hCheckBit);
257 if (mp->hUnCheckBit)
258 DPRINTF( ", Unc=0x%x", mp->hUnCheckBit);
260 if (typ == MFT_STRING) {
261 if (mp->text)
262 DPRINTF( ", Text=%s", debugstr_w(mp->text));
263 else
264 DPRINTF( ", Text=Null");
265 } else if (mp->text == NULL)
266 /* Nothing */ ;
267 else
268 DPRINTF( ", Text=%p", mp->text);
269 if (mp->dwItemData)
270 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
271 DPRINTF( " }");
272 } else {
273 DPRINTF( "NULL");
276 DPRINTF(" %s\n", postfix);
279 #undef MENUOUT
280 #undef MENUFLAG
283 /***********************************************************************
284 * MENU_GetMenu
286 * Validate the given menu handle and returns the menu structure pointer.
288 POPUPMENU *MENU_GetMenu(HMENU hMenu)
290 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
291 if (!menu || menu->wMagic != MENU_MAGIC)
293 WARN("invalid menu handle=%x, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
294 menu = NULL;
296 return menu;
299 /***********************************************************************
300 * MENU_CopySysPopup
302 * Return the default system menu.
304 static HMENU MENU_CopySysPopup(void)
306 HMENU hMenu = LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
308 if( hMenu ) {
309 POPUPMENU* menu = MENU_GetMenu(hMenu);
310 menu->wFlags |= MF_SYSMENU | MF_POPUP;
311 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
313 else
314 ERR("Unable to load default system menu\n" );
316 TRACE("returning %x.\n", hMenu );
318 return hMenu;
322 /**********************************************************************
323 * MENU_GetSysMenu
325 * Create a copy of the system menu. System menu in Windows is
326 * a special menu bar with the single entry - system menu popup.
327 * This popup is presented to the outside world as a "system menu".
328 * However, the real system menu handle is sometimes seen in the
329 * WM_MENUSELECT parameters (and Word 6 likes it this way).
331 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
333 HMENU hMenu;
335 if ((hMenu = CreateMenu()))
337 POPUPMENU *menu = MENU_GetMenu(hMenu);
338 menu->wFlags = MF_SYSMENU;
339 menu->hWnd = hWnd;
341 if (hPopupMenu == (HMENU)(-1))
342 hPopupMenu = MENU_CopySysPopup();
343 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
345 if (hPopupMenu)
347 InsertMenuA( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION, hPopupMenu, NULL );
349 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
350 menu->items[0].fState = 0;
351 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
353 TRACE("GetSysMenu hMenu=%04x (%04x)\n", hMenu, hPopupMenu );
354 return hMenu;
356 DestroyMenu( hMenu );
358 ERR("failed to load system menu!\n");
359 return 0;
363 /***********************************************************************
364 * MENU_Init
366 * Menus initialisation.
368 BOOL MENU_Init()
370 HBITMAP hBitmap;
371 NONCLIENTMETRICSA ncm;
373 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
374 0x55, 0, 0xAA, 0,
375 0x55, 0, 0xAA, 0,
376 0x55, 0, 0xAA, 0 };
378 /* Load menu bitmaps */
379 hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
380 /* Load system buttons bitmaps */
381 hBmpMinimize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCE));
382 hBmpMinimizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCED));
383 hBmpMaximize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORE));
384 hBmpMaximizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORED));
385 hBmpClose = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSE));
386 hBmpCloseD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSED));
388 if (hStdMnArrow)
390 BITMAP bm;
391 GetObjectA( hStdMnArrow, sizeof(bm), &bm );
392 arrow_bitmap_width = bm.bmWidth;
393 arrow_bitmap_height = bm.bmHeight;
394 } else
395 return FALSE;
397 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
398 return FALSE;
400 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
401 return FALSE;
403 DeleteObject( hBitmap );
404 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
405 return FALSE;
407 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
408 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0)))
409 return FALSE;
411 if (!(hMenuFont = CreateFontIndirectA( &ncm.lfMenuFont )))
412 return FALSE;
414 ncm.lfMenuFont.lfWeight += 300;
415 if ( ncm.lfMenuFont.lfWeight > 1000)
416 ncm.lfMenuFont.lfWeight = 1000;
418 if (!(hMenuFontBold = CreateFontIndirectA( &ncm.lfMenuFont )))
419 return FALSE;
421 return TRUE;
424 /***********************************************************************
425 * MENU_InitSysMenuPopup
427 * Grey the appropriate items in System menu.
429 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
431 BOOL gray;
433 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
434 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
435 gray = ((style & WS_MAXIMIZE) != 0);
436 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
437 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
438 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
439 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
440 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
441 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
442 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
443 gray = (clsStyle & CS_NOCLOSE) != 0;
445 /* The menu item must keep its state if it's disabled */
446 if(gray)
447 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
451 /******************************************************************************
453 * UINT MENU_GetStartOfNextColumn(
454 * HMENU hMenu )
456 *****************************************************************************/
458 static UINT MENU_GetStartOfNextColumn(
459 HMENU hMenu )
461 POPUPMENU *menu = MENU_GetMenu(hMenu);
462 UINT i;
464 if(!menu)
465 return NO_SELECTED_ITEM;
467 i = menu->FocusedItem + 1;
468 if( i == NO_SELECTED_ITEM )
469 return i;
471 for( ; i < menu->nItems; ++i ) {
472 if (menu->items[i].fType & MF_MENUBARBREAK)
473 return i;
476 return NO_SELECTED_ITEM;
480 /******************************************************************************
482 * UINT MENU_GetStartOfPrevColumn(
483 * HMENU hMenu )
485 *****************************************************************************/
487 static UINT MENU_GetStartOfPrevColumn(
488 HMENU hMenu )
490 POPUPMENU *menu = MENU_GetMenu(hMenu);
491 UINT i;
493 if( !menu )
494 return NO_SELECTED_ITEM;
496 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
497 return NO_SELECTED_ITEM;
499 /* Find the start of the column */
501 for(i = menu->FocusedItem; i != 0 &&
502 !(menu->items[i].fType & MF_MENUBARBREAK);
503 --i); /* empty */
505 if(i == 0)
506 return NO_SELECTED_ITEM;
508 for(--i; i != 0; --i) {
509 if (menu->items[i].fType & MF_MENUBARBREAK)
510 break;
513 TRACE("ret %d.\n", i );
515 return i;
520 /***********************************************************************
521 * MENU_FindItem
523 * Find a menu item. Return a pointer on the item, and modifies *hmenu
524 * in case the item was in a sub-menu.
526 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
528 POPUPMENU *menu;
529 UINT i;
531 if (((*hmenu)==0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
532 if (wFlags & MF_BYPOSITION)
534 if (*nPos >= menu->nItems) return NULL;
535 return &menu->items[*nPos];
537 else
539 MENUITEM *item = menu->items;
540 for (i = 0; i < menu->nItems; i++, item++)
542 if (item->wID == *nPos)
544 *nPos = i;
545 return item;
547 else if (item->fType & MF_POPUP)
549 HMENU hsubmenu = item->hSubMenu;
550 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
551 if (subitem)
553 *hmenu = hsubmenu;
554 return subitem;
559 return NULL;
562 /***********************************************************************
563 * MENU_FindSubMenu
565 * Find a Sub menu. Return the position of the submenu, and modifies
566 * *hmenu in case it is found in another sub-menu.
567 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
569 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
571 POPUPMENU *menu;
572 UINT i;
573 MENUITEM *item;
574 if (((*hmenu)==0xffff) ||
575 (!(menu = MENU_GetMenu(*hmenu))))
576 return NO_SELECTED_ITEM;
577 item = menu->items;
578 for (i = 0; i < menu->nItems; i++, item++) {
579 if(!(item->fType & MF_POPUP)) continue;
580 if (item->hSubMenu == hSubTarget) {
581 return i;
583 else {
584 HMENU hsubmenu = item->hSubMenu;
585 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
586 if (pos != NO_SELECTED_ITEM) {
587 *hmenu = hsubmenu;
588 return pos;
592 return NO_SELECTED_ITEM;
595 /***********************************************************************
596 * MENU_FreeItemData
598 static void MENU_FreeItemData( MENUITEM* item )
600 /* delete text */
601 if (IS_STRING_ITEM(item->fType) && item->text)
602 HeapFree( GetProcessHeap(), 0, item->text );
605 /***********************************************************************
606 * MENU_FindItemByCoords
608 * Find the item at the specified coordinates (screen coords). Does
609 * not work for child windows and therefore should not be called for
610 * an arbitrary system menu.
612 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
613 POINT pt, UINT *pos )
615 MENUITEM *item;
616 UINT i;
617 RECT wrect;
619 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
620 pt.x -= wrect.left;pt.y -= wrect.top;
621 item = menu->items;
622 for (i = 0; i < menu->nItems; i++, item++)
624 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
625 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
627 if (pos) *pos = i;
628 return item;
631 return NULL;
635 /***********************************************************************
636 * MENU_FindItemByKey
638 * Find the menu item selected by a key press.
639 * Return item id, -1 if none, -2 if we should close the menu.
641 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
642 UINT key, BOOL forceMenuChar )
644 TRACE("\tlooking for '%c' in [%04x]\n", (char)key, (UINT16)hmenu );
646 if (!IsMenu( hmenu ))
648 WND* w = WIN_FindWndPtr(hwndOwner);
649 hmenu = GetSubMenu(w->hSysMenu, 0);
650 WIN_ReleaseWndPtr(w);
653 if (hmenu)
655 POPUPMENU *menu = MENU_GetMenu( hmenu );
656 MENUITEM *item = menu->items;
657 LONG menuchar;
659 if( !forceMenuChar )
661 UINT i;
663 key = toupper(key);
664 for (i = 0; i < menu->nItems; i++, item++)
666 if (item->text && (IS_STRING_ITEM(item->fType)))
668 WCHAR *p = item->text - 2;
671 p = strchrW (p + 2, '&');
673 while (p != NULL && p [1] == '&');
674 if (p && (toupper(p[1]) == key)) return i;
678 menuchar = SendMessageA( hwndOwner, WM_MENUCHAR,
679 MAKEWPARAM( key, menu->wFlags ), hmenu );
680 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
681 if (HIWORD(menuchar) == 1) return (UINT)(-2);
683 return (UINT)(-1);
685 /***********************************************************************
686 * MENU_LoadMagicItem
688 * Load the bitmap associated with the magic menu item and its style
691 static HBITMAP MENU_LoadMagicItem(UINT id, BOOL hilite, DWORD dwItemData)
694 * Magic menu item id's section
695 * These magic id's are used by windows to insert "standard" mdi
696 * buttons (minimize,restore,close) on menu. Under windows,
697 * these magic id's make sure the right things appear when those
698 * bitmap buttons are pressed/selected/released.
701 switch(id & 0xffff)
702 { case HBMMENU_SYSTEM:
703 return (dwItemData) ?
704 (HBITMAP)dwItemData :
705 (hilite ? hBmpMinimizeD : hBmpMinimize);
706 case HBMMENU_MBAR_RESTORE:
707 return (hilite ? hBmpMaximizeD: hBmpMaximize);
708 case HBMMENU_MBAR_MINIMIZE:
709 return (hilite ? hBmpMinimizeD : hBmpMinimize);
710 case HBMMENU_MBAR_CLOSE:
711 return (hilite ? hBmpCloseD : hBmpClose);
712 case HBMMENU_CALLBACK:
713 case HBMMENU_MBAR_CLOSE_D:
714 case HBMMENU_MBAR_MINIMIZE_D:
715 case HBMMENU_POPUP_CLOSE:
716 case HBMMENU_POPUP_RESTORE:
717 case HBMMENU_POPUP_MAXIMIZE:
718 case HBMMENU_POPUP_MINIMIZE:
719 default:
720 FIXME("Magic 0x%08x not implemented\n", id);
721 return 0;
726 /***********************************************************************
727 * MENU_CalcItemSize
729 * Calculate the size of the menu item and store it in lpitem->rect.
731 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
732 INT orgX, INT orgY, BOOL menuBar )
734 WCHAR *p;
735 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
737 TRACE("dc=0x%04x owner=0x%04x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
738 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
739 (menuBar ? " (MenuBar)" : ""));
741 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
743 if (lpitem->fType & MF_OWNERDRAW)
746 ** Experimentation under Windows reveals that an owner-drawn
747 ** menu is expected to return the size of the content part of
748 ** the menu item, not including the checkmark nor the submenu
749 ** arrow. Windows adds those values itself and returns the
750 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
752 MEASUREITEMSTRUCT mis;
753 mis.CtlType = ODT_MENU;
754 mis.CtlID = 0;
755 mis.itemID = lpitem->wID;
756 mis.itemData = (DWORD)lpitem->dwItemData;
757 mis.itemHeight = 0;
758 mis.itemWidth = 0;
759 SendMessageA( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
760 lpitem->rect.right += mis.itemWidth;
762 if (menuBar)
764 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
767 /* under at least win95 you seem to be given a standard
768 height for the menu and the height value is ignored */
770 if (TWEAK_WineLook == WIN31_LOOK)
771 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
772 else
773 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
775 else
776 lpitem->rect.bottom += mis.itemHeight;
778 TRACE("id=%04x size=%dx%d\n",
779 lpitem->wID, mis.itemWidth, mis.itemHeight);
780 /* Fall through to get check/arrow width calculation. */
783 if (lpitem->fType & MF_SEPARATOR)
785 lpitem->rect.bottom += SEPARATOR_HEIGHT;
786 return;
789 if (!menuBar)
791 lpitem->rect.right += 2 * check_bitmap_width;
792 if (lpitem->fType & MF_POPUP)
793 lpitem->rect.right += arrow_bitmap_width;
796 if (lpitem->fType & MF_OWNERDRAW)
797 return;
799 if (IS_BITMAP_ITEM(lpitem->fType))
801 BITMAP bm;
802 HBITMAP resBmp = 0;
804 /* Check if there is a magic menu item associated with this item */
805 if (IS_MAGIC_ITEM(lpitem->text))
807 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fType & MF_HILITE),
808 lpitem->dwItemData);
810 else
811 resBmp = (HBITMAP)lpitem->text;
813 if (GetObjectA(resBmp, sizeof(bm), &bm ))
815 lpitem->rect.right += bm.bmWidth;
816 lpitem->rect.bottom += bm.bmHeight;
817 if (TWEAK_WineLook == WIN98_LOOK) {
818 /* Leave space for the sunken border */
819 lpitem->rect.right += 2;
820 lpitem->rect.bottom += 2;
827 /* it must be a text item - unless it's the system menu */
828 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
829 { SIZE size;
831 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
833 lpitem->rect.right += size.cx;
834 if (TWEAK_WineLook == WIN31_LOOK)
835 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
836 else
837 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
838 lpitem->xTab = 0;
840 if (menuBar)
842 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
844 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
846 /* Item contains a tab (only meaningful in popup menus) */
847 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
848 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
849 lpitem->rect.right += MENU_TAB_SPACE;
851 else
853 if (strchrW( lpitem->text, '\b' ))
854 lpitem->rect.right += MENU_TAB_SPACE;
855 lpitem->xTab = lpitem->rect.right - check_bitmap_width
856 - arrow_bitmap_width;
859 TRACE("(%d,%d)-(%d,%d)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
863 /***********************************************************************
864 * MENU_PopupMenuCalcSize
866 * Calculate the size of a popup menu.
868 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
870 MENUITEM *lpitem;
871 HDC hdc;
872 int start, i;
873 int orgX, orgY, maxX, maxTab, maxTabWidth;
875 lppop->Width = lppop->Height = 0;
876 if (lppop->nItems == 0) return;
877 hdc = GetDC( 0 );
879 SelectObject( hdc, hMenuFont);
881 start = 0;
882 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
884 while (start < lppop->nItems)
886 lpitem = &lppop->items[start];
887 orgX = maxX;
888 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
890 maxTab = maxTabWidth = 0;
892 /* Parse items until column break or end of menu */
893 for (i = start; i < lppop->nItems; i++, lpitem++)
895 if ((i != start) &&
896 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
898 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
900 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
901 maxX = max( maxX, lpitem->rect.right );
902 orgY = lpitem->rect.bottom;
903 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
905 maxTab = max( maxTab, lpitem->xTab );
906 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
910 /* Finish the column (set all items to the largest width found) */
911 maxX = max( maxX, maxTab + maxTabWidth );
912 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
914 lpitem->rect.right = maxX;
915 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
916 lpitem->xTab = maxTab;
919 lppop->Height = max( lppop->Height, orgY );
922 lppop->Width = maxX;
924 /* space for 3d border */
925 if(TWEAK_WineLook > WIN31_LOOK)
927 lppop->Height += 2;
928 lppop->Width += 2;
931 ReleaseDC( 0, hdc );
935 /***********************************************************************
936 * MENU_MenuBarCalcSize
938 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
939 * height is off by 1 pixel which causes lengthy window relocations when
940 * active document window is maximized/restored.
942 * Calculate the size of the menu bar.
944 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
945 LPPOPUPMENU lppop, HWND hwndOwner )
947 MENUITEM *lpitem;
948 int start, i, orgX, orgY, maxY, helpPos;
950 if ((lprect == NULL) || (lppop == NULL)) return;
951 if (lppop->nItems == 0) return;
952 TRACE("left=%d top=%d right=%d bottom=%d\n",
953 lprect->left, lprect->top, lprect->right, lprect->bottom);
954 lppop->Width = lprect->right - lprect->left;
955 lppop->Height = 0;
956 maxY = lprect->top+1;
957 start = 0;
958 helpPos = -1;
959 while (start < lppop->nItems)
961 lpitem = &lppop->items[start];
962 orgX = lprect->left;
963 orgY = maxY;
965 /* Parse items until line break or end of menu */
966 for (i = start; i < lppop->nItems; i++, lpitem++)
968 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
969 if ((i != start) &&
970 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
972 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
973 orgX, orgY );
974 debug_print_menuitem (" item: ", lpitem, "");
975 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
977 if (lpitem->rect.right > lprect->right)
979 if (i != start) break;
980 else lpitem->rect.right = lprect->right;
982 maxY = max( maxY, lpitem->rect.bottom );
983 orgX = lpitem->rect.right;
986 /* Finish the line (set all items to the largest height found) */
987 while (start < i) lppop->items[start++].rect.bottom = maxY;
990 lprect->bottom = maxY;
991 lppop->Height = lprect->bottom - lprect->top;
993 /* Flush right all items between the MF_RIGHTJUSTIFY and */
994 /* the last item (if several lines, only move the last line) */
995 lpitem = &lppop->items[lppop->nItems-1];
996 orgY = lpitem->rect.top;
997 orgX = lprect->right;
998 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
999 if ( (helpPos==-1) || (helpPos>i) )
1000 break; /* done */
1001 if (lpitem->rect.top != orgY) break; /* Other line */
1002 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1003 lpitem->rect.left += orgX - lpitem->rect.right;
1004 lpitem->rect.right = orgX;
1005 orgX = lpitem->rect.left;
1009 /***********************************************************************
1010 * MENU_DrawMenuItem
1012 * Draw a single menu item.
1014 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1015 UINT height, BOOL menuBar, UINT odaction )
1017 RECT rect;
1019 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1021 if (lpitem->fType & MF_SYSMENU)
1023 if( !IsIconic(hwnd) ) {
1024 if (TWEAK_WineLook > WIN31_LOOK)
1025 NC_DrawSysButton95( hwnd, hdc,
1026 lpitem->fState &
1027 (MF_HILITE | MF_MOUSESELECT) );
1028 else
1029 NC_DrawSysButton( hwnd, hdc,
1030 lpitem->fState &
1031 (MF_HILITE | MF_MOUSESELECT) );
1034 return;
1037 if (lpitem->fType & MF_OWNERDRAW)
1040 ** Experimentation under Windows reveals that an owner-drawn
1041 ** menu is given the rectangle which includes the space it requested
1042 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1043 ** and a popup-menu arrow. This is the value of lpitem->rect.
1044 ** Windows will leave all drawing to the application except for
1045 ** the popup-menu arrow. Windows always draws that itself, after
1046 ** the menu owner has finished drawing.
1048 DRAWITEMSTRUCT dis;
1050 dis.CtlType = ODT_MENU;
1051 dis.CtlID = 0;
1052 dis.itemID = lpitem->wID;
1053 dis.itemData = (DWORD)lpitem->dwItemData;
1054 dis.itemState = 0;
1055 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1056 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED;
1057 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1058 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1059 dis.hwndItem = hmenu;
1060 dis.hDC = hdc;
1061 dis.rcItem = lpitem->rect;
1062 TRACE("Ownerdraw: owner=%04x itemID=%d, itemState=%d, itemAction=%d, "
1063 "hwndItem=%04x, hdc=%04x, rcItem={%d,%d,%d,%d}\n", hwndOwner,
1064 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1065 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1066 dis.rcItem.bottom);
1067 SendMessageA( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1068 /* Fall through to draw popup-menu arrow */
1071 TRACE("rect={%d,%d,%d,%d}\n", lpitem->rect.left, lpitem->rect.top,
1072 lpitem->rect.right,lpitem->rect.bottom);
1074 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1076 rect = lpitem->rect;
1078 if (!(lpitem->fType & MF_OWNERDRAW))
1080 if (lpitem->fState & MF_HILITE)
1082 if(TWEAK_WineLook == WIN98_LOOK)
1084 if(menuBar)
1085 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1086 else
1087 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1089 else /* Not Win98 Look */
1091 if(!IS_BITMAP_ITEM(lpitem->fType))
1092 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1095 else
1096 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1099 SetBkMode( hdc, TRANSPARENT );
1101 if (!(lpitem->fType & MF_OWNERDRAW))
1103 /* vertical separator */
1104 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1106 if (TWEAK_WineLook > WIN31_LOOK)
1108 RECT rc = rect;
1109 rc.top = 3;
1110 rc.bottom = height - 3;
1111 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1113 else
1115 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1116 MoveToEx( hdc, rect.left, 0, NULL );
1117 LineTo( hdc, rect.left, height );
1121 /* horizontal separator */
1122 if (lpitem->fType & MF_SEPARATOR)
1124 if (TWEAK_WineLook > WIN31_LOOK)
1126 RECT rc = rect;
1127 rc.left++;
1128 rc.right--;
1129 rc.top += SEPARATOR_HEIGHT / 2;
1130 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1132 else
1134 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1135 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1136 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1138 return;
1142 /* Setup colors */
1144 if (lpitem->fState & MF_HILITE)
1146 if(TWEAK_WineLook == WIN98_LOOK)
1148 if(menuBar) {
1149 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1150 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1151 } else {
1152 if(lpitem->fState & MF_GRAYED)
1153 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1154 else
1155 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1156 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1159 else /* Not Win98 Look */
1161 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1162 if(!IS_BITMAP_ITEM(lpitem->fType))
1163 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1166 else
1168 if (lpitem->fState & MF_GRAYED)
1169 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1170 else
1171 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1172 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1175 /* helper lines for debugging */
1176 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1177 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1178 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1179 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1182 if (!menuBar)
1184 INT y = rect.top + rect.bottom;
1185 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1186 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1188 if (!(lpitem->fType & MF_OWNERDRAW))
1190 /* Draw the check mark
1192 * FIXME:
1193 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1195 HBITMAP bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1196 if (bm) /* we have a custom bitmap */
1198 HDC hdcMem = CreateCompatibleDC( hdc );
1199 SelectObject( hdcMem, bm );
1200 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1201 check_bitmap_width, check_bitmap_height,
1202 hdcMem, 0, 0, SRCCOPY );
1203 DeleteDC( hdcMem );
1205 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1207 RECT r;
1208 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1209 HDC hdcMem = CreateCompatibleDC( hdc );
1210 SelectObject( hdcMem, bm );
1211 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1212 DrawFrameControl( hdcMem, &r, DFC_MENU,
1213 (lpitem->fType & MFT_RADIOCHECK) ?
1214 DFCS_MENUBULLET : DFCS_MENUCHECK );
1215 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1216 hdcMem, 0, 0, SRCCOPY );
1217 DeleteDC( hdcMem );
1218 DeleteObject( bm );
1222 /* Draw the popup-menu arrow */
1223 if (lpitem->fType & MF_POPUP)
1225 HDC hdcMem = CreateCompatibleDC( hdc );
1226 HBITMAP hOrigBitmap;
1228 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1229 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1230 (y - arrow_bitmap_height) / 2,
1231 arrow_bitmap_width, arrow_bitmap_height,
1232 hdcMem, 0, 0, SRCCOPY );
1233 SelectObject( hdcMem, hOrigBitmap );
1234 DeleteDC( hdcMem );
1237 rect.left += check_bitmap_width;
1238 rect.right -= arrow_bitmap_width;
1241 /* Done for owner-drawn */
1242 if (lpitem->fType & MF_OWNERDRAW)
1243 return;
1245 /* Draw the item text or bitmap */
1246 if (IS_BITMAP_ITEM(lpitem->fType))
1248 int left,top,w,h;
1249 DWORD rop;
1251 HBITMAP resBmp = 0;
1253 HDC hdcMem = CreateCompatibleDC( hdc );
1256 * Check if there is a magic menu item associated with this item
1257 * and load the appropriate bitmap
1259 if (IS_MAGIC_ITEM(lpitem->text))
1261 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fState & MF_HILITE),
1262 lpitem->dwItemData);
1264 else
1265 resBmp = (HBITMAP)lpitem->text;
1267 if (resBmp)
1269 BITMAP bm;
1270 GetObjectA( resBmp, sizeof(bm), &bm );
1272 SelectObject(hdcMem,resBmp );
1274 /* handle fontsize > bitmap_height */
1275 h=rect.bottom - rect.top;
1276 top = (h>bm.bmHeight) ?
1277 rect.top+(h-bm.bmHeight)/2 : rect.top;
1278 w=rect.right - rect.left;
1279 left=rect.left;
1280 if (TWEAK_WineLook == WIN95_LOOK) {
1281 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
1282 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
1283 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1284 } else {
1285 left++;
1286 w-=2;
1287 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text) && (!menuBar)) ? MERGEPAINT : SRCCOPY;
1289 BitBlt( hdc, left, top, w,
1290 h, hdcMem, 0, 0,
1291 rop);
1293 DeleteDC( hdcMem );
1295 return;
1298 /* No bitmap - process text if present */
1299 else if (IS_STRING_ITEM(lpitem->fType))
1301 register int i;
1302 HFONT hfontOld = 0;
1304 UINT uFormat = (menuBar) ?
1305 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1306 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1308 if ( lpitem->fState & MFS_DEFAULT )
1310 hfontOld = SelectObject( hdc, hMenuFontBold);
1313 if (menuBar)
1315 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1316 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1317 i = strlenW( lpitem->text );
1319 else
1321 for (i = 0; lpitem->text[i]; i++)
1322 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1323 break;
1326 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1328 if (!(lpitem->fState & MF_HILITE) )
1330 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1331 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1332 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1333 --rect.left; --rect.top; --rect.right; --rect.bottom;
1335 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1338 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1340 /* paint the shortcut text */
1341 if (lpitem->text[i]) /* There's a tab or flush-right char */
1343 if (lpitem->text[i] == '\t')
1345 rect.left = lpitem->xTab;
1346 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1348 else
1350 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1353 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1355 if (!(lpitem->fState & MF_HILITE) )
1357 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1358 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1359 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1360 --rect.left; --rect.top; --rect.right; --rect.bottom;
1362 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1364 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1367 if (hfontOld)
1368 SelectObject (hdc, hfontOld);
1373 /***********************************************************************
1374 * MENU_DrawPopupMenu
1376 * Paint a popup menu.
1378 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1380 HBRUSH hPrevBrush = 0;
1381 RECT rect;
1383 TRACE("wnd=0x%04x dc=0x%04x menu=0x%04x\n", hwnd, hdc, hmenu);
1385 GetClientRect( hwnd, &rect );
1387 if(TWEAK_WineLook == WIN31_LOOK)
1389 rect.bottom -= POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1390 rect.right -= POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER);
1393 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1394 && (SelectObject( hdc, hMenuFont)))
1396 HPEN hPrevPen;
1398 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1400 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1401 if( hPrevPen )
1403 INT ropPrev, i;
1404 POPUPMENU *menu;
1406 /* draw 3-d shade */
1407 if(TWEAK_WineLook == WIN31_LOOK) {
1408 SelectObject( hdc, hShadeBrush );
1409 SetBkMode( hdc, TRANSPARENT );
1410 ropPrev = SetROP2( hdc, R2_MASKPEN );
1412 i = rect.right; /* why SetBrushOrg() doesn't? */
1413 PatBlt( hdc, i & 0xfffffffe,
1414 rect.top + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER),
1415 i%2 + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1416 rect.bottom - rect.top, 0x00a000c9 );
1417 i = rect.bottom;
1418 PatBlt( hdc, rect.left + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1419 i & 0xfffffffe,rect.right - rect.left,
1420 i%2 + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER), 0x00a000c9 );
1421 SelectObject( hdc, hPrevPen );
1422 SelectObject( hdc, hPrevBrush );
1423 SetROP2( hdc, ropPrev );
1425 else
1426 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1428 /* draw menu items */
1430 menu = MENU_GetMenu( hmenu );
1431 if (menu && menu->nItems)
1433 MENUITEM *item;
1434 UINT u;
1436 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1437 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1438 menu->Height, FALSE, ODA_DRAWENTIRE );
1441 } else
1443 SelectObject( hdc, hPrevBrush );
1448 /***********************************************************************
1449 * MENU_DrawMenuBar
1451 * Paint a menu bar. Returns the height of the menu bar.
1452 * called from [windows/nonclient.c]
1454 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1455 BOOL suppress_draw)
1457 LPPOPUPMENU lppop;
1458 UINT i,retvalue;
1459 HFONT hfontOld = 0;
1461 WND *wndPtr = WIN_FindWndPtr( hwnd );
1463 lppop = MENU_GetMenu ((HMENU)wndPtr->wIDmenu );
1464 if (lppop == NULL || lprect == NULL)
1466 retvalue = GetSystemMetrics(SM_CYMENU);
1467 goto END;
1470 TRACE("(%04x, %p, %p)\n", hDC, lprect, lppop);
1472 hfontOld = SelectObject( hDC, hMenuFont);
1474 if (lppop->Height == 0)
1475 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1477 lprect->bottom = lprect->top + lppop->Height;
1479 if (suppress_draw)
1481 retvalue = lppop->Height;
1482 goto END;
1485 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
1487 if (TWEAK_WineLook == WIN31_LOOK)
1489 SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1490 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1491 LineTo( hDC, lprect->right, lprect->bottom );
1493 else
1495 SelectObject( hDC, GetSysColorPen(COLOR_3DFACE));
1496 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1497 LineTo( hDC, lprect->right, lprect->bottom );
1500 if (lppop->nItems == 0)
1502 retvalue = GetSystemMetrics(SM_CYMENU);
1503 goto END;
1506 for (i = 0; i < lppop->nItems; i++)
1508 MENU_DrawMenuItem( hwnd, (HMENU)wndPtr->wIDmenu, hwnd,
1509 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
1511 retvalue = lppop->Height;
1513 END:
1514 if (hfontOld)
1515 SelectObject (hDC, hfontOld);
1517 WIN_ReleaseWndPtr(wndPtr);
1518 return retvalue;
1522 /***********************************************************************
1523 * MENU_ShowPopup
1525 * Display a popup menu.
1527 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1528 INT x, INT y, INT xanchor, INT yanchor )
1530 POPUPMENU *menu;
1531 WND *wndOwner = NULL;
1533 TRACE("owner=0x%04x hmenu=0x%04x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1534 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1536 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1537 if (menu->FocusedItem != NO_SELECTED_ITEM)
1539 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1540 menu->FocusedItem = NO_SELECTED_ITEM;
1543 /* store the owner for DrawItem */
1544 menu->hwndOwner = hwndOwner;
1546 if( (wndOwner = WIN_FindWndPtr( hwndOwner )) )
1548 UINT width, height;
1550 MENU_PopupMenuCalcSize( menu, hwndOwner );
1552 /* adjust popup menu pos so that it fits within the desktop */
1554 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1555 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1557 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1559 if( xanchor )
1560 x -= width - xanchor;
1561 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1562 x = GetSystemMetrics(SM_CXSCREEN) - width;
1564 if( x < 0 ) x = 0;
1566 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1568 if( yanchor )
1569 y -= height + yanchor;
1570 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1571 y = GetSystemMetrics(SM_CYSCREEN) - height;
1573 if( y < 0 ) y = 0;
1575 if( TWEAK_WineLook == WIN31_LOOK )
1577 width += POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER); /* add space for shading */
1578 height += POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1581 /* NOTE: In Windows, top menu popup is not owned. */
1582 menu->hWnd = CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1583 WS_POPUP, x, y, width, height,
1584 hwndOwner, 0, wndOwner->hInstance,
1585 (LPVOID)hmenu );
1586 if( !menu->hWnd )
1588 WIN_ReleaseWndPtr(wndOwner);
1589 return FALSE;
1591 if (!top_popup) top_popup = menu->hWnd;
1593 /* Display the window */
1595 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1596 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1597 UpdateWindow( menu->hWnd );
1598 WIN_ReleaseWndPtr(wndOwner);
1599 return TRUE;
1601 return FALSE;
1605 /***********************************************************************
1606 * MENU_SelectItem
1608 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1609 BOOL sendMenuSelect, HMENU topmenu )
1611 LPPOPUPMENU lppop;
1612 HDC hdc;
1614 TRACE("owner=0x%04x menu=0x%04x index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1616 lppop = MENU_GetMenu( hmenu );
1617 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1619 if (lppop->FocusedItem == wIndex) return;
1620 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1621 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1623 SelectObject( hdc, hMenuFont);
1625 /* Clear previous highlighted item */
1626 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1628 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1629 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1630 lppop->Height, !(lppop->wFlags & MF_POPUP),
1631 ODA_SELECT );
1634 /* Highlight new item (if any) */
1635 lppop->FocusedItem = wIndex;
1636 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1638 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1639 lppop->items[wIndex].fState |= MF_HILITE;
1640 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1641 &lppop->items[wIndex], lppop->Height,
1642 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1644 if (sendMenuSelect)
1646 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1647 SendMessageA( hwndOwner, WM_MENUSELECT,
1648 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1649 ip->fType | ip->fState | MF_MOUSESELECT |
1650 (lppop->wFlags & MF_SYSMENU)), hmenu);
1653 else if (sendMenuSelect) {
1654 if(topmenu){
1655 int pos;
1656 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1657 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1658 MENUITEM *ip = &ptm->items[pos];
1659 SendMessageA( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1660 ip->fType | ip->fState | MF_MOUSESELECT |
1661 (ptm->wFlags & MF_SYSMENU)), topmenu);
1665 ReleaseDC( lppop->hWnd, hdc );
1669 /***********************************************************************
1670 * MENU_MoveSelection
1672 * Moves currently selected item according to the offset parameter.
1673 * If there is no selection then it should select the last item if
1674 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1676 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1678 INT i;
1679 POPUPMENU *menu;
1681 TRACE("hwnd=0x%04x hmenu=0x%04x off=0x%04x\n", hwndOwner, hmenu, offset);
1683 menu = MENU_GetMenu( hmenu );
1684 if ((!menu) || (!menu->items)) return;
1686 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1688 if( menu->nItems == 1 ) return; else
1689 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1690 ; i += offset)
1691 if (!(menu->items[i].fType & MF_SEPARATOR))
1693 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1694 return;
1698 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1699 i >= 0 && i < menu->nItems ; i += offset)
1700 if (!(menu->items[i].fType & MF_SEPARATOR))
1702 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1703 return;
1708 /**********************************************************************
1709 * MENU_SetItemData
1711 * Set an item flags, id and text ptr. Called by InsertMenu() and
1712 * ModifyMenu().
1714 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT id,
1715 LPCWSTR str )
1717 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1719 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1720 TRACE("flags=%x str=%p\n", flags, str);
1722 if (IS_STRING_ITEM(flags))
1724 if (!str)
1726 flags |= MF_SEPARATOR;
1727 item->text = NULL;
1729 else
1731 LPWSTR text;
1732 /* Item beginning with a backspace is a help item */
1733 if (*str == '\b')
1735 flags |= MF_HELP;
1736 str++;
1738 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1739 return FALSE;
1740 strcpyW( text, str );
1741 item->text = text;
1744 else if (IS_BITMAP_ITEM(flags))
1745 item->text = (LPWSTR)(HBITMAP)LOWORD(str);
1746 else item->text = NULL;
1748 if (flags & MF_OWNERDRAW)
1749 item->dwItemData = (DWORD)str;
1750 else
1751 item->dwItemData = 0;
1753 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != id) )
1754 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1756 if (flags & MF_POPUP)
1758 POPUPMENU *menu = MENU_GetMenu((UINT16)id);
1759 if (menu) menu->wFlags |= MF_POPUP;
1760 else
1762 item->wID = 0;
1763 item->hSubMenu = 0;
1764 item->fType = 0;
1765 item->fState = 0;
1766 return FALSE;
1770 item->wID = id;
1771 if (flags & MF_POPUP)
1772 item->hSubMenu = id;
1774 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1775 flags |= MF_POPUP; /* keep popup */
1777 item->fType = flags & TYPE_MASK;
1778 item->fState = (flags & STATE_MASK) &
1779 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1782 /* Don't call SetRectEmpty here! */
1785 if (prevText) HeapFree( GetProcessHeap(), 0, prevText );
1787 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1788 return TRUE;
1792 /**********************************************************************
1793 * MENU_InsertItem
1795 * Insert a new item into a menu.
1797 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1799 MENUITEM *newItems;
1800 POPUPMENU *menu;
1802 if (!(menu = MENU_GetMenu(hMenu)))
1803 return NULL;
1805 /* Find where to insert new item */
1807 if (flags & MF_BYPOSITION) {
1808 if (pos > menu->nItems)
1809 pos = menu->nItems;
1810 } else {
1811 if (!MENU_FindItem( &hMenu, &pos, flags ))
1812 pos = menu->nItems;
1813 else {
1814 if (!(menu = MENU_GetMenu( hMenu )))
1815 return NULL;
1819 /* Create new items array */
1821 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1822 if (!newItems)
1824 WARN("allocation failed\n" );
1825 return NULL;
1827 if (menu->nItems > 0)
1829 /* Copy the old array into the new one */
1830 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1831 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1832 (menu->nItems-pos)*sizeof(MENUITEM) );
1833 HeapFree( GetProcessHeap(), 0, menu->items );
1835 menu->items = newItems;
1836 menu->nItems++;
1837 memset( &newItems[pos], 0, sizeof(*newItems) );
1838 menu->Height = 0; /* force size recalculate */
1839 return &newItems[pos];
1843 /**********************************************************************
1844 * MENU_ParseResource
1846 * Parse a standard menu resource and add items to the menu.
1847 * Return a pointer to the end of the resource.
1849 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1851 WORD flags, id = 0;
1852 LPCSTR str;
1856 flags = GET_WORD(res);
1857 res += sizeof(WORD);
1858 if (!(flags & MF_POPUP))
1860 id = GET_WORD(res);
1861 res += sizeof(WORD);
1863 if (!IS_STRING_ITEM(flags))
1864 ERR("not a string item %04x\n", flags );
1865 str = res;
1866 if (!unicode) res += strlen(str) + 1;
1867 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1868 if (flags & MF_POPUP)
1870 HMENU hSubMenu = CreatePopupMenu();
1871 if (!hSubMenu) return NULL;
1872 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1873 return NULL;
1874 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1875 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1877 else /* Not a popup */
1879 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1880 else AppendMenuW( hMenu, flags, id,
1881 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1883 } while (!(flags & MF_END));
1884 return res;
1888 /**********************************************************************
1889 * MENUEX_ParseResource
1891 * Parse an extended menu resource and add items to the menu.
1892 * Return a pointer to the end of the resource.
1894 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1896 WORD resinfo;
1897 do {
1898 MENUITEMINFOW mii;
1900 mii.cbSize = sizeof(mii);
1901 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1902 mii.fType = GET_DWORD(res);
1903 res += sizeof(DWORD);
1904 mii.fState = GET_DWORD(res);
1905 res += sizeof(DWORD);
1906 mii.wID = GET_DWORD(res);
1907 res += sizeof(DWORD);
1908 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1909 res += sizeof(WORD);
1910 /* Align the text on a word boundary. */
1911 res += (~((int)res - 1)) & 1;
1912 mii.dwTypeData = (LPWSTR) res;
1913 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1914 /* Align the following fields on a dword boundary. */
1915 res += (~((int)res - 1)) & 3;
1917 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1918 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1920 if (resinfo & 1) { /* Pop-up? */
1921 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1922 res += sizeof(DWORD);
1923 mii.hSubMenu = CreatePopupMenu();
1924 if (!mii.hSubMenu)
1925 return NULL;
1926 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1927 DestroyMenu(mii.hSubMenu);
1928 return NULL;
1930 mii.fMask |= MIIM_SUBMENU;
1931 mii.fType |= MF_POPUP;
1933 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1934 } while (!(resinfo & MF_END));
1935 return res;
1939 /***********************************************************************
1940 * MENU_GetSubPopup
1942 * Return the handle of the selected sub-popup menu (if any).
1944 static HMENU MENU_GetSubPopup( HMENU hmenu )
1946 POPUPMENU *menu;
1947 MENUITEM *item;
1949 menu = MENU_GetMenu( hmenu );
1951 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1953 item = &menu->items[menu->FocusedItem];
1954 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1955 return item->hSubMenu;
1956 return 0;
1960 /***********************************************************************
1961 * MENU_HideSubPopups
1963 * Hide the sub-popup menus of this menu.
1965 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1966 BOOL sendMenuSelect )
1968 POPUPMENU *menu = MENU_GetMenu( hmenu );
1970 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1972 if (menu && top_popup)
1974 HMENU hsubmenu;
1975 POPUPMENU *submenu;
1976 MENUITEM *item;
1978 if (menu->FocusedItem != NO_SELECTED_ITEM)
1980 item = &menu->items[menu->FocusedItem];
1981 if (!(item->fType & MF_POPUP) ||
1982 !(item->fState & MF_MOUSESELECT)) return;
1983 item->fState &= ~MF_MOUSESELECT;
1984 hsubmenu = item->hSubMenu;
1985 } else return;
1987 submenu = MENU_GetMenu( hsubmenu );
1988 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1989 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
1990 DestroyWindow( submenu->hWnd );
1991 submenu->hWnd = 0;
1996 /***********************************************************************
1997 * MENU_ShowSubPopup
1999 * Display the sub-menu of the selected item of this menu.
2000 * Return the handle of the submenu, or hmenu if no submenu to display.
2002 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2003 BOOL selectFirst, UINT wFlags )
2005 RECT rect;
2006 POPUPMENU *menu;
2007 MENUITEM *item;
2008 WND *wndPtr;
2009 HDC hdc;
2011 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, selectFirst);
2013 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2015 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd )) ||
2016 (menu->FocusedItem == NO_SELECTED_ITEM))
2018 WIN_ReleaseWndPtr(wndPtr);
2019 return hmenu;
2022 item = &menu->items[menu->FocusedItem];
2023 if (!(item->fType & MF_POPUP) ||
2024 (item->fState & (MF_GRAYED | MF_DISABLED)))
2026 WIN_ReleaseWndPtr(wndPtr);
2027 return hmenu;
2030 /* message must be sent before using item,
2031 because nearly everything may be changed by the application ! */
2033 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2034 if (!(wFlags & TPM_NONOTIFY))
2035 SendMessageA( hwndOwner, WM_INITMENUPOPUP, item->hSubMenu,
2036 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2038 item = &menu->items[menu->FocusedItem];
2039 rect = item->rect;
2041 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2042 if (!(item->fState & MF_HILITE))
2044 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2045 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2047 SelectObject( hdc, hMenuFont);
2049 item->fState |= MF_HILITE;
2050 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2051 ReleaseDC( menu->hWnd, hdc );
2053 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2054 item->rect = rect;
2056 item->fState |= MF_MOUSESELECT;
2058 if (IS_SYSTEM_MENU(menu))
2060 MENU_InitSysMenuPopup(item->hSubMenu, wndPtr->dwStyle, GetClassLongA(wndPtr->hwndSelf, GCL_STYLE));
2062 NC_GetSysPopupPos( wndPtr, &rect );
2063 rect.top = rect.bottom;
2064 rect.right = GetSystemMetrics(SM_CXSIZE);
2065 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2067 else
2069 if (menu->wFlags & MF_POPUP)
2071 rect.left = wndPtr->rectWindow.left + item->rect.right - GetSystemMetrics(SM_CXBORDER);
2072 rect.top = wndPtr->rectWindow.top + item->rect.top;
2073 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2074 rect.bottom = item->rect.top - item->rect.bottom;
2076 else
2078 rect.left = wndPtr->rectWindow.left + item->rect.left;
2079 rect.top = wndPtr->rectWindow.top + item->rect.bottom;
2080 rect.right = item->rect.right - item->rect.left;
2081 rect.bottom = item->rect.bottom - item->rect.top;
2085 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2086 rect.left, rect.top, rect.right, rect.bottom );
2087 if (selectFirst)
2088 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2089 WIN_ReleaseWndPtr(wndPtr);
2090 return item->hSubMenu;
2095 /**********************************************************************
2096 * MENU_IsMenuActive
2098 BOOL MENU_IsMenuActive(void)
2100 return (top_popup != 0);
2103 /***********************************************************************
2104 * MENU_PtMenu
2106 * Walks menu chain trying to find a menu pt maps to.
2108 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2110 POPUPMENU *menu = MENU_GetMenu( hMenu );
2111 register UINT ht = menu->FocusedItem;
2113 /* try subpopup first (if any) */
2114 ht = (ht != NO_SELECTED_ITEM &&
2115 (menu->items[ht].fType & MF_POPUP) &&
2116 (menu->items[ht].fState & MF_MOUSESELECT))
2117 ? (UINT) MENU_PtMenu(menu->items[ht].hSubMenu, pt) : 0;
2119 if( !ht ) /* check the current window (avoiding WM_HITTEST) */
2121 ht = (UINT)NC_HandleNCHitTest( menu->hWnd, pt );
2122 if( menu->wFlags & MF_POPUP )
2123 ht = (ht != (UINT)HTNOWHERE &&
2124 ht != (UINT)HTERROR) ? (UINT)hMenu : 0;
2125 else
2127 WND* wndPtr = WIN_FindWndPtr(menu->hWnd);
2129 ht = ( ht == HTSYSMENU ) ? (UINT)(wndPtr->hSysMenu)
2130 : ( ht == HTMENU ) ? (UINT)(wndPtr->wIDmenu) : 0;
2131 WIN_ReleaseWndPtr(wndPtr);
2134 return (HMENU)ht;
2137 /***********************************************************************
2138 * MENU_ExecFocusedItem
2140 * Execute a menu item (for instance when user pressed Enter).
2141 * Return the wID of the executed item. Otherwise, -1 indicating
2142 * that no menu item was executed;
2143 * Have to receive the flags for the TrackPopupMenu options to avoid
2144 * sending unwanted message.
2147 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2149 MENUITEM *item;
2150 POPUPMENU *menu = MENU_GetMenu( hMenu );
2152 TRACE("%p hmenu=0x%04x\n", pmt, hMenu);
2154 if (!menu || !menu->nItems ||
2155 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2157 item = &menu->items[menu->FocusedItem];
2159 TRACE("%08x %08x %08x\n",
2160 hMenu, item->wID, item->hSubMenu);
2162 if (!(item->fType & MF_POPUP))
2164 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2166 /* If TPM_RETURNCMD is set you return the id, but
2167 do not send a message to the owner */
2168 if(!(wFlags & TPM_RETURNCMD))
2170 if( menu->wFlags & MF_SYSMENU )
2171 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2172 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2173 else
2174 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2176 return item->wID;
2179 else
2180 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2182 return -1;
2185 /***********************************************************************
2186 * MENU_SwitchTracking
2188 * Helper function for menu navigation routines.
2190 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2192 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2193 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2195 TRACE("%p hmenu=0x%04x 0x%04x\n", pmt, hPtMenu, id);
2197 if( pmt->hTopMenu != hPtMenu &&
2198 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2200 /* both are top level menus (system and menu-bar) */
2201 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2202 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2203 pmt->hTopMenu = hPtMenu;
2205 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2206 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2210 /***********************************************************************
2211 * MENU_ButtonDown
2213 * Return TRUE if we can go on with menu tracking.
2215 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2217 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2219 if (hPtMenu)
2221 UINT id = 0;
2222 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2223 MENUITEM *item;
2225 if( IS_SYSTEM_MENU(ptmenu) )
2226 item = ptmenu->items;
2227 else
2228 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2230 if( item )
2232 if( ptmenu->FocusedItem != id )
2233 MENU_SwitchTracking( pmt, hPtMenu, id );
2235 /* If the popup menu is not already "popped" */
2236 if(!(item->fState & MF_MOUSESELECT ))
2238 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2240 /* In win31, a newly popped menu always remains opened for the next buttonup */
2241 if(TWEAK_WineLook == WIN31_LOOK)
2242 ptmenu->bTimeToHide = FALSE;
2245 return TRUE;
2247 /* Else the click was on the menu bar, finish the tracking */
2249 return FALSE;
2252 /***********************************************************************
2253 * MENU_ButtonUp
2255 * Return the value of MENU_ExecFocusedItem if
2256 * the selected item was not a popup. Else open the popup.
2257 * A -1 return value indicates that we go on with menu tracking.
2260 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2262 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2264 if (hPtMenu)
2266 UINT id = 0;
2267 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2268 MENUITEM *item;
2270 if( IS_SYSTEM_MENU(ptmenu) )
2271 item = ptmenu->items;
2272 else
2273 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2275 if( item && (ptmenu->FocusedItem == id ))
2277 if( !(item->fType & MF_POPUP) )
2278 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2280 /* If we are dealing with the top-level menu */
2281 /* and this is a click on an already "popped" item: */
2282 /* Stop the menu tracking and close the opened submenus */
2283 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2284 return 0;
2286 ptmenu->bTimeToHide = TRUE;
2288 return -1;
2292 /***********************************************************************
2293 * MENU_MouseMove
2295 * Return TRUE if we can go on with menu tracking.
2297 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2299 UINT id = NO_SELECTED_ITEM;
2300 POPUPMENU *ptmenu = NULL;
2302 if( hPtMenu )
2304 ptmenu = MENU_GetMenu( hPtMenu );
2305 if( IS_SYSTEM_MENU(ptmenu) )
2306 id = 0;
2307 else
2308 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2311 if( id == NO_SELECTED_ITEM )
2313 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2314 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2317 else if( ptmenu->FocusedItem != id )
2319 MENU_SwitchTracking( pmt, hPtMenu, id );
2320 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2322 return TRUE;
2326 /***********************************************************************
2327 * MENU_DoNextMenu
2329 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2331 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2333 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2335 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2336 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2338 WND* wndPtr;
2339 HMENU hNewMenu;
2340 HWND hNewWnd;
2341 UINT id = 0;
2342 LRESULT l = SendMessageA( pmt->hOwnerWnd, WM_NEXTMENU, vk,
2343 (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu );
2345 TRACE("%04x [%04x] -> %04x [%04x]\n",
2346 (UINT16)pmt->hCurrentMenu, (UINT16)pmt->hOwnerWnd, LOWORD(l), HIWORD(l) );
2348 if( l == 0 )
2350 wndPtr = WIN_FindWndPtr(pmt->hOwnerWnd);
2352 hNewWnd = pmt->hOwnerWnd;
2353 if( IS_SYSTEM_MENU(menu) )
2355 /* switch to the menu bar */
2357 if( wndPtr->dwStyle & WS_CHILD || !wndPtr->wIDmenu )
2359 WIN_ReleaseWndPtr(wndPtr);
2360 return FALSE;
2363 hNewMenu = wndPtr->wIDmenu;
2364 if( vk == VK_LEFT )
2366 menu = MENU_GetMenu( hNewMenu );
2367 id = menu->nItems - 1;
2370 else if( wndPtr->dwStyle & WS_SYSMENU )
2372 /* switch to the system menu */
2373 hNewMenu = wndPtr->hSysMenu;
2375 else
2377 WIN_ReleaseWndPtr(wndPtr);
2378 return FALSE;
2380 WIN_ReleaseWndPtr(wndPtr);
2382 else /* application returned a new menu to switch to */
2384 hNewMenu = LOWORD(l); hNewWnd = HIWORD(l);
2386 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2388 wndPtr = WIN_FindWndPtr(hNewWnd);
2390 if( wndPtr->dwStyle & WS_SYSMENU &&
2391 GetSubMenu(wndPtr->hSysMenu, 0) == hNewMenu )
2393 /* get the real system menu */
2394 hNewMenu = wndPtr->hSysMenu;
2396 else if( wndPtr->dwStyle & WS_CHILD || wndPtr->wIDmenu != hNewMenu )
2398 /* FIXME: Not sure what to do here;
2399 * perhaps try to track hNewMenu as a popup? */
2401 TRACE(" -- got confused.\n");
2402 WIN_ReleaseWndPtr(wndPtr);
2403 return FALSE;
2405 WIN_ReleaseWndPtr(wndPtr);
2407 else return FALSE;
2410 if( hNewMenu != pmt->hTopMenu )
2412 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2413 FALSE, 0 );
2414 if( pmt->hCurrentMenu != pmt->hTopMenu )
2415 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2418 if( hNewWnd != pmt->hOwnerWnd )
2420 ReleaseCapture();
2421 pmt->hOwnerWnd = hNewWnd;
2422 EVENT_Capture( pmt->hOwnerWnd, HTMENU );
2425 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2426 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2428 return TRUE;
2430 return FALSE;
2433 /***********************************************************************
2434 * MENU_SuspendPopup
2436 * The idea is not to show the popup if the next input message is
2437 * going to hide it anyway.
2439 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2441 MSG msg;
2443 msg.hwnd = pmt->hOwnerWnd;
2445 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2446 pmt->trackFlags |= TF_SKIPREMOVE;
2448 switch( uMsg )
2450 case WM_KEYDOWN:
2451 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2452 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2454 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2455 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2456 if( msg.message == WM_KEYDOWN &&
2457 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2459 pmt->trackFlags |= TF_SUSPENDPOPUP;
2460 return TRUE;
2463 break;
2466 /* failures go through this */
2467 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2468 return FALSE;
2471 /***********************************************************************
2472 * MENU_KeyEscape
2474 * Handle a VK_ESCAPE key event in a menu.
2476 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2478 BOOL bEndMenu = TRUE;
2480 if (pmt->hCurrentMenu != pmt->hTopMenu)
2482 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2484 if (menu->wFlags & MF_POPUP)
2486 HMENU hmenutmp, hmenuprev;
2488 hmenuprev = hmenutmp = pmt->hTopMenu;
2490 /* close topmost popup */
2491 while (hmenutmp != pmt->hCurrentMenu)
2493 hmenuprev = hmenutmp;
2494 hmenutmp = MENU_GetSubPopup( hmenuprev );
2497 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2498 pmt->hCurrentMenu = hmenuprev;
2499 bEndMenu = FALSE;
2503 return bEndMenu;
2506 /***********************************************************************
2507 * MENU_KeyLeft
2509 * Handle a VK_LEFT key event in a menu.
2511 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2513 POPUPMENU *menu;
2514 HMENU hmenutmp, hmenuprev;
2515 UINT prevcol;
2517 hmenuprev = hmenutmp = pmt->hTopMenu;
2518 menu = MENU_GetMenu( hmenutmp );
2520 /* Try to move 1 column left (if possible) */
2521 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2522 NO_SELECTED_ITEM ) {
2524 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2525 prevcol, TRUE, 0 );
2526 return;
2529 /* close topmost popup */
2530 while (hmenutmp != pmt->hCurrentMenu)
2532 hmenuprev = hmenutmp;
2533 hmenutmp = MENU_GetSubPopup( hmenuprev );
2536 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2537 pmt->hCurrentMenu = hmenuprev;
2539 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2541 /* move menu bar selection if no more popups are left */
2543 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2544 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2546 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2548 /* A sublevel menu was displayed - display the next one
2549 * unless there is another displacement coming up */
2551 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2552 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2553 pmt->hTopMenu, TRUE, wFlags);
2559 /***********************************************************************
2560 * MENU_KeyRight
2562 * Handle a VK_RIGHT key event in a menu.
2564 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2566 HMENU hmenutmp;
2567 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2568 UINT nextcol;
2570 TRACE("MENU_KeyRight called, cur %x (%s), top %x (%s).\n",
2571 pmt->hCurrentMenu,
2572 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->
2573 items[0].text),
2574 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2576 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2578 /* If already displaying a popup, try to display sub-popup */
2580 hmenutmp = pmt->hCurrentMenu;
2581 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2583 /* if subpopup was displayed then we are done */
2584 if (hmenutmp != pmt->hCurrentMenu) return;
2587 /* Check to see if there's another column */
2588 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2589 NO_SELECTED_ITEM ) {
2590 TRACE("Going to %d.\n", nextcol );
2591 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2592 nextcol, TRUE, 0 );
2593 return;
2596 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2598 if( pmt->hCurrentMenu != pmt->hTopMenu )
2600 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2601 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2602 } else hmenutmp = 0;
2604 /* try to move to the next item */
2605 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2606 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2608 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2609 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2610 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2611 pmt->hTopMenu, TRUE, wFlags);
2615 /***********************************************************************
2616 * MENU_TrackMenu
2618 * Menu tracking code.
2620 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2621 HWND hwnd, const RECT *lprect )
2623 MSG msg;
2624 POPUPMENU *menu;
2625 BOOL fRemove;
2626 INT executedMenuId = -1;
2627 MTRACKER mt;
2628 BOOL enterIdleSent = FALSE;
2630 mt.trackFlags = 0;
2631 mt.hCurrentMenu = hmenu;
2632 mt.hTopMenu = hmenu;
2633 mt.hOwnerWnd = hwnd;
2634 mt.pt.x = x;
2635 mt.pt.y = y;
2637 TRACE("hmenu=0x%04x flags=0x%08x (%d,%d) hwnd=0x%04x (%d,%d)-(%d,%d)\n",
2638 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2639 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2641 fEndMenu = FALSE;
2642 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2644 if (wFlags & TPM_BUTTONDOWN)
2646 /* Get the result in order to start the tracking or not */
2647 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2648 fEndMenu = !fRemove;
2651 EVENT_Capture( mt.hOwnerWnd, HTMENU );
2653 while (!fEndMenu)
2655 menu = MENU_GetMenu( mt.hCurrentMenu );
2656 if (!menu) /* sometimes happens if I do a window manager close */
2657 break;
2658 msg.hwnd = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2660 /* we have to keep the message in the queue until it's
2661 * clear that menu loop is not over yet. */
2663 if (!MSG_InternalGetMessage( &msg, msg.hwnd, mt.hOwnerWnd, 0, 0,
2664 MSGF_MENU, PM_NOREMOVE, !enterIdleSent, &enterIdleSent )) break;
2666 /* check if EndMenu() tried to cancel us, by posting this message */
2667 if(msg.message == WM_CANCELMODE)
2669 /* we are now out of the loop */
2670 fEndMenu = TRUE;
2672 /* remove the message from the queue */
2673 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2675 /* break out of internal loop, ala ESCAPE */
2676 break;
2679 TranslateMessage( &msg );
2680 mt.pt = msg.pt;
2682 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2683 enterIdleSent=FALSE;
2685 fRemove = FALSE;
2686 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2689 * use the mouse coordinates in lParam instead of those in the MSG
2690 * struct to properly handle synthetic messages. lParam coords are
2691 * relative to client area, so they must be converted; since they can
2692 * be negative, we must use SLOWORD/SHIWORD instead of LOWORD/HIWORD.
2694 mt.pt.x = SLOWORD(msg.lParam);
2695 mt.pt.y = SHIWORD(msg.lParam);
2696 ClientToScreen(msg.hwnd,&mt.pt);
2698 /* Find a menu for this mouse event */
2699 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2701 switch(msg.message)
2703 /* no WM_NC... messages in captured state */
2705 case WM_RBUTTONDBLCLK:
2706 case WM_RBUTTONDOWN:
2707 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2708 /* fall through */
2709 case WM_LBUTTONDBLCLK:
2710 case WM_LBUTTONDOWN:
2711 /* If the message belongs to the menu, removes it from the queue */
2712 /* Else, end menu tracking */
2713 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2714 fEndMenu = !fRemove;
2715 break;
2717 case WM_RBUTTONUP:
2718 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2719 /* fall through */
2720 case WM_LBUTTONUP:
2721 /* Check if a menu was selected by the mouse */
2722 if (hmenu)
2724 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2726 /* End the loop if executedMenuId is an item ID */
2727 /* or if the job was done (executedMenuId = 0). */
2728 fEndMenu = fRemove = (executedMenuId != -1);
2730 /* No menu was selected by the mouse */
2731 /* if the function was called by TrackPopupMenu, continue
2732 with the menu tracking. If not, stop it */
2733 else
2734 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2736 break;
2738 case WM_MOUSEMOVE:
2739 /* In win95 winelook, the selected menu item must be changed every time the
2740 mouse moves. In Win31 winelook, the mouse button has to be held down */
2742 if ( (TWEAK_WineLook > WIN31_LOOK) ||
2743 ( (msg.wParam & MK_LBUTTON) ||
2744 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON))) )
2746 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2748 } /* switch(msg.message) - mouse */
2750 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2752 fRemove = TRUE; /* Keyboard messages are always removed */
2753 switch(msg.message)
2755 case WM_KEYDOWN:
2756 switch(msg.wParam)
2758 case VK_HOME:
2759 case VK_END:
2760 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2761 NO_SELECTED_ITEM, FALSE, 0 );
2762 /* fall through */
2763 case VK_UP:
2764 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2765 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2766 break;
2768 case VK_DOWN: /* If on menu bar, pull-down the menu */
2770 menu = MENU_GetMenu( mt.hCurrentMenu );
2771 if (!(menu->wFlags & MF_POPUP))
2772 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2773 else /* otherwise try to move selection */
2774 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2775 break;
2777 case VK_LEFT:
2778 MENU_KeyLeft( &mt, wFlags );
2779 break;
2781 case VK_RIGHT:
2782 MENU_KeyRight( &mt, wFlags );
2783 break;
2785 case VK_ESCAPE:
2786 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2787 break;
2789 case VK_F1:
2791 HELPINFO hi;
2792 hi.cbSize = sizeof(HELPINFO);
2793 hi.iContextType = HELPINFO_MENUITEM;
2794 if (menu->FocusedItem == NO_SELECTED_ITEM)
2795 hi.iCtrlId = 0;
2796 else
2797 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2798 hi.hItemHandle = hmenu;
2799 hi.dwContextId = menu->dwContextHelpID;
2800 hi.MousePos = msg.pt;
2801 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2802 break;
2805 default:
2806 break;
2808 break; /* WM_KEYDOWN */
2810 case WM_SYSKEYDOWN:
2811 switch(msg.wParam)
2813 case VK_MENU:
2814 fEndMenu = TRUE;
2815 break;
2818 break; /* WM_SYSKEYDOWN */
2820 case WM_CHAR:
2822 UINT pos;
2824 if (msg.wParam == '\r' || msg.wParam == ' ')
2826 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2827 fEndMenu = (executedMenuId != -1);
2829 break;
2832 /* Hack to avoid control chars. */
2833 /* We will find a better way real soon... */
2834 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2836 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2837 LOWORD(msg.wParam), FALSE );
2838 if (pos == (UINT)-2) fEndMenu = TRUE;
2839 else if (pos == (UINT)-1) MessageBeep(0);
2840 else
2842 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2843 TRUE, 0 );
2844 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2845 fEndMenu = (executedMenuId != -1);
2848 break;
2849 } /* switch(msg.message) - kbd */
2851 else
2853 DispatchMessageA( &msg );
2856 if (!fEndMenu) fRemove = TRUE;
2858 /* finally remove message from the queue */
2860 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2861 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2862 else mt.trackFlags &= ~TF_SKIPREMOVE;
2865 ReleaseCapture();
2867 /* If dropdown is still painted and the close box is clicked on
2868 then the menu will be destroyed as part of the DispatchMessage above.
2869 This will then invalidate the menu handle in mt.hTopMenu. We should
2870 check for this first. */
2871 if( IsMenu( mt.hTopMenu ) )
2873 menu = MENU_GetMenu( mt.hTopMenu );
2875 if( IsWindow( mt.hOwnerWnd ) )
2877 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2879 if (menu && menu->wFlags & MF_POPUP)
2881 DestroyWindow( menu->hWnd );
2882 menu->hWnd = 0;
2884 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2885 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2888 /* Reset the variable for hiding menu */
2889 if( menu ) menu->bTimeToHide = FALSE;
2892 /* The return value is only used by TrackPopupMenu */
2893 return ((executedMenuId != -1) ? executedMenuId : 0);
2896 /***********************************************************************
2897 * MENU_InitTracking
2899 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2901 TRACE("hwnd=0x%04x hmenu=0x%04x\n", hWnd, hMenu);
2903 HideCaret(0);
2905 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2906 if (!(wFlags & TPM_NONOTIFY))
2907 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2909 SendMessageA( hWnd, WM_SETCURSOR, hWnd, HTCAPTION );
2911 if (!(wFlags & TPM_NONOTIFY))
2913 POPUPMENU *menu;
2914 SendMessageA( hWnd, WM_INITMENU, hMenu, 0 );
2915 if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
2916 { /* app changed/recreated menu bar entries in WM_INITMENU
2917 Recalculate menu sizes else clicks will not work */
2918 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2919 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2923 return TRUE;
2925 /***********************************************************************
2926 * MENU_ExitTracking
2928 static BOOL MENU_ExitTracking(HWND hWnd)
2930 TRACE("hwnd=0x%04x\n", hWnd);
2932 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
2933 ShowCaret(0);
2934 return TRUE;
2937 /***********************************************************************
2938 * MENU_TrackMouseMenuBar
2940 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2942 void MENU_TrackMouseMenuBar( WND* wndPtr, INT ht, POINT pt )
2944 HWND hWnd = wndPtr->hwndSelf;
2945 HMENU hMenu = (ht == HTSYSMENU) ? wndPtr->hSysMenu : wndPtr->wIDmenu;
2946 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2948 TRACE("pwnd=%p ht=0x%04x (%ld,%ld)\n", wndPtr, ht, pt.x, pt.y);
2950 if (IsMenu(hMenu))
2952 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2953 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2954 MENU_ExitTracking(hWnd);
2959 /***********************************************************************
2960 * MENU_TrackKbdMenuBar
2962 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2964 void MENU_TrackKbdMenuBar( WND* wndPtr, UINT wParam, INT vkey)
2966 UINT uItem = NO_SELECTED_ITEM;
2967 HMENU hTrackMenu;
2968 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2970 /* find window that has a menu */
2972 while( wndPtr->dwStyle & WS_CHILD)
2973 if( !(wndPtr = wndPtr->parent) ) return;
2975 /* check if we have to track a system menu */
2977 if( (wndPtr->dwStyle & (WS_CHILD | WS_MINIMIZE)) ||
2978 !wndPtr->wIDmenu || vkey == VK_SPACE )
2980 if( !(wndPtr->dwStyle & WS_SYSMENU) ) return;
2981 hTrackMenu = wndPtr->hSysMenu;
2982 uItem = 0;
2983 wParam |= HTSYSMENU; /* prevent item lookup */
2985 else
2986 hTrackMenu = wndPtr->wIDmenu;
2988 if (IsMenu( hTrackMenu ))
2990 MENU_InitTracking( wndPtr->hwndSelf, hTrackMenu, FALSE, wFlags );
2992 if( vkey && vkey != VK_SPACE )
2994 uItem = MENU_FindItemByKey( wndPtr->hwndSelf, hTrackMenu,
2995 vkey, (wParam & HTSYSMENU) );
2996 if( uItem >= (UINT)(-2) )
2998 if( uItem == (UINT)(-1) ) MessageBeep(0);
2999 hTrackMenu = 0;
3003 if( hTrackMenu )
3005 MENU_SelectItem( wndPtr->hwndSelf, hTrackMenu, uItem, TRUE, 0 );
3007 if( uItem == NO_SELECTED_ITEM )
3008 MENU_MoveSelection( wndPtr->hwndSelf, hTrackMenu, ITEM_NEXT );
3009 else if( vkey )
3010 PostMessageA( wndPtr->hwndSelf, WM_KEYDOWN, VK_DOWN, 0L );
3012 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, wndPtr->hwndSelf, NULL );
3015 MENU_ExitTracking (wndPtr->hwndSelf);
3020 /**********************************************************************
3021 * TrackPopupMenu (USER.416)
3023 BOOL16 WINAPI TrackPopupMenu16( HMENU16 hMenu, UINT16 wFlags, INT16 x, INT16 y,
3024 INT16 nReserved, HWND16 hWnd, const RECT16 *lpRect )
3026 RECT r;
3027 if (lpRect)
3028 CONV_RECT16TO32( lpRect, &r );
3029 return TrackPopupMenu( hMenu, wFlags, x, y, nReserved, hWnd,
3030 lpRect ? &r : NULL );
3034 /**********************************************************************
3035 * TrackPopupMenu (USER32.@)
3037 * Like the win32 API, the function return the command ID only if the
3038 * flag TPM_RETURNCMD is on.
3041 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3042 INT nReserved, HWND hWnd, const RECT *lpRect )
3044 BOOL ret = FALSE;
3046 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3048 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3049 if (!(wFlags & TPM_NONOTIFY))
3050 SendMessageA( hWnd, WM_INITMENUPOPUP, hMenu, 0);
3052 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3053 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3054 MENU_ExitTracking(hWnd);
3056 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3057 ret = 1;
3059 return ret;
3062 /**********************************************************************
3063 * TrackPopupMenuEx (USER32.@)
3065 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3066 HWND hWnd, LPTPMPARAMS lpTpm )
3068 FIXME("not fully implemented\n" );
3069 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3070 lpTpm ? &lpTpm->rcExclude : NULL );
3073 /***********************************************************************
3074 * PopupMenuWndProc
3076 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3078 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3080 TRACE("hwnd=0x%04x msg=0x%04x wp=0x%04x lp=0x%08lx\n",
3081 hwnd, message, wParam, lParam);
3083 switch(message)
3085 case WM_CREATE:
3087 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3088 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3089 return 0;
3092 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3093 return MA_NOACTIVATE;
3095 case WM_PAINT:
3097 PAINTSTRUCT ps;
3098 BeginPaint( hwnd, &ps );
3099 MENU_DrawPopupMenu( hwnd, ps.hdc,
3100 (HMENU)GetWindowLongA( hwnd, 0 ) );
3101 EndPaint( hwnd, &ps );
3102 return 0;
3104 case WM_ERASEBKGND:
3105 return 1;
3107 case WM_DESTROY:
3108 /* zero out global pointer in case resident popup window was destroyed. */
3109 if (hwnd == top_popup) top_popup = 0;
3110 break;
3112 case WM_SHOWWINDOW:
3114 if( wParam )
3116 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3118 else
3119 SetWindowLongW( hwnd, 0, 0 );
3120 break;
3122 case MM_SETMENUHANDLE:
3123 SetWindowLongW( hwnd, 0, wParam );
3124 break;
3126 case MM_GETMENUHANDLE:
3127 return GetWindowLongW( hwnd, 0 );
3129 default:
3130 return DefWindowProcW( hwnd, message, wParam, lParam );
3132 return 0;
3136 /***********************************************************************
3137 * MENU_GetMenuBarHeight
3139 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3141 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3142 INT orgX, INT orgY )
3144 HDC hdc;
3145 RECT rectBar;
3146 WND *wndPtr;
3147 LPPOPUPMENU lppop;
3148 UINT retvalue;
3150 TRACE("HWND 0x%x, width %d, at (%d, %d).\n",
3151 hwnd, menubarWidth, orgX, orgY );
3153 if (!(wndPtr = WIN_FindWndPtr( hwnd )))
3154 return 0;
3156 if (!(lppop = MENU_GetMenu((HMENU16)wndPtr->wIDmenu)))
3158 WIN_ReleaseWndPtr(wndPtr);
3159 return 0;
3162 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3163 SelectObject( hdc, hMenuFont);
3164 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3165 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3166 ReleaseDC( hwnd, hdc );
3167 retvalue = lppop->Height;
3168 WIN_ReleaseWndPtr(wndPtr);
3169 return retvalue;
3173 /*******************************************************************
3174 * ChangeMenu (USER.153)
3176 BOOL16 WINAPI ChangeMenu16( HMENU16 hMenu, UINT16 pos, SEGPTR data,
3177 UINT16 id, UINT16 flags )
3179 TRACE("menu=%04x pos=%d data=%08lx id=%04x flags=%04x\n",
3180 hMenu, pos, (DWORD)data, id, flags );
3181 if (flags & MF_APPEND) return AppendMenu16( hMenu, flags & ~MF_APPEND,
3182 id, data );
3184 /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
3185 /* for MF_DELETE. We should check the parameters for all others */
3186 /* MF_* actions also (anybody got a doc on ChangeMenu?). */
3188 if (flags & MF_DELETE) return DeleteMenu16(hMenu, pos, flags & ~MF_DELETE);
3189 if (flags & MF_CHANGE) return ModifyMenu16(hMenu, pos, flags & ~MF_CHANGE,
3190 id, data );
3191 if (flags & MF_REMOVE) return RemoveMenu16(hMenu,
3192 flags & MF_BYPOSITION ? pos : id,
3193 flags & ~MF_REMOVE );
3194 /* Default: MF_INSERT */
3195 return InsertMenu16( hMenu, pos, flags, id, data );
3199 /*******************************************************************
3200 * ChangeMenuA (USER32.@)
3202 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3203 UINT id, UINT flags )
3205 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3206 hMenu, pos, (DWORD)data, id, flags );
3207 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3208 id, data );
3209 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3210 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3211 id, data );
3212 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3213 flags & MF_BYPOSITION ? pos : id,
3214 flags & ~MF_REMOVE );
3215 /* Default: MF_INSERT */
3216 return InsertMenuA( hMenu, pos, flags, id, data );
3220 /*******************************************************************
3221 * ChangeMenuW (USER32.@)
3223 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3224 UINT id, UINT flags )
3226 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3227 hMenu, pos, (DWORD)data, id, flags );
3228 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3229 id, data );
3230 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3231 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3232 id, data );
3233 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3234 flags & MF_BYPOSITION ? pos : id,
3235 flags & ~MF_REMOVE );
3236 /* Default: MF_INSERT */
3237 return InsertMenuW( hMenu, pos, flags, id, data );
3241 /*******************************************************************
3242 * CheckMenuItem (USER.154)
3244 BOOL16 WINAPI CheckMenuItem16( HMENU16 hMenu, UINT16 id, UINT16 flags )
3246 return (BOOL16)CheckMenuItem( hMenu, id, flags );
3250 /*******************************************************************
3251 * CheckMenuItem (USER32.@)
3253 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3255 MENUITEM *item;
3256 DWORD ret;
3258 TRACE("menu=%04x id=%04x flags=%04x\n", hMenu, id, flags );
3259 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3260 ret = item->fState & MF_CHECKED;
3261 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3262 else item->fState &= ~MF_CHECKED;
3263 return ret;
3267 /**********************************************************************
3268 * EnableMenuItem (USER.155)
3270 UINT16 WINAPI EnableMenuItem16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3272 return EnableMenuItem( hMenu, wItemID, wFlags );
3276 /**********************************************************************
3277 * EnableMenuItem (USER32.@)
3279 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3281 UINT oldflags;
3282 MENUITEM *item;
3283 POPUPMENU *menu;
3285 TRACE("(%04x, %04X, %04X) !\n",
3286 hMenu, wItemID, wFlags);
3288 /* Get the Popupmenu to access the owner menu */
3289 if (!(menu = MENU_GetMenu(hMenu)))
3290 return (UINT)-1;
3292 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3293 return (UINT)-1;
3295 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3296 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3298 /* In win95 if the close item in the system menu change update the close button */
3299 if (TWEAK_WineLook == WIN95_LOOK)
3300 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3302 if (menu->hSysMenuOwner != 0)
3304 POPUPMENU* parentMenu;
3306 /* Get the parent menu to access*/
3307 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3308 return (UINT)-1;
3310 /* Refresh the frame to reflect the change*/
3311 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3312 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3316 return oldflags;
3320 /*******************************************************************
3321 * GetMenuString (USER.161)
3323 INT16 WINAPI GetMenuString16( HMENU16 hMenu, UINT16 wItemID,
3324 LPSTR str, INT16 nMaxSiz, UINT16 wFlags )
3326 return GetMenuStringA( hMenu, wItemID, str, nMaxSiz, wFlags );
3330 /*******************************************************************
3331 * GetMenuStringA (USER32.@)
3333 INT WINAPI GetMenuStringA(
3334 HMENU hMenu, /* [in] menuhandle */
3335 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3336 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3337 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3338 UINT wFlags /* [in] MF_ flags */
3340 MENUITEM *item;
3342 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3343 hMenu, wItemID, str, nMaxSiz, wFlags );
3344 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3345 if (!IS_STRING_ITEM(item->fType)) return 0;
3346 if (!str || !nMaxSiz) return strlenW(item->text);
3347 str[0] = '\0';
3348 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3349 str[nMaxSiz-1] = 0;
3350 TRACE("returning '%s'\n", str );
3351 return strlen(str);
3355 /*******************************************************************
3356 * GetMenuStringW (USER32.@)
3358 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3359 LPWSTR str, INT nMaxSiz, UINT wFlags )
3361 MENUITEM *item;
3363 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3364 hMenu, wItemID, str, nMaxSiz, wFlags );
3365 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3366 if (!IS_STRING_ITEM(item->fType)) return 0;
3367 if (!str || !nMaxSiz) return strlenW(item->text);
3368 str[0] = '\0';
3369 lstrcpynW( str, item->text, nMaxSiz );
3370 return strlenW(str);
3374 /**********************************************************************
3375 * HiliteMenuItem (USER.162)
3377 BOOL16 WINAPI HiliteMenuItem16( HWND16 hWnd, HMENU16 hMenu, UINT16 wItemID,
3378 UINT16 wHilite )
3380 return HiliteMenuItem( hWnd, hMenu, wItemID, wHilite );
3384 /**********************************************************************
3385 * HiliteMenuItem (USER32.@)
3387 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3388 UINT wHilite )
3390 LPPOPUPMENU menu;
3391 TRACE("(%04x, %04x, %04x, %04x);\n",
3392 hWnd, hMenu, wItemID, wHilite);
3393 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3394 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3395 if (menu->FocusedItem == wItemID) return TRUE;
3396 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3397 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3398 return TRUE;
3402 /**********************************************************************
3403 * GetMenuState (USER.250)
3405 UINT16 WINAPI GetMenuState16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3407 return GetMenuState( hMenu, wItemID, wFlags );
3411 /**********************************************************************
3412 * GetMenuState (USER32.@)
3414 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3416 MENUITEM *item;
3417 TRACE("(menu=%04x, id=%04x, flags=%04x);\n",
3418 hMenu, wItemID, wFlags);
3419 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3420 debug_print_menuitem (" item: ", item, "");
3421 if (item->fType & MF_POPUP)
3423 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3424 if (!menu) return -1;
3425 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3427 else
3429 /* We used to (from way back then) mask the result to 0xff. */
3430 /* I don't know why and it seems wrong as the documented */
3431 /* return flag MF_SEPARATOR is outside that mask. */
3432 return (item->fType | item->fState);
3437 /**********************************************************************
3438 * GetMenuItemCount (USER.263)
3440 INT16 WINAPI GetMenuItemCount16( HMENU16 hMenu )
3442 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3443 if (!menu) return -1;
3444 TRACE("(%04x) returning %d\n",
3445 hMenu, menu->nItems );
3446 return menu->nItems;
3450 /**********************************************************************
3451 * GetMenuItemCount (USER32.@)
3453 INT WINAPI GetMenuItemCount( HMENU hMenu )
3455 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3456 if (!menu) return -1;
3457 TRACE("(%04x) returning %d\n",
3458 hMenu, menu->nItems );
3459 return menu->nItems;
3462 /**********************************************************************
3463 * GetMenuItemID (USER.264)
3465 UINT16 WINAPI GetMenuItemID16( HMENU16 hMenu, INT16 nPos )
3467 return (UINT16) GetMenuItemID (hMenu, nPos);
3470 /**********************************************************************
3471 * GetMenuItemID (USER32.@)
3473 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3475 MENUITEM * lpmi;
3477 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3478 if (lpmi->fType & MF_POPUP) return -1;
3479 return lpmi->wID;
3483 /*******************************************************************
3484 * InsertMenu (USER.410)
3486 BOOL16 WINAPI InsertMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3487 UINT16 id, SEGPTR data )
3489 UINT pos32 = (UINT)pos;
3490 if ((pos == (UINT16)-1) && (flags & MF_BYPOSITION)) pos32 = (UINT)-1;
3491 if (IS_STRING_ITEM(flags) && data)
3492 return InsertMenuA( hMenu, pos32, flags, id, MapSL(data) );
3493 return InsertMenuA( hMenu, pos32, flags, id, (LPSTR)data );
3497 /*******************************************************************
3498 * InsertMenuW (USER32.@)
3500 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3501 UINT id, LPCWSTR str )
3503 MENUITEM *item;
3505 if (IS_STRING_ITEM(flags) && str)
3506 TRACE("hMenu %04x, pos %d, flags %08x, "
3507 "id %04x, str %s\n",
3508 hMenu, pos, flags, id, debugstr_w(str) );
3509 else TRACE("hMenu %04x, pos %d, flags %08x, "
3510 "id %04x, str %08lx (not a string)\n",
3511 hMenu, pos, flags, id, (DWORD)str );
3513 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3515 if (!(MENU_SetItemData( item, flags, id, str )))
3517 RemoveMenu( hMenu, pos, flags );
3518 return FALSE;
3521 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3522 (MENU_GetMenu((HMENU16)id))->wFlags |= MF_POPUP;
3524 item->hCheckBit = item->hUnCheckBit = 0;
3525 return TRUE;
3529 /*******************************************************************
3530 * InsertMenuA (USER32.@)
3532 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3533 UINT id, LPCSTR str )
3535 BOOL ret = FALSE;
3537 if (IS_STRING_ITEM(flags) && str)
3539 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3540 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3541 if (newstr)
3543 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3544 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3545 HeapFree( GetProcessHeap(), 0, newstr );
3547 return ret;
3549 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3553 /*******************************************************************
3554 * AppendMenu (USER.411)
3556 BOOL16 WINAPI AppendMenu16(HMENU16 hMenu, UINT16 flags, UINT16 id, SEGPTR data)
3558 return InsertMenu16( hMenu, -1, flags | MF_BYPOSITION, id, data );
3562 /*******************************************************************
3563 * AppendMenuA (USER32.@)
3565 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3566 UINT id, LPCSTR data )
3568 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3572 /*******************************************************************
3573 * AppendMenuW (USER32.@)
3575 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3576 UINT id, LPCWSTR data )
3578 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3582 /**********************************************************************
3583 * RemoveMenu (USER.412)
3585 BOOL16 WINAPI RemoveMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3587 return RemoveMenu( hMenu, nPos, wFlags );
3591 /**********************************************************************
3592 * RemoveMenu (USER32.@)
3594 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3596 LPPOPUPMENU menu;
3597 MENUITEM *item;
3599 TRACE("(menu=%04x pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3600 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3601 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3603 /* Remove item */
3605 MENU_FreeItemData( item );
3607 if (--menu->nItems == 0)
3609 HeapFree( GetProcessHeap(), 0, menu->items );
3610 menu->items = NULL;
3612 else
3614 while(nPos < menu->nItems)
3616 *item = *(item+1);
3617 item++;
3618 nPos++;
3620 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3621 menu->nItems * sizeof(MENUITEM) );
3623 return TRUE;
3627 /**********************************************************************
3628 * DeleteMenu (USER.413)
3630 BOOL16 WINAPI DeleteMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3632 return DeleteMenu( hMenu, nPos, wFlags );
3636 /**********************************************************************
3637 * DeleteMenu (USER32.@)
3639 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3641 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3642 if (!item) return FALSE;
3643 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3644 /* nPos is now the position of the item */
3645 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3646 return TRUE;
3650 /*******************************************************************
3651 * ModifyMenu (USER.414)
3653 BOOL16 WINAPI ModifyMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3654 UINT16 id, SEGPTR data )
3656 if (IS_STRING_ITEM(flags))
3657 return ModifyMenuA( hMenu, pos, flags, id, MapSL(data) );
3658 return ModifyMenuA( hMenu, pos, flags, id, (LPSTR)data );
3662 /*******************************************************************
3663 * ModifyMenuW (USER32.@)
3665 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3666 UINT id, LPCWSTR str )
3668 MENUITEM *item;
3670 if (IS_STRING_ITEM(flags))
3672 TRACE("%04x %d %04x %04x %s\n",
3673 hMenu, pos, flags, id, debugstr_w(str) );
3674 if (!str) return FALSE;
3676 else
3678 TRACE("%04x %d %04x %04x %08lx\n",
3679 hMenu, pos, flags, id, (DWORD)str );
3682 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3683 return MENU_SetItemData( item, flags, id, str );
3687 /*******************************************************************
3688 * ModifyMenuA (USER32.@)
3690 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3691 UINT id, LPCSTR str )
3693 BOOL ret = FALSE;
3695 if (IS_STRING_ITEM(flags) && str)
3697 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3698 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3699 if (newstr)
3701 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3702 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3703 HeapFree( GetProcessHeap(), 0, newstr );
3705 return ret;
3707 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3711 /**********************************************************************
3712 * CreatePopupMenu (USER.415)
3714 HMENU16 WINAPI CreatePopupMenu16(void)
3716 return CreatePopupMenu();
3720 /**********************************************************************
3721 * CreatePopupMenu (USER32.@)
3723 HMENU WINAPI CreatePopupMenu(void)
3725 HMENU hmenu;
3726 POPUPMENU *menu;
3728 if (!(hmenu = CreateMenu())) return 0;
3729 menu = MENU_GetMenu( hmenu );
3730 menu->wFlags |= MF_POPUP;
3731 menu->bTimeToHide = FALSE;
3732 return hmenu;
3736 /**********************************************************************
3737 * GetMenuCheckMarkDimensions (USER.417)
3738 * GetMenuCheckMarkDimensions (USER32.@)
3740 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3742 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3746 /**********************************************************************
3747 * SetMenuItemBitmaps (USER.418)
3749 BOOL16 WINAPI SetMenuItemBitmaps16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags,
3750 HBITMAP16 hNewUnCheck, HBITMAP16 hNewCheck)
3752 return SetMenuItemBitmaps( hMenu, nPos, wFlags, hNewUnCheck, hNewCheck );
3756 /**********************************************************************
3757 * SetMenuItemBitmaps (USER32.@)
3759 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3760 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3762 MENUITEM *item;
3763 TRACE("(%04x, %04x, %04x, %04x, %04x)\n",
3764 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3765 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3767 if (!hNewCheck && !hNewUnCheck)
3769 item->fState &= ~MF_USECHECKBITMAPS;
3771 else /* Install new bitmaps */
3773 item->hCheckBit = hNewCheck;
3774 item->hUnCheckBit = hNewUnCheck;
3775 item->fState |= MF_USECHECKBITMAPS;
3777 return TRUE;
3781 /**********************************************************************
3782 * CreateMenu (USER.151)
3784 HMENU16 WINAPI CreateMenu16(void)
3786 return CreateMenu();
3790 /**********************************************************************
3791 * CreateMenu (USER32.@)
3793 HMENU WINAPI CreateMenu(void)
3795 HMENU hMenu;
3796 LPPOPUPMENU menu;
3797 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3798 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3800 ZeroMemory(menu, sizeof(POPUPMENU));
3801 menu->wMagic = MENU_MAGIC;
3802 menu->FocusedItem = NO_SELECTED_ITEM;
3803 menu->bTimeToHide = FALSE;
3805 TRACE("return %04x\n", hMenu );
3807 return hMenu;
3811 /**********************************************************************
3812 * DestroyMenu (USER.152)
3814 BOOL16 WINAPI DestroyMenu16( HMENU16 hMenu )
3816 return DestroyMenu( hMenu );
3820 /**********************************************************************
3821 * DestroyMenu (USER32.@)
3823 BOOL WINAPI DestroyMenu( HMENU hMenu )
3825 TRACE("(%04x)\n", hMenu);
3827 /* Silently ignore attempts to destroy default system popup */
3829 if (hMenu && hMenu != MENU_DefSysPopup)
3831 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3833 if (!lppop) return FALSE;
3835 lppop->wMagic = 0; /* Mark it as destroyed */
3837 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3839 DestroyWindow( lppop->hWnd );
3840 lppop->hWnd = 0;
3843 if (lppop->items) /* recursively destroy submenus */
3845 int i;
3846 MENUITEM *item = lppop->items;
3847 for (i = lppop->nItems; i > 0; i--, item++)
3849 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3850 MENU_FreeItemData( item );
3852 HeapFree( GetProcessHeap(), 0, lppop->items );
3854 USER_HEAP_FREE( hMenu );
3856 return (hMenu != MENU_DefSysPopup);
3860 /**********************************************************************
3861 * GetSystemMenu (USER.156)
3863 HMENU16 WINAPI GetSystemMenu16( HWND16 hWnd, BOOL16 bRevert )
3865 return GetSystemMenu( hWnd, bRevert );
3869 /**********************************************************************
3870 * GetSystemMenu (USER32.@)
3872 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3874 WND *wndPtr = WIN_FindWndPtr( hWnd );
3875 HMENU retvalue = 0;
3877 if (wndPtr)
3879 if( wndPtr->hSysMenu )
3881 if( bRevert )
3883 DestroyMenu(wndPtr->hSysMenu);
3884 wndPtr->hSysMenu = 0;
3886 else
3888 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3889 if( menu )
3891 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3892 menu->items[0].hSubMenu = MENU_CopySysPopup();
3894 else
3896 WARN("Current sys-menu (%04x) of wnd %04x is broken\n",
3897 wndPtr->hSysMenu, hWnd);
3898 wndPtr->hSysMenu = 0;
3903 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3904 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3906 if( wndPtr->hSysMenu )
3908 POPUPMENU *menu;
3909 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3911 /* Store the dummy sysmenu handle to facilitate the refresh */
3912 /* of the close button if the SC_CLOSE item change */
3913 menu = MENU_GetMenu(retvalue);
3914 if ( menu )
3915 menu->hSysMenuOwner = wndPtr->hSysMenu;
3917 WIN_ReleaseWndPtr(wndPtr);
3919 return bRevert ? 0 : retvalue;
3923 /*******************************************************************
3924 * SetSystemMenu (USER.280)
3926 BOOL16 WINAPI SetSystemMenu16( HWND16 hwnd, HMENU16 hMenu )
3928 return SetSystemMenu( hwnd, hMenu );
3932 /*******************************************************************
3933 * SetSystemMenu (USER32.@)
3935 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3937 WND *wndPtr = WIN_FindWndPtr(hwnd);
3939 if (wndPtr)
3941 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3942 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3943 WIN_ReleaseWndPtr(wndPtr);
3944 return TRUE;
3946 return FALSE;
3950 /**********************************************************************
3951 * GetMenu (USER.157)
3953 HMENU16 WINAPI GetMenu16( HWND16 hWnd )
3955 return (HMENU16)GetMenu(hWnd);
3959 /**********************************************************************
3960 * GetMenu (USER32.@)
3962 HMENU WINAPI GetMenu( HWND hWnd )
3964 HMENU retvalue;
3965 WND * wndPtr = WIN_FindWndPtr(hWnd);
3967 if (!wndPtr) return 0;
3969 retvalue = (HMENU)wndPtr->wIDmenu;
3970 TRACE("for %swindow %04x returning %04x\n",
3971 (wndPtr->dwStyle & WS_CHILD) ? "child " : "", hWnd, retvalue);
3972 WIN_ReleaseWndPtr(wndPtr);
3973 return retvalue;
3977 /**********************************************************************
3978 * SetMenu (USER.158)
3980 BOOL16 WINAPI SetMenu16( HWND16 hWnd, HMENU16 hMenu )
3982 return SetMenu( hWnd, hMenu );
3986 /**********************************************************************
3987 * SetMenu (USER32.@)
3989 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3991 WND * wndPtr = WIN_FindWndPtr(hWnd);
3992 BOOL res = FALSE;
3994 TRACE("(%04x, %04x);\n", hWnd, hMenu);
3996 if (hMenu && !IsMenu(hMenu))
3998 WARN("hMenu is not a menu handle\n");
3999 goto exit;
4002 if (wndPtr && !(wndPtr->dwStyle & WS_CHILD))
4004 if (GetCapture() == hWnd) ReleaseCapture();
4006 wndPtr->wIDmenu = (UINT)hMenu;
4007 if (hMenu != 0)
4009 LPPOPUPMENU lpmenu;
4011 if (!(lpmenu = MENU_GetMenu(hMenu)))
4012 goto exit;
4014 lpmenu->hWnd = hWnd;
4015 lpmenu->Height = 0; /* Make sure we recalculate the size */
4017 if (IsWindowVisible(hWnd))
4018 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4019 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4020 res = TRUE;
4022 exit:
4023 WIN_ReleaseWndPtr(wndPtr);
4024 return res;
4029 /**********************************************************************
4030 * GetSubMenu (USER.159)
4032 HMENU16 WINAPI GetSubMenu16( HMENU16 hMenu, INT16 nPos )
4034 return GetSubMenu( hMenu, nPos );
4038 /**********************************************************************
4039 * GetSubMenu (USER32.@)
4041 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4043 MENUITEM * lpmi;
4045 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
4046 if (!(lpmi->fType & MF_POPUP)) return 0;
4047 return lpmi->hSubMenu;
4051 /**********************************************************************
4052 * DrawMenuBar (USER.160)
4054 void WINAPI DrawMenuBar16( HWND16 hWnd )
4056 DrawMenuBar( hWnd );
4060 /**********************************************************************
4061 * DrawMenuBar (USER32.@)
4063 BOOL WINAPI DrawMenuBar( HWND hWnd )
4065 LPPOPUPMENU lppop;
4066 WND *wndPtr = WIN_FindWndPtr(hWnd);
4067 if (wndPtr && !(wndPtr->dwStyle & WS_CHILD) && wndPtr->wIDmenu)
4069 lppop = MENU_GetMenu((HMENU16)wndPtr->wIDmenu);
4070 if (lppop == NULL)
4072 WIN_ReleaseWndPtr(wndPtr);
4073 return FALSE;
4076 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4077 lppop->hwndOwner = hWnd;
4078 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4079 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4080 WIN_ReleaseWndPtr(wndPtr);
4081 return TRUE;
4083 WIN_ReleaseWndPtr(wndPtr);
4084 return FALSE;
4087 /***********************************************************************
4088 * DrawMenuBarTemp (USER32.@)
4090 DWORD WINAPI DrawMenuBarTemp(DWORD p1, DWORD p2)
4092 FIXME("(%08lx %08lx): stub\n", p1, p2);
4093 return 0;
4096 /***********************************************************************
4097 * EndMenu (USER.187)
4098 * EndMenu (USER32.@)
4100 void WINAPI EndMenu(void)
4102 /* if we are in the menu code, and it is active */
4103 if (!fEndMenu && top_popup)
4105 /* terminate the menu handling code */
4106 fEndMenu = TRUE;
4108 /* needs to be posted to wakeup the internal menu handler */
4109 /* which will now terminate the menu, in the event that */
4110 /* the main window was minimized, or lost focus, so we */
4111 /* don't end up with an orphaned menu */
4112 PostMessageA( top_popup, WM_CANCELMODE, 0, 0);
4117 /***********************************************************************
4118 * LookupMenuHandle (USER.217)
4120 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4122 HMENU hmenu32 = hmenu;
4123 UINT id32 = id;
4124 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4125 else return hmenu32;
4129 /**********************************************************************
4130 * LoadMenu (USER.150)
4132 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4134 HRSRC16 hRsrc;
4135 HGLOBAL16 handle;
4136 HMENU16 hMenu;
4138 TRACE("(%04x,%s)\n", instance, debugres_a(name) );
4140 if (HIWORD(name))
4142 if (name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4145 if (!name) return 0;
4147 /* check for Win32 module */
4148 if (HIWORD(instance)) return LoadMenuA( instance, name );
4149 instance = GetExePtr( instance );
4151 if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
4152 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4153 hMenu = LoadMenuIndirect16(LockResource16(handle));
4154 FreeResource16( handle );
4155 return hMenu;
4159 /*****************************************************************
4160 * LoadMenuA (USER32.@)
4162 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4164 HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
4165 if (!hrsrc) return 0;
4166 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4170 /*****************************************************************
4171 * LoadMenuW (USER32.@)
4173 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4175 HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
4176 if (!hrsrc) return 0;
4177 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4181 /**********************************************************************
4182 * LoadMenuIndirect (USER.220)
4184 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4186 HMENU16 hMenu;
4187 WORD version, offset;
4188 LPCSTR p = (LPCSTR)template;
4190 TRACE("(%p)\n", template );
4191 version = GET_WORD(p);
4192 p += sizeof(WORD);
4193 if (version)
4195 WARN("version must be 0 for Win16\n" );
4196 return 0;
4198 offset = GET_WORD(p);
4199 p += sizeof(WORD) + offset;
4200 if (!(hMenu = CreateMenu())) return 0;
4201 if (!MENU_ParseResource( p, hMenu, FALSE ))
4203 DestroyMenu( hMenu );
4204 return 0;
4206 return hMenu;
4210 /**********************************************************************
4211 * LoadMenuIndirectA (USER32.@)
4213 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4215 HMENU16 hMenu;
4216 WORD version, offset;
4217 LPCSTR p = (LPCSTR)template;
4219 TRACE("%p\n", template );
4220 version = GET_WORD(p);
4221 p += sizeof(WORD);
4222 switch (version)
4224 case 0:
4225 offset = GET_WORD(p);
4226 p += sizeof(WORD) + offset;
4227 if (!(hMenu = CreateMenu())) return 0;
4228 if (!MENU_ParseResource( p, hMenu, TRUE ))
4230 DestroyMenu( hMenu );
4231 return 0;
4233 return hMenu;
4234 case 1:
4235 offset = GET_WORD(p);
4236 p += sizeof(WORD) + offset;
4237 if (!(hMenu = CreateMenu())) return 0;
4238 if (!MENUEX_ParseResource( p, hMenu))
4240 DestroyMenu( hMenu );
4241 return 0;
4243 return hMenu;
4244 default:
4245 ERR("version %d not supported.\n", version);
4246 return 0;
4251 /**********************************************************************
4252 * LoadMenuIndirectW (USER32.@)
4254 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4256 /* FIXME: is there anything different between A and W? */
4257 return LoadMenuIndirectA( template );
4261 /**********************************************************************
4262 * IsMenu (USER.358)
4264 BOOL16 WINAPI IsMenu16( HMENU16 hmenu )
4266 return IsMenu( hmenu );
4270 /**********************************************************************
4271 * IsMenu (USER32.@)
4273 BOOL WINAPI IsMenu(HMENU hmenu)
4275 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4276 return menu != NULL;
4279 /**********************************************************************
4280 * GetMenuItemInfo_common
4283 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4284 LPMENUITEMINFOW lpmii, BOOL unicode)
4286 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4288 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4290 if (!menu)
4291 return FALSE;
4293 if (lpmii->fMask & MIIM_TYPE) {
4294 lpmii->fType = menu->fType;
4295 switch (MENU_ITEM_TYPE(menu->fType)) {
4296 case MF_STRING:
4297 break; /* will be done below */
4298 case MF_OWNERDRAW:
4299 case MF_BITMAP:
4300 lpmii->dwTypeData = menu->text;
4301 /* fall through */
4302 default:
4303 lpmii->cch = 0;
4307 /* copy the text string */
4308 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4309 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4311 int len;
4312 if (unicode)
4314 len = strlenW(menu->text);
4315 if(lpmii->dwTypeData && lpmii->cch)
4316 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4318 else
4320 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4321 if(lpmii->dwTypeData && lpmii->cch)
4322 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4323 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4324 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4326 /* if we've copied a substring we return its length */
4327 if(lpmii->dwTypeData && lpmii->cch)
4329 if (lpmii->cch <= len) lpmii->cch--;
4331 else /* return length of string */
4332 lpmii->cch = len;
4335 if (lpmii->fMask & MIIM_FTYPE)
4336 lpmii->fType = menu->fType;
4338 if (lpmii->fMask & MIIM_BITMAP)
4339 lpmii->hbmpItem = menu->hbmpItem;
4341 if (lpmii->fMask & MIIM_STATE)
4342 lpmii->fState = menu->fState;
4344 if (lpmii->fMask & MIIM_ID)
4345 lpmii->wID = menu->wID;
4347 if (lpmii->fMask & MIIM_SUBMENU)
4348 lpmii->hSubMenu = menu->hSubMenu;
4350 if (lpmii->fMask & MIIM_CHECKMARKS) {
4351 lpmii->hbmpChecked = menu->hCheckBit;
4352 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4354 if (lpmii->fMask & MIIM_DATA)
4355 lpmii->dwItemData = menu->dwItemData;
4357 return TRUE;
4360 /**********************************************************************
4361 * GetMenuItemInfoA (USER32.@)
4363 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4364 LPMENUITEMINFOA lpmii)
4366 return GetMenuItemInfo_common (hmenu, item, bypos,
4367 (LPMENUITEMINFOW)lpmii, FALSE);
4370 /**********************************************************************
4371 * GetMenuItemInfoW (USER32.@)
4373 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4374 LPMENUITEMINFOW lpmii)
4376 return GetMenuItemInfo_common (hmenu, item, bypos,
4377 lpmii, TRUE);
4381 /* set a menu item text from a ASCII or Unicode string */
4382 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4384 if (!text)
4386 menu->text = NULL;
4387 menu->fType |= MF_SEPARATOR;
4389 else if (unicode)
4391 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4392 strcpyW( menu->text, text );
4394 else
4396 LPCSTR str = (LPCSTR)text;
4397 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4398 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4399 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4404 /**********************************************************************
4405 * SetMenuItemInfo_common
4408 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4409 const MENUITEMINFOW *lpmii,
4410 BOOL unicode)
4412 if (!menu) return FALSE;
4414 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4416 if (lpmii->fMask & MIIM_TYPE ) {
4417 /* Get rid of old string. */
4418 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4419 HeapFree(GetProcessHeap(), 0, menu->text);
4420 menu->text = NULL;
4423 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4424 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4425 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4427 menu->text = lpmii->dwTypeData;
4429 if (IS_STRING_ITEM(menu->fType))
4430 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4433 if (lpmii->fMask & MIIM_FTYPE ) {
4434 /* free the string when the type is changing */
4435 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4436 HeapFree(GetProcessHeap(), 0, menu->text);
4437 menu->text = NULL;
4439 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4440 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4441 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4442 menu->fType |= MF_SEPARATOR;
4445 if (lpmii->fMask & MIIM_STRING ) {
4446 /* free the string when used */
4447 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4448 HeapFree(GetProcessHeap(), 0, menu->text);
4449 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4453 if (lpmii->fMask & MIIM_STATE)
4455 /* fixme: MFS_DEFAULT do we have to reset the other menu items? */
4456 menu->fState = lpmii->fState;
4459 if (lpmii->fMask & MIIM_ID)
4460 menu->wID = lpmii->wID;
4462 if (lpmii->fMask & MIIM_SUBMENU) {
4463 menu->hSubMenu = lpmii->hSubMenu;
4464 if (menu->hSubMenu) {
4465 POPUPMENU *subMenu = MENU_GetMenu((UINT16)menu->hSubMenu);
4466 if (subMenu) {
4467 subMenu->wFlags |= MF_POPUP;
4468 menu->fType |= MF_POPUP;
4470 else
4471 /* FIXME: Return an error ? */
4472 menu->fType &= ~MF_POPUP;
4474 else
4475 menu->fType &= ~MF_POPUP;
4478 if (lpmii->fMask & MIIM_CHECKMARKS)
4480 if (lpmii->fType & MFT_RADIOCHECK)
4481 menu->fType |= MFT_RADIOCHECK;
4483 menu->hCheckBit = lpmii->hbmpChecked;
4484 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4486 if (lpmii->fMask & MIIM_DATA)
4487 menu->dwItemData = lpmii->dwItemData;
4489 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4490 return TRUE;
4493 /**********************************************************************
4494 * SetMenuItemInfoA (USER32.@)
4496 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4497 const MENUITEMINFOA *lpmii)
4499 if ((lpmii->fType & (MF_HILITE|MF_POPUP)) || (lpmii->fState)) {
4500 /* QuickTime does pass invalid data into SetMenuItemInfo.
4501 * do some of the checks Windows does.
4503 WARN("Bad masks for type (0x%08x) or state (0x%08x)\n",
4504 lpmii->fType,lpmii->fState );
4505 return FALSE;
4508 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4509 (const MENUITEMINFOW *)lpmii, FALSE);
4512 /**********************************************************************
4513 * SetMenuItemInfoW (USER32.@)
4515 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4516 const MENUITEMINFOW *lpmii)
4518 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4519 lpmii, TRUE);
4522 /**********************************************************************
4523 * SetMenuDefaultItem (USER32.@)
4526 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4528 UINT i;
4529 POPUPMENU *menu;
4530 MENUITEM *item;
4532 TRACE("(0x%x,%d,%d)\n", hmenu, uItem, bypos);
4534 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4536 /* reset all default-item flags */
4537 item = menu->items;
4538 for (i = 0; i < menu->nItems; i++, item++)
4540 item->fState &= ~MFS_DEFAULT;
4543 /* no default item */
4544 if ( -1 == uItem)
4546 return TRUE;
4549 item = menu->items;
4550 if ( bypos )
4552 if ( uItem >= menu->nItems ) return FALSE;
4553 item[uItem].fState |= MFS_DEFAULT;
4554 return TRUE;
4556 else
4558 for (i = 0; i < menu->nItems; i++, item++)
4560 if (item->wID == uItem)
4562 item->fState |= MFS_DEFAULT;
4563 return TRUE;
4568 return FALSE;
4571 /**********************************************************************
4572 * GetMenuDefaultItem (USER32.@)
4574 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4576 POPUPMENU *menu;
4577 MENUITEM * item;
4578 UINT i = 0;
4580 TRACE("(0x%x,%d,%d)\n", hmenu, bypos, flags);
4582 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4584 /* find default item */
4585 item = menu->items;
4587 /* empty menu */
4588 if (! item) return -1;
4590 while ( !( item->fState & MFS_DEFAULT ) )
4592 i++; item++;
4593 if (i >= menu->nItems ) return -1;
4596 /* default: don't return disabled items */
4597 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4599 /* search rekursiv when needed */
4600 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4602 UINT ret;
4603 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4604 if ( -1 != ret ) return ret;
4606 /* when item not found in submenu, return the popup item */
4608 return ( bypos ) ? i : item->wID;
4612 /*******************************************************************
4613 * InsertMenuItem (USER.441)
4615 * FIXME: untested
4617 BOOL16 WINAPI InsertMenuItem16( HMENU16 hmenu, UINT16 pos, BOOL16 byposition,
4618 const MENUITEMINFO16 *mii )
4620 MENUITEMINFOA miia;
4622 miia.cbSize = sizeof(miia);
4623 miia.fMask = mii->fMask;
4624 miia.dwTypeData = (LPSTR)mii->dwTypeData;
4625 miia.fType = mii->fType;
4626 miia.fState = mii->fState;
4627 miia.wID = mii->wID;
4628 miia.hSubMenu = mii->hSubMenu;
4629 miia.hbmpChecked = mii->hbmpChecked;
4630 miia.hbmpUnchecked = mii->hbmpUnchecked;
4631 miia.dwItemData = mii->dwItemData;
4632 miia.cch = mii->cch;
4633 if (IS_STRING_ITEM(miia.fType))
4634 miia.dwTypeData = MapSL(mii->dwTypeData);
4635 return InsertMenuItemA( hmenu, pos, byposition, &miia );
4639 /**********************************************************************
4640 * InsertMenuItemA (USER32.@)
4642 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4643 const MENUITEMINFOA *lpmii)
4645 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4646 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4650 /**********************************************************************
4651 * InsertMenuItemW (USER32.@)
4653 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4654 const MENUITEMINFOW *lpmii)
4656 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4657 return SetMenuItemInfo_common(item, lpmii, TRUE);
4660 /**********************************************************************
4661 * CheckMenuRadioItem (USER32.@)
4664 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4665 UINT first, UINT last, UINT check,
4666 UINT bypos)
4668 MENUITEM *mifirst, *milast, *micheck;
4669 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4671 TRACE("ox%x: %d-%d, check %d, bypos=%d\n",
4672 hMenu, first, last, check, bypos);
4674 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4675 milast = MENU_FindItem (&mlast, &last, bypos);
4676 micheck = MENU_FindItem (&mcheck, &check, bypos);
4678 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4679 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4680 micheck > milast || micheck < mifirst)
4681 return FALSE;
4683 while (mifirst <= milast)
4685 if (mifirst == micheck)
4687 mifirst->fType |= MFT_RADIOCHECK;
4688 mifirst->fState |= MFS_CHECKED;
4689 } else {
4690 mifirst->fType &= ~MFT_RADIOCHECK;
4691 mifirst->fState &= ~MFS_CHECKED;
4693 mifirst++;
4696 return TRUE;
4699 /**********************************************************************
4700 * CheckMenuRadioItem (USER.666)
4702 BOOL16 WINAPI CheckMenuRadioItem16(HMENU16 hMenu,
4703 UINT16 first, UINT16 last, UINT16 check,
4704 BOOL16 bypos)
4706 return CheckMenuRadioItem (hMenu, first, last, check, bypos);
4709 /**********************************************************************
4710 * GetMenuItemRect (USER32.@)
4712 * ATTENTION: Here, the returned values in rect are the screen
4713 * coordinates of the item just like if the menu was
4714 * always on the upper left side of the application.
4717 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4718 LPRECT rect)
4720 POPUPMENU *itemMenu;
4721 MENUITEM *item;
4722 HWND referenceHwnd;
4724 TRACE("(0x%x,0x%x,%d,%p)\n", hwnd, hMenu, uItem, rect);
4726 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4727 referenceHwnd = hwnd;
4729 if(!hwnd)
4731 itemMenu = MENU_GetMenu(hMenu);
4732 if (itemMenu == NULL)
4733 return FALSE;
4735 if(itemMenu->hWnd == 0)
4736 return FALSE;
4737 referenceHwnd = itemMenu->hWnd;
4740 if ((rect == NULL) || (item == NULL))
4741 return FALSE;
4743 *rect = item->rect;
4745 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4747 return TRUE;
4750 /**********************************************************************
4751 * GetMenuItemRect (USER.665)
4754 BOOL16 WINAPI GetMenuItemRect16 (HWND16 hwnd, HMENU16 hMenu, UINT16 uItem,
4755 LPRECT16 rect)
4757 RECT r32;
4758 BOOL res;
4760 if (!rect) return FALSE;
4761 res = GetMenuItemRect (hwnd, hMenu, uItem, &r32);
4762 CONV_RECT32TO16 (&r32, rect);
4763 return res;
4766 /**********************************************************************
4767 * SetMenuInfo (USER32.@)
4769 * FIXME
4770 * MIM_APPLYTOSUBMENUS
4771 * actually use the items to draw the menu
4773 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4775 POPUPMENU *menu;
4777 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4779 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4782 if (lpmi->fMask & MIM_BACKGROUND)
4783 menu->hbrBack = lpmi->hbrBack;
4785 if (lpmi->fMask & MIM_HELPID)
4786 menu->dwContextHelpID = lpmi->dwContextHelpID;
4788 if (lpmi->fMask & MIM_MAXHEIGHT)
4789 menu->cyMax = lpmi->cyMax;
4791 if (lpmi->fMask & MIM_MENUDATA)
4792 menu->dwMenuData = lpmi->dwMenuData;
4794 if (lpmi->fMask & MIM_STYLE)
4795 menu->dwStyle = lpmi->dwStyle;
4797 return TRUE;
4799 return FALSE;
4802 /**********************************************************************
4803 * GetMenuInfo (USER32.@)
4805 * NOTES
4806 * win98/NT5.0
4809 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4810 { POPUPMENU *menu;
4812 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4814 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4817 if (lpmi->fMask & MIM_BACKGROUND)
4818 lpmi->hbrBack = menu->hbrBack;
4820 if (lpmi->fMask & MIM_HELPID)
4821 lpmi->dwContextHelpID = menu->dwContextHelpID;
4823 if (lpmi->fMask & MIM_MAXHEIGHT)
4824 lpmi->cyMax = menu->cyMax;
4826 if (lpmi->fMask & MIM_MENUDATA)
4827 lpmi->dwMenuData = menu->dwMenuData;
4829 if (lpmi->fMask & MIM_STYLE)
4830 lpmi->dwStyle = menu->dwStyle;
4832 return TRUE;
4834 return FALSE;
4837 /**********************************************************************
4838 * SetMenuContextHelpId (USER.384)
4840 BOOL16 WINAPI SetMenuContextHelpId16( HMENU16 hMenu, DWORD dwContextHelpID)
4842 return SetMenuContextHelpId( hMenu, dwContextHelpID );
4846 /**********************************************************************
4847 * SetMenuContextHelpId (USER32.@)
4849 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4851 LPPOPUPMENU menu;
4853 TRACE("(0x%04x 0x%08lx)\n", hMenu, dwContextHelpID);
4855 if ((menu = MENU_GetMenu(hMenu)))
4857 menu->dwContextHelpID = dwContextHelpID;
4858 return TRUE;
4860 return FALSE;
4863 /**********************************************************************
4864 * GetMenuContextHelpId (USER.385)
4866 DWORD WINAPI GetMenuContextHelpId16( HMENU16 hMenu )
4868 return GetMenuContextHelpId( hMenu );
4871 /**********************************************************************
4872 * GetMenuContextHelpId (USER32.@)
4874 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4876 LPPOPUPMENU menu;
4878 TRACE("(0x%04x)\n", hMenu);
4880 if ((menu = MENU_GetMenu(hMenu)))
4882 return menu->dwContextHelpID;
4884 return 0;
4887 /**********************************************************************
4888 * MenuItemFromPoint (USER32.@)
4890 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4892 FIXME("(0x%04x,0x%04x,(%ld,%ld)):stub\n",
4893 hWnd, hMenu, ptScreen.x, ptScreen.y);
4894 return 0;
4898 /**********************************************************************
4899 * translate_accelerator
4901 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4902 BYTE fVirt, WORD key, WORD cmd )
4904 UINT mesg = 0;
4906 if (wParam != key) return FALSE;
4908 if (message == WM_CHAR)
4910 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4912 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4913 goto found;
4916 else
4918 if(fVirt & FVIRTKEY)
4920 INT mask = 0;
4921 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4922 wParam, 0xff & HIWORD(lParam));
4923 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4924 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4925 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4926 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4927 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4929 else
4931 if (!(lParam & 0x01000000)) /* no special_key */
4933 if ((fVirt & FALT) && (lParam & 0x20000000))
4934 { /* ^^ ALT pressed */
4935 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4936 goto found;
4941 return FALSE;
4943 found:
4944 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4945 mesg = 1;
4946 else if (GetCapture())
4947 mesg = 2;
4948 else if (!IsWindowEnabled(hWnd))
4949 mesg = 3;
4950 else
4952 HMENU hMenu, hSubMenu, hSysMenu;
4953 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4954 WND* wndPtr = WIN_FindWndPtr(hWnd);
4956 hMenu = (wndPtr->dwStyle & WS_CHILD) ? 0 : (HMENU)wndPtr->wIDmenu;
4957 hSysMenu = wndPtr->hSysMenu;
4958 WIN_ReleaseWndPtr(wndPtr);
4960 /* find menu item and ask application to initialize it */
4961 /* 1. in the system menu */
4962 hSubMenu = hSysMenu;
4963 nPos = cmd;
4964 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4966 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4967 if(hSubMenu != hSysMenu)
4969 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4970 TRACE_(accel)("hSysMenu = %04x, hSubMenu = %04x, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4971 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4973 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4975 else /* 2. in the window's menu */
4977 hSubMenu = hMenu;
4978 nPos = cmd;
4979 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4981 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4982 if(hSubMenu != hMenu)
4984 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4985 TRACE_(accel)("hMenu = %04x, hSubMenu = %04x, nPos = %d\n", hMenu, hSubMenu, nPos);
4986 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4988 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4992 if (uSysStat != (UINT)-1)
4994 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4995 mesg=4;
4996 else
4997 mesg=WM_SYSCOMMAND;
4999 else
5001 if (uStat != (UINT)-1)
5003 if (IsIconic(hWnd))
5004 mesg=5;
5005 else
5007 if (uStat & (MF_DISABLED|MF_GRAYED))
5008 mesg=6;
5009 else
5010 mesg=WM_COMMAND;
5013 else
5014 mesg=WM_COMMAND;
5018 if( mesg==WM_COMMAND )
5020 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5021 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
5023 else if( mesg==WM_SYSCOMMAND )
5025 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5026 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
5028 else
5030 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5031 * #0: unknown (please report!)
5032 * #1: for WM_KEYUP,WM_SYSKEYUP
5033 * #2: mouse is captured
5034 * #3: window is disabled
5035 * #4: it's a disabled system menu option
5036 * #5: it's a menu option, but window is iconic
5037 * #6: it's a menu option, but disabled
5039 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5040 if(mesg==0)
5041 ERR_(accel)(" unknown reason - please report!");
5043 return TRUE;
5046 /**********************************************************************
5047 * TranslateAccelerator (USER32.@)
5048 * TranslateAcceleratorA (USER32.@)
5049 * TranslateAcceleratorW (USER32.@)
5051 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
5053 /* YES, Accel16! */
5054 LPACCEL16 lpAccelTbl;
5055 int i;
5057 if (msg == NULL)
5059 WARN_(accel)("msg null; should hang here to be win compatible\n");
5060 return 0;
5062 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
5064 WARN_(accel)("invalid accel handle=%x\n", hAccel);
5065 return 0;
5067 if ((msg->message != WM_KEYDOWN &&
5068 msg->message != WM_KEYUP &&
5069 msg->message != WM_SYSKEYDOWN &&
5070 msg->message != WM_SYSKEYUP &&
5071 msg->message != WM_CHAR)) return 0;
5073 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5074 "msg->hwnd=%04x, msg->message=%04x, wParam=%08x, lParam=%lx\n",
5075 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5077 i = 0;
5080 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5081 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5082 return 1;
5083 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5084 WARN_(accel)("couldn't translate accelerator key\n");
5085 return 0;
5089 /**********************************************************************
5090 * TranslateAccelerator (USER.178)
5092 INT16 WINAPI TranslateAccelerator16( HWND16 hWnd, HACCEL16 hAccel, LPMSG16 msg )
5094 LPACCEL16 lpAccelTbl;
5095 int i;
5097 if (msg == NULL)
5099 WARN_(accel)("msg null; should hang here to be win compatible\n");
5100 return 0;
5102 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
5104 WARN_(accel)("invalid accel handle=%x\n", hAccel);
5105 return 0;
5107 if ((msg->message != WM_KEYDOWN &&
5108 msg->message != WM_KEYUP &&
5109 msg->message != WM_SYSKEYDOWN &&
5110 msg->message != WM_SYSKEYUP &&
5111 msg->message != WM_CHAR)) return 0;
5113 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5114 "msg->hwnd=%04x, msg->message=%04x, wParam=%04x, lParam=%lx\n",
5115 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5117 i = 0;
5120 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5121 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd ))
5122 return 1;
5123 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5124 WARN_(accel)("couldn't translate accelerator key\n");
5125 return 0;