xref: /reactos/win32ss/user/ntuser/menu.c (revision c81af08f)
1 /*
2  * COPYRIGHT:        See COPYING in the top level directory
3  * PROJECT:          ReactOS kernel
4  * PURPOSE:          Menus
5  * FILE:             win32ss/user/ntuser/menu.c
6  * PROGRAMER:        Thomas Weidenmueller (w3seek@users.sourceforge.net)
7  */
8 
9 #include <win32k.h>
10 DBG_DEFAULT_CHANNEL(UserMenu);
11 
12 /* INTERNAL ******************************************************************/
13 
14 HFONT ghMenuFont = NULL;
15 HFONT ghMenuFontBold = NULL;
16 static SIZE MenuCharSize;
17 
18 /* Use global popup window because there's no way 2 menus can
19  * be tracked at the same time.  */
20 static HWND top_popup = NULL;
21 static HMENU top_popup_hmenu = NULL;
22 
23 BOOL fInsideMenuLoop = FALSE;
24 BOOL fInEndMenu = FALSE;
25 
26 /* internal popup menu window messages */
27 
28 #define MM_SETMENUHANDLE        (WM_USER + 0)
29 #define MM_GETMENUHANDLE        (WM_USER + 1)
30 
31 /* internal flags for menu tracking */
32 
33 #define TF_ENDMENU              0x10000
34 #define TF_SUSPENDPOPUP         0x20000
35 #define TF_SKIPREMOVE           0x40000
36 
37 
38 /* maximum allowed depth of any branch in the menu tree.
39  * This value is slightly larger than in windows (25) to
40  * stay on the safe side. */
41 #define MAXMENUDEPTH 30
42 
43 #define MNS_STYLE_MASK (MNS_NOCHECK|MNS_MODELESS|MNS_DRAGDROP|MNS_AUTODISMISS|MNS_NOTIFYBYPOS|MNS_CHECKORBMP)
44 
45 #define MENUITEMINFO_TYPE_MASK \
46                 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
47                  MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
48                  MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
49 
50 #define TYPE_MASK  (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
51 
52 #define STATE_MASK (~TYPE_MASK)
53 
54 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
55 
56 #define MII_STATE_MASK (MFS_GRAYED|MFS_CHECKED|MFS_HILITE|MFS_DEFAULT)
57 
58 #define IS_SYSTEM_MENU(MenuInfo)  \
59 	(!((MenuInfo)->fFlags & MNF_POPUP) && ((MenuInfo)->fFlags & MNF_SYSMENU))
60 
61 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
62 
63 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
64 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
65 
66 /* Maximum number of menu items a menu can contain */
67 #define MAX_MENU_ITEMS (0x4000)
68 #define MAX_GOINTOSUBMENU (0x10)
69 
70 /* Space between 2 columns */
71 #define MENU_COL_SPACE 4
72 
73 #define MENU_ITEM_HBMP_SPACE (5)
74 #define MENU_BAR_ITEMS_SPACE (12)
75 #define SEPARATOR_HEIGHT (5)
76 #define MENU_TAB_SPACE (8)
77 
78 typedef struct
79 {
80   UINT  TrackFlags;
81   PMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/
82   PMENU TopMenu;     /* initial menu */
83   PWND  OwnerWnd;    /* where notifications are sent */
84   POINT Pt;
85 } MTRACKER;
86 
87 /* Internal MenuTrackMenu() flags */
88 #define TPM_INTERNAL            0xF0000000
89 #define TPM_BUTTONDOWN          0x40000000              /* menu was clicked before tracking */
90 #define TPM_POPUPMENU           0x20000000              /* menu is a popup menu */
91 
92 #define ITEM_PREV               -1
93 #define ITEM_NEXT                1
94 
95 #define UpdateMenuItemState(state, change) \
96 {\
97   if((change) & MF_GRAYED) { \
98     (state) |= MF_GRAYED; \
99   } else { \
100     (state) &= ~MF_GRAYED; \
101   } /* Separate the two for test_menu_resource_layout.*/ \
102   if((change) & MF_DISABLED) { \
103     (state) |= MF_DISABLED; \
104   } else { \
105     (state) &= ~MF_DISABLED; \
106   } \
107   if((change) & MFS_CHECKED) { \
108     (state) |= MFS_CHECKED; \
109   } else { \
110     (state) &= ~MFS_CHECKED; \
111   } \
112   if((change) & MFS_HILITE) { \
113     (state) |= MFS_HILITE; \
114   } else { \
115     (state) &= ~MFS_HILITE; \
116   } \
117   if((change) & MFS_DEFAULT) { \
118     (state) |= MFS_DEFAULT; \
119   } else { \
120     (state) &= ~MFS_DEFAULT; \
121   } \
122   if((change) & MF_MOUSESELECT) { \
123     (state) |= MF_MOUSESELECT; \
124   } else { \
125     (state) &= ~MF_MOUSESELECT; \
126   } \
127 }
128 
129 #if 0
130 void FASTCALL
131 DumpMenuItemList(PMENU Menu, PITEM MenuItem)
132 {
133    UINT cnt = 0, i = Menu->cItems;
134    while(i)
135    {
136       if(MenuItem->lpstr.Length)
137          DbgPrint(" %d. %wZ\n", ++cnt, &MenuItem->lpstr);
138       else
139          DbgPrint(" %d. NO TEXT dwTypeData==%d\n", ++cnt, (DWORD)MenuItem->lpstr.Buffer);
140       DbgPrint("   fType=");
141       if(MFT_BITMAP & MenuItem->fType)
142          DbgPrint("MFT_BITMAP ");
143       if(MFT_MENUBARBREAK & MenuItem->fType)
144          DbgPrint("MFT_MENUBARBREAK ");
145       if(MFT_MENUBREAK & MenuItem->fType)
146          DbgPrint("MFT_MENUBREAK ");
147       if(MFT_OWNERDRAW & MenuItem->fType)
148          DbgPrint("MFT_OWNERDRAW ");
149       if(MFT_RADIOCHECK & MenuItem->fType)
150          DbgPrint("MFT_RADIOCHECK ");
151       if(MFT_RIGHTJUSTIFY & MenuItem->fType)
152          DbgPrint("MFT_RIGHTJUSTIFY ");
153       if(MFT_SEPARATOR & MenuItem->fType)
154          DbgPrint("MFT_SEPARATOR ");
155       if(MFT_STRING & MenuItem->fType)
156          DbgPrint("MFT_STRING ");
157       DbgPrint("\n   fState=");
158       if(MFS_DISABLED & MenuItem->fState)
159          DbgPrint("MFS_DISABLED ");
160       else
161          DbgPrint("MFS_ENABLED ");
162       if(MFS_CHECKED & MenuItem->fState)
163          DbgPrint("MFS_CHECKED ");
164       else
165          DbgPrint("MFS_UNCHECKED ");
166       if(MFS_HILITE & MenuItem->fState)
167          DbgPrint("MFS_HILITE ");
168       else
169          DbgPrint("MFS_UNHILITE ");
170       if(MFS_DEFAULT & MenuItem->fState)
171          DbgPrint("MFS_DEFAULT ");
172       if(MFS_GRAYED & MenuItem->fState)
173          DbgPrint("MFS_GRAYED ");
174       DbgPrint("\n   wId=%d\n", MenuItem->wID);
175       MenuItem++;
176       i--;
177    }
178    DbgPrint("Entries: %d\n", cnt);
179    return;
180 }
181 #endif
182 
183 #define FreeMenuText(Menu,MenuItem) \
184 { \
185   if((MENU_ITEM_TYPE((MenuItem)->fType) == MF_STRING) && \
186            (MenuItem)->lpstr.Length) { \
187     DesktopHeapFree(((PMENU)Menu)->head.rpdesk, (MenuItem)->lpstr.Buffer); \
188   } \
189 }
190 
191 PMENU FASTCALL
192 IntGetMenuObject(HMENU hMenu)
193 {
194    PMENU Menu = UserGetMenuObject(hMenu);
195    if (Menu)
196       Menu->head.cLockObj++;
197 
198    return Menu;
199 }
200 
201 PMENU FASTCALL VerifyMenu(PMENU pMenu)
202 {
203    HMENU hMenu;
204    PITEM pItem;
205    ULONG Error;
206    UINT i;
207    if (!pMenu) return NULL;
208 
209    Error = EngGetLastError();
210 
211    _SEH2_TRY
212    {
213       hMenu = UserHMGetHandle(pMenu);
214       pItem = pMenu->rgItems;
215       if (pItem)
216       {
217          i = pItem[0].wID;
218          pItem[0].wID = i;
219       }
220    }
221    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
222    {
223       ERR("Run away LOOP!\n");
224       EngSetLastError(Error);
225       _SEH2_YIELD(return NULL);
226    }
227    _SEH2_END
228 
229    if ( UserObjectInDestroy(hMenu))
230    {
231       ERR("Menu is marked for destruction!\n");
232       pMenu = NULL;
233    }
234    EngSetLastError(Error);
235    return pMenu;
236 }
237 
238 BOOL
239 FASTCALL
240 IntIsMenu(HMENU Menu)
241 {
242   if (UserGetMenuObject(Menu)) return TRUE;
243   return FALSE;
244 }
245 
246 
247 PMENU WINAPI
248 IntGetMenu(HWND hWnd)
249 {
250        PWND Wnd = ValidateHwndNoErr(hWnd);
251 
252        if (!Wnd)
253                return NULL;
254 
255        return UserGetMenuObject(UlongToHandle(Wnd->IDMenu));
256 }
257 
258 PMENU get_win_sys_menu( HWND hwnd )
259 {
260    PMENU ret = 0;
261    WND *win = ValidateHwndNoErr( hwnd );
262    if (win)
263    {
264       ret = UserGetMenuObject(win->SystemMenu);
265    }
266    return ret;
267 }
268 
269 BOOL IntDestroyMenu( PMENU pMenu, BOOL bRecurse)
270 {
271     PMENU SubMenu;
272 
273     ASSERT(UserIsEnteredExclusive());
274     if (pMenu->rgItems) /* recursively destroy submenus */
275     {
276        int i;
277        ITEM *item = pMenu->rgItems;
278        for (i = pMenu->cItems; i > 0; i--, item++)
279        {
280            SubMenu = item->spSubMenu;
281            item->spSubMenu = NULL;
282 
283            /* Remove Item Text */
284            FreeMenuText(pMenu,item);
285 
286            /* Remove Item Bitmap and set it for this process */
287            if (item->hbmp && !(item->fState & MFS_HBMMENUBMP))
288            {
289               GreSetObjectOwner(item->hbmp, GDI_OBJ_HMGR_POWNED);
290               item->hbmp = NULL;
291            }
292 
293            /* Remove Item submenu */
294            if (bRecurse && SubMenu)//VerifyMenu(SubMenu))
295            {
296               /* Release submenu since it was referenced when inserted */
297               IntReleaseMenuObject(SubMenu);
298               IntDestroyMenuObject(SubMenu, bRecurse);
299            }
300        }
301        /* Free the Item */
302        DesktopHeapFree(pMenu->head.rpdesk, pMenu->rgItems );
303        pMenu->rgItems = NULL;
304        pMenu->cItems = 0;
305     }
306     return TRUE;
307 }
308 
309 /* Callback for the object manager */
310 BOOLEAN
311 UserDestroyMenuObject(PVOID Object)
312 {
313     return IntDestroyMenuObject(Object, TRUE);
314 }
315 
316 BOOL FASTCALL
317 IntDestroyMenuObject(PMENU Menu, BOOL bRecurse)
318 {
319    ASSERT(UserIsEnteredExclusive());
320    if (Menu)
321    {
322       PWND Window;
323 
324       if (PsGetCurrentProcessSessionId() == Menu->head.rpdesk->rpwinstaParent->dwSessionId)
325       {
326          BOOL ret;
327          if (Menu->hWnd)
328          {
329             Window = ValidateHwndNoErr(Menu->hWnd);
330             if (Window)
331             {
332                //Window->IDMenu = 0; Only in Win9x!! wine win test_SetMenu test...
333 
334                /* DestroyMenu should not destroy system menu popup owner */
335                if ((Menu->fFlags & (MNF_POPUP | MNF_SYSSUBMENU)) == MNF_POPUP)
336                {
337                   // Should we check it to see if it has Class?
338                   ERR("FIXME Pop up menu window thing'ie\n");
339                   //co_UserDestroyWindow( Window );
340                   //Menu->hWnd = 0;
341                }
342             }
343          }
344 
345          if (!UserMarkObjectDestroy(Menu)) return TRUE;
346 
347          /* Remove all menu items */
348          IntDestroyMenu( Menu, bRecurse);
349 
350          ret = UserDeleteObject(Menu->head.h, TYPE_MENU);
351          TRACE("IntDestroyMenuObject %d\n",ret);
352          return ret;
353       }
354    }
355    return FALSE;
356 }
357 
358 BOOL
359 MenuInit(VOID)
360 {
361   NONCLIENTMETRICSW ncm;
362 
363   /* get the menu font */
364   if (!ghMenuFont || !ghMenuFontBold)
365   {
366     ncm.cbSize = sizeof(ncm);
367     if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
368     {
369       ERR("MenuInit(): SystemParametersInfo(SPI_GETNONCLIENTMETRICS) failed!\n");
370       return FALSE;
371     }
372 
373     ghMenuFont = GreCreateFontIndirectW(&ncm.lfMenuFont);
374     if (ghMenuFont == NULL)
375     {
376       ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
377       return FALSE;
378     }
379     ncm.lfMenuFont.lfWeight = min(ncm.lfMenuFont.lfWeight + (FW_BOLD - FW_NORMAL), FW_HEAVY);
380     ghMenuFontBold = GreCreateFontIndirectW(&ncm.lfMenuFont);
381     if (ghMenuFontBold == NULL)
382     {
383       ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
384       GreDeleteObject(ghMenuFont);
385       ghMenuFont = NULL;
386       return FALSE;
387     }
388 
389     GreSetObjectOwner(ghMenuFont, GDI_OBJ_HMGR_PUBLIC);
390     GreSetObjectOwner(ghMenuFontBold, GDI_OBJ_HMGR_PUBLIC);
391 
392     co_IntSetupOBM();
393   }
394 
395   return TRUE;
396 }
397 
398 
399 /**********************************************************************
400  *		MENU_depth
401  *
402  * detect if there are loops in the menu tree (or the depth is too large)
403  */
404 int FASTCALL MENU_depth( PMENU pmenu, int depth)
405 {
406     UINT i;
407     ITEM *item;
408     int subdepth;
409 
410     if (!pmenu) return depth;
411 
412     depth++;
413     if( depth > MAXMENUDEPTH) return depth;
414     item = pmenu->rgItems;
415     subdepth = depth;
416     for( i = 0; i < pmenu->cItems && subdepth <= MAXMENUDEPTH; i++, item++)
417     {
418         if( item->spSubMenu)//VerifyMenu(item->spSubMenu))
419         {
420             int bdepth = MENU_depth( item->spSubMenu, depth);
421             if( bdepth > subdepth) subdepth = bdepth;
422         }
423         if( subdepth > MAXMENUDEPTH)
424             TRACE("<- hmenu %p\n", item->spSubMenu);
425     }
426     return subdepth;
427 }
428 
429 
430 /******************************************************************************
431  *
432  *   UINT MenuGetStartOfNextColumn(
433  *     PMENU Menu)
434  *
435  *****************************************************************************/
436 
437 static UINT  MENU_GetStartOfNextColumn(
438     PMENU  menu )
439 {
440     PITEM pItem;
441     UINT i;
442 
443     if(!menu)
444 	return NO_SELECTED_ITEM;
445 
446     i = menu->iItem + 1;
447     if( i == NO_SELECTED_ITEM )
448 	return i;
449 
450     pItem = menu->rgItems;
451     if (!pItem) return NO_SELECTED_ITEM;
452 
453     for( ; i < menu->cItems; ++i ) {
454 	if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
455 	    return i;
456     }
457 
458     return NO_SELECTED_ITEM;
459 }
460 
461 /******************************************************************************
462  *
463  *   UINT MenuGetStartOfPrevColumn(
464  *     PMENU Menu)
465  *
466  *****************************************************************************/
467 static UINT  MENU_GetStartOfPrevColumn(
468     PMENU  menu )
469 {
470     UINT  i;
471     PITEM pItem;
472 
473     if( !menu )
474 	return NO_SELECTED_ITEM;
475 
476     if( menu->iItem == 0 || menu->iItem == NO_SELECTED_ITEM )
477 	return NO_SELECTED_ITEM;
478 
479     pItem = menu->rgItems;
480     if (!pItem) return NO_SELECTED_ITEM;
481 
482     /* Find the start of the column */
483 
484     for(i = menu->iItem; i != 0 &&
485 	 !(pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
486 	--i); /* empty */
487 
488     if(i == 0)
489 	return NO_SELECTED_ITEM;
490 
491     for(--i; i != 0; --i) {
492 	if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
493 	    break;
494     }
495 
496     TRACE("ret %d.\n", i );
497 
498     return i;
499 }
500 
501 /***********************************************************************
502  *           MENU_FindItem
503  *
504  * Find a menu item. Return a pointer on the item, and modifies *hmenu
505  * in case the item was in a sub-menu.
506  */
507 PITEM FASTCALL MENU_FindItem( PMENU *pmenu, UINT *nPos, UINT wFlags )
508 {
509     MENU *menu = *pmenu;
510     ITEM *fallback = NULL;
511     UINT fallback_pos = 0;
512     UINT i;
513 
514     if (!menu) return NULL;
515 
516     if (wFlags & MF_BYPOSITION)
517     {
518         if (!menu->cItems) return NULL;
519 	if (*nPos >= menu->cItems) return NULL;
520 	return &menu->rgItems[*nPos];
521     }
522     else
523     {
524         PITEM item = menu->rgItems;
525 	for (i = 0; i < menu->cItems; i++, item++)
526 	{
527 	    if (item->spSubMenu)
528 	    {
529 		PMENU psubmenu = item->spSubMenu;//VerifyMenu(item->spSubMenu);
530 		PITEM subitem = MENU_FindItem( &psubmenu, nPos, wFlags );
531 		if (subitem)
532 		{
533 		    *pmenu = psubmenu;
534 		    return subitem;
535 		}
536 		else if (item->wID == *nPos)
537 		{
538 		    /* fallback to this item if nothing else found */
539 		    fallback_pos = i;
540 		    fallback = item;
541 		}
542 	    }
543 	    else if (item->wID == *nPos)
544 	    {
545 		*nPos = i;
546 		return item;
547 	    }
548 	}
549     }
550 
551     if (fallback)
552         *nPos = fallback_pos;
553 
554     return fallback;
555 }
556 
557 /***********************************************************************
558  *           MenuFindSubMenu
559  *
560  * Find a Sub menu. Return the position of the submenu, and modifies
561  * *hmenu in case it is found in another sub-menu.
562  * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
563  */
564 static UINT FASTCALL MENU_FindSubMenu(PMENU *menu, PMENU SubTarget )
565 {
566     UINT i;
567     PITEM item;
568 
569     item = ((PMENU)*menu)->rgItems;
570     for (i = 0; i < ((PMENU)*menu)->cItems; i++, item++)
571     {
572         if (!item->spSubMenu)
573            continue;
574         else
575         {
576            if (item->spSubMenu == SubTarget)
577            {
578               return i;
579            }
580            else
581            {
582               PMENU pSubMenu = item->spSubMenu;
583               UINT pos = MENU_FindSubMenu( &pSubMenu, SubTarget );
584               if (pos != NO_SELECTED_ITEM)
585               {
586                   *menu = pSubMenu;
587                   return pos;
588               }
589            }
590         }
591     }
592     return NO_SELECTED_ITEM;
593 }
594 
595 BOOL FASTCALL
596 IntRemoveMenuItem( PMENU pMenu, UINT nPos, UINT wFlags, BOOL bRecurse )
597 {
598     PITEM item;
599     PITEM newItems;
600 
601     TRACE("(menu=%p pos=%04x flags=%04x)\n",pMenu, nPos, wFlags);
602     if (!(item = MENU_FindItem( &pMenu, &nPos, wFlags ))) return FALSE;
603 
604     /* Remove item */
605 
606     FreeMenuText(pMenu,item);
607     if (bRecurse && item->spSubMenu)
608     {
609        IntDestroyMenuObject(item->spSubMenu, bRecurse);
610     }
611     ////// Use cAlloced with inc's of 8's....
612     if (--pMenu->cItems == 0)
613     {
614         DesktopHeapFree(pMenu->head.rpdesk, pMenu->rgItems );
615         pMenu->rgItems = NULL;
616     }
617     else
618     {
619         while (nPos < pMenu->cItems)
620         {
621             *item = *(item+1);
622             item++;
623             nPos++;
624         }
625         newItems = DesktopHeapReAlloc(pMenu->head.rpdesk, pMenu->rgItems, pMenu->cItems * sizeof(ITEM));
626         if (newItems)
627         {
628             pMenu->rgItems = newItems;
629         }
630     }
631     return TRUE;
632 }
633 
634 /**********************************************************************
635  *         MENU_InsertItem
636  *
637  * Insert (allocate) a new item into a menu.
638  */
639 ITEM *MENU_InsertItem( PMENU menu, UINT pos, UINT flags, PMENU *submenu, UINT *npos )
640 {
641     ITEM *newItems;
642 
643     /* Find where to insert new item */
644 
645     if (flags & MF_BYPOSITION) {
646         if (pos > menu->cItems)
647             pos = menu->cItems;
648     } else {
649         if (!MENU_FindItem( &menu, &pos, flags ))
650         {
651             if (submenu) *submenu = menu;
652             if (npos) *npos = pos;
653             pos = menu->cItems;
654         }
655     }
656 
657     /* Make sure that MDI system buttons stay on the right side.
658      * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
659      * regardless of their id.
660      */
661     while ( pos > 0 &&
662            (INT_PTR)menu->rgItems[pos - 1].hbmp >= (INT_PTR)HBMMENU_SYSTEM &&
663            (INT_PTR)menu->rgItems[pos - 1].hbmp <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
664         pos--;
665 
666     TRACE("inserting at %u flags %x\n", pos, flags);
667 
668     /* Create new items array */
669 
670     newItems = DesktopHeapAlloc(menu->head.rpdesk, sizeof(ITEM) * (menu->cItems+1) );
671     if (!newItems)
672     {
673         WARN("allocation failed\n" );
674         return NULL;
675     }
676     if (menu->cItems > 0)
677     {
678        /* Copy the old array into the new one */
679        if (pos > 0) RtlCopyMemory( newItems, menu->rgItems, pos * sizeof(ITEM) );
680        if (pos < menu->cItems) RtlCopyMemory( &newItems[pos+1], &menu->rgItems[pos], (menu->cItems-pos)*sizeof(ITEM) );
681        DesktopHeapFree(menu->head.rpdesk, menu->rgItems );
682     }
683     menu->rgItems = newItems;
684     menu->cItems++;
685     RtlZeroMemory( &newItems[pos], sizeof(*newItems) );
686     menu->cyMenu = 0; /* force size recalculate */
687     return &newItems[pos];
688 }
689 
690 BOOL FASTCALL
691 IntInsertMenuItem(
692     _In_ PMENU MenuObject,
693     UINT uItem,
694     BOOL fByPosition,
695     PROSMENUITEMINFO ItemInfo,
696     PUNICODE_STRING lpstr)
697 {
698    PITEM MenuItem;
699    PMENU SubMenu = NULL;
700 
701    NT_ASSERT(MenuObject != NULL);
702 
703    if (MAX_MENU_ITEMS <= MenuObject->cItems)
704    {
705       EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
706       return FALSE;
707    }
708 
709    SubMenu = MenuObject;
710 
711    if(!(MenuItem = MENU_InsertItem( SubMenu, uItem, fByPosition ? MF_BYPOSITION : MF_BYCOMMAND, &SubMenu, &uItem ))) return FALSE;
712 
713    if(!IntSetMenuItemInfo(SubMenu, MenuItem, ItemInfo, lpstr))
714    {
715       IntRemoveMenuItem(SubMenu, uItem, fByPosition ? MF_BYPOSITION : MF_BYCOMMAND, FALSE);
716       return FALSE;
717    }
718 
719    /* Force size recalculation! */
720    SubMenu->cyMenu = 0;
721    MenuItem->hbmpChecked = MenuItem->hbmpUnchecked = 0;
722 
723    TRACE("IntInsertMenuItemToList = %u %i\n", uItem, (BOOL)((INT)uItem >= 0));
724 
725    return TRUE;
726 }
727 
728 PMENU FASTCALL
729 IntCreateMenu(
730     _Out_ PHANDLE Handle,
731     _In_ BOOL IsMenuBar,
732     _In_ PDESKTOP Desktop,
733     _In_ PPROCESSINFO ppi)
734 {
735    PMENU Menu;
736 
737    Menu = (PMENU)UserCreateObject( gHandleTable,
738                                           Desktop,
739                                           ppi->ptiList,
740                                           Handle,
741                                           TYPE_MENU,
742                                           sizeof(MENU));
743    if(!Menu)
744    {
745       *Handle = 0;
746       return NULL;
747    }
748 
749    Menu->cyMax = 0; /* Default */
750    Menu->hbrBack = NULL; /* No brush */
751    Menu->dwContextHelpId = 0; /* Default */
752    Menu->dwMenuData = 0; /* Default */
753    Menu->iItem = NO_SELECTED_ITEM; // Focused item
754    Menu->fFlags = (IsMenuBar ? 0 : MNF_POPUP);
755    Menu->spwndNotify = NULL;
756    Menu->cyMenu = 0; // Height
757    Menu->cxMenu = 0; // Width
758    Menu->cItems = 0; // Item count
759    Menu->iTop = 0;
760    Menu->iMaxTop = 0;
761    Menu->cxTextAlign = 0;
762    Menu->rgItems = NULL;
763 
764    Menu->hWnd = NULL;
765    Menu->TimeToHide = FALSE;
766 
767    return Menu;
768 }
769 
770 BOOL FASTCALL
771 IntCloneMenuItems(PMENU Destination, PMENU Source)
772 {
773    PITEM MenuItem, NewMenuItem = NULL;
774    UINT i;
775 
776    if(!Source->cItems)
777       return FALSE;
778 
779    NewMenuItem = DesktopHeapAlloc(Destination->head.rpdesk, Source->cItems * sizeof(ITEM));
780    if(!NewMenuItem) return FALSE;
781 
782    RtlZeroMemory(NewMenuItem, Source->cItems * sizeof(ITEM));
783 
784    Destination->rgItems = NewMenuItem;
785 
786    MenuItem = Source->rgItems;
787    for (i = 0; i < Source->cItems; i++, MenuItem++, NewMenuItem++)
788    {
789       NewMenuItem->fType = MenuItem->fType;
790       NewMenuItem->fState = MenuItem->fState;
791       NewMenuItem->wID = MenuItem->wID;
792       NewMenuItem->spSubMenu = MenuItem->spSubMenu;
793       NewMenuItem->hbmpChecked = MenuItem->hbmpChecked;
794       NewMenuItem->hbmpUnchecked = MenuItem->hbmpUnchecked;
795       NewMenuItem->dwItemData = MenuItem->dwItemData;
796       if (MenuItem->lpstr.Length)
797       {
798          NewMenuItem->lpstr.Length = 0;
799          NewMenuItem->lpstr.MaximumLength = MenuItem->lpstr.MaximumLength;
800          NewMenuItem->lpstr.Buffer = DesktopHeapAlloc(Destination->head.rpdesk, MenuItem->lpstr.MaximumLength);
801          if (!NewMenuItem->lpstr.Buffer)
802          {
803              DesktopHeapFree(Destination->head.rpdesk, NewMenuItem);
804              break;
805          }
806          RtlCopyUnicodeString(&NewMenuItem->lpstr, &MenuItem->lpstr);
807          NewMenuItem->lpstr.Buffer[MenuItem->lpstr.Length / sizeof(WCHAR)] = 0;
808          NewMenuItem->Xlpstr = NewMenuItem->lpstr.Buffer;
809       }
810       else
811       {
812          NewMenuItem->lpstr.Buffer = MenuItem->lpstr.Buffer;
813          NewMenuItem->Xlpstr = NewMenuItem->lpstr.Buffer;
814       }
815       NewMenuItem->hbmp = MenuItem->hbmp;
816       Destination->cItems = i + 1;
817    }
818    return TRUE;
819 }
820 
821 PMENU FASTCALL
822 IntCloneMenu(PMENU Source)
823 {
824    HANDLE hMenu;
825    PMENU Menu;
826 
827    if(!Source)
828       return NULL;
829 
830    /* A menu is valid process wide. We can pass to the object manager any thread ptr */
831    Menu = (PMENU)UserCreateObject( gHandleTable,
832                                    Source->head.rpdesk,
833                                    ((PPROCESSINFO)Source->head.hTaskWow)->ptiList,
834                                    &hMenu,
835                                    TYPE_MENU,
836                                    sizeof(MENU));
837    if(!Menu)
838       return NULL;
839 
840    Menu->fFlags = Source->fFlags;
841    Menu->cyMax = Source->cyMax;
842    Menu->hbrBack = Source->hbrBack;
843    Menu->dwContextHelpId = Source->dwContextHelpId;
844    Menu->dwMenuData = Source->dwMenuData;
845    Menu->iItem = NO_SELECTED_ITEM;
846    Menu->spwndNotify = NULL;
847    Menu->cyMenu = 0;
848    Menu->cxMenu = 0;
849    Menu->cItems = 0;
850    Menu->iTop = 0;
851    Menu->iMaxTop = 0;
852    Menu->cxTextAlign = 0;
853    Menu->rgItems = NULL;
854 
855    Menu->hWnd = NULL;
856    Menu->TimeToHide = FALSE;
857 
858    IntCloneMenuItems(Menu, Source);
859 
860    return Menu;
861 }
862 
863 BOOL FASTCALL
864 IntSetMenuFlagRtoL(PMENU Menu)
865 {
866    ERR("SetMenuFlagRtoL\n");
867    Menu->fFlags |= MNF_RTOL;
868    return TRUE;
869 }
870 
871 BOOL FASTCALL
872 IntSetMenuContextHelpId(PMENU Menu, DWORD dwContextHelpId)
873 {
874    Menu->dwContextHelpId = dwContextHelpId;
875    return TRUE;
876 }
877 
878 BOOL FASTCALL
879 IntGetMenuInfo(PMENU Menu, PROSMENUINFO lpmi)
880 {
881    if(lpmi->fMask & MIM_BACKGROUND)
882       lpmi->hbrBack = Menu->hbrBack;
883    if(lpmi->fMask & MIM_HELPID)
884       lpmi->dwContextHelpID = Menu->dwContextHelpId;
885    if(lpmi->fMask & MIM_MAXHEIGHT)
886       lpmi->cyMax = Menu->cyMax;
887    if(lpmi->fMask & MIM_MENUDATA)
888       lpmi->dwMenuData = Menu->dwMenuData;
889    if(lpmi->fMask & MIM_STYLE)
890       lpmi->dwStyle = Menu->fFlags & MNS_STYLE_MASK;
891 
892    if (sizeof(MENUINFO) < lpmi->cbSize)
893    {
894      lpmi->cItems = Menu->cItems;
895 
896      lpmi->iItem = Menu->iItem;
897      lpmi->cxMenu = Menu->cxMenu;
898      lpmi->cyMenu = Menu->cyMenu;
899      lpmi->spwndNotify = Menu->spwndNotify;
900      lpmi->cxTextAlign = Menu->cxTextAlign;
901      lpmi->iTop = Menu->iTop;
902      lpmi->iMaxTop = Menu->iMaxTop;
903      lpmi->dwArrowsOn = Menu->dwArrowsOn;
904 
905      lpmi->fFlags = Menu->fFlags;
906      lpmi->Self = Menu->head.h;
907      lpmi->TimeToHide = Menu->TimeToHide;
908      lpmi->Wnd = Menu->hWnd;
909    }
910    return TRUE;
911 }
912 
913 BOOL FASTCALL
914 IntSetMenuInfo(PMENU Menu, PROSMENUINFO lpmi)
915 {
916    if(lpmi->fMask & MIM_BACKGROUND)
917       Menu->hbrBack = lpmi->hbrBack;
918    if(lpmi->fMask & MIM_HELPID)
919       Menu->dwContextHelpId = lpmi->dwContextHelpID;
920    if(lpmi->fMask & MIM_MAXHEIGHT)
921       Menu->cyMax = lpmi->cyMax;
922    if(lpmi->fMask & MIM_MENUDATA)
923       Menu->dwMenuData = lpmi->dwMenuData;
924    if(lpmi->fMask & MIM_STYLE)
925       Menu->fFlags ^= (Menu->fFlags ^ lpmi->dwStyle) & MNS_STYLE_MASK;
926    if(lpmi->fMask & MIM_APPLYTOSUBMENUS)
927    {
928       int i;
929       PITEM item = Menu->rgItems;
930       for ( i = Menu->cItems; i; i--, item++)
931       {
932          if ( item->spSubMenu )
933          {
934             IntSetMenuInfo( item->spSubMenu, lpmi);
935          }
936       }
937    }
938    if (sizeof(MENUINFO) < lpmi->cbSize)
939    {
940       Menu->iItem = lpmi->iItem;
941       Menu->cyMenu = lpmi->cyMenu;
942       Menu->cxMenu = lpmi->cxMenu;
943       Menu->spwndNotify = lpmi->spwndNotify;
944       Menu->cxTextAlign = lpmi->cxTextAlign;
945       Menu->iTop = lpmi->iTop;
946       Menu->iMaxTop = lpmi->iMaxTop;
947       Menu->dwArrowsOn = lpmi->dwArrowsOn;
948 
949       Menu->TimeToHide = lpmi->TimeToHide;
950       Menu->hWnd = lpmi->Wnd;
951    }
952    if ( lpmi->fMask & MIM_STYLE)
953    {
954       if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented wine\n");
955       if (lpmi->dwStyle & MNS_DRAGDROP)    FIXME("MNS_DRAGDROP unimplemented wine\n");
956       if (lpmi->dwStyle & MNS_MODELESS)    FIXME("MNS_MODELESS unimplemented wine\n");
957    }
958    return TRUE;
959 }
960 
961 BOOL FASTCALL
962 IntGetMenuItemInfo(PMENU Menu, /* UNUSED PARAM!! */
963                    PITEM MenuItem, PROSMENUITEMINFO lpmii)
964 {
965    NTSTATUS Status;
966 
967    if(lpmii->fMask & (MIIM_FTYPE | MIIM_TYPE))
968    {
969       lpmii->fType = MenuItem->fType;
970    }
971    if(lpmii->fMask & MIIM_BITMAP)
972    {
973       lpmii->hbmpItem = MenuItem->hbmp;
974    }
975    if(lpmii->fMask & MIIM_CHECKMARKS)
976    {
977       lpmii->hbmpChecked = MenuItem->hbmpChecked;
978       lpmii->hbmpUnchecked = MenuItem->hbmpUnchecked;
979    }
980    if(lpmii->fMask & MIIM_DATA)
981    {
982       lpmii->dwItemData = MenuItem->dwItemData;
983    }
984    if(lpmii->fMask & MIIM_ID)
985    {
986       lpmii->wID = MenuItem->wID;
987    }
988    if(lpmii->fMask & MIIM_STATE)
989    {
990       lpmii->fState = MenuItem->fState;
991    }
992    if(lpmii->fMask & MIIM_SUBMENU)
993    {
994       lpmii->hSubMenu = MenuItem->spSubMenu ? MenuItem->spSubMenu->head.h : NULL;
995    }
996 
997    if ((lpmii->fMask & MIIM_STRING) ||
998       ((lpmii->fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(lpmii->fType) == MF_STRING)))
999    {
1000       if (lpmii->dwTypeData == NULL)
1001       {
1002          lpmii->cch = MenuItem->lpstr.Length / sizeof(WCHAR);
1003       }
1004       else
1005       {  //// lpmii->lpstr can be read in user mode!!!!
1006          Status = MmCopyToCaller(lpmii->dwTypeData, MenuItem->lpstr.Buffer,
1007                                  min(lpmii->cch * sizeof(WCHAR),
1008                                      MenuItem->lpstr.MaximumLength));
1009          if (! NT_SUCCESS(Status))
1010          {
1011             SetLastNtError(Status);
1012             return FALSE;
1013          }
1014       }
1015    }
1016 
1017    if (sizeof(ROSMENUITEMINFO) == lpmii->cbSize)
1018    {
1019       lpmii->Rect.left   = MenuItem->xItem;
1020       lpmii->Rect.top    = MenuItem->yItem;
1021       lpmii->Rect.right  = MenuItem->cxItem; // Do this for now......
1022       lpmii->Rect.bottom = MenuItem->cyItem;
1023       lpmii->dxTab = MenuItem->dxTab;
1024       lpmii->lpstr = MenuItem->lpstr.Buffer;
1025       lpmii->maxBmpSize.cx = MenuItem->cxBmp;
1026       lpmii->maxBmpSize.cy = MenuItem->cyBmp;
1027    }
1028 
1029    return TRUE;
1030 }
1031 
1032 BOOL FASTCALL
1033 IntSetMenuItemInfo(PMENU MenuObject, PITEM MenuItem, PROSMENUITEMINFO lpmii, PUNICODE_STRING lpstr)
1034 {
1035    PMENU SubMenuObject;
1036    BOOL circref = FALSE;
1037 
1038    if(!MenuItem || !MenuObject || !lpmii)
1039    {
1040       return FALSE;
1041    }
1042    if ( lpmii->fMask & MIIM_FTYPE )
1043    {
1044       MenuItem->fType &= ~MENUITEMINFO_TYPE_MASK;
1045       MenuItem->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
1046    }
1047    if (lpmii->fMask & MIIM_TYPE)
1048    {
1049       #if 0 //// Done in User32.
1050       if (lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP))
1051       {
1052          ERR("IntSetMenuItemInfo: Invalid combination of fMask bits used\n");
1053          KeRosDumpStackFrames(NULL, 20);
1054          /* This does not happen on Win9x/ME */
1055          SetLastNtError( ERROR_INVALID_PARAMETER);
1056          return FALSE;
1057       }
1058       #endif
1059       /*
1060        * Delete the menu item type when changing type from
1061        * MF_STRING.
1062        */
1063       if (MenuItem->fType != lpmii->fType &&
1064             MENU_ITEM_TYPE(MenuItem->fType) == MFT_STRING)
1065       {
1066          FreeMenuText(MenuObject,MenuItem);
1067          RtlInitUnicodeString(&MenuItem->lpstr, NULL);
1068          MenuItem->Xlpstr = NULL;
1069       }
1070       if(lpmii->fType & MFT_BITMAP)
1071       {
1072          if(lpmii->hbmpItem)
1073            MenuItem->hbmp = lpmii->hbmpItem;
1074          else
1075          { /* Win 9x/Me stuff */
1076            MenuItem->hbmp = (HBITMAP)((ULONG_PTR)(LOWORD(lpmii->dwTypeData)));
1077          }
1078          lpmii->dwTypeData = 0;
1079       }
1080    }
1081    if(lpmii->fMask & MIIM_BITMAP)
1082    {
1083       MenuItem->hbmp = lpmii->hbmpItem;
1084       if (MenuItem->hbmp <= HBMMENU_POPUP_MINIMIZE && MenuItem->hbmp >= HBMMENU_CALLBACK)
1085          MenuItem->fState |= MFS_HBMMENUBMP;
1086       else
1087          MenuItem->fState &= ~MFS_HBMMENUBMP;
1088    }
1089    if(lpmii->fMask & MIIM_CHECKMARKS)
1090    {
1091       MenuItem->hbmpChecked = lpmii->hbmpChecked;
1092       MenuItem->hbmpUnchecked = lpmii->hbmpUnchecked;
1093    }
1094    if(lpmii->fMask & MIIM_DATA)
1095    {
1096       MenuItem->dwItemData = lpmii->dwItemData;
1097    }
1098    if(lpmii->fMask & MIIM_ID)
1099    {
1100       MenuItem->wID = lpmii->wID;
1101    }
1102    if(lpmii->fMask & MIIM_STATE)
1103    {
1104       /* Remove MFS_DEFAULT flag from all other menu items if this item
1105          has the MFS_DEFAULT state */
1106       if(lpmii->fState & MFS_DEFAULT)
1107          UserSetMenuDefaultItem(MenuObject, -1, 0);
1108       /* Update the menu item state flags */
1109       UpdateMenuItemState(MenuItem->fState, lpmii->fState);
1110    }
1111 
1112    if(lpmii->fMask & MIIM_SUBMENU)
1113    {
1114       if (lpmii->hSubMenu)
1115       {
1116          SubMenuObject = UserGetMenuObject(lpmii->hSubMenu);
1117          if ( SubMenuObject && !(UserObjectInDestroy(lpmii->hSubMenu)) )
1118          {
1119             //// wine Bug 12171 : Adding Popup Menu to itself! Could create endless loops.
1120             //// CORE-7967.
1121             if (MenuObject == SubMenuObject)
1122             {
1123                HANDLE hMenu;
1124                ERR("Pop Up Menu Double Trouble!\n");
1125                SubMenuObject = IntCreateMenu(&hMenu,
1126                    FALSE,
1127                    MenuObject->head.rpdesk,
1128                    (PPROCESSINFO)MenuObject->head.hTaskWow); // It will be marked.
1129                if (!SubMenuObject) return FALSE;
1130                IntReleaseMenuObject(SubMenuObject); // This will be referenced again after insertion.
1131                circref = TRUE;
1132             }
1133             if ( MENU_depth( SubMenuObject, 0) > MAXMENUDEPTH )
1134             {
1135                ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
1136                if (circref) IntDestroyMenuObject(SubMenuObject, FALSE);
1137                return FALSE;
1138             }
1139             /* Make sure the submenu is marked as a popup menu */
1140             SubMenuObject->fFlags |= MNF_POPUP;
1141             // Now fix the test_subpopup_locked_by_menu tests....
1142             if (MenuItem->spSubMenu) IntReleaseMenuObject(MenuItem->spSubMenu);
1143             MenuItem->spSubMenu = SubMenuObject;
1144             UserReferenceObject(SubMenuObject);
1145          }
1146          else
1147          {
1148             EngSetLastError( ERROR_INVALID_PARAMETER);
1149             return FALSE;
1150          }
1151       }
1152       else
1153       {  // If submenu just dereference it.
1154          if (MenuItem->spSubMenu) IntReleaseMenuObject(MenuItem->spSubMenu);
1155          MenuItem->spSubMenu = NULL;
1156       }
1157    }
1158 
1159    if ((lpmii->fMask & MIIM_STRING) ||
1160       ((lpmii->fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(lpmii->fType) == MF_STRING)))
1161    {
1162       /* free the string when used */
1163       FreeMenuText(MenuObject,MenuItem);
1164       RtlInitUnicodeString(&MenuItem->lpstr, NULL);
1165       MenuItem->Xlpstr = NULL;
1166 
1167       if(lpmii->dwTypeData && lpmii->cch && lpstr && lpstr->Buffer)
1168       {
1169          UNICODE_STRING Source;
1170 
1171          if (!NT_VERIFY(lpmii->cch <= UNICODE_STRING_MAX_CHARS))
1172          {
1173              return FALSE;
1174          }
1175 
1176          Source.Length = Source.MaximumLength = (USHORT)(lpmii->cch * sizeof(WCHAR));
1177          Source.Buffer = lpmii->dwTypeData;
1178 
1179          MenuItem->lpstr.Buffer = DesktopHeapAlloc( MenuObject->head.rpdesk, Source.Length + sizeof(WCHAR));
1180          if(MenuItem->lpstr.Buffer != NULL)
1181          {
1182             MenuItem->lpstr.Length = 0;
1183             MenuItem->lpstr.MaximumLength = Source.Length + sizeof(WCHAR);
1184             RtlCopyUnicodeString(&MenuItem->lpstr, &Source);
1185             MenuItem->lpstr.Buffer[MenuItem->lpstr.Length / sizeof(WCHAR)] = 0;
1186 
1187             MenuItem->cch = MenuItem->lpstr.Length / sizeof(WCHAR);
1188             MenuItem->Xlpstr = (USHORT*)MenuItem->lpstr.Buffer;
1189          }
1190       }
1191    }
1192 
1193    if( !(MenuObject->fFlags & MNF_SYSMENU) &&
1194        !MenuItem->Xlpstr &&
1195        !lpmii->dwTypeData &&
1196        !(MenuItem->fType & MFT_OWNERDRAW) &&
1197        !MenuItem->hbmp)
1198       MenuItem->fType |= MFT_SEPARATOR;
1199 
1200    if (sizeof(ROSMENUITEMINFO) == lpmii->cbSize)
1201    {
1202       MenuItem->xItem  = lpmii->Rect.left;
1203       MenuItem->yItem  = lpmii->Rect.top;
1204       MenuItem->cxItem = lpmii->Rect.right; // Do this for now......
1205       MenuItem->cyItem = lpmii->Rect.bottom;
1206       MenuItem->dxTab = lpmii->dxTab;
1207       lpmii->lpstr = MenuItem->lpstr.Buffer; /* Send back new allocated string or zero */
1208       MenuItem->cxBmp = lpmii->maxBmpSize.cx;
1209       MenuItem->cyBmp = lpmii->maxBmpSize.cy;
1210    }
1211 
1212    return TRUE;
1213 }
1214 
1215 
1216 UINT FASTCALL
1217 IntEnableMenuItem(PMENU MenuObject, UINT uIDEnableItem, UINT uEnable)
1218 {
1219    PITEM MenuItem;
1220    UINT res;
1221 
1222    if (!(MenuItem = MENU_FindItem( &MenuObject, &uIDEnableItem, uEnable ))) return (UINT)-1;
1223 
1224    res = MenuItem->fState & (MF_GRAYED | MF_DISABLED);
1225 
1226    MenuItem->fState ^= (res ^ uEnable) & (MF_GRAYED | MF_DISABLED);
1227 
1228    /* If the close item in the system menu change update the close button */
1229    if (res != uEnable)
1230    {
1231       switch (MenuItem->wID) // More than just close.
1232       {
1233         case SC_CLOSE:
1234         case SC_MAXIMIZE:
1235         case SC_MINIMIZE:
1236         case SC_MOVE:
1237         case SC_RESTORE:
1238         case SC_SIZE:
1239 	if (MenuObject->fFlags & MNF_SYSSUBMENU && MenuObject->spwndNotify != 0)
1240 	{
1241             //RECTL rc = MenuObject->spwndNotify->rcWindow;
1242 
1243             /* Refresh the frame to reflect the change */
1244             //IntMapWindowPoints(0, MenuObject->spwndNotify, (POINT *)&rc, 2);
1245             //rc.bottom = 0;
1246             //co_UserRedrawWindow(MenuObject->spwndNotify, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
1247 
1248             // Allow UxTheme!
1249             UserPaintCaption(MenuObject->spwndNotify, DC_BUTTONS);
1250 	}
1251 	default:
1252            break;
1253       }
1254    }
1255    return res;
1256 }
1257 
1258 DWORD FASTCALL
1259 IntCheckMenuItem(PMENU MenuObject, UINT uIDCheckItem, UINT uCheck)
1260 {
1261    PITEM MenuItem;
1262    DWORD res;
1263 
1264    if (!(MenuItem = MENU_FindItem( &MenuObject, &uIDCheckItem, uCheck ))) return -1;
1265 
1266    res = (DWORD)(MenuItem->fState & MF_CHECKED);
1267 
1268    MenuItem->fState ^= (res ^ uCheck) & MF_CHECKED;
1269 
1270    return res;
1271 }
1272 
1273 BOOL FASTCALL
1274 UserSetMenuDefaultItem(PMENU MenuObject, UINT uItem, UINT fByPos)
1275 {
1276    UINT i;
1277    PITEM MenuItem = MenuObject->rgItems;
1278 
1279    if (!MenuItem) return FALSE;
1280 
1281    /* reset all default-item flags */
1282    for (i = 0; i < MenuObject->cItems; i++, MenuItem++)
1283    {
1284        MenuItem->fState &= ~MFS_DEFAULT;
1285    }
1286 
1287    /* no default item */
1288    if(uItem == (UINT)-1)
1289    {
1290       return TRUE;
1291    }
1292    MenuItem = MenuObject->rgItems;
1293    if ( fByPos )
1294    {
1295       if ( uItem >= MenuObject->cItems ) return FALSE;
1296          MenuItem[uItem].fState |= MFS_DEFAULT;
1297       return TRUE;
1298    }
1299    else
1300    {
1301       for (i = 0; i < MenuObject->cItems; i++, MenuItem++)
1302       {
1303           if (MenuItem->wID == uItem)
1304           {
1305              MenuItem->fState |= MFS_DEFAULT;
1306              return TRUE;
1307           }
1308       }
1309 
1310    }
1311    return FALSE;
1312 }
1313 
1314 UINT FASTCALL
1315 IntGetMenuDefaultItem(PMENU MenuObject, UINT fByPos, UINT gmdiFlags, DWORD *gismc)
1316 {
1317    UINT i = 0;
1318    PITEM MenuItem = MenuObject->rgItems;
1319 
1320    /* empty menu */
1321    if (!MenuItem) return -1;
1322 
1323    while ( !( MenuItem->fState & MFS_DEFAULT ) )
1324    {
1325       i++; MenuItem++;
1326       if  (i >= MenuObject->cItems ) return -1;
1327    }
1328 
1329    /* default: don't return disabled items */
1330    if ( (!(GMDI_USEDISABLED & gmdiFlags)) && (MenuItem->fState & MFS_DISABLED )) return -1;
1331 
1332    /* search rekursiv when needed */
1333    if ( (gmdiFlags & GMDI_GOINTOPOPUPS) && MenuItem->spSubMenu )
1334    {
1335       UINT ret;
1336       (*gismc)++;
1337       ret = IntGetMenuDefaultItem( MenuItem->spSubMenu, fByPos, gmdiFlags, gismc );
1338       (*gismc)--;
1339       if ( -1 != ret ) return ret;
1340 
1341       /* when item not found in submenu, return the popup item */
1342    }
1343    return ( fByPos ) ? i : MenuItem->wID;
1344 }
1345 
1346 PMENU
1347 FASTCALL
1348 co_IntGetSubMenu(
1349   PMENU pMenu,
1350   int nPos)
1351 {
1352   PITEM pItem;
1353   if (!(pItem = MENU_FindItem( &pMenu, (UINT*)&nPos, MF_BYPOSITION ))) return NULL;
1354   return pItem->spSubMenu;
1355 }
1356 
1357 /***********************************************************************
1358  *           MenuInitSysMenuPopup
1359  *
1360  * Grey the appropriate items in System menu.
1361  */
1362 void FASTCALL MENU_InitSysMenuPopup(PMENU menu, DWORD style, DWORD clsStyle, LONG HitTest )
1363 {
1364     BOOL gray;
1365     UINT DefItem;
1366 
1367     gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
1368     IntEnableMenuItem( menu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
1369     gray = ((style & WS_MAXIMIZE) != 0);
1370     IntEnableMenuItem( menu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
1371     gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
1372     IntEnableMenuItem( menu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
1373     gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
1374     IntEnableMenuItem( menu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
1375     gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
1376     IntEnableMenuItem( menu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
1377     gray = (clsStyle & CS_NOCLOSE) != 0;
1378 
1379     /* The menu item must keep its state if it's disabled */
1380     if(gray)
1381         IntEnableMenuItem( menu, SC_CLOSE, MF_GRAYED);
1382 
1383     /* Set default menu item */
1384     if(style & WS_MINIMIZE) DefItem = SC_RESTORE;
1385     else if(HitTest == HTCAPTION) DefItem = ((style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE);
1386     else DefItem = SC_CLOSE;
1387 
1388     UserSetMenuDefaultItem(menu, DefItem, MF_BYCOMMAND);
1389 }
1390 
1391 
1392 /***********************************************************************
1393  *           MenuDrawPopupGlyph
1394  *
1395  * Draws popup magic glyphs (can be found in system menu).
1396  */
1397 static void FASTCALL
1398 MENU_DrawPopupGlyph(HDC dc, LPRECT r, INT_PTR popupMagic, BOOL inactive, BOOL hilite)
1399 {
1400   LOGFONTW lf;
1401   HFONT hFont, hOldFont;
1402   COLORREF clrsave;
1403   INT bkmode;
1404   WCHAR symbol;
1405   switch (popupMagic)
1406   {
1407   case (INT_PTR) HBMMENU_POPUP_RESTORE:
1408     symbol = '2';
1409     break;
1410   case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
1411     symbol = '0';
1412     break;
1413   case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
1414     symbol = '1';
1415     break;
1416   case (INT_PTR) HBMMENU_POPUP_CLOSE:
1417     symbol = 'r';
1418     break;
1419   default:
1420     ERR("Invalid popup magic bitmap %d\n", (int)popupMagic);
1421     return;
1422   }
1423   RtlZeroMemory(&lf, sizeof(LOGFONTW));
1424   RECTL_vInflateRect(r, -2, -2);
1425   lf.lfHeight = r->bottom - r->top;
1426   lf.lfWidth = 0;
1427   lf.lfWeight = FW_NORMAL;
1428   lf.lfCharSet = DEFAULT_CHARSET;
1429   RtlCopyMemory(lf.lfFaceName, L"Marlett", sizeof(L"Marlett"));
1430   hFont = GreCreateFontIndirectW(&lf);
1431   /* save font and text color */
1432   hOldFont = NtGdiSelectFont(dc, hFont);
1433   clrsave = GreGetTextColor(dc);
1434   bkmode = GreGetBkMode(dc);
1435   /* set color and drawing mode */
1436   IntGdiSetBkMode(dc, TRANSPARENT);
1437   if (inactive)
1438   {
1439     /* draw shadow */
1440     if (!hilite)
1441     {
1442       IntGdiSetTextColor(dc, IntGetSysColor(COLOR_HIGHLIGHTTEXT));
1443       GreTextOutW(dc, r->left + 1, r->top + 1, &symbol, 1);
1444     }
1445   }
1446   IntGdiSetTextColor(dc, IntGetSysColor(inactive ? COLOR_GRAYTEXT : (hilite ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)));
1447   /* draw selected symbol */
1448   GreTextOutW(dc, r->left, r->top, &symbol, 1);
1449   /* restore previous settings */
1450   IntGdiSetTextColor(dc, clrsave);
1451   NtGdiSelectFont(dc, hOldFont);
1452   IntGdiSetBkMode(dc, bkmode);
1453   GreDeleteObject(hFont);
1454 }
1455 
1456 /***********************************************************************
1457  *           MENU_AdjustMenuItemRect
1458  *
1459  * Adjust menu item rectangle according to scrolling state.
1460  */
1461 VOID FASTCALL
1462 MENU_AdjustMenuItemRect(PMENU menu, PRECTL rect)
1463 {
1464     if (menu->dwArrowsOn)
1465     {
1466         UINT arrow_bitmap_height;
1467         arrow_bitmap_height = gpsi->oembmi[OBI_UPARROW].cy; ///// Menu up arrow! OBM_UPARROW
1468         rect->top += arrow_bitmap_height - menu->iTop;
1469         rect->bottom += arrow_bitmap_height - menu->iTop;
1470     }
1471 }
1472 
1473 /***********************************************************************
1474  *           MENU_FindItemByCoords
1475  *
1476  * Find the item at the specified coordinates (screen coords). Does
1477  * not work for child windows and therefore should not be called for
1478  * an arbitrary system menu.
1479  */
1480 static ITEM *MENU_FindItemByCoords( MENU *menu, POINT pt, UINT *pos )
1481 {
1482     ITEM *item;
1483     UINT i;
1484     RECT rect;
1485     PWND pWnd = ValidateHwndNoErr(menu->hWnd);
1486 
1487     if (!IntGetWindowRect(pWnd, &rect)) return NULL;
1488     if (pWnd->ExStyle & WS_EX_LAYOUTRTL)
1489        pt.x = rect.right - 1 - pt.x;
1490     else
1491        pt.x -= rect.left;
1492     pt.y -= rect.top;
1493     item = menu->rgItems;
1494     for (i = 0; i < menu->cItems; i++, item++)
1495     {
1496         //rect = item->rect;
1497         rect.left   = item->xItem;
1498         rect.top    = item->yItem;
1499         rect.right  = item->cxItem; // Do this for now......
1500         rect.bottom = item->cyItem;
1501 
1502         MENU_AdjustMenuItemRect(menu, &rect);
1503 	if (RECTL_bPointInRect(&rect, pt.x, pt.y))
1504 	{
1505 	    if (pos) *pos = i;
1506 	    return item;
1507 	}
1508     }
1509     return NULL;
1510 }
1511 
1512 INT FASTCALL IntMenuItemFromPoint(PWND pWnd, HMENU hMenu, POINT ptScreen)
1513 {
1514     MENU *menu = UserGetMenuObject(hMenu);
1515     UINT pos;
1516 
1517     /*FIXME: Do we have to handle hWnd here? */
1518     if (!menu) return -1;
1519     if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
1520     return pos;
1521 }
1522 
1523 /***********************************************************************
1524  *           MenuFindItemByKey
1525  *
1526  * Find the menu item selected by a key press.
1527  * Return item id, -1 if none, -2 if we should close the menu.
1528  */
1529 static UINT FASTCALL MENU_FindItemByKey(PWND WndOwner, PMENU menu,
1530                   WCHAR Key, BOOL ForceMenuChar)
1531 {
1532   LRESULT MenuChar;
1533   WORD Flags = 0;
1534 
1535   TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)Key, Key, menu );
1536 
1537   if (!menu || !VerifyMenu(menu))
1538      menu = co_IntGetSubMenu( UserGetMenuObject(WndOwner->SystemMenu), 0 );
1539   if (menu)
1540   {
1541      ITEM *item = menu->rgItems;
1542 
1543      if ( !ForceMenuChar )
1544      {
1545         UINT i;
1546         BOOL cjk = UserGetSystemMetrics( SM_DBCSENABLED );
1547 
1548         for (i = 0; i < menu->cItems; i++, item++)
1549         {
1550            LPWSTR text = item->Xlpstr;
1551            if( text)
1552            {
1553               const WCHAR *p = text - 2;
1554               do
1555               {
1556                  const WCHAR *q = p + 2;
1557                  p = wcschr (q, '&');
1558                  if (!p && cjk) p = wcschr (q, '\036'); /* Japanese Win16 */
1559               }
1560               while (p != NULL && p [1] == '&');
1561               if (p && (towupper(p[1]) == towupper(Key))) return i;
1562            }
1563         }
1564      }
1565 
1566      Flags |= menu->fFlags & MNF_POPUP ? MF_POPUP : 0;
1567      Flags |= menu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0;
1568 
1569      MenuChar = co_IntSendMessage( UserHMGetHandle(WndOwner), WM_MENUCHAR,
1570                               MAKEWPARAM(Key, Flags), (LPARAM) UserHMGetHandle(menu));
1571      if (HIWORD(MenuChar) == MNC_EXECUTE) return LOWORD(MenuChar);
1572      if (HIWORD(MenuChar) == MNC_CLOSE) return (UINT)(-2);
1573   }
1574   return (UINT)(-1);
1575 }
1576 
1577 /***********************************************************************
1578  *           MenuGetBitmapItemSize
1579  *
1580  * Get the size of a bitmap item.
1581  */
1582 static void FASTCALL MENU_GetBitmapItemSize(PITEM lpitem, SIZE *size, PWND WndOwner)
1583 {
1584     BITMAP bm;
1585     HBITMAP bmp = lpitem->hbmp;
1586 
1587     size->cx = size->cy = 0;
1588 
1589     /* check if there is a magic menu item associated with this item */
1590     if (IS_MAGIC_BITMAP(bmp))
1591     {
1592       switch((INT_PTR) bmp)
1593         {
1594             case (INT_PTR)HBMMENU_CALLBACK:
1595             {
1596                 MEASUREITEMSTRUCT measItem;
1597                 measItem.CtlType = ODT_MENU;
1598                 measItem.CtlID = 0;
1599                 measItem.itemID = lpitem->wID;
1600                 measItem.itemWidth  = lpitem->cxItem - lpitem->xItem; //lpitem->Rect.right  - lpitem->Rect.left;
1601                 measItem.itemHeight = lpitem->cyItem - lpitem->yItem; //lpitem->Rect.bottom - lpitem->Rect.top;
1602                 measItem.itemData = lpitem->dwItemData;
1603                 co_IntSendMessage( UserHMGetHandle(WndOwner), WM_MEASUREITEM, 0, (LPARAM)&measItem);
1604                 size->cx = measItem.itemWidth;
1605                 size->cy = measItem.itemHeight;
1606                 TRACE("HBMMENU_CALLBACK Height %d Width %d\n",measItem.itemHeight,measItem.itemWidth);
1607                 return;
1608             }
1609             break;
1610 
1611           case (INT_PTR) HBMMENU_SYSTEM:
1612             if (lpitem->dwItemData)
1613               {
1614                 bmp = (HBITMAP) lpitem->dwItemData;
1615                 break;
1616               }
1617             /* fall through */
1618           case (INT_PTR) HBMMENU_MBAR_RESTORE:
1619           case (INT_PTR) HBMMENU_MBAR_MINIMIZE:
1620           case (INT_PTR) HBMMENU_MBAR_CLOSE:
1621           case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D:
1622           case (INT_PTR) HBMMENU_MBAR_CLOSE_D:
1623           case (INT_PTR) HBMMENU_POPUP_CLOSE:
1624           case (INT_PTR) HBMMENU_POPUP_RESTORE:
1625           case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
1626           case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
1627             /* FIXME: Why we need to subtract these magic values? */
1628             /* to make them smaller than the menu bar? */
1629             size->cx = UserGetSystemMetrics(SM_CXSIZE) - 2;
1630             size->cy = UserGetSystemMetrics(SM_CYSIZE) - 4;
1631             return;
1632         }
1633     }
1634 
1635     if (GreGetObject(bmp, sizeof(BITMAP), &bm))
1636     {
1637         size->cx = bm.bmWidth;
1638         size->cy = bm.bmHeight;
1639     }
1640 }
1641 
1642 /***********************************************************************
1643  *           MenuDrawBitmapItem
1644  *
1645  * Draw a bitmap item.
1646  */
1647 static void FASTCALL MENU_DrawBitmapItem(HDC hdc, PITEM lpitem, const RECT *rect,
1648                     PMENU Menu, PWND WndOwner, UINT odaction, BOOL MenuBar)
1649 {
1650     BITMAP bm;
1651     DWORD rop;
1652     HDC hdcMem;
1653     HBITMAP bmp;
1654     int w = rect->right - rect->left;
1655     int h = rect->bottom - rect->top;
1656     int bmp_xoffset = 0;
1657     int left, top;
1658     BOOL flat_menu;
1659     HBITMAP hbmToDraw = lpitem->hbmp;
1660     bmp = hbmToDraw;
1661 
1662     UserSystemParametersInfo(SPI_GETFLATMENU, 0, &flat_menu, 0);
1663 
1664     /* Check if there is a magic menu item associated with this item */
1665     if (IS_MAGIC_BITMAP(hbmToDraw))
1666     {
1667         UINT flags = 0;
1668         RECT r;
1669 
1670       r = *rect;
1671       switch ((INT_PTR)hbmToDraw)
1672       {
1673         case (INT_PTR)HBMMENU_SYSTEM:
1674             if (lpitem->dwItemData)
1675             {
1676                 if (ValidateHwndNoErr((HWND)lpitem->dwItemData))
1677                 {
1678                    ERR("Get Item Data from this Window!!!\n");
1679                 }
1680 
1681                 ERR("Draw Bitmap\n");
1682                 bmp = (HBITMAP)lpitem->dwItemData;
1683                 if (!GreGetObject( bmp, sizeof(bm), &bm )) return;
1684             }
1685             else
1686             {
1687                 PCURICON_OBJECT pIcon = NULL;
1688                 //if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
1689                 //bmp = BmpSysMenu;
1690                 //if (! GreGetObject(bmp, sizeof(bm), &bm)) return;
1691                 /* only use right half of the bitmap */
1692                 //bmp_xoffset = bm.bmWidth / 2;
1693                 //bm.bmWidth -= bmp_xoffset;
1694                 if (WndOwner)
1695                 {
1696                     pIcon = NC_IconForWindow(WndOwner);
1697                     // FIXME: NC_IconForWindow should reference it for us */
1698                     if (pIcon) UserReferenceObject(pIcon);
1699                 }
1700                 ERR("Draw ICON\n");
1701                 if (pIcon)
1702                 {
1703                    LONG cx = UserGetSystemMetrics(SM_CXSMICON);
1704                    LONG cy = UserGetSystemMetrics(SM_CYSMICON);
1705                    LONG x = rect->left - cx/2 + 1 + (rect->bottom - rect->top)/2; // this is really what Window does
1706                    LONG y = (rect->top + rect->bottom)/2 - cy/2; // center
1707                    UserDrawIconEx(hdc, x, y, pIcon, cx, cy, 0, NULL, DI_NORMAL);
1708                    UserDereferenceObject(pIcon);
1709                  }
1710                  return;
1711             }
1712             goto got_bitmap;
1713         case (INT_PTR)HBMMENU_MBAR_RESTORE:
1714             flags = DFCS_CAPTIONRESTORE;
1715             break;
1716         case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
1717             r.right += 1;
1718             flags = DFCS_CAPTIONMIN;
1719             break;
1720         case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
1721             r.right += 1;
1722             flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
1723             break;
1724         case (INT_PTR)HBMMENU_MBAR_CLOSE:
1725             flags = DFCS_CAPTIONCLOSE;
1726             break;
1727         case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
1728             flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
1729             break;
1730         case (INT_PTR)HBMMENU_CALLBACK:
1731             {
1732                 DRAWITEMSTRUCT drawItem;
1733                 POINT origorg;
1734                 drawItem.CtlType = ODT_MENU;
1735                 drawItem.CtlID = 0;
1736                 drawItem.itemID = lpitem->wID;
1737                 drawItem.itemAction = odaction;
1738                 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1739                 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1740                 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1741                 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1742                 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1743                 drawItem.itemState |= (!(Menu->fFlags & MNF_UNDERLINE))?ODS_NOACCEL:0;
1744                 drawItem.itemState |= (Menu->fFlags & MNF_INACTIVE)?ODS_INACTIVE:0;
1745                 drawItem.hwndItem = (HWND)UserHMGetHandle(Menu);
1746                 drawItem.hDC = hdc;
1747                 drawItem.rcItem = *rect;
1748                 drawItem.itemData = lpitem->dwItemData;
1749                 /* some applications make this assumption on the DC's origin */
1750                 GreSetViewportOrgEx( hdc, lpitem->xItem, lpitem->yItem, &origorg);
1751                 RECTL_vOffsetRect(&drawItem.rcItem, -(LONG)lpitem->xItem, -(LONG)lpitem->yItem);
1752                 co_IntSendMessage( UserHMGetHandle(WndOwner), WM_DRAWITEM, 0, (LPARAM)&drawItem);
1753                 GreSetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1754                 return;
1755             }
1756             break;
1757 
1758           case (INT_PTR) HBMMENU_POPUP_CLOSE:
1759           case (INT_PTR) HBMMENU_POPUP_RESTORE:
1760           case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
1761           case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
1762             MENU_DrawPopupGlyph(hdc, &r, (INT_PTR)hbmToDraw, lpitem->fState & MF_GRAYED, lpitem->fState & MF_HILITE);
1763             return;
1764       }
1765       RECTL_vInflateRect(&r, -1, -1);
1766       if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
1767       DrawFrameControl(hdc, &r, DFC_CAPTION, flags);
1768       return;
1769     }
1770 
1771     if (!bmp || !GreGetObject( bmp, sizeof(bm), &bm )) return;
1772 
1773  got_bitmap:
1774     hdcMem = NtGdiCreateCompatibleDC( hdc );
1775     NtGdiSelectBitmap( hdcMem, bmp );
1776     /* handle fontsize > bitmap_height */
1777     top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
1778     left=rect->left;
1779     rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
1780     if ((lpitem->fState & MF_HILITE) && lpitem->hbmp)
1781         IntGdiSetBkColor(hdc, IntGetSysColor(COLOR_HIGHLIGHT));
1782     if (MenuBar &&
1783         !flat_menu &&
1784         (lpitem->fState & (MF_HILITE | MF_GRAYED)) == MF_HILITE)
1785     {
1786         ++left;
1787         ++top;
1788     }
1789     NtGdiBitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop , 0, 0);
1790     IntGdiDeleteDC( hdcMem, FALSE );
1791 }
1792 
1793 LONG
1794 IntGetDialogBaseUnits(VOID)
1795 {
1796     static DWORD units;
1797 
1798     if (!units)
1799     {
1800         HDC hdc;
1801         SIZE size;
1802 
1803         if ((hdc = UserGetDCEx(NULL, NULL, DCX_CACHE)))
1804         {
1805             size.cx = IntGetCharDimensions( hdc, NULL, (PDWORD)&size.cy );
1806             if (size.cx) units = MAKELONG( size.cx, size.cy );
1807             UserReleaseDC( 0, hdc, FALSE);
1808         }
1809     }
1810     return units;
1811 }
1812 
1813 
1814 /***********************************************************************
1815  *           MenuCalcItemSize
1816  *
1817  * Calculate the size of the menu item and store it in lpitem->rect.
1818  */
1819 static void FASTCALL MENU_CalcItemSize( HDC hdc, PITEM lpitem, PMENU Menu, PWND pwndOwner,
1820                  INT orgX, INT orgY, BOOL menuBar, BOOL textandbmp)
1821 {
1822     WCHAR *p;
1823     UINT check_bitmap_width = UserGetSystemMetrics( SM_CXMENUCHECK );
1824     UINT arrow_bitmap_width;
1825     RECT Rect;
1826     INT itemheight = 0;
1827 
1828     TRACE("dc=%x owner=%x (%d,%d)\n", hdc, pwndOwner, orgX, orgY);
1829 
1830     arrow_bitmap_width = gpsi->oembmi[OBI_MNARROW].cx;
1831 
1832     MenuCharSize.cx = IntGetCharDimensions( hdc, NULL, (PDWORD)&MenuCharSize.cy );
1833 
1834     RECTL_vSetRect( &Rect, orgX, orgY, orgX, orgY );
1835 
1836     if (lpitem->fType & MF_OWNERDRAW)
1837     {
1838         MEASUREITEMSTRUCT mis;
1839         mis.CtlType    = ODT_MENU;
1840         mis.CtlID      = 0;
1841         mis.itemID     = lpitem->wID;
1842         mis.itemData   = lpitem->dwItemData;
1843         mis.itemHeight = HIWORD( IntGetDialogBaseUnits());
1844         mis.itemWidth  = 0;
1845         co_IntSendMessage( UserHMGetHandle(pwndOwner), WM_MEASUREITEM, 0, (LPARAM)&mis );
1846         /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1847          * width of a menufont character to the width of an owner-drawn menu.
1848          */
1849         Rect.right += mis.itemWidth + 2 * MenuCharSize.cx;
1850         if (menuBar) {
1851             /* under at least win95 you seem to be given a standard
1852                height for the menu and the height value is ignored */
1853             Rect.bottom += UserGetSystemMetrics(SM_CYMENUSIZE);
1854         } else
1855             Rect.bottom += mis.itemHeight;
1856         // Or this,
1857         //lpitem->cxBmp = mis.itemWidth;
1858         //lpitem->cyBmp = mis.itemHeight;
1859         TRACE("MF_OWNERDRAW Height %d Width %d\n",mis.itemHeight,mis.itemWidth);
1860         TRACE("MF_OWNERDRAW id=%04lx size=%dx%d cx %d cy %d\n",
1861                 lpitem->wID, Rect.right-Rect.left,
1862                  Rect.bottom-Rect.top, MenuCharSize.cx, MenuCharSize.cy);
1863 
1864         lpitem->xItem = Rect.left;
1865         lpitem->yItem = Rect.top;
1866         lpitem->cxItem = Rect.right;
1867         lpitem->cyItem = Rect.bottom;
1868 
1869         return;
1870     }
1871 
1872     lpitem->xItem  = orgX;
1873     lpitem->yItem  = orgY;
1874     lpitem->cxItem = orgX;
1875     lpitem->cyItem = orgY;
1876 
1877     if (lpitem->fType & MF_SEPARATOR)
1878     {
1879         lpitem->cyItem += UserGetSystemMetrics( SM_CYMENUSIZE)/2;//SEPARATOR_HEIGHT;
1880         if( !menuBar)
1881             lpitem->cxItem += arrow_bitmap_width + MenuCharSize.cx;
1882         return;
1883     }
1884 
1885     lpitem->dxTab = 0;
1886 
1887     if (lpitem->hbmp)
1888     {
1889         SIZE size;
1890 
1891         if (!menuBar) {
1892             MENU_GetBitmapItemSize(lpitem, &size, pwndOwner );
1893             /* Keep the size of the bitmap in callback mode to be able
1894              * to draw it correctly */
1895             lpitem->cxBmp = size.cx;
1896             lpitem->cyBmp = size.cy;
1897             Menu->cxTextAlign = max(Menu->cxTextAlign, size.cx);
1898             lpitem->cxItem += size.cx + 2;
1899             itemheight = size.cy + 2;
1900 
1901             if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
1902                 lpitem->cxItem += 2 * check_bitmap_width;
1903             lpitem->cxItem += 4 + MenuCharSize.cx;
1904             lpitem->dxTab = lpitem->cxItem;
1905             lpitem->cxItem += arrow_bitmap_width;
1906         } else /* hbmpItem & MenuBar */ {
1907             MENU_GetBitmapItemSize(lpitem, &size, pwndOwner );
1908             lpitem->cxItem  += size.cx;
1909             if( lpitem->Xlpstr) lpitem->cxItem  += 2;
1910             itemheight = size.cy;
1911 
1912             /* Special case: Minimize button doesn't have a space behind it. */
1913             if (lpitem->hbmp == (HBITMAP)HBMMENU_MBAR_MINIMIZE ||
1914                 lpitem->hbmp == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D)
1915             lpitem->cxItem -= 1;
1916         }
1917     }
1918     else if (!menuBar) {
1919         if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
1920              lpitem->cxItem += check_bitmap_width;
1921         lpitem->cxItem += 4 + MenuCharSize.cx;
1922         lpitem->dxTab = lpitem->cxItem;
1923         lpitem->cxItem += arrow_bitmap_width;
1924     }
1925 
1926     /* it must be a text item - unless it's the system menu */
1927     if (!(lpitem->fType & MF_SYSMENU) && lpitem->Xlpstr) {
1928         HFONT hfontOld = NULL;
1929         RECT rc;// = lpitem->Rect;
1930         LONG txtheight, txtwidth;
1931 
1932         rc.left   = lpitem->xItem;
1933         rc.top    = lpitem->yItem;
1934         rc.right  = lpitem->cxItem; // Do this for now......
1935         rc.bottom = lpitem->cyItem;
1936 
1937         if ( lpitem->fState & MFS_DEFAULT ) {
1938             hfontOld = NtGdiSelectFont( hdc, ghMenuFontBold );
1939         }
1940         if (menuBar) {
1941             txtheight = DrawTextW( hdc, lpitem->Xlpstr, -1, &rc, DT_SINGLELINE|DT_CALCRECT);
1942 
1943             lpitem->cxItem  += rc.right - rc.left;
1944             itemheight = max( max( itemheight, txtheight), UserGetSystemMetrics( SM_CYMENU) - 1);
1945 
1946             lpitem->cxItem +=  2 * MenuCharSize.cx;
1947         } else {
1948             if ((p = wcschr( lpitem->Xlpstr, '\t' )) != NULL) {
1949                 RECT tmprc = rc;
1950                 LONG tmpheight;
1951                 int n = (int)( p - lpitem->Xlpstr);
1952                 /* Item contains a tab (only meaningful in popup menus) */
1953                 /* get text size before the tab */
1954                 txtheight = DrawTextW( hdc, lpitem->Xlpstr, n, &rc,
1955                         DT_SINGLELINE|DT_CALCRECT);
1956                 txtwidth = rc.right - rc.left;
1957                 p += 1; /* advance past the Tab */
1958                 /* get text size after the tab */
1959                 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1960                         DT_SINGLELINE|DT_CALCRECT);
1961                 lpitem->dxTab += txtwidth;
1962                 txtheight = max( txtheight, tmpheight);
1963                 txtwidth += MenuCharSize.cx + /* space for the tab */
1964                     tmprc.right - tmprc.left; /* space for the short cut */
1965             } else {
1966                 txtheight = DrawTextW( hdc, lpitem->Xlpstr, -1, &rc,
1967                         DT_SINGLELINE|DT_CALCRECT);
1968                 txtwidth = rc.right - rc.left;
1969                 lpitem->dxTab += txtwidth;
1970             }
1971             lpitem->cxItem  += 2 + txtwidth;
1972             itemheight = max( itemheight,
1973 				    max( txtheight + 2, MenuCharSize.cy + 4));
1974         }
1975         if (hfontOld)
1976         {
1977            NtGdiSelectFont (hdc, hfontOld);
1978         }
1979     } else if( menuBar) {
1980         itemheight = max( itemheight, UserGetSystemMetrics(SM_CYMENU)-1);
1981     }
1982     lpitem->cyItem += itemheight;
1983     TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->xItem, lpitem->yItem, lpitem->cxItem, lpitem->cyItem);
1984 }
1985 
1986 /***********************************************************************
1987  *           MENU_GetMaxPopupHeight
1988  */
1989 static UINT
1990 MENU_GetMaxPopupHeight(PMENU lppop)
1991 {
1992     if (lppop->cyMax)
1993     {
1994        //ERR("MGMaxPH cyMax %d\n",lppop->cyMax);
1995        return lppop->cyMax;
1996     }
1997     //ERR("MGMaxPH SyMax %d\n",UserGetSystemMetrics(SM_CYSCREEN) - UserGetSystemMetrics(SM_CYBORDER));
1998     return UserGetSystemMetrics(SM_CYSCREEN) - UserGetSystemMetrics(SM_CYBORDER);
1999 }
2000 
2001 /***********************************************************************
2002  *           MenuPopupMenuCalcSize
2003  *
2004  * Calculate the size of a popup menu.
2005  */
2006 static void FASTCALL MENU_PopupMenuCalcSize(PMENU Menu, PWND WndOwner)
2007 {
2008     PITEM lpitem;
2009     HDC hdc;
2010     int start, i;
2011     int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
2012     BOOL textandbmp = FALSE;
2013 
2014     Menu->cxMenu = Menu->cyMenu = 0;
2015     if (Menu->cItems == 0) return;
2016 
2017     hdc = UserGetDCEx(NULL, NULL, DCX_CACHE);
2018 
2019     NtGdiSelectFont( hdc, ghMenuFont );
2020 
2021     start = 0;
2022     maxX = 0;
2023 
2024     Menu->cxTextAlign = 0;
2025 
2026     while (start < Menu->cItems)
2027     {
2028       lpitem = &Menu->rgItems[start];
2029       orgX = maxX;
2030       if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
2031           orgX += MENU_COL_SPACE;
2032       orgY = 0;
2033 
2034       maxTab = maxTabWidth = 0;
2035       /* Parse items until column break or end of menu */
2036       for (i = start; i < Menu->cItems; i++, lpitem++)
2037       {
2038           if (i != start &&
2039                (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
2040 
2041           MENU_CalcItemSize(hdc, lpitem, Menu, WndOwner, orgX, orgY, FALSE, textandbmp);
2042           maxX = max(maxX, lpitem->cxItem);
2043           orgY = lpitem->cyItem;
2044           if (IS_STRING_ITEM(lpitem->fType) && lpitem->dxTab )
2045           {
2046               maxTab = max( maxTab, lpitem->dxTab );
2047               maxTabWidth = max(maxTabWidth, lpitem->cxItem - lpitem->dxTab);
2048           }
2049           if( lpitem->Xlpstr && lpitem->hbmp) textandbmp = TRUE;
2050       }
2051 
2052         /* Finish the column (set all items to the largest width found) */
2053       maxX = max( maxX, maxTab + maxTabWidth );
2054       for (lpitem = &Menu->rgItems[start]; start < i; start++, lpitem++)
2055       {
2056           lpitem->cxItem = maxX;
2057            if (IS_STRING_ITEM(lpitem->fType) && lpitem->dxTab)
2058                lpitem->dxTab = maxTab;
2059       }
2060       Menu->cyMenu = max(Menu->cyMenu, orgY);
2061     }
2062 
2063     Menu->cxMenu  = maxX;
2064     /* if none of the items have both text and bitmap then
2065      * the text and bitmaps are all aligned on the left. If there is at
2066      * least one item with both text and bitmap then bitmaps are
2067      * on the left and texts left aligned with the right hand side
2068      * of the bitmaps */
2069     if( !textandbmp) Menu->cxTextAlign = 0;
2070 
2071     /* Adjust popup height if it exceeds maximum */
2072     maxHeight = MENU_GetMaxPopupHeight(Menu);
2073     Menu->iMaxTop = Menu->cyMenu;
2074     if (Menu->cyMenu >= maxHeight)
2075     {
2076        Menu->cyMenu = maxHeight;
2077        Menu->dwArrowsOn = 1;
2078     }
2079     else
2080     {
2081        Menu->dwArrowsOn = 0;
2082     }
2083     UserReleaseDC( 0, hdc, FALSE );
2084 }
2085 
2086 /***********************************************************************
2087  *           MENU_MenuBarCalcSize
2088  *
2089  * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
2090  * height is off by 1 pixel which causes lengthy window relocations when
2091  * active document window is maximized/restored.
2092  *
2093  * Calculate the size of the menu bar.
2094  */
2095 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect, PMENU lppop, PWND pwndOwner )
2096 {
2097     ITEM *lpitem;
2098     UINT start, i, helpPos;
2099     int orgX, orgY, maxY;
2100 
2101     if ((lprect == NULL) || (lppop == NULL)) return;
2102     if (lppop->cItems == 0) return;
2103     //TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
2104     lppop->cxMenu  = lprect->right - lprect->left;
2105     lppop->cyMenu = 0;
2106     maxY = lprect->top;
2107     start = 0;
2108     helpPos = ~0U;
2109     lppop->cxTextAlign = 0;
2110     while (start < lppop->cItems)
2111     {
2112 	lpitem = &lppop->rgItems[start];
2113 	orgX = lprect->left;
2114 	orgY = maxY;
2115 
2116 	  /* Parse items until line break or end of menu */
2117 	for (i = start; i < lppop->cItems; i++, lpitem++)
2118 	{
2119 	    if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
2120 	    if ((i != start) &&
2121 		(lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
2122 
2123 	    TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
2124 	    //debug_print_menuitem ("  item: ", lpitem, "");
2125 	    //MENU_CalcItemSize( hdc, lpitem, pwndOwner, orgX, orgY, TRUE, lppop );
2126             MENU_CalcItemSize(hdc, lpitem, lppop, pwndOwner, orgX, orgY, TRUE, FALSE);
2127 
2128 	    if (lpitem->cxItem > lprect->right)
2129 	    {
2130 		if (i != start) break;
2131 		else lpitem->cxItem = lprect->right;
2132 	    }
2133 	    maxY = max( maxY, lpitem->cyItem );
2134 	    orgX = lpitem->cxItem;
2135 	}
2136 
2137 	  /* Finish the line (set all items to the largest height found) */
2138 
2139 /* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
2140    HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
2141 #if 0
2142 	while (start < i) lppop->rgItems[start++].cyItem = maxY;
2143 #endif
2144 	start = i; /* This works! */
2145     }
2146 
2147     lprect->bottom = maxY + 1;
2148     lppop->cyMenu = lprect->bottom - lprect->top;
2149 
2150     /* Flush right all items between the MF_RIGHTJUSTIFY and */
2151     /* the last item (if several lines, only move the last line) */
2152     if (helpPos == ~0U) return;
2153     lpitem = &lppop->rgItems[lppop->cItems-1];
2154     orgY = lpitem->yItem;
2155     orgX = lprect->right;
2156     for (i = lppop->cItems - 1; i >= helpPos; i--, lpitem--) {
2157         if (lpitem->yItem != orgY) break;	/* Other line */
2158         if (lpitem->cxItem >= orgX) break;	/* Too far right already */
2159         lpitem->xItem += orgX - lpitem->cxItem;
2160         lpitem->cxItem = orgX;
2161         orgX = lpitem->xItem;
2162     }
2163 }
2164 
2165 /***********************************************************************
2166  *           MENU_DrawScrollArrows
2167  *
2168  * Draw scroll arrows.
2169  */
2170 static void MENU_DrawScrollArrows(PMENU lppop, HDC hdc)
2171 {
2172     UINT arrow_bitmap_height;
2173     RECT rect;
2174     UINT Flags = 0;
2175 
2176     arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy;
2177 
2178     rect.left = 0;
2179     rect.top = 0;
2180     rect.right = lppop->cxMenu;
2181     rect.bottom = arrow_bitmap_height;
2182     FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENU));
2183     DrawFrameControl(hdc, &rect, DFC_MENU, (lppop->iTop ? 0 : DFCS_INACTIVE)|DFCS_MENUARROWUP);
2184 
2185     rect.top = lppop->cyMenu - arrow_bitmap_height;
2186     rect.bottom = lppop->cyMenu;
2187     FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENU));
2188     if (!(lppop->iTop < lppop->iMaxTop - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height)))
2189        Flags = DFCS_INACTIVE;
2190     DrawFrameControl(hdc, &rect, DFC_MENU, Flags|DFCS_MENUARROWDOWN);
2191 }
2192 
2193 /***********************************************************************
2194  *           MenuDrawMenuItem
2195  *
2196  * Draw a single menu item.
2197  */
2198 static void FASTCALL MENU_DrawMenuItem(PWND Wnd, PMENU Menu, PWND WndOwner, HDC hdc,
2199                  PITEM lpitem, UINT Height, BOOL menuBar, UINT odaction)
2200 {
2201     RECT rect;
2202     PWCHAR Text;
2203     BOOL flat_menu = FALSE;
2204     int bkgnd;
2205     UINT arrow_bitmap_width = 0;
2206     //RECT bmprc;
2207 
2208     if (!menuBar) {
2209         arrow_bitmap_width  = gpsi->oembmi[OBI_MNARROW].cx;
2210     }
2211 
2212     if (lpitem->fType & MF_SYSMENU)
2213     {
2214         if (!(Wnd->style & WS_MINIMIZE))
2215         {
2216           NC_GetInsideRect(Wnd, &rect);
2217           UserDrawSysMenuButton(Wnd, hdc, &rect, lpitem->fState & (MF_HILITE | MF_MOUSESELECT));
2218 	}
2219         return;
2220     }
2221 
2222     UserSystemParametersInfo (SPI_GETFLATMENU, 0, &flat_menu, 0);
2223     bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
2224 
2225     /* Setup colors */
2226 
2227     if (lpitem->fState & MF_HILITE)
2228     {
2229         if(menuBar && !flat_menu) {
2230             IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_MENUTEXT));
2231             IntGdiSetBkColor(hdc, IntGetSysColor(COLOR_MENU));
2232         } else {
2233             if (lpitem->fState & MF_GRAYED)
2234                 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_GRAYTEXT));
2235             else
2236                 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_HIGHLIGHTTEXT));
2237             IntGdiSetBkColor(hdc, IntGetSysColor(COLOR_HIGHLIGHT));
2238         }
2239     }
2240     else
2241     {
2242         if (lpitem->fState & MF_GRAYED)
2243             IntGdiSetTextColor( hdc, IntGetSysColor( COLOR_GRAYTEXT ) );
2244         else
2245             IntGdiSetTextColor( hdc, IntGetSysColor( COLOR_MENUTEXT ) );
2246         IntGdiSetBkColor( hdc, IntGetSysColor( bkgnd ) );
2247     }
2248 
2249     //TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->Rect));
2250     //rect = lpitem->Rect;
2251     rect.left   = lpitem->xItem;
2252     rect.top    = lpitem->yItem;
2253     rect.right  = lpitem->cxItem; // Do this for now......
2254     rect.bottom = lpitem->cyItem;
2255 
2256     MENU_AdjustMenuItemRect(Menu, &rect);
2257 
2258     if (lpitem->fType & MF_OWNERDRAW)
2259     {
2260         /*
2261         ** Experimentation under Windows reveals that an owner-drawn
2262         ** menu is given the rectangle which includes the space it requested
2263         ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
2264         ** and a popup-menu arrow.  This is the value of lpitem->rect.
2265         ** Windows will leave all drawing to the application except for
2266         ** the popup-menu arrow.  Windows always draws that itself, after
2267         ** the menu owner has finished drawing.
2268         */
2269         DRAWITEMSTRUCT dis;
2270         COLORREF old_bk, old_text;
2271 
2272         dis.CtlType   = ODT_MENU;
2273         dis.CtlID     = 0;
2274         dis.itemID    = lpitem->wID;
2275         dis.itemData  = (DWORD)lpitem->dwItemData;
2276         dis.itemState = 0;
2277         if (lpitem->fState & MF_CHECKED)  dis.itemState |= ODS_CHECKED;
2278         if (lpitem->fState & MF_DEFAULT)  dis.itemState |= ODS_DEFAULT;
2279         if (lpitem->fState & MF_DISABLED) dis.itemState |= ODS_DISABLED;
2280         if (lpitem->fState & MF_GRAYED)   dis.itemState |= ODS_GRAYED | ODS_DISABLED;
2281         if (lpitem->fState & MF_HILITE)   dis.itemState |= ODS_SELECTED;
2282         if (!(Menu->fFlags & MNF_UNDERLINE)) dis.itemState |= ODS_NOACCEL;
2283         if (Menu->fFlags & MNF_INACTIVE) dis.itemState |= ODS_INACTIVE;
2284         dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
2285         dis.hwndItem   = (HWND) UserHMGetHandle(Menu);
2286         dis.hDC        = hdc;
2287         dis.rcItem     = rect;
2288         TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
2289 	        "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", Wnd,
2290 	        dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
2291 	        dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
2292 	        dis.rcItem.bottom);
2293         TRACE("Ownerdraw: Width %d Height %d\n", dis.rcItem.right-dis.rcItem.left, dis.rcItem.bottom-dis.rcItem.top);
2294         old_bk = GreGetBkColor(hdc);
2295         old_text = GreGetTextColor(hdc);
2296         co_IntSendMessage(UserHMGetHandle(WndOwner), WM_DRAWITEM, 0, (LPARAM) &dis);
2297         IntGdiSetBkColor(hdc, old_bk);
2298         IntGdiSetTextColor(hdc, old_text);
2299         /* Draw the popup-menu arrow */
2300         if (!menuBar && lpitem->spSubMenu)
2301         {
2302             RECT rectTemp;
2303             RtlCopyMemory(&rectTemp, &rect, sizeof(RECT));
2304             rectTemp.left = rectTemp.right - UserGetSystemMetrics(SM_CXMENUCHECK);
2305             DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
2306         }
2307         return;
2308     }
2309 
2310     if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
2311 
2312     if (lpitem->fState & MF_HILITE)
2313     {
2314         if (flat_menu)
2315         {
2316             RECTL_vInflateRect (&rect, -1, -1);
2317             FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENUHILIGHT));
2318             RECTL_vInflateRect (&rect, 1, 1);
2319             FrameRect(hdc, &rect, IntGetSysColorBrush(COLOR_HIGHLIGHT));
2320         }
2321         else
2322         {
2323             if (menuBar)
2324             {
2325                 FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENU));
2326                 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
2327             }
2328             else
2329             {
2330                 FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_HIGHLIGHT));
2331             }
2332         }
2333     }
2334     else
2335         FillRect( hdc, &rect, IntGetSysColorBrush(bkgnd) );
2336 
2337     IntGdiSetBkMode( hdc, TRANSPARENT );
2338 
2339     /* vertical separator */
2340     if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
2341     {
2342         HPEN oldPen;
2343         RECT rc = rect;
2344 
2345         rc.left -= 3;//MENU_COL_SPACE / 2 + 1; == 3!!
2346         rc.top = 3;
2347         rc.bottom = Height - 3;
2348         if (flat_menu)
2349         {
2350             oldPen = NtGdiSelectPen( hdc, NtGdiGetStockObject(DC_PEN) );
2351             IntSetDCPenColor(hdc, IntGetSysColor(COLOR_BTNSHADOW));
2352             GreMoveTo( hdc, rc.left, rc.top, NULL );
2353             NtGdiLineTo( hdc, rc.left, rc.bottom );
2354             NtGdiSelectPen( hdc, oldPen );
2355         }
2356         else
2357             DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
2358     }
2359 
2360     /* horizontal separator */
2361     if (lpitem->fType & MF_SEPARATOR)
2362     {
2363         HPEN oldPen;
2364         RECT rc = rect;
2365 
2366         rc.left++;
2367         rc.right--;
2368         rc.top = (rc.top + rc.bottom) / 2 - 1;
2369         if (flat_menu)
2370         {
2371             oldPen = NtGdiSelectPen( hdc, NtGdiGetStockObject(DC_PEN) );
2372             IntSetDCPenColor( hdc, IntGetSysColor(COLOR_BTNSHADOW));
2373             GreMoveTo( hdc, rc.left, rc.top, NULL );
2374             NtGdiLineTo( hdc, rc.right, rc.top );
2375             NtGdiSelectPen( hdc, oldPen );
2376         }
2377         else
2378             DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
2379         return;
2380     }
2381 #if 0
2382     /* helper lines for debugging */
2383     /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
2384     FrameRect(hdc, &rect, NtGdiGetStockObject(BLACK_BRUSH));
2385     NtGdiSelectPen(hdc, NtGdiGetStockObject(DC_PEN));
2386     IntSetDCPenColor(hdc, IntGetSysColor(COLOR_WINDOWFRAME));
2387     GreMoveTo(hdc, rect.left, (rect.top + rect.bottom) / 2, NULL);
2388     NtGdiLineTo(hdc, rect.right, (rect.top + rect.bottom) / 2);
2389 #endif
2390 #if 0 // breaks mdi menu bar icons.
2391     if (lpitem->hbmp) {
2392         /* calculate the bitmap rectangle in coordinates relative
2393          * to the item rectangle */
2394         if( menuBar) {
2395             if( lpitem->hbmp == HBMMENU_CALLBACK)
2396                 bmprc.left = 3;
2397             else
2398                 bmprc.left = lpitem->Xlpstr ? MenuCharSize.cx : 0;
2399         }
2400         else if ((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)
2401             bmprc.left = 4;
2402         else if ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)
2403             bmprc.left = 2;
2404         else
2405             bmprc.left = 4 + UserGetSystemMetrics(SM_CXMENUCHECK);
2406 
2407         bmprc.right =  bmprc.left + lpitem->cxBmp;
2408 
2409         if( menuBar && !(lpitem->hbmp == HBMMENU_CALLBACK))
2410             bmprc.top = 0;
2411         else
2412             bmprc.top = (rect.bottom - rect.top - lpitem->cyBmp) / 2;
2413 
2414         bmprc.bottom =  bmprc.top + lpitem->cyBmp;
2415     }
2416 #endif
2417     if (!menuBar)
2418     {
2419         HBITMAP bm;
2420         INT y = rect.top + rect.bottom;
2421         RECT rc = rect;
2422         BOOL checked = FALSE;
2423         UINT check_bitmap_width = UserGetSystemMetrics( SM_CXMENUCHECK );
2424         UINT check_bitmap_height = UserGetSystemMetrics( SM_CYMENUCHECK );
2425         /* Draw the check mark
2426          *
2427          * FIXME:
2428          * Custom checkmark bitmaps are monochrome but not always 1bpp.
2429          */
2430         if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)) {
2431             bm = (lpitem->fState & MF_CHECKED) ? lpitem->hbmpChecked :
2432                 lpitem->hbmpUnchecked;
2433             if (bm)  /* we have a custom bitmap */
2434             {
2435                 HDC hdcMem = NtGdiCreateCompatibleDC( hdc );
2436 
2437                 NtGdiSelectBitmap( hdcMem, bm );
2438                 NtGdiBitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
2439                         check_bitmap_width, check_bitmap_height,
2440                         hdcMem, 0, 0, SRCCOPY, 0,0);
2441                 IntGdiDeleteDC( hdcMem, FALSE );
2442                 checked = TRUE;
2443             }
2444             else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
2445             {
2446                 RECT r;
2447                 r = rect;
2448                 r.right = r.left + check_bitmap_width;
2449                 DrawFrameControl( hdc, &r, DFC_MENU,
2450                                  (lpitem->fType & MFT_RADIOCHECK) ?
2451                                  DFCS_MENUBULLET : DFCS_MENUCHECK);
2452                 checked = TRUE;
2453             }
2454         }
2455         if ( lpitem->hbmp )//&& !( checked && ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)))
2456         {
2457             RECT bmpRect = rect;
2458             if (!((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP) && !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
2459                 bmpRect.left += check_bitmap_width + 2;
2460             if (!(checked && ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)))
2461             {
2462                 bmpRect.right = bmpRect.left + lpitem->cxBmp;
2463                 MENU_DrawBitmapItem(hdc, lpitem, &bmpRect, Menu, WndOwner, odaction, menuBar);
2464             }
2465         }
2466         /* Draw the popup-menu arrow */
2467         if (lpitem->spSubMenu)
2468         {
2469             RECT rectTemp;
2470             RtlCopyMemory(&rectTemp, &rect, sizeof(RECT));
2471             rectTemp.left = rectTemp.right - check_bitmap_width;
2472             DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
2473         }
2474         rect.left += 4;
2475         if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
2476             rect.left += check_bitmap_width;
2477         rect.right -= arrow_bitmap_width;
2478     }
2479     else if( lpitem->hbmp)
2480     { /* Draw the bitmap */
2481         MENU_DrawBitmapItem(hdc, lpitem, &rect/*bmprc*/, Menu, WndOwner, odaction, menuBar);
2482     }
2483 
2484     /* process text if present */
2485     if (lpitem->Xlpstr)
2486     {
2487         int i = 0;
2488         HFONT hfontOld = 0;
2489 
2490         UINT uFormat = menuBar ?
2491                        DT_CENTER | DT_VCENTER | DT_SINGLELINE :
2492                        DT_LEFT | DT_VCENTER | DT_SINGLELINE;
2493 
2494         if (((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP))
2495              rect.left += max(0, (int)(Menu->cxTextAlign - UserGetSystemMetrics(SM_CXMENUCHECK)));
2496         else
2497              rect.left += Menu->cxTextAlign;
2498 
2499         if ( lpitem->fState & MFS_DEFAULT )
2500         {
2501             hfontOld = NtGdiSelectFont(hdc, ghMenuFontBold);
2502         }
2503 
2504         if (menuBar) {
2505             if( lpitem->hbmp)
2506               rect.left += lpitem->cxBmp;
2507             if( !(lpitem->hbmp == HBMMENU_CALLBACK))
2508               rect.left += MenuCharSize.cx;
2509             rect.right -= MenuCharSize.cx;
2510         }
2511 
2512         Text = lpitem->Xlpstr;
2513         if(Text)
2514         {
2515             for (i = 0; Text[i]; i++)
2516                 if (Text[i] == L'\t' || Text[i] == L'\b')
2517                     break;
2518         }
2519 
2520         if (menuBar &&
2521             !flat_menu &&
2522             (lpitem->fState & (MF_HILITE | MF_GRAYED)) == MF_HILITE)
2523         {
2524             RECTL_vOffsetRect(&rect, +1, +1);
2525         }
2526 
2527         if (!menuBar)
2528             --rect.bottom;
2529 
2530         if(lpitem->fState & MF_GRAYED)
2531         {
2532             if (!(lpitem->fState & MF_HILITE) )
2533             {
2534                 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
2535                 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_BTNHIGHLIGHT));
2536                 DrawTextW( hdc, Text, i, &rect, uFormat );
2537                 --rect.left; --rect.top; --rect.right; --rect.bottom;
2538             }
2539             IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_BTNSHADOW));
2540         }
2541         DrawTextW( hdc, Text, i, &rect, uFormat);
2542 
2543         /* paint the shortcut text */
2544         if (!menuBar && L'\0' != Text[i])  /* There's a tab or flush-right char */
2545         {
2546             if (L'\t' == Text[i])
2547             {
2548                 rect.left = lpitem->dxTab;
2549                 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
2550             }
2551             else
2552             {
2553                 rect.right = lpitem->dxTab;
2554                 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
2555             }
2556 
2557             if (lpitem->fState & MF_GRAYED)
2558             {
2559                 if (!(lpitem->fState & MF_HILITE) )
2560                 {
2561                     ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
2562                     IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_BTNHIGHLIGHT));
2563                     DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat);
2564                     --rect.left; --rect.top; --rect.right; --rect.bottom;
2565                 }
2566                 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_BTNSHADOW));
2567             }
2568             DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat );
2569         }
2570 
2571         if (!menuBar)
2572             ++rect.bottom;
2573 
2574         if (menuBar &&
2575             !flat_menu &&
2576             (lpitem->fState & (MF_HILITE | MF_GRAYED)) == MF_HILITE)
2577         {
2578             RECTL_vOffsetRect(&rect, -1, -1);
2579         }
2580 
2581         if (hfontOld)
2582         {
2583            NtGdiSelectFont (hdc, hfontOld);
2584         }
2585     }
2586 }
2587 
2588 /***********************************************************************
2589  *           MenuDrawPopupMenu
2590  *
2591  * Paint a popup menu.
2592  */
2593 static void FASTCALL MENU_DrawPopupMenu(PWND wnd, HDC hdc, PMENU menu )
2594 {
2595     HBRUSH hPrevBrush = 0, brush = IntGetSysColorBrush(COLOR_MENU);
2596     RECT rect;
2597 
2598     TRACE("DPM wnd=%p dc=%p menu=%p\n", wnd, hdc, menu);
2599 
2600     IntGetClientRect( wnd, &rect );
2601 
2602     if (menu && menu->hbrBack) brush = menu->hbrBack;
2603     if((hPrevBrush = NtGdiSelectBrush( hdc, brush ))
2604         && (NtGdiSelectFont( hdc, ghMenuFont)))
2605     {
2606         HPEN hPrevPen;
2607 
2608         /* FIXME: Maybe we don't have to fill the background manually */
2609         FillRect(hdc, &rect, brush);
2610 
2611         hPrevPen = NtGdiSelectPen( hdc, NtGdiGetStockObject( NULL_PEN ) );
2612         if ( hPrevPen )
2613         {
2614             TRACE("hmenu %p Style %08x\n", UserHMGetHandle(menu), (menu->fFlags & MNS_STYLE_MASK));
2615             /* draw menu items */
2616             if (menu && menu->cItems)
2617             {
2618                 ITEM *item;
2619                 UINT u;
2620 
2621                 item = menu->rgItems;
2622                 for( u = menu->cItems; u > 0; u--, item++)
2623                 {
2624                     MENU_DrawMenuItem(wnd, menu, menu->spwndNotify, hdc, item,
2625                                          menu->cyMenu, FALSE, ODA_DRAWENTIRE);
2626                 }
2627                 /* draw scroll arrows */
2628                 if (menu->dwArrowsOn)
2629                 {
2630                    MENU_DrawScrollArrows(menu, hdc);
2631                 }
2632             }
2633         }
2634         else
2635         {
2636             NtGdiSelectBrush( hdc, hPrevBrush );
2637         }
2638     }
2639 }
2640 
2641 /**********************************************************************
2642  *         MENU_IsMenuActive
2643  */
2644 PWND MENU_IsMenuActive(VOID)
2645 {
2646    return ValidateHwndNoErr(top_popup);
2647 }
2648 
2649 /**********************************************************************
2650  *         MENU_EndMenu
2651  *
2652  * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2653  *
2654  * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2655  */
2656 void MENU_EndMenu( PWND pwnd )
2657 {
2658     PMENU menu = NULL;
2659     menu = UserGetMenuObject(top_popup_hmenu);
2660     if ( menu && ( UserHMGetHandle(pwnd) == menu->hWnd || pwnd == menu->spwndNotify ) )
2661     {
2662        if (fInsideMenuLoop && top_popup)
2663        {
2664           fInsideMenuLoop = FALSE;
2665 
2666           if (fInEndMenu)
2667           {
2668              ERR("Already in End loop\n");
2669              return;
2670           }
2671 
2672           fInEndMenu = TRUE;
2673           UserPostMessage( top_popup, WM_CANCELMODE, 0, 0);
2674        }
2675     }
2676 }
2677 
2678 DWORD WINAPI
2679 IntDrawMenuBarTemp(PWND pWnd, HDC hDC, LPRECT Rect, PMENU pMenu, HFONT Font)
2680 {
2681   UINT i;
2682   HFONT FontOld = NULL;
2683   BOOL flat_menu = FALSE;
2684 
2685   UserSystemParametersInfo(SPI_GETFLATMENU, 0, &flat_menu, 0);
2686 
2687   if (!pMenu)
2688   {
2689       pMenu = UserGetMenuObject(UlongToHandle(pWnd->IDMenu));
2690   }
2691 
2692   if (!Font)
2693   {
2694       Font = ghMenuFont;
2695   }
2696 
2697   if (Rect == NULL || !pMenu)
2698   {
2699       return UserGetSystemMetrics(SM_CYMENU);
2700   }
2701 
2702   TRACE("(%x, %x, %p, %x, %x)\n", pWnd, hDC, Rect, pMenu, Font);
2703 
2704   FontOld = NtGdiSelectFont(hDC, Font);
2705 
2706   if (pMenu->cyMenu == 0)
2707   {
2708       MENU_MenuBarCalcSize(hDC, Rect, pMenu, pWnd);
2709   }
2710 
2711   Rect->bottom = Rect->top + pMenu->cyMenu;
2712 
2713   FillRect(hDC, Rect, IntGetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2714 
2715   NtGdiSelectPen(hDC, NtGdiGetStockObject(DC_PEN));
2716   IntSetDCPenColor(hDC, IntGetSysColor(COLOR_3DFACE));
2717   GreMoveTo(hDC, Rect->left, Rect->bottom - 1, NULL);
2718   NtGdiLineTo(hDC, Rect->right, Rect->bottom - 1);
2719 
2720   if (pMenu->cItems == 0)
2721   {
2722       NtGdiSelectFont(hDC, FontOld);
2723       return UserGetSystemMetrics(SM_CYMENU);
2724   }
2725 
2726   for (i = 0; i < pMenu->cItems; i++)
2727   {
2728       MENU_DrawMenuItem(pWnd, pMenu, pWnd, hDC, &pMenu->rgItems[i], pMenu->cyMenu, TRUE, ODA_DRAWENTIRE);
2729   }
2730 
2731   NtGdiSelectFont(hDC, FontOld);
2732 
2733   return pMenu->cyMenu;
2734 }
2735 
2736 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, PWND pWnd, BOOL suppress_draw )
2737 {
2738     HFONT hfontOld = 0;
2739     PMENU lppop = UserGetMenuObject(UlongToHandle(pWnd->IDMenu));
2740 
2741     if (lppop == NULL)
2742     {
2743         // No menu. Do not reserve any space
2744         return 0;
2745     }
2746 
2747     if (lprect == NULL)
2748     {
2749         return UserGetSystemMetrics(SM_CYMENU);
2750     }
2751 
2752     if (suppress_draw)
2753     {
2754        hfontOld = NtGdiSelectFont(hDC, ghMenuFont);
2755 
2756        MENU_MenuBarCalcSize(hDC, lprect, lppop, pWnd);
2757 
2758        lprect->bottom = lprect->top + lppop->cyMenu;
2759 
2760        if (hfontOld) NtGdiSelectFont( hDC, hfontOld);
2761 
2762        return lppop->cyMenu;
2763     }
2764     else
2765     {
2766        return IntDrawMenuBarTemp(pWnd, hDC, lprect, lppop, NULL);
2767     }
2768 }
2769 
2770 /***********************************************************************
2771  *           MENU_InitPopup
2772  *
2773  * Popup menu initialization before WM_ENTERMENULOOP.
2774  */
2775 static BOOL MENU_InitPopup( PWND pWndOwner, PMENU menu, UINT flags )
2776 {
2777     PWND pWndCreated;
2778     PPOPUPMENU pPopupMenu;
2779     CREATESTRUCTW Cs;
2780     LARGE_STRING WindowName;
2781     UNICODE_STRING ClassName;
2782     DWORD ex_style = WS_EX_PALETTEWINDOW | WS_EX_DLGMODALFRAME;
2783 
2784     TRACE("owner=%p hmenu=%p\n", pWndOwner, menu);
2785 
2786     menu->spwndNotify = pWndOwner;
2787 
2788     if (flags & TPM_LAYOUTRTL || pWndOwner->ExStyle & WS_EX_LAYOUTRTL)
2789        ex_style |= WS_EX_LAYOUTRTL;
2790 
2791     ClassName.Buffer = WC_MENU;
2792     ClassName.Length = 0;
2793 
2794     RtlZeroMemory(&WindowName, sizeof(WindowName));
2795     RtlZeroMemory(&Cs, sizeof(Cs));
2796     Cs.style = WS_POPUP | WS_CLIPSIBLINGS | WS_BORDER;
2797     Cs.dwExStyle = ex_style;
2798     Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
2799     Cs.lpszName = (LPCWSTR) &WindowName;
2800     Cs.lpszClass = (LPCWSTR) &ClassName;
2801     Cs.lpCreateParams = UserHMGetHandle(menu);
2802     Cs.hwndParent = UserHMGetHandle(pWndOwner);
2803 
2804     /* NOTE: In Windows, top menu popup is not owned. */
2805     pWndCreated = co_UserCreateWindowEx( &Cs, &ClassName, &WindowName, NULL);
2806 
2807     if( !pWndCreated ) return FALSE;
2808 
2809     //
2810     //  Setup pop up menu structure.
2811     //
2812     menu->hWnd = UserHMGetHandle(pWndCreated);
2813 
2814     pPopupMenu = ((PMENUWND)pWndCreated)->ppopupmenu;
2815 
2816     pPopupMenu->spwndActivePopup = pWndCreated; // top_popup = MenuInfo.Wnd or menu->hWnd
2817     pPopupMenu->spwndNotify = pWndOwner;        // Same as MenuInfo.spwndNotify(which could be wrong) or menu->hwndOwner
2818     //pPopupMenu->spmenu = menu; Should be set up already from WM_CREATE!
2819 
2820     pPopupMenu->fIsTrackPopup = !!(flags & TPM_POPUPMENU);
2821     pPopupMenu->fIsSysMenu    = !!(flags & TPM_SYSTEM_MENU);
2822     pPopupMenu->fNoNotify     = !!(flags & TPM_NONOTIFY);
2823     pPopupMenu->fRightButton  = !!(flags & TPM_RIGHTBUTTON);
2824     pPopupMenu->fSynchronous  = !!(flags & TPM_RETURNCMD);
2825 
2826     if (pPopupMenu->fRightButton)
2827        pPopupMenu->fFirstClick = !!(UserGetKeyState(VK_RBUTTON) & 0x8000);
2828     else
2829        pPopupMenu->fFirstClick = !!(UserGetKeyState(VK_LBUTTON) & 0x8000);
2830 
2831     if (gpsi->aiSysMet[SM_MENUDROPALIGNMENT] ||
2832         menu->fFlags & MNF_RTOL)
2833     {
2834        pPopupMenu->fDroppedLeft = TRUE;
2835     }
2836     return TRUE;
2837 }
2838 
2839 
2840 #define SHOW_DEBUGRECT      0
2841 
2842 #if SHOW_DEBUGRECT
2843 static void DebugRect(const RECT* rectl, COLORREF color)
2844 {
2845     HBRUSH brush;
2846     RECT rr;
2847     HDC hdc;
2848 
2849     if (!rectl)
2850         return;
2851 
2852     hdc = UserGetDCEx(NULL, 0, DCX_USESTYLE);
2853 
2854     brush = IntGdiCreateSolidBrush(color);
2855 
2856     rr = *rectl;
2857     RECTL_vInflateRect(&rr, 1, 1);
2858     FrameRect(hdc, rectl, brush);
2859     FrameRect(hdc, &rr, brush);
2860 
2861     NtGdiDeleteObjectApp(brush);
2862     UserReleaseDC(NULL, hdc, TRUE);
2863 }
2864 
2865 static void DebugPoint(INT x, INT y, COLORREF color)
2866 {
2867     RECT r1 = {x-10, y, x+10, y};
2868     RECT r2 = {x, y-10, x, y+10};
2869     DebugRect(&r1, color);
2870     DebugRect(&r2, color);
2871 }
2872 #endif
2873 
2874 static BOOL RECTL_Intersect(const RECT* pRect, INT x, INT y, UINT width, UINT height)
2875 {
2876     RECT other = {x, y, x + width, y + height};
2877     RECT dum;
2878 
2879     return RECTL_bIntersectRect(&dum, pRect, &other);
2880 }
2881 
2882 static BOOL MENU_MoveRect(UINT flags, INT* x, INT* y, INT width, INT height, const RECT* pExclude, PMONITOR monitor)
2883 {
2884     /* Figure out if we should move vertical or horizontal */
2885     if (flags & TPM_VERTICAL)
2886     {
2887         /* Move in the vertical direction: TPM_BOTTOMALIGN means drop it above, otherways drop it below */
2888         if (flags & TPM_BOTTOMALIGN)
2889         {
2890             if (pExclude->top - height >= monitor->rcMonitor.top)
2891             {
2892                 *y = pExclude->top - height;
2893                 return TRUE;
2894             }
2895         }
2896         else
2897         {
2898             if (pExclude->bottom + height < monitor->rcMonitor.bottom)
2899             {
2900                 *y = pExclude->bottom;
2901                 return TRUE;
2902             }
2903         }
2904     }
2905     else
2906     {
2907         /* Move in the horizontal direction: TPM_RIGHTALIGN means drop it to the left, otherways go right */
2908         if (flags & TPM_RIGHTALIGN)
2909         {
2910             if (pExclude->left - width >= monitor->rcMonitor.left)
2911             {
2912                 *x = pExclude->left - width;
2913                 return TRUE;
2914             }
2915         }
2916         else
2917         {
2918             if (pExclude->right + width < monitor->rcMonitor.right)
2919             {
2920                 *x = pExclude->right;
2921                 return TRUE;
2922             }
2923         }
2924     }
2925     return FALSE;
2926 }
2927 
2928 /***********************************************************************
2929  *           MenuShowPopup
2930  *
2931  * Display a popup menu.
2932  */
2933 static BOOL FASTCALL MENU_ShowPopup(PWND pwndOwner, PMENU menu, UINT id, UINT flags,
2934                               INT x, INT y, const RECT* pExclude)
2935 {
2936     UINT width, height;
2937     POINT ptx;
2938     PMONITOR monitor;
2939     PWND pWnd;
2940     USER_REFERENCE_ENTRY Ref;
2941     BOOL bIsPopup = (flags & TPM_POPUPMENU) != 0;
2942 
2943     TRACE("owner=%p menu=%p id=0x%04x x=0x%04x y=0x%04x\n",
2944           pwndOwner, menu, id, x, y);
2945 
2946     if (menu->iItem != NO_SELECTED_ITEM)
2947     {
2948         menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
2949         menu->iItem = NO_SELECTED_ITEM;
2950     }
2951 
2952 #if SHOW_DEBUGRECT
2953     if (pExclude)
2954         DebugRect(pExclude, RGB(255, 0, 0));
2955 #endif
2956 
2957     menu->dwArrowsOn = 0;
2958     MENU_PopupMenuCalcSize(menu, pwndOwner);
2959 
2960     /* adjust popup menu pos so that it fits within the desktop */
2961 
2962     width = menu->cxMenu + UserGetSystemMetrics(SM_CXDLGFRAME) * 2;
2963     height = menu->cyMenu + UserGetSystemMetrics(SM_CYDLGFRAME) * 2;
2964 
2965     if (flags & TPM_LAYOUTRTL)
2966         flags ^= TPM_RIGHTALIGN;
2967 
2968     if (flags & TPM_RIGHTALIGN)
2969         x -= width;
2970     if (flags & TPM_CENTERALIGN)
2971         x -= width / 2;
2972 
2973     if (flags & TPM_BOTTOMALIGN)
2974         y -= height;
2975     if (flags & TPM_VCENTERALIGN)
2976         y -= height / 2;
2977 
2978     /* FIXME: should use item rect */
2979     ptx.x = x;
2980     ptx.y = y;
2981 #if SHOW_DEBUGRECT
2982     DebugPoint(x, y, RGB(0, 0, 255));
2983 #endif
2984     monitor = UserMonitorFromPoint( ptx, MONITOR_DEFAULTTONEAREST );
2985 
2986     /* We are off the right side of the screen */
2987     if (x + width > monitor->rcMonitor.right)
2988     {
2989         if ((x - width) < monitor->rcMonitor.left || x >= monitor->rcMonitor.right)
2990             x = monitor->rcMonitor.right - width;
2991         else
2992             x -= width;
2993     }
2994 
2995     /* We are off the left side of the screen */
2996     if (x < monitor->rcMonitor.left)
2997     {
2998         /* Re-orient the menu around the x-axis */
2999         x += width;
3000 
3001         if (x < monitor->rcMonitor.left || x >= monitor->rcMonitor.right || bIsPopup)
3002             x = monitor->rcMonitor.left;
3003     }
3004 
3005     /* Same here, but then the top */
3006     if (y < monitor->rcMonitor.top)
3007     {
3008         y += height;
3009 
3010         if (y < monitor->rcMonitor.top || y >= monitor->rcMonitor.bottom || bIsPopup)
3011             y = monitor->rcMonitor.top;
3012     }
3013 
3014     /* And the bottom */
3015     if (y + height > monitor->rcMonitor.bottom)
3016     {
3017         if ((y - height) < monitor->rcMonitor.top || y >= monitor->rcMonitor.bottom)
3018             y = monitor->rcMonitor.bottom - height;
3019         else
3020             y -= height;
3021     }
3022 
3023     if (pExclude)
3024     {
3025         RECT Cleaned;
3026 
3027         if (RECTL_bIntersectRect(&Cleaned, pExclude, &monitor->rcMonitor) &&
3028             RECTL_Intersect(&Cleaned, x, y, width, height))
3029         {
3030             UINT flag_mods[] = {
3031                 0,                                                  /* First try the 'normal' way */
3032                 TPM_BOTTOMALIGN | TPM_RIGHTALIGN,                   /* Then try the opposite side */
3033                 TPM_VERTICAL,                                       /* Then swap horizontal / vertical */
3034                 TPM_BOTTOMALIGN | TPM_RIGHTALIGN | TPM_VERTICAL,    /* Then the other side again (still swapped hor/ver) */
3035             };
3036 
3037             UINT n;
3038             for (n = 0; n < RTL_NUMBER_OF(flag_mods); ++n)
3039             {
3040                 INT tx = x;
3041                 INT ty = y;
3042 
3043                 /* Try to move a bit around */
3044                 if (MENU_MoveRect(flags ^ flag_mods[n], &tx, &ty, width, height, &Cleaned, monitor) &&
3045                     !RECTL_Intersect(&Cleaned, tx, ty, width, height))
3046                 {
3047                     x = tx;
3048                     y = ty;
3049                     break;
3050                 }
3051             }
3052             /* If none worked, we go with the original x/y */
3053         }
3054     }
3055 
3056 #if SHOW_DEBUGRECT
3057     {
3058         RECT rr = {x, y, x + width, y + height};
3059         DebugRect(&rr, RGB(0, 255, 0));
3060     }
3061 #endif
3062 
3063     pWnd = ValidateHwndNoErr( menu->hWnd );
3064 
3065     if (!pWnd)
3066     {
3067        ERR("menu->hWnd bad hwnd %p\n",menu->hWnd);
3068        return FALSE;
3069     }
3070 
3071     if (!top_popup) {
3072         top_popup = menu->hWnd;
3073         top_popup_hmenu = UserHMGetHandle(menu);
3074     }
3075 
3076     /* Display the window */
3077     UserRefObjectCo(pWnd, &Ref);
3078     co_WinPosSetWindowPos( pWnd, HWND_TOPMOST, x, y, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE);
3079 
3080     co_IntUpdateWindows(pWnd, RDW_ALLCHILDREN, FALSE);
3081 
3082     IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, pWnd, OBJID_CLIENT, CHILDID_SELF, 0);
3083     UserDerefObjectCo(pWnd);
3084 
3085     return TRUE;
3086 }
3087 
3088 /***********************************************************************
3089  *           MENU_EnsureMenuItemVisible
3090  */
3091 void MENU_EnsureMenuItemVisible(PMENU lppop, UINT wIndex, HDC hdc)
3092 {
3093     USER_REFERENCE_ENTRY Ref;
3094     if (lppop->dwArrowsOn)
3095     {
3096         ITEM *item = &lppop->rgItems[wIndex];
3097         UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
3098         UINT nOldPos = lppop->iTop;
3099         RECT rc;
3100         UINT arrow_bitmap_height;
3101         PWND pWnd = ValidateHwndNoErr(lppop->hWnd);
3102 
3103         IntGetClientRect(pWnd, &rc);
3104 
3105         arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy;
3106 
3107         rc.top += arrow_bitmap_height;
3108         rc.bottom -= arrow_bitmap_height;
3109 
3110         nMaxHeight -= UserGetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
3111         UserRefObjectCo(pWnd, &Ref);
3112         if (item->cyItem > lppop->iTop + nMaxHeight)
3113         {
3114             lppop->iTop = item->cyItem - nMaxHeight;
3115             IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc);
3116             MENU_DrawScrollArrows(lppop, hdc);
3117             //ERR("Scroll Down iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
3118         }
3119         else if (item->yItem < lppop->iTop)
3120         {
3121             lppop->iTop = item->yItem;
3122             IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc);
3123             MENU_DrawScrollArrows(lppop, hdc);
3124             //ERR("Scroll Up   iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
3125         }
3126         UserDerefObjectCo(pWnd);
3127     }
3128 }
3129 
3130 /***********************************************************************
3131  *           MenuSelectItem
3132  */
3133 static void FASTCALL MENU_SelectItem(PWND pwndOwner, PMENU menu, UINT wIndex,
3134                                     BOOL sendMenuSelect, PMENU topmenu)
3135 {
3136     HDC hdc;
3137     PWND pWnd;
3138 
3139     TRACE("M_SI: owner=%p menu=%p index=0x%04x select=0x%04x\n", pwndOwner, menu, wIndex, sendMenuSelect);
3140 
3141     if (!menu || !menu->cItems) return;
3142 
3143     pWnd = ValidateHwndNoErr(menu->hWnd);
3144 
3145     if (!pWnd) return;
3146 
3147     if (menu->iItem == wIndex) return;
3148 
3149     if (menu->fFlags & MNF_POPUP)
3150        hdc = UserGetDCEx(pWnd, 0, DCX_USESTYLE);
3151     else
3152        hdc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW);
3153 
3154     if (!top_popup) {
3155         top_popup = menu->hWnd;                  //pPopupMenu->spwndActivePopup or
3156                                                  //pPopupMenu->fIsTrackPopup set pPopupMenu->spwndPopupMenu;
3157         top_popup_hmenu = UserHMGetHandle(menu); //pPopupMenu->spmenu
3158     }
3159 
3160     NtGdiSelectFont( hdc, ghMenuFont );
3161 
3162      /* Clear previous highlighted item */
3163     if (menu->iItem != NO_SELECTED_ITEM)
3164     {
3165         menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
3166         MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc, &menu->rgItems[menu->iItem],
3167                        menu->cyMenu, !(menu->fFlags & MNF_POPUP),
3168                        ODA_SELECT);
3169     }
3170 
3171     /* Highlight new item (if any) */
3172     menu->iItem = wIndex;
3173     if (menu->iItem != NO_SELECTED_ITEM)
3174     {
3175         if (!(menu->rgItems[wIndex].fType & MF_SEPARATOR))
3176         {
3177              menu->rgItems[wIndex].fState |= MF_HILITE;
3178              MENU_EnsureMenuItemVisible(menu, wIndex, hdc);
3179              MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc,
3180                                &menu->rgItems[wIndex], menu->cyMenu, !(menu->fFlags & MNF_POPUP), ODA_SELECT);
3181         }
3182         if (sendMenuSelect)
3183         {
3184            ITEM *ip = &menu->rgItems[menu->iItem];
3185            WPARAM wParam = MAKEWPARAM( ip->spSubMenu ? wIndex : ip->wID,
3186                                        ip->fType | ip->fState |
3187                                       (ip->spSubMenu ? MF_POPUP : 0) |
3188                                       (menu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
3189 
3190            co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(menu));
3191         }
3192     }
3193     else if (sendMenuSelect)
3194     {
3195         if (topmenu)
3196         {
3197             int pos;
3198             pos = MENU_FindSubMenu(&topmenu, menu);
3199             if (pos != NO_SELECTED_ITEM)
3200             {
3201                ITEM *ip = &topmenu->rgItems[pos];
3202                WPARAM wParam = MAKEWPARAM( Pos, ip->fType | ip->fState |
3203                                            (ip->spSubMenu ? MF_POPUP : 0) |
3204                                            (topmenu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
3205 
3206                co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(topmenu));
3207             }
3208         }
3209     }
3210     UserReleaseDC(pWnd, hdc, FALSE);
3211 }
3212 
3213 /***********************************************************************
3214  *           MenuMoveSelection
3215  *
3216  * Moves currently selected item according to the Offset parameter.
3217  * If there is no selection then it should select the last item if
3218  * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
3219  */
3220 static void FASTCALL MENU_MoveSelection(PWND pwndOwner, PMENU menu, INT offset)
3221 {
3222     INT i;
3223 
3224     TRACE("pwnd=%x menu=%x off=0x%04x\n", pwndOwner, menu, offset);
3225 
3226     if ((!menu) || (!menu->rgItems)) return;
3227 
3228     if ( menu->iItem != NO_SELECTED_ITEM )
3229     {
3230 	if ( menu->cItems == 1 )
3231 	   return;
3232 	else
3233 	for (i = menu->iItem + offset ; i >= 0 && i < menu->cItems
3234 					    ; i += offset)
3235 	    if (!(menu->rgItems[i].fType & MF_SEPARATOR))
3236 	    {
3237 		MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 );
3238 		return;
3239 	    }
3240     }
3241 
3242     for ( i = (offset > 0) ? 0 : menu->cItems - 1;
3243 		  i >= 0 && i < menu->cItems ; i += offset)
3244 	if (!(menu->rgItems[i].fType & MF_SEPARATOR))
3245 	{
3246 	    MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 );
3247 	    return;
3248 	}
3249 }
3250 
3251 /***********************************************************************
3252  *           MenuHideSubPopups
3253  *
3254  * Hide the sub-popup menus of this menu.
3255  */
3256 static void FASTCALL MENU_HideSubPopups(PWND pWndOwner, PMENU Menu,
3257                                BOOL SendMenuSelect, UINT wFlags)
3258 {
3259   TRACE("owner=%x menu=%x 0x%04x\n", pWndOwner, Menu, SendMenuSelect);
3260 
3261   if ( Menu && top_popup )
3262   {
3263       PITEM Item;
3264 
3265       if (Menu->iItem != NO_SELECTED_ITEM)
3266       {
3267          Item = &Menu->rgItems[Menu->iItem];
3268          if (!(Item->spSubMenu) ||
3269              !(Item->fState & MF_MOUSESELECT)) return;
3270          Item->fState &= ~MF_MOUSESELECT;
3271       }
3272       else
3273          return;
3274 
3275       if (Item->spSubMenu)
3276       {
3277           PWND pWnd;
3278           if (!VerifyMenu(Item->spSubMenu)) return;
3279           pWnd = ValidateHwndNoErr(Item->spSubMenu->hWnd);
3280           MENU_HideSubPopups(pWndOwner, Item->spSubMenu, FALSE, wFlags);
3281           MENU_SelectItem(pWndOwner, Item->spSubMenu, NO_SELECTED_ITEM, SendMenuSelect, NULL);
3282           TRACE("M_HSP top p hm %p  pWndOwner IDMenu %p\n",top_popup_hmenu,pWndOwner->IDMenu);
3283           co_UserDestroyWindow(pWnd);
3284 
3285           /* Native returns handle to destroyed window */
3286           if (!(wFlags & TPM_NONOTIFY))
3287           {
3288              co_IntSendMessage( UserHMGetHandle(pWndOwner), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(Item->spSubMenu),
3289                                  MAKELPARAM(0, IS_SYSTEM_MENU(Item->spSubMenu)) );
3290           }
3291           ////
3292           // Call WM_UNINITMENUPOPUP FIRST before destroy!!
3293           // Fixes todo_wine User32 test menu.c line 2239 GetMenuBarInfo callback....
3294           //
3295           Item->spSubMenu->hWnd = NULL;
3296           ////
3297       }
3298   }
3299 }
3300 
3301 /***********************************************************************
3302  *           MenuShowSubPopup
3303  *
3304  * Display the sub-menu of the selected item of this menu.
3305  * Return the handle of the submenu, or menu if no submenu to display.
3306  */
3307 static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFirst, UINT Flags)
3308 {
3309   RECT Rect, ParentRect;
3310   ITEM *Item;
3311   HDC Dc;
3312   PWND pWnd;
3313 
3314   TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, Menu, SelectFirst);
3315 
3316   if (!Menu) return Menu;
3317 
3318   if (Menu->iItem == NO_SELECTED_ITEM) return Menu;
3319 
3320   Item = &Menu->rgItems[Menu->iItem];
3321   if (!(Item->spSubMenu) || (Item->fState & (MF_GRAYED | MF_DISABLED)))
3322       return Menu;
3323 
3324   /* message must be sent before using item,
3325      because nearly everything may be changed by the application ! */
3326 
3327   /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3328   if (!(Flags & TPM_NONOTIFY))
3329   {
3330       co_IntSendMessage(UserHMGetHandle(WndOwner), WM_INITMENUPOPUP,
3331                         (WPARAM) UserHMGetHandle(Item->spSubMenu),
3332                          MAKELPARAM(Menu->iItem, IS_SYSTEM_MENU(Menu)));
3333   }
3334 
3335   Item = &Menu->rgItems[Menu->iItem];
3336   //Rect = ItemInfo.Rect;
3337   Rect.left   = Item->xItem;
3338   Rect.top    = Item->yItem;
3339   Rect.right  = Item->cxItem; // Do this for now......
3340   Rect.bottom = Item->cyItem;
3341 
3342   pWnd = ValidateHwndNoErr(Menu->hWnd);
3343 
3344   /* Grab the rect of our (entire) parent menu, so we can try to not overlap it */
3345   if (Menu->fFlags & MNF_POPUP)
3346   {
3347     if (!IntGetWindowRect(pWnd, &ParentRect))
3348     {
3349         ERR("No pWnd\n");
3350         ParentRect = Rect;
3351     }
3352 
3353     /* Ensure we can slightly overlap our parent */
3354     RECTL_vInflateRect(&ParentRect, -UserGetSystemMetrics(SM_CXEDGE) * 2, 0);
3355   }
3356   else
3357   {
3358     /* Inside the menu bar, we do not want to grab the entire window... */
3359     ParentRect = Rect;
3360     if (pWnd)
3361         RECTL_vOffsetRect(&ParentRect, pWnd->rcWindow.left, pWnd->rcWindow.top);
3362   }
3363 
3364   /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
3365   if (!(Item->fState & MF_HILITE))
3366   {
3367       if (Menu->fFlags & MNF_POPUP) Dc = UserGetDCEx(pWnd, NULL, DCX_USESTYLE);
3368       else Dc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW);
3369 
3370       NtGdiSelectFont(Dc, ghMenuFont);
3371 
3372       Item->fState |= MF_HILITE;
3373       MENU_DrawMenuItem(pWnd, Menu, WndOwner, Dc, Item, Menu->cyMenu,
3374                        !(Menu->fFlags & MNF_POPUP), ODA_DRAWENTIRE);
3375 
3376       UserReleaseDC(pWnd, Dc, FALSE);
3377   }
3378 
3379   if (!Item->yItem && !Item->xItem && !Item->cyItem && !Item->cxItem)
3380   {
3381       Item->xItem  = Rect.left;
3382       Item->yItem  = Rect.top;
3383       Item->cxItem = Rect.right; // Do this for now......
3384       Item->cyItem = Rect.bottom;
3385   }
3386   Item->fState |= MF_MOUSESELECT;
3387 
3388   if (IS_SYSTEM_MENU(Menu))
3389   {
3390       MENU_InitSysMenuPopup(Item->spSubMenu, pWnd->style, pWnd->pcls->style, HTSYSMENU);
3391 
3392       NC_GetSysPopupPos(pWnd, &Rect);
3393       /* Ensure we do not overlap this */
3394       ParentRect = Rect;
3395       if (Flags & TPM_LAYOUTRTL) Rect.left = Rect.right;
3396       Rect.top = Rect.bottom;
3397       Rect.right = UserGetSystemMetrics(SM_CXSIZE);
3398       Rect.bottom = UserGetSystemMetrics(SM_CYSIZE);
3399   }
3400   else
3401   {
3402       IntGetWindowRect(pWnd, &Rect);
3403       if (Menu->fFlags & MNF_POPUP)
3404       {
3405           RECT rc;
3406           rc.left   = Item->xItem;
3407           rc.top    = Item->yItem;
3408           rc.right  = Item->cxItem; // Do this for now......
3409           rc.bottom = Item->cyItem;
3410 
3411           MENU_AdjustMenuItemRect(Menu, &rc);
3412 
3413           /* The first item in the popup menu has to be at the
3414              same y position as the focused menu item */
3415           if(Flags & TPM_LAYOUTRTL)
3416              Rect.left += UserGetSystemMetrics(SM_CXDLGFRAME);
3417           else
3418              Rect.left += rc.right - UserGetSystemMetrics(SM_CXDLGFRAME);
3419 
3420           Rect.top += rc.top;
3421       }
3422       else
3423       {
3424           if(Flags & TPM_LAYOUTRTL)
3425               Rect.left += Rect.right - Item->xItem; //ItemInfo.Rect.left;
3426           else
3427               Rect.left += Item->xItem; //ItemInfo.Rect.left;
3428           Rect.top += Item->cyItem; //ItemInfo.Rect.bottom;
3429           Rect.right  = Item->cxItem - Item->xItem; //ItemInfo.Rect.right - ItemInfo.Rect.left;
3430           Rect.bottom = Item->cyItem - Item->yItem; //ItemInfo.Rect.bottom - ItemInfo.Rect.top;
3431       }
3432   }
3433 
3434   /* Next menu does not need to be shown vertical anymore */
3435   if (Menu->fFlags & MNF_POPUP)
3436       Flags &= (~TPM_VERTICAL);
3437 
3438 
3439 
3440   /* use default alignment for submenus */
3441   Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
3442 
3443   MENU_InitPopup( WndOwner, Item->spSubMenu, Flags );
3444 
3445   MENU_ShowPopup( WndOwner, Item->spSubMenu, Menu->iItem, Flags,
3446                 Rect.left, Rect.top, &ParentRect);
3447   if (SelectFirst)
3448   {
3449       MENU_MoveSelection(WndOwner, Item->spSubMenu, ITEM_NEXT);
3450   }
3451   return Item->spSubMenu;
3452 }
3453 
3454 /***********************************************************************
3455  *           MenuExecFocusedItem
3456  *
3457  * Execute a menu item (for instance when user pressed Enter).
3458  * Return the wID of the executed item. Otherwise, -1 indicating
3459  * that no menu item was executed, -2 if a popup is shown;
3460  * Have to receive the flags for the TrackPopupMenu options to avoid
3461  * sending unwanted message.
3462  *
3463  */
3464 static INT FASTCALL MENU_ExecFocusedItem(MTRACKER *pmt, PMENU Menu, UINT Flags)
3465 {
3466   PITEM Item;
3467 
3468   TRACE("%p menu=%p\n", pmt, Menu);
3469 
3470   if (!Menu || !Menu->cItems || Menu->iItem == NO_SELECTED_ITEM)
3471   {
3472       return -1;
3473   }
3474 
3475   Item = &Menu->rgItems[Menu->iItem];
3476 
3477   TRACE("%p %08x %p\n", Menu, Item->wID, Item->spSubMenu);
3478 
3479   if (!(Item->spSubMenu))
3480   {
3481       if (!(Item->fState & (MF_GRAYED | MF_DISABLED)) && !(Item->fType & MF_SEPARATOR))
3482       {
3483           /* If TPM_RETURNCMD is set you return the id, but
3484             do not send a message to the owner */
3485           if (!(Flags & TPM_RETURNCMD))
3486           {
3487               if (Menu->fFlags & MNF_SYSMENU)
3488               {
3489                   UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_SYSCOMMAND, Item->wID,
3490                                MAKELPARAM((SHORT) pmt->Pt.x, (SHORT) pmt->Pt.y));
3491               }
3492               else
3493               {
3494                   DWORD dwStyle = ((Menu->fFlags & MNS_STYLE_MASK) | ( pmt->TopMenu ? (pmt->TopMenu->fFlags & MNS_STYLE_MASK) : 0) );
3495 
3496                   if (dwStyle & MNS_NOTIFYBYPOS)
3497                       UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_MENUCOMMAND, Menu->iItem, (LPARAM)UserHMGetHandle(Menu));
3498                   else
3499                       UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_COMMAND, Item->wID, 0);
3500               }
3501           }
3502           return Item->wID;
3503       }
3504   }
3505   else
3506   {
3507       pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, Menu, TRUE, Flags);
3508       return -2;
3509   }
3510 
3511   return -1;
3512 }
3513 
3514 /***********************************************************************
3515  *           MenuSwitchTracking
3516  *
3517  * Helper function for menu navigation routines.
3518  */
3519 static void FASTCALL MENU_SwitchTracking(MTRACKER* pmt, PMENU PtMenu, UINT Index, UINT wFlags)
3520 {
3521   TRACE("%x menu=%x 0x%04x\n", pmt, PtMenu, Index);
3522 
3523   if ( pmt->TopMenu != PtMenu &&
3524       !((PtMenu->fFlags | pmt->TopMenu->fFlags) & MNF_POPUP) )
3525   {
3526       /* both are top level menus (system and menu-bar) */
3527       MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags);
3528       MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, NO_SELECTED_ITEM, FALSE, NULL);
3529       pmt->TopMenu = PtMenu;
3530   }
3531   else
3532   {
3533       MENU_HideSubPopups(pmt->OwnerWnd, PtMenu, FALSE, wFlags);
3534   }
3535 
3536   MENU_SelectItem(pmt->OwnerWnd, PtMenu, Index, TRUE, NULL);
3537 }
3538 
3539 /***********************************************************************
3540  *           MenuButtonDown
3541  *
3542  * Return TRUE if we can go on with menu tracking.
3543  */
3544 static BOOL FASTCALL MENU_ButtonDown(MTRACKER* pmt, PMENU PtMenu, UINT Flags)
3545 {
3546   TRACE("%x PtMenu=%p\n", pmt, PtMenu);
3547 
3548   if (PtMenu)
3549   {
3550       UINT id = 0;
3551       PITEM item;
3552       if (IS_SYSTEM_MENU(PtMenu))
3553       {
3554          item = PtMenu->rgItems;
3555       }
3556       else
3557       {
3558          item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &id );
3559       }
3560 
3561       if (item)
3562       {
3563           if (PtMenu->iItem != id)
3564               MENU_SwitchTracking(pmt, PtMenu, id, Flags);
3565 
3566           /* If the popup menu is not already "popped" */
3567           if (!(item->fState & MF_MOUSESELECT))
3568           {
3569               pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags);
3570           }
3571 
3572           return TRUE;
3573       }
3574       /* Else the click was on the menu bar, finish the tracking */
3575   }
3576   return FALSE;
3577 }
3578 
3579 /***********************************************************************
3580  *           MenuButtonUp
3581  *
3582  * Return the value of MenuExecFocusedItem if
3583  * the selected item was not a popup. Else open the popup.
3584  * A -1 return value indicates that we go on with menu tracking.
3585  *
3586  */
3587 static INT FASTCALL MENU_ButtonUp(MTRACKER *pmt, PMENU PtMenu, UINT Flags)
3588 {
3589   TRACE("%p pmenu=%x\n", pmt, PtMenu);
3590 
3591   if (PtMenu)
3592   {
3593       UINT Id = 0;
3594       ITEM *item;
3595 
3596       if ( IS_SYSTEM_MENU(PtMenu) )
3597       {
3598           item = PtMenu->rgItems;
3599       }
3600       else
3601       {
3602           item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &Id );
3603       }
3604 
3605       if (item && ( PtMenu->iItem == Id))
3606       {
3607           if (!(item->spSubMenu))
3608           {
3609               INT ExecutedMenuId = MENU_ExecFocusedItem( pmt, PtMenu, Flags);
3610               if (ExecutedMenuId == -1 || ExecutedMenuId == -2) return -1;
3611               return ExecutedMenuId;
3612           }
3613 
3614           /* If we are dealing with the menu bar                  */
3615           /* and this is a click on an already "popped" item:     */
3616           /* Stop the menu tracking and close the opened submenus */
3617           if (pmt->TopMenu == PtMenu && PtMenu->TimeToHide)
3618           {
3619               return 0;
3620           }
3621       }
3622       if ( IntGetMenu(PtMenu->hWnd) == PtMenu )
3623       {
3624           PtMenu->TimeToHide = TRUE;
3625       }
3626   }
3627   return -1;
3628 }
3629 
3630 /***********************************************************************
3631  *           MenuPtMenu
3632  *
3633  * Walks menu chain trying to find a menu pt maps to.
3634  */
3635 static PMENU FASTCALL MENU_PtMenu(PMENU menu, POINT pt)
3636 {
3637   PITEM pItem;
3638   PMENU ret = NULL;
3639 
3640   if (!menu) return NULL;
3641 
3642   /* try subpopup first (if any) */
3643   if (menu->iItem != NO_SELECTED_ITEM)
3644   {
3645      pItem = menu->rgItems;
3646      if ( pItem ) pItem = &pItem[menu->iItem];
3647      if ( pItem && pItem->spSubMenu && pItem->fState & MF_MOUSESELECT)
3648      {
3649         ret = MENU_PtMenu( pItem->spSubMenu, pt);
3650      }
3651   }
3652 
3653   /* check the current window (avoiding WM_HITTEST) */
3654   if (!ret)
3655   {
3656      PWND pWnd = ValidateHwndNoErr(menu->hWnd);
3657      INT ht = GetNCHitEx(pWnd, pt);
3658      if ( menu->fFlags & MNF_POPUP )
3659      {
3660         if (ht != HTNOWHERE && ht != HTERROR) ret = menu;
3661      }
3662      else if (ht == HTSYSMENU)
3663         ret = get_win_sys_menu(menu->hWnd);
3664      else if (ht == HTMENU)
3665         ret = IntGetMenu( menu->hWnd );
3666   }
3667   return ret;
3668 }
3669 
3670 /***********************************************************************
3671  *           MenuMouseMove
3672  *
3673  * Return TRUE if we can go on with menu tracking.
3674  */
3675 static BOOL FASTCALL MENU_MouseMove(MTRACKER *pmt, PMENU PtMenu, UINT Flags)
3676 {
3677   UINT Index = NO_SELECTED_ITEM;
3678 
3679   if ( PtMenu )
3680   {
3681       if (IS_SYSTEM_MENU(PtMenu))
3682       {
3683           Index = 0;
3684           //// ReactOS only HACK: CORE-2338
3685           // Windows tracks mouse moves to the system menu but does not open it.
3686           // Only keyboard tracking can do that.
3687           //
3688           TRACE("SystemMenu\n");
3689           return TRUE; // Stay inside the Loop!
3690       }
3691       else
3692           MENU_FindItemByCoords( PtMenu, pmt->Pt, &Index );
3693   }
3694 
3695   if (Index == NO_SELECTED_ITEM)
3696   {
3697       MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NO_SELECTED_ITEM, TRUE, pmt->TopMenu);
3698   }
3699   else if (PtMenu->iItem != Index)
3700   {
3701       MENU_SwitchTracking(pmt, PtMenu, Index, Flags);
3702       pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags);
3703   }
3704   return TRUE;
3705 }
3706 
3707 /***********************************************************************
3708  *           MenuGetSubPopup
3709  *
3710  * Return the handle of the selected sub-popup menu (if any).
3711  */
3712 static PMENU MENU_GetSubPopup( PMENU menu )
3713 {
3714     ITEM *item;
3715 
3716     if ((!menu) || (menu->iItem == NO_SELECTED_ITEM)) return 0;
3717 
3718     item = &menu->rgItems[menu->iItem];
3719     if (item && (item->spSubMenu) && (item->fState & MF_MOUSESELECT))
3720     {
3721        return item->spSubMenu;
3722     }
3723     return 0;
3724 }
3725 
3726 /***********************************************************************
3727  *           MenuDoNextMenu
3728  *
3729  * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
3730  */
3731 static LRESULT FASTCALL MENU_DoNextMenu(MTRACKER* pmt, UINT Vk, UINT wFlags)
3732 {
3733     BOOL atEnd = FALSE;
3734 
3735     /* When skipping left, we need to do something special after the
3736        first menu.                                                  */
3737     if (Vk == VK_LEFT && pmt->TopMenu->iItem == 0)
3738     {
3739         atEnd = TRUE;
3740     }
3741     /* When skipping right, for the non-system menu, we need to
3742        handle the last non-special menu item (ie skip any window
3743        icons such as MDI maximize, restore or close)             */
3744     else if ((Vk == VK_RIGHT) && !IS_SYSTEM_MENU(pmt->TopMenu))
3745     {
3746         UINT i = pmt->TopMenu->iItem + 1;
3747         while (i < pmt->TopMenu->cItems) {
3748             if ((pmt->TopMenu->rgItems[i].wID >= SC_SIZE &&
3749                  pmt->TopMenu->rgItems[i].wID <= SC_RESTORE)) {
3750                 i++;
3751             } else break;
3752         }
3753         if (i == pmt->TopMenu->cItems) {
3754             atEnd = TRUE;
3755         }
3756     }
3757     /* When skipping right, we need to cater for the system menu */
3758     else if ((Vk == VK_RIGHT) && IS_SYSTEM_MENU(pmt->TopMenu))
3759     {
3760         if (pmt->TopMenu->iItem == (pmt->TopMenu->cItems - 1)) {
3761             atEnd = TRUE;
3762         }
3763     }
3764 
3765    if ( atEnd )
3766    {
3767       MDINEXTMENU NextMenu;
3768       PMENU MenuTmp;
3769       PWND pwndTemp;
3770       HMENU hNewMenu;
3771       HWND hNewWnd;
3772       UINT Id = 0;
3773 
3774       MenuTmp = (IS_SYSTEM_MENU(pmt->TopMenu)) ? co_IntGetSubMenu(pmt->TopMenu, 0) : pmt->TopMenu;
3775       NextMenu.hmenuIn = UserHMGetHandle(MenuTmp);
3776       NextMenu.hmenuNext = NULL;
3777       NextMenu.hwndNext = NULL;
3778       co_IntSendMessage(UserHMGetHandle(pmt->OwnerWnd), WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
3779 
3780       TRACE("%p [%p] -> %p [%p]\n",
3781              pmt->CurrentMenu, pmt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
3782 
3783       if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
3784       {
3785           hNewWnd = UserHMGetHandle(pmt->OwnerWnd);
3786           if (IS_SYSTEM_MENU(pmt->TopMenu))
3787           {
3788               /* switch to the menu bar */
3789 
3790               if (pmt->OwnerWnd->style & WS_CHILD || !(MenuTmp = IntGetMenu(hNewWnd))) return FALSE;
3791 
3792               if (Vk == VK_LEFT)
3793               {
3794                   Id = MenuTmp->cItems - 1;
3795 
3796                   /* Skip backwards over any system predefined icons,
3797                      eg. MDI close, restore etc icons                 */
3798                    while ((Id > 0) &&
3799                           (MenuTmp->rgItems[Id].wID >= SC_SIZE &&
3800                            MenuTmp->rgItems[Id].wID <= SC_RESTORE)) Id--;
3801 
3802               }
3803               hNewMenu = UserHMGetHandle(MenuTmp);
3804           }
3805           else if (pmt->OwnerWnd->style & WS_SYSMENU)
3806           {
3807               /* switch to the system menu */
3808               MenuTmp = get_win_sys_menu(hNewWnd);
3809               if (MenuTmp) hNewMenu = UserHMGetHandle(MenuTmp);
3810           }
3811           else
3812               return FALSE;
3813       }
3814       else    /* application returned a new menu to switch to */
3815       {
3816           hNewMenu = NextMenu.hmenuNext;
3817           hNewWnd = NextMenu.hwndNext;
3818 
3819           if ((MenuTmp = UserGetMenuObject(hNewMenu)) && (pwndTemp = ValidateHwndNoErr(hNewWnd)))
3820           {
3821               if ( pwndTemp->style & WS_SYSMENU && (get_win_sys_menu(hNewWnd) == MenuTmp) )
3822               {
3823                   /* get the real system menu */
3824                   MenuTmp = get_win_sys_menu(hNewWnd);
3825                   hNewMenu = UserHMGetHandle(MenuTmp);
3826               }
3827               else if (pwndTemp->style & WS_CHILD || IntGetMenu(hNewWnd) != MenuTmp)
3828               {
3829                   /* FIXME: Not sure what to do here;
3830                    * perhaps try to track NewMenu as a popup? */
3831 
3832                   WARN(" -- got confused.\n");
3833                   return FALSE;
3834               }
3835           }
3836           else return FALSE;
3837       }
3838 
3839       if (hNewMenu != UserHMGetHandle(pmt->TopMenu))
3840       {
3841           MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3842 
3843           if (pmt->CurrentMenu != pmt->TopMenu)
3844               MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags);
3845       }
3846 
3847       if (hNewWnd != UserHMGetHandle(pmt->OwnerWnd))
3848       {
3849           PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
3850           pmt->OwnerWnd = ValidateHwndNoErr(hNewWnd);
3851           ///// Use thread pms!!!!
3852           MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, hNewWnd);
3853           pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
3854           co_UserSetCapture(UserHMGetHandle(pmt->OwnerWnd));
3855           pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED;
3856       }
3857 
3858       pmt->TopMenu = pmt->CurrentMenu = UserGetMenuObject(hNewMenu); /* all subpopups are hidden */
3859       MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, Id, TRUE, 0);
3860 
3861       return TRUE;
3862    }
3863    return FALSE;
3864 }
3865 
3866 /***********************************************************************
3867  *           MenuSuspendPopup
3868  *
3869  * The idea is not to show the popup if the next input message is
3870  * going to hide it anyway.
3871  */
3872 static BOOL FASTCALL MENU_SuspendPopup(MTRACKER* pmt, UINT uMsg)
3873 {
3874     MSG msg;
3875 
3876     msg.hwnd = UserHMGetHandle(pmt->OwnerWnd); ////// ? silly wine'isms?
3877 
3878     co_IntGetPeekMessage( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE, FALSE);
3879     pmt->TrackFlags |= TF_SKIPREMOVE;
3880 
3881     switch( uMsg )
3882     {
3883     case WM_KEYDOWN:
3884         co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE, FALSE);
3885         if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
3886         {
3887             co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE, FALSE);
3888             co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE, FALSE);
3889             if( msg.message == WM_KEYDOWN &&
3890                 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
3891             {
3892                  pmt->TrackFlags |= TF_SUSPENDPOPUP;
3893                  return TRUE;
3894             }
3895         }
3896         break;
3897     }
3898     /* failures go through this */
3899     pmt->TrackFlags &= ~TF_SUSPENDPOPUP;
3900     return FALSE;
3901 }
3902 
3903 /***********************************************************************
3904  *           MenuKeyEscape
3905  *
3906  * Handle a VK_ESCAPE key event in a menu.
3907  */
3908 static BOOL FASTCALL MENU_KeyEscape(MTRACKER *pmt, UINT Flags)
3909 {
3910   BOOL EndMenu = TRUE;
3911 
3912   if (pmt->CurrentMenu != pmt->TopMenu)
3913   {
3914       if (pmt->CurrentMenu && (pmt->CurrentMenu->fFlags & MNF_POPUP))
3915       {
3916           PMENU MenuPrev, MenuTmp;
3917 
3918           MenuPrev = MenuTmp = pmt->TopMenu;
3919 
3920           /* close topmost popup */
3921           while (MenuTmp != pmt->CurrentMenu)
3922           {
3923               MenuPrev = MenuTmp;
3924               MenuTmp = MENU_GetSubPopup(MenuPrev);
3925           }
3926 
3927           MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags);
3928           pmt->CurrentMenu = MenuPrev;
3929           EndMenu = FALSE;
3930       }
3931   }
3932 
3933   return EndMenu;
3934 }
3935 
3936 /***********************************************************************
3937  *           MenuKeyLeft
3938  *
3939  * Handle a VK_LEFT key event in a menu.
3940  */
3941 static void FASTCALL MENU_KeyLeft(MTRACKER* pmt, UINT Flags, UINT msg)
3942 {
3943   PMENU MenuTmp, MenuPrev;
3944   UINT PrevCol;
3945 
3946   MenuPrev = MenuTmp = pmt->TopMenu;
3947 
3948   /* Try to move 1 column left (if possible) */
3949   if ( (PrevCol = MENU_GetStartOfPrevColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM)
3950   {
3951      MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, PrevCol, TRUE, 0);
3952      return;
3953   }
3954 
3955   /* close topmost popup */
3956   while (MenuTmp != pmt->CurrentMenu)
3957   {
3958       MenuPrev = MenuTmp;
3959       MenuTmp = MENU_GetSubPopup(MenuPrev);
3960   }
3961 
3962   MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags);
3963   pmt->CurrentMenu = MenuPrev;
3964 
3965   if ((MenuPrev == pmt->TopMenu) && !(pmt->TopMenu->fFlags & MNF_POPUP))
3966   {
3967       /* move menu bar selection if no more popups are left */
3968 
3969       if (!MENU_DoNextMenu(pmt, VK_LEFT, Flags))
3970           MENU_MoveSelection(pmt->OwnerWnd, pmt->TopMenu, ITEM_PREV);
3971 
3972       if (MenuPrev != MenuTmp || pmt->TrackFlags & TF_SUSPENDPOPUP)
3973       {
3974           /* A sublevel menu was displayed - display the next one
3975            * unless there is another displacement coming up */
3976 
3977           if (!MENU_SuspendPopup(pmt, msg))
3978               pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu,
3979                                                  TRUE, Flags);
3980       }
3981   }
3982 }
3983 
3984 /***********************************************************************
3985  *           MenuKeyRight
3986  *
3987  * Handle a VK_RIGHT key event in a menu.
3988  */
3989 static void FASTCALL MENU_KeyRight(MTRACKER *pmt, UINT Flags, UINT msg)
3990 {
3991     PMENU menutmp;
3992     UINT NextCol;
3993 
3994     TRACE("MenuKeyRight called, cur %p, top %p.\n",
3995          pmt->CurrentMenu, pmt->TopMenu);
3996 
3997     if ((pmt->TopMenu->fFlags & MNF_POPUP) || (pmt->CurrentMenu != pmt->TopMenu))
3998     {
3999       /* If already displaying a popup, try to display sub-popup */
4000 
4001       menutmp = pmt->CurrentMenu;
4002       pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, menutmp, TRUE, Flags);
4003 
4004       /* if subpopup was displayed then we are done */
4005       if (menutmp != pmt->CurrentMenu) return;
4006     }
4007 
4008     /* Check to see if there's another column */
4009     if ( (NextCol = MENU_GetStartOfNextColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM)
4010     {
4011        TRACE("Going to %d.\n", NextCol);
4012        MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NextCol, TRUE, 0);
4013        return;
4014     }
4015 
4016     if (!(pmt->TopMenu->fFlags & MNF_POPUP)) /* menu bar tracking */
4017     {
4018        if (pmt->CurrentMenu != pmt->TopMenu)
4019        {
4020           MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, Flags);
4021           menutmp = pmt->CurrentMenu = pmt->TopMenu;
4022        }
4023        else
4024        {
4025           menutmp = NULL;
4026        }
4027 
4028        /* try to move to the next item */
4029        if ( !MENU_DoNextMenu(pmt, VK_RIGHT, Flags))
4030            MENU_MoveSelection(pmt->OwnerWnd, pmt->TopMenu, ITEM_NEXT);
4031 
4032        if ( menutmp || pmt->TrackFlags & TF_SUSPENDPOPUP )
4033        {
4034            if ( !MENU_SuspendPopup(pmt, msg) )
4035                pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu, TRUE, Flags);
4036        }
4037     }
4038 }
4039 
4040 /***********************************************************************
4041  *           MenuTrackMenu
4042  *
4043  * Menu tracking code.
4044  */
4045 static INT FASTCALL MENU_TrackMenu(PMENU pmenu, UINT wFlags, INT x, INT y,
4046                             PWND pwnd)
4047 {
4048     MSG msg;
4049     BOOL fRemove;
4050     INT executedMenuId = -1;
4051     MTRACKER mt;
4052     HWND capture_win;
4053     PMENU pmMouse;
4054     BOOL enterIdleSent = FALSE;
4055     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
4056 
4057     if (pti != pwnd->head.pti)
4058     {
4059        ERR("Not the same PTI!!!!\n");
4060     }
4061 
4062     mt.TrackFlags = 0;
4063     mt.CurrentMenu = pmenu;
4064     mt.TopMenu = pmenu;
4065     mt.OwnerWnd = pwnd;
4066     mt.Pt.x = x;
4067     mt.Pt.y = y;
4068 
4069     TRACE("MTM : hmenu=%p flags=0x%08x (%d,%d) hwnd=%x\n",
4070          UserHMGetHandle(pmenu), wFlags, x, y, UserHMGetHandle(pwnd));
4071 
4072     pti->MessageQueue->QF_flags &= ~QF_ACTIVATIONCHANGE;
4073 
4074     if (wFlags & TPM_BUTTONDOWN)
4075     {
4076         /* Get the result in order to start the tracking or not */
4077         fRemove = MENU_ButtonDown( &mt, pmenu, wFlags );
4078         fInsideMenuLoop = fRemove;
4079     }
4080 
4081     if (wFlags & TF_ENDMENU) fInsideMenuLoop = FALSE;
4082 
4083     if (wFlags & TPM_POPUPMENU && pmenu->cItems == 0) // Tracking empty popup menu...
4084     {
4085        MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL);
4086        pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
4087        co_UserSetCapture(NULL); /* release the capture */
4088        return 0;
4089     }
4090 
4091     capture_win = IntGetCapture();
4092 
4093     while (fInsideMenuLoop)
4094     {
4095         BOOL ErrorExit = FALSE;
4096         if (!VerifyMenu( mt.CurrentMenu )) /* sometimes happens if I do a window manager close */
4097            break;
4098 
4099         /* we have to keep the message in the queue until it's
4100          * clear that menu loop is not over yet. */
4101 
4102         for (;;)
4103         {
4104             if (co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOREMOVE, FALSE ))
4105             {
4106                 if (!IntCallMsgFilter( &msg, MSGF_MENU )) break;
4107                 /* remove the message from the queue */
4108                 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4109             }
4110             else
4111             {
4112                 /* ReactOS Checks */
4113                 if (!VerifyWnd(mt.OwnerWnd)                            ||
4114                     !ValidateHwndNoErr(mt.CurrentMenu->hWnd)           ||
4115                      pti->MessageQueue->QF_flags & QF_ACTIVATIONCHANGE ||
4116                      capture_win != IntGetCapture() ) // Should not happen, but this is ReactOS...
4117                 {
4118                    ErrorExit = TRUE; // Do not wait on dead windows, now win test_capture_4 works.
4119                    break;
4120                 }
4121 
4122                 if (!enterIdleSent)
4123                 {
4124                   HWND win = mt.CurrentMenu->fFlags & MNF_POPUP ? mt.CurrentMenu->hWnd : NULL;
4125                   enterIdleSent = TRUE;
4126                   co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_ENTERIDLE, MSGF_MENU, (LPARAM) win);
4127                 }
4128                 co_IntWaitMessage(NULL, 0, 0);
4129             }
4130         }
4131 
4132         if (ErrorExit) break; // Gracefully dropout.
4133 
4134         /* check if EndMenu() tried to cancel us, by posting this message */
4135         if (msg.message == WM_CANCELMODE)
4136         {
4137             /* we are now out of the loop */
4138             fInsideMenuLoop = FALSE;
4139 
4140             /* remove the message from the queue */
4141             co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4142 
4143             /* break out of internal loop, ala ESCAPE */
4144             break;
4145         }
4146 
4147         mt.Pt = msg.pt;
4148 
4149         if ( (msg.hwnd == mt.CurrentMenu->hWnd) || ((msg.message!=WM_TIMER) && (msg.message!=WM_SYSTIMER)) )
4150             enterIdleSent=FALSE;
4151 
4152         fRemove = FALSE;
4153         if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
4154         {
4155             /*
4156              * Use the mouse coordinates in lParam instead of those in the MSG
4157              * struct to properly handle synthetic messages. They are already
4158              * in screen coordinates.
4159              */
4160             mt.Pt.x = (short)LOWORD(msg.lParam);
4161             mt.Pt.y = (short)HIWORD(msg.lParam);
4162 
4163             /* Find a menu for this mouse event */
4164             pmMouse = MENU_PtMenu( mt.TopMenu, mt.Pt );
4165 
4166             switch(msg.message)
4167             {
4168                 /* no WM_NC... messages in captured state */
4169 
4170                 case WM_RBUTTONDBLCLK:
4171                 case WM_RBUTTONDOWN:
4172                      if (!(wFlags & TPM_RIGHTBUTTON))
4173                      {
4174                         if ( msg.message == WM_RBUTTONDBLCLK ) fInsideMenuLoop = FALSE; // Must exit or loop forever!
4175                         break;
4176                      }
4177                     /* fall through */
4178                 case WM_LBUTTONDBLCLK:
4179                 case WM_LBUTTONDOWN:
4180                     /* If the message belongs to the menu, removes it from the queue */
4181                     /* Else, end menu tracking */
4182                     fRemove = MENU_ButtonDown(&mt, pmMouse, wFlags);
4183                     fInsideMenuLoop = fRemove;
4184                     if ( msg.message == WM_LBUTTONDBLCLK ||
4185                          msg.message == WM_RBUTTONDBLCLK ) fInsideMenuLoop = FALSE; // Must exit or loop forever!
4186                     break;
4187 
4188                 case WM_RBUTTONUP:
4189                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
4190                     /* fall through */
4191                 case WM_LBUTTONUP:
4192                     /* Check if a menu was selected by the mouse */
4193                     if (pmMouse)
4194                     {
4195                         executedMenuId = MENU_ButtonUp( &mt, pmMouse, wFlags);
4196 
4197                     /* End the loop if executedMenuId is an item ID */
4198                     /* or if the job was done (executedMenuId = 0). */
4199                         fRemove = (executedMenuId != -1);
4200                         fInsideMenuLoop = !fRemove;
4201                     }
4202                     /* No menu was selected by the mouse */
4203                     /* if the function was called by TrackPopupMenu, continue
4204                        with the menu tracking. If not, stop it */
4205                     else
4206                         fInsideMenuLoop = ((wFlags & TPM_POPUPMENU) ? TRUE : FALSE);
4207 
4208                     break;
4209 
4210                 case WM_MOUSEMOVE:
4211                     /* the selected menu item must be changed every time */
4212                     /* the mouse moves. */
4213 
4214                     if (pmMouse)
4215                         fInsideMenuLoop |= MENU_MouseMove( &mt, pmMouse, wFlags );
4216 
4217 	    } /* switch(msg.message) - mouse */
4218         }
4219         else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
4220         {
4221             fRemove = TRUE;  /* Keyboard messages are always removed */
4222             switch(msg.message)
4223             {
4224                 case WM_KEYDOWN:
4225                 case WM_SYSKEYDOWN:
4226                 switch(msg.wParam)
4227                 {
4228                     case VK_MENU:
4229                     case VK_F10:
4230                         fInsideMenuLoop = FALSE;
4231                         break;
4232 
4233                     case VK_HOME:
4234                     case VK_END:
4235                         MENU_SelectItem(mt.OwnerWnd, mt.CurrentMenu, NO_SELECTED_ITEM, FALSE, 0 );
4236                         MENU_MoveSelection(mt.OwnerWnd, mt.CurrentMenu, VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV);
4237                         break;
4238 
4239                     case VK_UP:
4240                     case VK_DOWN: /* If on menu bar, pull-down the menu */
4241                         if (!(mt.CurrentMenu->fFlags & MNF_POPUP))
4242                             mt.CurrentMenu = MENU_ShowSubPopup(mt.OwnerWnd, mt.TopMenu, TRUE, wFlags);
4243                         else      /* otherwise try to move selection */
4244                             MENU_MoveSelection(mt.OwnerWnd, mt.CurrentMenu, (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
4245                         break;
4246 
4247                     case VK_LEFT:
4248                         MENU_KeyLeft( &mt, wFlags, msg.message );
4249                         break;
4250 
4251                     case VK_RIGHT:
4252                         MENU_KeyRight( &mt, wFlags, msg.message );
4253                         break;
4254 
4255                     case VK_ESCAPE:
4256                         fInsideMenuLoop = !MENU_KeyEscape(&mt, wFlags);
4257                         break;
4258 
4259                     case VK_F1:
4260                     {
4261                         HELPINFO hi;
4262                         hi.cbSize = sizeof(HELPINFO);
4263                         hi.iContextType = HELPINFO_MENUITEM;
4264                         if (mt.CurrentMenu->iItem == NO_SELECTED_ITEM)
4265                             hi.iCtrlId = 0;
4266                         else
4267                             hi.iCtrlId = pmenu->rgItems[mt.CurrentMenu->iItem].wID;
4268                         hi.hItemHandle = UserHMGetHandle(mt.CurrentMenu);
4269                         hi.dwContextId = pmenu->dwContextHelpId;
4270                         hi.MousePos = msg.pt;
4271                         co_IntSendMessage( UserHMGetHandle(pwnd), WM_HELP, 0, (LPARAM)&hi);
4272                         break;
4273                     }
4274 
4275                     default:
4276                         IntTranslateKbdMessage(&msg, 0);
4277                         break;
4278                 }
4279                 break;  /* WM_KEYDOWN */
4280 
4281                 case WM_CHAR:
4282                 case WM_SYSCHAR:
4283                 {
4284                     UINT pos;
4285                     BOOL fEndMenu;
4286 
4287                     if (msg.wParam == L'\r' || msg.wParam == L' ')
4288                     {
4289                         executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags);
4290                         fEndMenu = (executedMenuId != -2);
4291                         fInsideMenuLoop = !fEndMenu;
4292                         break;
4293                     }
4294 
4295                     /* Hack to avoid control chars. */
4296                     /* We will find a better way real soon... */
4297                     if (msg.wParam < 32) break;
4298 
4299                     pos = MENU_FindItemByKey(mt.OwnerWnd, mt.CurrentMenu, LOWORD(msg.wParam), FALSE);
4300 
4301                     if (pos == (UINT)-2) fInsideMenuLoop = FALSE;
4302                     else if (pos == (UINT)-1) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0);
4303                     else
4304                     {
4305                         MENU_SelectItem(mt.OwnerWnd, mt.CurrentMenu, pos, TRUE, 0);
4306                         executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags);
4307                         fEndMenu = (executedMenuId != -2);
4308                         fInsideMenuLoop = !fEndMenu;
4309                     }
4310                 }
4311                 break;
4312             }  /* switch(msg.message) - kbd */
4313         }
4314         else
4315         {
4316             co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4317             IntDispatchMessage( &msg );
4318             continue;
4319         }
4320 
4321         if (fInsideMenuLoop) fRemove = TRUE;
4322 
4323         /* finally remove message from the queue */
4324 
4325         if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) )
4326             co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4327         else mt.TrackFlags &= ~TF_SKIPREMOVE;
4328     }
4329 
4330     MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL);
4331     pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
4332     co_UserSetCapture(NULL); /* release the capture */
4333 
4334     /* If dropdown is still painted and the close box is clicked on
4335        then the menu will be destroyed as part of the DispatchMessage above.
4336        This will then invalidate the menu handle in mt.hTopMenu. We should
4337        check for this first.  */
4338     if ( VerifyMenu( mt.TopMenu ) )
4339     {
4340        if (VerifyWnd(mt.OwnerWnd))
4341        {
4342            MENU_HideSubPopups(mt.OwnerWnd, mt.TopMenu, FALSE, wFlags);
4343 
4344            if (mt.TopMenu->fFlags & MNF_POPUP)
4345            {
4346               PWND pwndTM = ValidateHwndNoErr(mt.TopMenu->hWnd);
4347               if (pwndTM)
4348               {
4349                  IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, pwndTM, OBJID_CLIENT, CHILDID_SELF, 0);
4350 
4351                  co_UserDestroyWindow(pwndTM);
4352               }
4353               mt.TopMenu->hWnd = NULL;
4354 
4355               if (!(wFlags & TPM_NONOTIFY))
4356               {
4357                  co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(mt.TopMenu),
4358                                  MAKELPARAM(0, IS_SYSTEM_MENU(mt.TopMenu)) );
4359               }
4360             }
4361             MENU_SelectItem( mt.OwnerWnd, mt.TopMenu, NO_SELECTED_ITEM, FALSE, 0 );
4362             co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 );
4363        }
4364 
4365        /* Reset the variable for hiding menu */
4366        mt.TopMenu->TimeToHide = FALSE;
4367     }
4368 
4369     EngSetLastError( ERROR_SUCCESS );
4370     /* The return value is only used by TrackPopupMenu */
4371     if (!(wFlags & TPM_RETURNCMD)) return TRUE;
4372     if (executedMenuId == -1) executedMenuId = 0;
4373     return executedMenuId;
4374 }
4375 
4376 /***********************************************************************
4377  *           MenuInitTracking
4378  */
4379 static BOOL FASTCALL MENU_InitTracking(PWND pWnd, PMENU Menu, BOOL bPopup, UINT wFlags)
4380 {
4381     HWND capture_win;
4382     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
4383 
4384     TRACE("hwnd=%p hmenu=%p\n", UserHMGetHandle(pWnd), UserHMGetHandle(Menu));
4385 
4386     co_UserHideCaret(0);
4387 
4388     /* This makes the menus of applications built with Delphi work.
4389      * It also enables menus to be displayed in more than one window,
4390      * but there are some bugs left that need to be fixed in this case.
4391      */
4392     if (!bPopup)
4393     {
4394         Menu->hWnd = UserHMGetHandle(pWnd);
4395     }
4396 
4397     if (!top_popup) {
4398        top_popup = Menu->hWnd;
4399        top_popup_hmenu = UserHMGetHandle(Menu);
4400     }
4401 
4402     fInsideMenuLoop = TRUE;
4403     fInEndMenu = FALSE;
4404 
4405     /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
4406     if (!(wFlags & TPM_NONOTIFY))
4407     {
4408        co_IntSendMessage( UserHMGetHandle(pWnd), WM_ENTERMENULOOP, bPopup, 0 );
4409     }
4410 
4411     //
4412     // Capture is set before calling WM_INITMENU and after WM_ENTERMENULOOP, see msg_menu.
4413     //
4414     capture_win = (wFlags & TPM_POPUPMENU) ? Menu->hWnd : UserHMGetHandle(pWnd);
4415     MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, capture_win); // 1
4416     co_UserSetCapture(capture_win);                           // 2
4417     pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED; // Set the Q bits so noone can change this!
4418 
4419     co_IntSendMessage( UserHMGetHandle(pWnd), WM_SETCURSOR, (WPARAM)UserHMGetHandle(pWnd), HTCAPTION );
4420 
4421     if (!(wFlags & TPM_NONOTIFY))
4422     {
4423        co_IntSendMessage( UserHMGetHandle(pWnd), WM_INITMENU, (WPARAM)UserHMGetHandle(Menu), 0 );
4424        /* If an app changed/recreated menu bar entries in WM_INITMENU
4425         * menu sizes will be recalculated once the menu created/shown.
4426         */
4427     }
4428 
4429     IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART,
4430                        pWnd,
4431                        Menu->fFlags & MNF_SYSMENU ? OBJID_SYSMENU : OBJID_MENU,
4432                        CHILDID_SELF, 0);
4433     return TRUE;
4434 }
4435 
4436 /***********************************************************************
4437  *           MenuExitTracking
4438  */
4439 static BOOL FASTCALL MENU_ExitTracking(PWND pWnd, BOOL bPopup, UINT wFlags)
4440 {
4441     TRACE("Exit Track hwnd=%p bPopup %d\n", UserHMGetHandle(pWnd), bPopup);
4442 
4443     IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, pWnd, OBJID_WINDOW, CHILDID_SELF, 0);
4444 
4445     if (!(wFlags & TPM_NONOTIFY))
4446        co_IntSendMessage( UserHMGetHandle(pWnd), WM_EXITMENULOOP, bPopup, 0 );
4447 
4448     co_UserShowCaret(0);
4449 
4450     top_popup = 0;
4451     top_popup_hmenu = NULL;
4452 
4453     return TRUE;
4454 }
4455 
4456 /***********************************************************************
4457  *           MenuTrackMouseMenuBar
4458  *
4459  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
4460  */
4461 VOID MENU_TrackMouseMenuBar( PWND pWnd, ULONG ht, POINT pt)
4462 {
4463     PMENU pMenu = (ht == HTSYSMENU) ? IntGetSystemMenu(pWnd, FALSE) : IntGetMenu( UserHMGetHandle(pWnd) ); // See 74276 and CORE-12801
4464     UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL;
4465 
4466     TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", pWnd, ht, pt.x, pt.y);
4467 
4468     if (pWnd->ExStyle & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
4469     if (VerifyMenu(pMenu))
4470     {
4471         /* map point to parent client coordinates */
4472         PWND Parent = UserGetAncestor(pWnd, GA_PARENT );
4473         if (Parent != UserGetDesktopWindow())
4474         {
4475             IntScreenToClient(Parent, &pt);
4476         }
4477 
4478         MENU_InitTracking(pWnd, pMenu, FALSE, wFlags);
4479         /* fetch the window menu again, it may have changed */
4480         pMenu = (ht == HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pWnd) ) : IntGetMenu( UserHMGetHandle(pWnd) );
4481         MENU_TrackMenu(pMenu, wFlags, pt.x, pt.y, pWnd);
4482         MENU_ExitTracking(pWnd, FALSE, wFlags);
4483     }
4484 }
4485 
4486 /***********************************************************************
4487  *           MenuTrackKbdMenuBar
4488  *
4489  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
4490  */
4491 VOID MENU_TrackKbdMenuBar(PWND pwnd, UINT wParam, WCHAR wChar)
4492 {
4493     UINT uItem = NO_SELECTED_ITEM;
4494     PMENU TrackMenu;
4495     UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
4496 
4497     TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", UserHMGetHandle(pwnd), wParam, wChar);
4498 
4499     /* find window that has a menu */
4500 
4501     while (!( (pwnd->style & (WS_CHILD | WS_POPUP)) != WS_CHILD ) )
4502         if (!(pwnd = UserGetAncestor( pwnd, GA_PARENT ))) return;
4503 
4504     /* check if we have to track a system menu */
4505 
4506     TrackMenu = IntGetMenu( UserHMGetHandle(pwnd) );
4507     if (!TrackMenu || (pwnd->style & WS_MINIMIZE) != 0 || wChar == ' ' )
4508     {
4509         if (!(pwnd->style & WS_SYSMENU)) return;
4510         TrackMenu = get_win_sys_menu( UserHMGetHandle(pwnd) );
4511         uItem = 0;
4512         wParam |= HTSYSMENU; /* prevent item lookup */
4513     }
4514 
4515     if (!VerifyMenu( TrackMenu )) return;
4516 
4517     MENU_InitTracking( pwnd, TrackMenu, FALSE, wFlags );
4518 
4519     /* fetch the window menu again, it may have changed */
4520     TrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pwnd) ) : IntGetMenu( UserHMGetHandle(pwnd) );
4521 
4522     if( wChar && wChar != ' ' )
4523     {
4524         uItem = MENU_FindItemByKey( pwnd, TrackMenu, wChar, (wParam & HTSYSMENU) );
4525         if ( uItem >= (UINT)(-2) )
4526         {
4527             if( uItem == (UINT)(-1) ) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0);
4528             /* schedule end of menu tracking */
4529             wFlags |= TF_ENDMENU;
4530             goto track_menu;
4531         }
4532     }
4533 
4534     MENU_SelectItem( pwnd, TrackMenu, uItem, TRUE, 0 );
4535 
4536     if (!(wParam & HTSYSMENU) || wChar == ' ')
4537     {
4538         if( uItem == NO_SELECTED_ITEM )
4539             MENU_MoveSelection( pwnd, TrackMenu, ITEM_NEXT );
4540         else
4541             UserPostMessage( UserHMGetHandle(pwnd), WM_KEYDOWN, VK_RETURN, 0 );
4542     }
4543 
4544 track_menu:
4545     MENU_TrackMenu( TrackMenu, wFlags, 0, 0, pwnd );
4546     MENU_ExitTracking( pwnd, FALSE, wFlags);
4547 }
4548 
4549 /**********************************************************************
4550  *           TrackPopupMenuEx   (USER32.@)
4551  */
4552 BOOL WINAPI IntTrackPopupMenuEx( PMENU menu, UINT wFlags, int x, int y,
4553                               PWND pWnd, LPTPMPARAMS lpTpm)
4554 {
4555     BOOL ret = FALSE;
4556     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
4557 
4558     if (pti != pWnd->head.pti)
4559     {
4560        ERR("Must be the same pti!\n");
4561        return ret;
4562     }
4563 
4564     TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p \n", //rect %s\n",
4565             UserHMGetHandle(menu), wFlags, x, y, UserHMGetHandle(pWnd), lpTpm); //,
4566             //lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
4567 
4568     if (menu->hWnd && IntIsWindow(menu->hWnd))
4569     {
4570         EngSetLastError( ERROR_POPUP_ALREADY_ACTIVE );
4571         return FALSE;
4572     }
4573 
4574     if (MENU_InitPopup( pWnd, menu, wFlags ))
4575     {
4576        MENU_InitTracking(pWnd, menu, TRUE, wFlags);
4577 
4578        /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
4579        if (!(wFlags & TPM_NONOTIFY))
4580        {
4581           co_IntSendMessage( UserHMGetHandle(pWnd), WM_INITMENUPOPUP, (WPARAM) UserHMGetHandle(menu), 0);
4582        }
4583 
4584        if (menu->fFlags & MNF_SYSMENU)
4585           MENU_InitSysMenuPopup( menu, pWnd->style, pWnd->pcls->style, HTSYSMENU);
4586 
4587        if (MENU_ShowPopup(pWnd, menu, 0, wFlags | TPM_POPUPMENU, x, y, lpTpm ? &lpTpm->rcExclude : NULL))
4588           ret = MENU_TrackMenu( menu, wFlags | TPM_POPUPMENU, 0, 0, pWnd);
4589        else
4590        {
4591           MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL);
4592           pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
4593           co_UserSetCapture(NULL); /* release the capture */
4594        }
4595 
4596        MENU_ExitTracking(pWnd, TRUE, wFlags);
4597 
4598        if (menu->hWnd)
4599        {
4600           PWND pwndM = ValidateHwndNoErr( menu->hWnd );
4601           if (pwndM) // wine hack around this with their destroy function.
4602           {
4603              if (!(pWnd->state & WNDS_DESTROYED))
4604                 co_UserDestroyWindow( pwndM ); // Fix wrong error return.
4605           }
4606           menu->hWnd = 0;
4607 
4608           if (!(wFlags & TPM_NONOTIFY))
4609           {
4610              co_IntSendMessage( UserHMGetHandle(pWnd), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(menu),
4611                                             MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
4612           }
4613        }
4614     }
4615     return ret;
4616 }
4617 
4618 //
4619 // Menu Class Proc.
4620 //
4621 BOOL WINAPI
4622 PopupMenuWndProc(
4623    PWND Wnd,
4624    UINT Message,
4625    WPARAM wParam,
4626    LPARAM lParam,
4627    LRESULT *lResult)
4628 {
4629   PPOPUPMENU pPopupMenu;
4630 
4631   *lResult = 0;
4632 
4633   TRACE("PMWP : pwnd=%x msg=%d wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
4634 
4635   if (Wnd)
4636   {
4637      if (!Wnd->fnid)
4638      {
4639         if (Message != WM_NCCREATE)
4640         {
4641            *lResult = IntDefWindowProc(Wnd, Message, wParam, lParam, FALSE);
4642            return TRUE;
4643         }
4644         Wnd->fnid = FNID_MENU;
4645         pPopupMenu = DesktopHeapAlloc( Wnd->head.rpdesk, sizeof(POPUPMENU) );
4646         if (pPopupMenu == NULL)
4647         {
4648             return TRUE;
4649         }
4650         pPopupMenu->posSelectedItem = NO_SELECTED_ITEM;
4651         pPopupMenu->spwndPopupMenu = Wnd;
4652         ((PMENUWND)Wnd)->ppopupmenu = pPopupMenu;
4653         TRACE("Pop Up Menu is Setup! Msg %d\n",Message);
4654         *lResult = 1;
4655         return TRUE;
4656      }
4657      else
4658      {
4659         if (Wnd->fnid != FNID_MENU)
4660         {
4661            ERR("Wrong window class for Menu! fnid %x\n",Wnd->fnid);
4662            return TRUE;
4663         }
4664         pPopupMenu = ((PMENUWND)Wnd)->ppopupmenu;
4665      }
4666   }
4667 
4668   switch(Message)
4669     {
4670     case WM_CREATE:
4671       {
4672         CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
4673         pPopupMenu->spmenu = UserGetMenuObject(cs->lpCreateParams);
4674         if (pPopupMenu->spmenu)
4675         {
4676            UserReferenceObject(pPopupMenu->spmenu);
4677         }
4678         break;
4679       }
4680 
4681     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
4682       *lResult = MA_NOACTIVATE;
4683       break;
4684 
4685     case WM_PAINT:
4686       {
4687         PAINTSTRUCT ps;
4688         IntBeginPaint(Wnd, &ps);
4689         MENU_DrawPopupMenu(Wnd, ps.hdc, pPopupMenu->spmenu);
4690         IntEndPaint(Wnd, &ps);
4691         break;
4692       }
4693 
4694     case WM_PRINTCLIENT:
4695       {
4696          MENU_DrawPopupMenu( Wnd, (HDC)wParam, pPopupMenu->spmenu);
4697          break;
4698       }
4699 
4700     case WM_ERASEBKGND:
4701       *lResult = 1;
4702       break;
4703 
4704     case WM_DESTROY:
4705       /* zero out global pointer in case resident popup window was destroyed. */
4706       if (pPopupMenu)
4707       {
4708          if (UserHMGetHandle(Wnd) == top_popup)
4709          {
4710              top_popup = NULL;
4711              top_popup_hmenu = NULL;
4712          }
4713       }
4714       else
4715       {
4716          ERR("No Window Pop Up!\n");
4717       }
4718       break;
4719 
4720     case WM_NCDESTROY:
4721       {
4722          if (pPopupMenu->spmenu)
4723          {
4724             IntReleaseMenuObject(pPopupMenu->spmenu);
4725          }
4726          DesktopHeapFree(Wnd->head.rpdesk, pPopupMenu );
4727          ((PMENUWND)Wnd)->ppopupmenu = 0;
4728          Wnd->fnid = FNID_DESTROY;
4729          break;
4730       }
4731 
4732     case MM_SETMENUHANDLE: // wine'isms
4733     case MN_SETHMENU:
4734       {
4735         PMENU pmenu = UserGetMenuObject((HMENU)wParam);
4736         if (!pmenu)
4737         {
4738            ERR("Bad Menu Handle\n");
4739            break;
4740         }
4741         UserReferenceObject(pmenu);
4742         if (pPopupMenu->spmenu)
4743         {
4744            IntReleaseMenuObject(pPopupMenu->spmenu);
4745         }
4746         pPopupMenu->spmenu = pmenu;
4747         break;
4748       }
4749 
4750     case MM_GETMENUHANDLE: // wine'isms
4751     case MN_GETHMENU:
4752          *lResult = (LRESULT)(pPopupMenu ? (pPopupMenu->spmenu ? UserHMGetHandle(pPopupMenu->spmenu) : NULL) : NULL);
4753          break;
4754 
4755     default:
4756       if (Message > MN_GETHMENU && Message < MN_GETHMENU+19)
4757       {
4758          ERR("Someone is passing unknown menu messages %d\n",Message);
4759       }
4760       TRACE("PMWP to IDWP %d\n",Message);
4761       *lResult = IntDefWindowProc(Wnd, Message, wParam, lParam, FALSE);
4762       break;
4763     }
4764 
4765   return TRUE;
4766 }
4767 
4768 BOOL FASTCALL
4769 IntHiliteMenuItem(PWND WindowObject,
4770                   PMENU MenuObject,
4771                   UINT uItemHilite,
4772                   UINT uHilite)
4773 {
4774    PITEM MenuItem;
4775    UINT uItem = uItemHilite;
4776 
4777    if (!(MenuItem = MENU_FindItem( &MenuObject, &uItem, uHilite ))) return TRUE;
4778 
4779    if (uHilite & MF_HILITE)
4780    {
4781       MenuItem->fState |= MF_HILITE;
4782    }
4783    else
4784    {
4785       MenuItem->fState &= ~MF_HILITE;
4786    }
4787    if (MenuObject->iItem == uItemHilite) return TRUE;
4788    MENU_HideSubPopups( WindowObject, MenuObject, FALSE, 0 );
4789    MENU_SelectItem( WindowObject, MenuObject, uItemHilite, TRUE, 0 );
4790 
4791    return TRUE; // Always returns true!!!!
4792 }
4793 
4794 BOOLEAN APIENTRY
4795 intGetTitleBarInfo(PWND pWindowObject, PTITLEBARINFO bti)
4796 {
4797 
4798     DWORD dwStyle = 0;
4799     DWORD dwExStyle = 0;
4800     BOOLEAN retValue = TRUE;
4801 
4802     if (bti->cbSize == sizeof(TITLEBARINFO))
4803     {
4804         RtlZeroMemory(&bti->rgstate[0],sizeof(DWORD)*(CCHILDREN_TITLEBAR+1));
4805 
4806         bti->rgstate[0] = STATE_SYSTEM_FOCUSABLE;
4807 
4808         dwStyle = pWindowObject->style;
4809         dwExStyle = pWindowObject->ExStyle;
4810 
4811         bti->rcTitleBar.top  = 0;
4812         bti->rcTitleBar.left = 0;
4813         bti->rcTitleBar.right  = pWindowObject->rcWindow.right - pWindowObject->rcWindow.left;
4814         bti->rcTitleBar.bottom = pWindowObject->rcWindow.bottom - pWindowObject->rcWindow.top;
4815 
4816         /* Is it iconiced ? */
4817         if ((dwStyle & WS_ICONIC)!=WS_ICONIC)
4818         {
4819             /* Remove frame from rectangle */
4820             if (HAS_THICKFRAME( dwStyle, dwExStyle ))
4821             {
4822                 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXFRAME) and UserGetSystemMetrics(SM_CYFRAME) */
4823                 RECTL_vInflateRect( &bti->rcTitleBar, -UserGetSystemMetrics(SM_CXFRAME), -UserGetSystemMetrics(SM_CYFRAME) );
4824             }
4825             else if (HAS_DLGFRAME( dwStyle, dwExStyle ))
4826             {
4827                 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXDLGFRAME) and UserGetSystemMetrics(SM_CYDLGFRAME) */
4828                 RECTL_vInflateRect( &bti->rcTitleBar, -UserGetSystemMetrics(SM_CXDLGFRAME), -UserGetSystemMetrics(SM_CYDLGFRAME));
4829             }
4830             else if (HAS_THINFRAME( dwStyle, dwExStyle))
4831             {
4832                 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXBORDER) and UserGetSystemMetrics(SM_CYBORDER) */
4833                 RECTL_vInflateRect( &bti->rcTitleBar, -UserGetSystemMetrics(SM_CXBORDER), -UserGetSystemMetrics(SM_CYBORDER) );
4834             }
4835 
4836             /* We have additional border information if the window
4837              * is a child (but not an MDI child) */
4838             if ( (dwStyle & WS_CHILD)  &&
4839                  ((dwExStyle & WS_EX_MDICHILD) == 0 ) )
4840             {
4841                 if (dwExStyle & WS_EX_CLIENTEDGE)
4842                 {
4843                     /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXEDGE) and UserGetSystemMetrics(SM_CYEDGE) */
4844                     RECTL_vInflateRect (&bti->rcTitleBar, -UserGetSystemMetrics(SM_CXEDGE), -UserGetSystemMetrics(SM_CYEDGE));
4845                 }
4846 
4847                 if (dwExStyle & WS_EX_STATICEDGE)
4848                 {
4849                     /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXBORDER) and UserGetSystemMetrics(SM_CYBORDER) */
4850                     RECTL_vInflateRect (&bti->rcTitleBar, -UserGetSystemMetrics(SM_CXBORDER), -UserGetSystemMetrics(SM_CYBORDER));
4851                 }
4852             }
4853         }
4854 
4855         bti->rcTitleBar.top += pWindowObject->rcWindow.top;
4856         bti->rcTitleBar.left += pWindowObject->rcWindow.left;
4857         bti->rcTitleBar.right += pWindowObject->rcWindow.left;
4858 
4859         bti->rcTitleBar.bottom = bti->rcTitleBar.top;
4860         if (dwExStyle & WS_EX_TOOLWINDOW)
4861         {
4862             /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CYSMCAPTION) */
4863             bti->rcTitleBar.bottom += UserGetSystemMetrics(SM_CYSMCAPTION);
4864         }
4865         else
4866         {
4867             /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CYCAPTION) and UserGetSystemMetrics(SM_CXSIZE) */
4868             bti->rcTitleBar.bottom += UserGetSystemMetrics(SM_CYCAPTION);
4869             bti->rcTitleBar.left += UserGetSystemMetrics(SM_CXSIZE);
4870         }
4871 
4872         if (dwStyle & WS_CAPTION)
4873         {
4874             bti->rgstate[1] = STATE_SYSTEM_INVISIBLE;
4875             if (dwStyle & WS_SYSMENU)
4876             {
4877                 if (!(dwStyle & (WS_MINIMIZEBOX|WS_MAXIMIZEBOX)))
4878                 {
4879                     bti->rgstate[2] = STATE_SYSTEM_INVISIBLE;
4880                     bti->rgstate[3] = STATE_SYSTEM_INVISIBLE;
4881                 }
4882                 else
4883                 {
4884                     if (!(dwStyle & WS_MINIMIZEBOX))
4885                     {
4886                         bti->rgstate[2] = STATE_SYSTEM_UNAVAILABLE;
4887                     }
4888                     if (!(dwStyle & WS_MAXIMIZEBOX))
4889                     {
4890                         bti->rgstate[3] = STATE_SYSTEM_UNAVAILABLE;
4891                     }
4892                 }
4893 
4894                 if (!(dwExStyle & WS_EX_CONTEXTHELP))
4895                 {
4896                     bti->rgstate[4] = STATE_SYSTEM_INVISIBLE;
4897                 }
4898                 if (pWindowObject->pcls->style & CS_NOCLOSE)
4899                 {
4900                     bti->rgstate[5] = STATE_SYSTEM_UNAVAILABLE;
4901                 }
4902             }
4903             else
4904             {
4905                 bti->rgstate[2] = STATE_SYSTEM_INVISIBLE;
4906                 bti->rgstate[3] = STATE_SYSTEM_INVISIBLE;
4907                 bti->rgstate[4] = STATE_SYSTEM_INVISIBLE;
4908                 bti->rgstate[5] = STATE_SYSTEM_INVISIBLE;
4909             }
4910         }
4911         else
4912         {
4913             bti->rgstate[0] |= STATE_SYSTEM_INVISIBLE;
4914         }
4915     }
4916     else
4917     {
4918         EngSetLastError(ERROR_INVALID_PARAMETER);
4919         retValue = FALSE;
4920     }
4921 
4922     return retValue;
4923 }
4924 
4925 DWORD FASTCALL
4926 UserInsertMenuItem(
4927    PMENU Menu,
4928    UINT uItem,
4929    BOOL fByPosition,
4930    LPCMENUITEMINFOW UnsafeItemInfo,
4931    PUNICODE_STRING lpstr)
4932 {
4933    NTSTATUS Status;
4934    ROSMENUITEMINFO ItemInfo;
4935 
4936    /* Try to copy the whole MENUITEMINFOW structure */
4937    Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, sizeof(MENUITEMINFOW));
4938    if (NT_SUCCESS(Status))
4939    {
4940       if (sizeof(MENUITEMINFOW) != ItemInfo.cbSize
4941          && FIELD_OFFSET(MENUITEMINFOW, hbmpItem) != ItemInfo.cbSize)
4942       {
4943          EngSetLastError(ERROR_INVALID_PARAMETER);
4944          return FALSE;
4945       }
4946       return IntInsertMenuItem(Menu, uItem, fByPosition, &ItemInfo, lpstr);
4947    }
4948 
4949    /* Try to copy without last field (not present in older versions) */
4950    Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, FIELD_OFFSET(MENUITEMINFOW, hbmpItem));
4951    if (NT_SUCCESS(Status))
4952    {
4953       if (FIELD_OFFSET(MENUITEMINFOW, hbmpItem) != ItemInfo.cbSize)
4954       {
4955          EngSetLastError(ERROR_INVALID_PARAMETER);
4956          return FALSE;
4957       }
4958       ItemInfo.hbmpItem = (HBITMAP)0;
4959       return IntInsertMenuItem(Menu, uItem, fByPosition, &ItemInfo, lpstr);
4960    }
4961 
4962    SetLastNtError(Status);
4963    return FALSE;
4964 }
4965 
4966 UINT FASTCALL IntGetMenuState( HMENU hMenu, UINT uId, UINT uFlags)
4967 {
4968    PMENU MenuObject;
4969    PITEM pItem;
4970 
4971    if (!(MenuObject = UserGetMenuObject(hMenu)))
4972    {
4973       return (UINT)-1;
4974    }
4975 
4976    if (!(pItem = MENU_FindItem( &MenuObject, &uId, uFlags ))) return -1;
4977 
4978    if (pItem->spSubMenu)
4979    {
4980       return (pItem->spSubMenu->cItems << 8) | ((pItem->fState|pItem->fType|MF_POPUP) & 0xff);
4981    }
4982    else
4983       return (pItem->fType | pItem->fState);
4984 }
4985 
4986 HMENU FASTCALL IntGetSubMenu( HMENU hMenu, int nPos)
4987 {
4988    PMENU MenuObject;
4989    PITEM pItem;
4990 
4991    if (!(MenuObject = UserGetMenuObject(hMenu)))
4992    {
4993       return NULL;
4994    }
4995 
4996    if (!(pItem = MENU_FindItem( &MenuObject, (UINT*)&nPos, MF_BYPOSITION ))) return NULL;
4997 
4998    if (pItem->spSubMenu)
4999    {
5000       HMENU hsubmenu = UserHMGetHandle(pItem->spSubMenu);
5001       return hsubmenu;
5002    }
5003    return NULL;
5004 }
5005 
5006 UINT FASTCALL IntFindSubMenu(HMENU *hMenu, HMENU hSubTarget )
5007 {
5008     PMENU menu, pSubTarget;
5009     UINT Pos;
5010     if (((*hMenu)==(HMENU)0xffff) ||(!(menu = UserGetMenuObject(*hMenu))))
5011         return NO_SELECTED_ITEM;
5012 
5013     pSubTarget = UserGetMenuObject(hSubTarget);
5014 
5015     Pos = MENU_FindSubMenu(&menu, pSubTarget );
5016 
5017     *hMenu = (menu ? UserHMGetHandle(menu) : NULL);
5018 
5019     return Pos;
5020 }
5021 
5022 
5023 HMENU FASTCALL UserCreateMenu(PDESKTOP Desktop, BOOL PopupMenu)
5024 {
5025    PWINSTATION_OBJECT WinStaObject;
5026    HANDLE Handle;
5027    PMENU Menu;
5028    NTSTATUS Status;
5029    PEPROCESS CurrentProcess = PsGetCurrentProcess();
5030 
5031    if (gpepCSRSS != CurrentProcess)
5032    {
5033       /*
5034        * gpepCSRSS does not have a Win32WindowStation
5035        */
5036 
5037       Status = IntValidateWindowStationHandle(CurrentProcess->Win32WindowStation,
5038                      UserMode,
5039                      0,
5040                      &WinStaObject,
5041                      0);
5042 
5043        if (!NT_SUCCESS(Status))
5044        {
5045           ERR("Validation of window station handle (%p) failed\n",
5046               CurrentProcess->Win32WindowStation);
5047           SetLastNtError(Status);
5048           return (HMENU)0;
5049        }
5050        Menu = IntCreateMenu(&Handle, !PopupMenu, Desktop, GetW32ProcessInfo());
5051        if (Menu && Menu->head.rpdesk->rpwinstaParent != WinStaObject)
5052        {
5053           ERR("Desktop Window Station does not match Process one!\n");
5054        }
5055        ObDereferenceObject(WinStaObject);
5056    }
5057    else
5058    {
5059        Menu = IntCreateMenu(&Handle, !PopupMenu, GetW32ThreadInfo()->rpdesk, GetW32ProcessInfo());
5060    }
5061 
5062    if (Menu) UserDereferenceObject(Menu);
5063    return (HMENU)Handle;
5064 }
5065 
5066 BOOL FASTCALL
5067 IntMenuItemInfo(
5068    PMENU Menu,
5069    UINT Item,
5070    BOOL ByPosition,
5071    PROSMENUITEMINFO ItemInfo,
5072    BOOL SetOrGet,
5073    PUNICODE_STRING lpstr)
5074 {
5075    PITEM MenuItem;
5076    BOOL Ret;
5077 
5078    if (!(MenuItem = MENU_FindItem( &Menu, &Item, (ByPosition ? MF_BYPOSITION : MF_BYCOMMAND) )))
5079    {
5080       EngSetLastError(ERROR_MENU_ITEM_NOT_FOUND);
5081       return( FALSE);
5082    }
5083    if (SetOrGet)
5084    {
5085       Ret = IntSetMenuItemInfo(Menu, MenuItem, ItemInfo, lpstr);
5086    }
5087    else
5088    {
5089       Ret = IntGetMenuItemInfo(Menu, MenuItem, ItemInfo);
5090    }
5091    return( Ret);
5092 }
5093 
5094 BOOL FASTCALL
5095 UserMenuItemInfo(
5096    PMENU Menu,
5097    UINT Item,
5098    BOOL ByPosition,
5099    PROSMENUITEMINFO UnsafeItemInfo,
5100    BOOL SetOrGet,
5101    PUNICODE_STRING lpstr)
5102 {
5103    PITEM MenuItem;
5104    ROSMENUITEMINFO ItemInfo;
5105    NTSTATUS Status;
5106    UINT Size;
5107    BOOL Ret;
5108 
5109    Status = MmCopyFromCaller(&Size, &UnsafeItemInfo->cbSize, sizeof(UINT));
5110    if (! NT_SUCCESS(Status))
5111    {
5112       SetLastNtError(Status);
5113       return( FALSE);
5114    }
5115    if ( Size != sizeof(MENUITEMINFOW) &&
5116         Size != FIELD_OFFSET(MENUITEMINFOW, hbmpItem) &&
5117         Size != sizeof(ROSMENUITEMINFO) )
5118    {
5119       EngSetLastError(ERROR_INVALID_PARAMETER);
5120       return( FALSE);
5121    }
5122    Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, Size);
5123    if (! NT_SUCCESS(Status))
5124    {
5125       SetLastNtError(Status);
5126       return( FALSE);
5127    }
5128    /* If this is a pre-0x0500 _WIN32_WINNT MENUITEMINFOW, you can't
5129       set/get hbmpItem */
5130    if (FIELD_OFFSET(MENUITEMINFOW, hbmpItem) == Size
5131          && 0 != (ItemInfo.fMask & MIIM_BITMAP))
5132    {
5133       EngSetLastError(ERROR_INVALID_PARAMETER);
5134       return( FALSE);
5135    }
5136 
5137    if (!(MenuItem = MENU_FindItem( &Menu, &Item, (ByPosition ? MF_BYPOSITION : MF_BYCOMMAND) )))
5138    {
5139       /* workaround for Word 95: pretend that SC_TASKLIST item exists. */
5140       if ( SetOrGet && Item == SC_TASKLIST && !ByPosition )
5141          return TRUE;
5142 
5143       EngSetLastError(ERROR_MENU_ITEM_NOT_FOUND);
5144       return( FALSE);
5145    }
5146 
5147    if (SetOrGet)
5148    {
5149       Ret = IntSetMenuItemInfo(Menu, MenuItem, &ItemInfo, lpstr);
5150    }
5151    else
5152    {
5153       Ret = IntGetMenuItemInfo(Menu, MenuItem, &ItemInfo);
5154       if (Ret)
5155       {
5156          Status = MmCopyToCaller(UnsafeItemInfo, &ItemInfo, Size);
5157          if (! NT_SUCCESS(Status))
5158          {
5159             SetLastNtError(Status);
5160             return( FALSE);
5161          }
5162       }
5163    }
5164 
5165    return( Ret);
5166 }
5167 
5168 BOOL FASTCALL
5169 UserMenuInfo(
5170    PMENU Menu,
5171    PROSMENUINFO UnsafeMenuInfo,
5172    BOOL SetOrGet)
5173 {
5174    BOOL Res;
5175    DWORD Size;
5176    NTSTATUS Status;
5177    ROSMENUINFO MenuInfo;
5178 
5179    Status = MmCopyFromCaller(&Size, &UnsafeMenuInfo->cbSize, sizeof(DWORD));
5180    if (! NT_SUCCESS(Status))
5181    {
5182       SetLastNtError(Status);
5183       return( FALSE);
5184    }
5185    if ( Size < sizeof(MENUINFO) || Size > sizeof(ROSMENUINFO) )
5186    {
5187       EngSetLastError(ERROR_INVALID_PARAMETER);
5188       return( FALSE);
5189    }
5190    Status = MmCopyFromCaller(&MenuInfo, UnsafeMenuInfo, Size);
5191    if (! NT_SUCCESS(Status))
5192    {
5193       SetLastNtError(Status);
5194       return( FALSE);
5195    }
5196 
5197    if(SetOrGet)
5198    {
5199       /* Set MenuInfo */
5200       Res = IntSetMenuInfo(Menu, &MenuInfo);
5201    }
5202    else
5203    {
5204       /* Get MenuInfo */
5205       Res = IntGetMenuInfo(Menu, &MenuInfo);
5206       if (Res)
5207       {
5208          Status = MmCopyToCaller(UnsafeMenuInfo, &MenuInfo, Size);
5209          if (! NT_SUCCESS(Status))
5210          {
5211             SetLastNtError(Status);
5212             return( FALSE);
5213          }
5214       }
5215    }
5216 
5217    return( Res);
5218 }
5219 
5220 BOOL FASTCALL
5221 IntGetMenuItemRect(
5222    PWND pWnd,
5223    PMENU Menu,
5224    UINT uItem,
5225    PRECTL Rect)
5226 {
5227    LONG XMove, YMove;
5228    PITEM MenuItem;
5229    UINT I = uItem;
5230 
5231    if ((MenuItem = MENU_FindItem (&Menu, &I, MF_BYPOSITION)))
5232    {
5233       Rect->left   = MenuItem->xItem;
5234       Rect->top    = MenuItem->yItem;
5235       Rect->right  = MenuItem->cxItem; // Do this for now......
5236       Rect->bottom = MenuItem->cyItem;
5237    }
5238    else
5239    {
5240       ERR("Failed Item Lookup! %u\n", uItem);
5241       return FALSE;
5242    }
5243 
5244    if (!pWnd)
5245    {
5246       HWND hWnd = Menu->hWnd;
5247       if (!(pWnd = UserGetWindowObject(hWnd))) return FALSE;
5248    }
5249 
5250    if (Menu->fFlags & MNF_POPUP)
5251    {
5252      XMove = pWnd->rcClient.left;
5253      YMove = pWnd->rcClient.top;
5254    }
5255    else
5256    {
5257      XMove = pWnd->rcWindow.left;
5258      YMove = pWnd->rcWindow.top;
5259    }
5260 
5261    Rect->left   += XMove;
5262    Rect->top    += YMove;
5263    Rect->right  += XMove;
5264    Rect->bottom += YMove;
5265 
5266    return TRUE;
5267 }
5268 
5269 PMENU FASTCALL MENU_GetSystemMenu(PWND Window, PMENU Popup)
5270 {
5271    PMENU Menu, NewMenu = NULL, SysMenu = NULL;
5272    HMENU hSysMenu, hNewMenu = NULL;
5273    ROSMENUITEMINFO ItemInfoSet = {0};
5274    ROSMENUITEMINFO ItemInfo = {0};
5275    UNICODE_STRING MenuName;
5276 
5277    hSysMenu = UserCreateMenu(Window->head.rpdesk, FALSE);
5278    if (NULL == hSysMenu)
5279    {
5280       return NULL;
5281    }
5282    SysMenu = UserGetMenuObject(hSysMenu);
5283    if (NULL == SysMenu)
5284    {
5285        UserDestroyMenu(hSysMenu);
5286        return NULL;
5287    }
5288 
5289    SysMenu->fFlags |= MNF_SYSMENU;
5290    SysMenu->hWnd = UserHMGetHandle(Window);
5291 
5292    if (!Popup)
5293    {
5294       //hNewMenu = co_IntLoadSysMenuTemplate();
5295       if ( Window->ExStyle & WS_EX_MDICHILD )
5296       {
5297          RtlInitUnicodeString( &MenuName, L"SYSMENUMDI");
5298          hNewMenu = co_IntCallLoadMenu( hModClient, &MenuName);
5299       }
5300       else
5301       {
5302          RtlInitUnicodeString( &MenuName, L"SYSMENU");
5303          hNewMenu = co_IntCallLoadMenu( hModClient, &MenuName);
5304          //ERR("%wZ\n",&MenuName);
5305       }
5306       if (!hNewMenu)
5307       {
5308          ERR("No Menu!!\n");
5309          IntDestroyMenuObject(SysMenu, FALSE);
5310          return NULL;
5311       }
5312       Menu = UserGetMenuObject(hNewMenu);
5313       if (!Menu)
5314       {
5315          IntDestroyMenuObject(SysMenu, FALSE);
5316          return NULL;
5317       }
5318 
5319       // Do the rest in here.
5320 
5321       Menu->fFlags |= MNS_CHECKORBMP | MNF_SYSMENU  | MNF_POPUP;
5322 
5323       ItemInfoSet.cbSize = sizeof( MENUITEMINFOW);
5324       ItemInfoSet.fMask = MIIM_BITMAP;
5325       ItemInfoSet.hbmpItem = HBMMENU_POPUP_CLOSE;
5326       IntMenuItemInfo(Menu, SC_CLOSE, FALSE, &ItemInfoSet, TRUE, NULL);
5327       ItemInfoSet.hbmpItem = HBMMENU_POPUP_RESTORE;
5328       IntMenuItemInfo(Menu, SC_RESTORE, FALSE, &ItemInfoSet, TRUE, NULL);
5329       ItemInfoSet.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
5330       IntMenuItemInfo(Menu, SC_MAXIMIZE, FALSE, &ItemInfoSet, TRUE, NULL);
5331       ItemInfoSet.hbmpItem = HBMMENU_POPUP_MINIMIZE;
5332       IntMenuItemInfo(Menu, SC_MINIMIZE, FALSE, &ItemInfoSet, TRUE, NULL);
5333 
5334       NewMenu = IntCloneMenu(Menu);
5335       if (NewMenu == NULL)
5336       {
5337          IntDestroyMenuObject(Menu, FALSE);
5338          IntDestroyMenuObject(SysMenu, FALSE);
5339          return NULL;
5340       }
5341 
5342       IntReleaseMenuObject(NewMenu);
5343       UserSetMenuDefaultItem(NewMenu, SC_CLOSE, FALSE);
5344 
5345       IntDestroyMenuObject(Menu, FALSE);
5346    }
5347    else
5348    {
5349       NewMenu = Popup;
5350    }
5351    if (NewMenu)
5352    {
5353       NewMenu->fFlags |= MNF_SYSMENU | MNF_POPUP;
5354 
5355       if (Window->pcls->style & CS_NOCLOSE)
5356          IntRemoveMenuItem(NewMenu, SC_CLOSE, MF_BYCOMMAND, TRUE);
5357 
5358       ItemInfo.cbSize = sizeof(MENUITEMINFOW);
5359       ItemInfo.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_SUBMENU;
5360       ItemInfo.fType = MF_POPUP;
5361       ItemInfo.fState = MFS_ENABLED;
5362       ItemInfo.dwTypeData = NULL;
5363       ItemInfo.cch = 0;
5364       ItemInfo.hSubMenu = UserHMGetHandle(NewMenu);
5365       IntInsertMenuItem(SysMenu, (UINT) -1, TRUE, &ItemInfo, NULL);
5366 
5367       return SysMenu;
5368    }
5369    ERR("failed to load system menu!\n");
5370    return NULL;
5371 }
5372 
5373 PMENU FASTCALL
5374 IntGetSystemMenu(PWND Window, BOOL bRevert)
5375 {
5376    PMENU Menu;
5377 
5378    if (bRevert)
5379    {
5380       if (Window->SystemMenu)
5381       {
5382          Menu = UserGetMenuObject(Window->SystemMenu);
5383          if (Menu && !(Menu->fFlags & MNF_SYSDESKMN))
5384          {
5385             IntDestroyMenuObject(Menu, TRUE);
5386             Window->SystemMenu = NULL;
5387          }
5388       }
5389    }
5390    else
5391    {
5392       Menu = Window->SystemMenu ? UserGetMenuObject(Window->SystemMenu) : NULL;
5393       if ((!Menu || Menu->fFlags & MNF_SYSDESKMN) && Window->style & WS_SYSMENU)
5394       {
5395          Menu = MENU_GetSystemMenu(Window, NULL);
5396          Window->SystemMenu = Menu ? UserHMGetHandle(Menu) : NULL;
5397       }
5398    }
5399 
5400    if (Window->SystemMenu)
5401    {
5402       HMENU hMenu = IntGetSubMenu( Window->SystemMenu, 0);
5403       /* Store the dummy sysmenu handle to facilitate the refresh */
5404       /* of the close button if the SC_CLOSE item change */
5405       Menu = UserGetMenuObject(hMenu);
5406       if (Menu)
5407       {
5408          Menu->spwndNotify = Window;
5409          Menu->fFlags |= MNF_SYSSUBMENU;
5410       }
5411       return Menu;
5412    }
5413    return NULL;
5414 }
5415 
5416 BOOL FASTCALL
5417 IntSetSystemMenu(PWND Window, PMENU Menu)
5418 {
5419    PMENU OldMenu;
5420 
5421    if (!(Window->style & WS_SYSMENU)) return FALSE;
5422 
5423    if (Window->SystemMenu)
5424    {
5425       OldMenu = UserGetMenuObject(Window->SystemMenu);
5426       if (OldMenu)
5427       {
5428           OldMenu->fFlags &= ~MNF_SYSMENU;
5429           IntDestroyMenuObject(OldMenu, TRUE);
5430       }
5431    }
5432 
5433    OldMenu = MENU_GetSystemMenu(Window, Menu);
5434    if (OldMenu)
5435    {  // Use spmenuSys too!
5436       Window->SystemMenu = UserHMGetHandle(OldMenu);
5437    }
5438    else
5439       Window->SystemMenu = NULL;
5440 
5441    if (Menu && Window != Menu->spwndNotify)
5442    {
5443       Menu->spwndNotify = Window;
5444    }
5445 
5446    return TRUE;
5447 }
5448 
5449 BOOL FASTCALL
5450 IntSetMenu(
5451    PWND Wnd,
5452    HMENU Menu,
5453    BOOL *Changed)
5454 {
5455    PMENU OldMenu, NewMenu = NULL;
5456 
5457    if ((Wnd->style & (WS_CHILD | WS_POPUP)) == WS_CHILD)
5458    {
5459       ERR("SetMenu: Window is a Child 0x%p!\n",UserHMGetHandle(Wnd));
5460       EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5461       return FALSE;
5462    }
5463 
5464    *Changed = (UlongToHandle(Wnd->IDMenu) != Menu);
5465    if (! *Changed)
5466    {
5467       return TRUE;
5468    }
5469 
5470    if (Wnd->IDMenu)
5471    {
5472       OldMenu = IntGetMenuObject(UlongToHandle(Wnd->IDMenu));
5473       ASSERT(NULL == OldMenu || OldMenu->hWnd == UserHMGetHandle(Wnd));
5474    }
5475    else
5476    {
5477       OldMenu = NULL;
5478    }
5479 
5480    if (NULL != Menu)
5481    {
5482       NewMenu = IntGetMenuObject(Menu);
5483       if (NULL == NewMenu)
5484       {
5485          if (NULL != OldMenu)
5486          {
5487             IntReleaseMenuObject(OldMenu);
5488          }
5489          EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5490          return FALSE;
5491       }
5492       if (NULL != NewMenu->hWnd)
5493       {
5494          /* Can't use the same menu for two windows */
5495          if (NULL != OldMenu)
5496          {
5497             IntReleaseMenuObject(OldMenu);
5498          }
5499          EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5500          return FALSE;
5501       }
5502 
5503    }
5504 
5505    Wnd->IDMenu = (UINT_PTR) Menu;
5506    if (NULL != NewMenu)
5507    {
5508       NewMenu->hWnd = UserHMGetHandle(Wnd);
5509       IntReleaseMenuObject(NewMenu);
5510    }
5511    if (NULL != OldMenu)
5512    {
5513       OldMenu->hWnd = NULL;
5514       IntReleaseMenuObject(OldMenu);
5515    }
5516 
5517    return TRUE;
5518 }
5519 
5520 
5521 /* FUNCTIONS *****************************************************************/
5522 
5523 /*
5524  * @implemented
5525  */
5526 /* http://www.cyber-ta.org/releases/malware-analysis/public/SOURCES/b47155634ccb2c30630da7e3666d3d07/b47155634ccb2c30630da7e3666d3d07.trace.html#NtUserGetIconSize */
5527 DWORD
5528 APIENTRY
5529 NtUserCalcMenuBar(
5530     HWND   hwnd,
5531     DWORD  leftBorder,
5532     DWORD  rightBorder,
5533     DWORD  top,
5534     LPRECT prc )
5535 {
5536     HDC hdc;
5537     PWND Window;
5538     RECT Rect;
5539     DWORD ret;
5540 
5541     UserEnterExclusive();
5542 
5543     if(!(Window = UserGetWindowObject(hwnd)))
5544     {
5545         EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5546         UserLeave();
5547         return 0;
5548     }
5549 
5550     hdc = UserGetDCEx(NULL, NULL, DCX_CACHE);
5551     if (!hdc)
5552     {
5553         UserLeave();
5554         return 0;
5555     }
5556 
5557     Rect.left = leftBorder;
5558     Rect.right = Window->rcWindow.right - Window->rcWindow.left - rightBorder;
5559     Rect.top = top;
5560     Rect.bottom = 0;
5561 
5562     ret = MENU_DrawMenuBar(hdc, &Rect, Window, TRUE);
5563 
5564     UserReleaseDC( 0, hdc, FALSE );
5565 
5566     UserLeave();
5567 
5568     return ret;
5569 }
5570 
5571 /*
5572  * @implemented
5573  */
5574 DWORD APIENTRY
5575 NtUserCheckMenuItem(
5576    HMENU hMenu,
5577    UINT uIDCheckItem,
5578    UINT uCheck)
5579 {
5580    PMENU Menu;
5581    DECLARE_RETURN(DWORD);
5582 
5583    TRACE("Enter NtUserCheckMenuItem\n");
5584    UserEnterExclusive();
5585 
5586    if(!(Menu = UserGetMenuObject(hMenu)))
5587    {
5588       RETURN( (DWORD)-1);
5589    }
5590 
5591    RETURN( IntCheckMenuItem(Menu, uIDCheckItem, uCheck));
5592 
5593 CLEANUP:
5594    TRACE("Leave NtUserCheckMenuItem, ret=%lu\n",_ret_);
5595    UserLeave();
5596    END_CLEANUP;
5597 }
5598 
5599 /*
5600  * @implemented
5601  */
5602 BOOL APIENTRY
5603 NtUserDeleteMenu(
5604    HMENU hMenu,
5605    UINT uPosition,
5606    UINT uFlags)
5607 {
5608    PMENU Menu;
5609    DECLARE_RETURN(BOOL);
5610 
5611    TRACE("Enter NtUserDeleteMenu\n");
5612    UserEnterExclusive();
5613 
5614    if(!(Menu = UserGetMenuObject(hMenu)))
5615    {
5616       RETURN( FALSE);
5617    }
5618 
5619    RETURN( IntRemoveMenuItem(Menu, uPosition, uFlags, TRUE));
5620 
5621 CLEANUP:
5622    TRACE("Leave NtUserDeleteMenu, ret=%i\n",_ret_);
5623    UserLeave();
5624    END_CLEANUP;
5625 }
5626 
5627 /*
5628  * NtUserGetSystemMenu
5629  *
5630  * The NtUserGetSystemMenu function allows the application to access the
5631  * window menu (also known as the system menu or the control menu) for
5632  * copying and modifying.
5633  *
5634  * Parameters
5635  *    hWnd
5636  *       Handle to the window that will own a copy of the window menu.
5637  *    bRevert
5638  *       Specifies the action to be taken. If this parameter is FALSE,
5639  *       NtUserGetSystemMenu returns a handle to the copy of the window menu
5640  *       currently in use. The copy is initially identical to the window menu
5641  *       but it can be modified.
5642  *       If this parameter is TRUE, GetSystemMenu resets the window menu back
5643  *       to the default state. The previous window menu, if any, is destroyed.
5644  *
5645  * Return Value
5646  *    If the bRevert parameter is FALSE, the return value is a handle to a
5647  *    copy of the window menu. If the bRevert parameter is TRUE, the return
5648  *    value is NULL.
5649  *
5650  * Status
5651  *    @implemented
5652  */
5653 
5654 HMENU APIENTRY
5655 NtUserGetSystemMenu(HWND hWnd, BOOL bRevert)
5656 {
5657    PWND Window;
5658    PMENU Menu;
5659    DECLARE_RETURN(HMENU);
5660 
5661    TRACE("Enter NtUserGetSystemMenu\n");
5662    UserEnterExclusive();
5663 
5664    if (!(Window = UserGetWindowObject(hWnd)))
5665    {
5666       RETURN(NULL);
5667    }
5668 
5669    if (!(Menu = IntGetSystemMenu(Window, bRevert)))
5670    {
5671       RETURN(NULL);
5672    }
5673 
5674    RETURN(Menu->head.h);
5675 
5676 CLEANUP:
5677    TRACE("Leave NtUserGetSystemMenu, ret=%p\n", _ret_);
5678    UserLeave();
5679    END_CLEANUP;
5680 }
5681 
5682 /*
5683  * NtUserSetSystemMenu
5684  *
5685  * Status
5686  *    @implemented
5687  */
5688 
5689 BOOL APIENTRY
5690 NtUserSetSystemMenu(HWND hWnd, HMENU hMenu)
5691 {
5692    BOOL Result = FALSE;
5693    PWND Window;
5694    PMENU Menu;
5695    DECLARE_RETURN(BOOL);
5696 
5697    TRACE("Enter NtUserSetSystemMenu\n");
5698    UserEnterExclusive();
5699 
5700    if (!(Window = UserGetWindowObject(hWnd)))
5701    {
5702       RETURN( FALSE);
5703    }
5704 
5705    if (hMenu)
5706    {
5707       /*
5708        * Assign new menu handle and Up the Lock Count.
5709        */
5710       if (!(Menu = IntGetMenuObject(hMenu)))
5711       {
5712          RETURN( FALSE);
5713       }
5714 
5715       Result = IntSetSystemMenu(Window, Menu);
5716    }
5717    else
5718       EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5719 
5720    RETURN( Result);
5721 
5722 CLEANUP:
5723    TRACE("Leave NtUserSetSystemMenu, ret=%i\n",_ret_);
5724    UserLeave();
5725    END_CLEANUP;
5726 }
5727 
5728 /*
5729  * @implemented
5730  */
5731 BOOLEAN APIENTRY
5732 NtUserGetTitleBarInfo(
5733     HWND hwnd,
5734     PTITLEBARINFO bti)
5735 {
5736     PWND WindowObject;
5737     TITLEBARINFO bartitleinfo;
5738     DECLARE_RETURN(BOOLEAN);
5739     BOOLEAN retValue = TRUE;
5740 
5741     TRACE("Enter NtUserGetTitleBarInfo\n");
5742     UserEnterExclusive();
5743 
5744     /* Vaildate the windows handle */
5745     if (!(WindowObject = UserGetWindowObject(hwnd)))
5746     {
5747         EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5748         retValue = FALSE;
5749     }
5750 
5751     _SEH2_TRY
5752     {
5753         /* Copy our usermode buffer bti to local buffer bartitleinfo */
5754         ProbeForRead(bti, sizeof(TITLEBARINFO), 1);
5755         RtlCopyMemory(&bartitleinfo, bti, sizeof(TITLEBARINFO));
5756     }
5757     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5758     {
5759         /* Fail copy the data */
5760         EngSetLastError(ERROR_INVALID_PARAMETER);
5761         retValue = FALSE;
5762     }
5763     _SEH2_END
5764 
5765     /* Get the tile bar info */
5766     if (retValue)
5767     {
5768         retValue = intGetTitleBarInfo(WindowObject, &bartitleinfo);
5769         if (retValue)
5770         {
5771             _SEH2_TRY
5772             {
5773                 /* Copy our buffer to user mode buffer bti */
5774                 ProbeForWrite(bti, sizeof(TITLEBARINFO), 1);
5775                 RtlCopyMemory(bti, &bartitleinfo, sizeof(TITLEBARINFO));
5776             }
5777             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5778             {
5779                 /* Fail copy the data */
5780                 EngSetLastError(ERROR_INVALID_PARAMETER);
5781                 retValue = FALSE;
5782             }
5783             _SEH2_END
5784         }
5785     }
5786 
5787     RETURN( retValue );
5788 
5789 CLEANUP:
5790     TRACE("Leave NtUserGetTitleBarInfo, ret=%u\n",_ret_);
5791     UserLeave();
5792     END_CLEANUP;
5793 }
5794 
5795 /*
5796  * @implemented
5797  */
5798 BOOL FASTCALL UserDestroyMenu(HMENU hMenu)
5799 {
5800    PMENU Menu;
5801    PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
5802 
5803    if(!(Menu = UserGetMenuObject(hMenu)))
5804    {
5805       return FALSE;
5806    }
5807 
5808    if (Menu->head.rpdesk != pti->rpdesk)
5809    {
5810       EngSetLastError(ERROR_ACCESS_DENIED);
5811       return FALSE;
5812    }
5813    return IntDestroyMenuObject(Menu, FALSE);
5814 }
5815 
5816 /*
5817  * @implemented
5818  */
5819 BOOL APIENTRY
5820 NtUserDestroyMenu(
5821    HMENU hMenu)
5822 {
5823    PMENU Menu;
5824    DECLARE_RETURN(BOOL);
5825 
5826    TRACE("Enter NtUserDestroyMenu\n");
5827    UserEnterExclusive();
5828 
5829    if(!(Menu = UserGetMenuObject(hMenu)))
5830    {
5831       RETURN( FALSE);
5832    }
5833    if (Menu->head.rpdesk != gptiCurrent->rpdesk)
5834    {
5835       EngSetLastError(ERROR_ACCESS_DENIED);
5836       RETURN( FALSE);
5837    }
5838    RETURN( IntDestroyMenuObject(Menu, TRUE));
5839 
5840 CLEANUP:
5841    TRACE("Leave NtUserDestroyMenu, ret=%i\n",_ret_);
5842    UserLeave();
5843    END_CLEANUP;
5844 }
5845 
5846 /*
5847  * @implemented
5848  */
5849 UINT APIENTRY
5850 NtUserEnableMenuItem(
5851    HMENU hMenu,
5852    UINT uIDEnableItem,
5853    UINT uEnable)
5854 {
5855    PMENU Menu;
5856    DECLARE_RETURN(UINT);
5857 
5858    TRACE("Enter NtUserEnableMenuItem\n");
5859    UserEnterExclusive();
5860 
5861    if(!(Menu = UserGetMenuObject(hMenu)))
5862    {
5863       RETURN(-1);
5864    }
5865 
5866    RETURN( IntEnableMenuItem(Menu, uIDEnableItem, uEnable));
5867 
5868 CLEANUP:
5869    TRACE("Leave NtUserEnableMenuItem, ret=%u\n",_ret_);
5870    UserLeave();
5871    END_CLEANUP;
5872 }
5873 
5874 /*
5875  * @implemented
5876  */
5877 BOOL APIENTRY
5878 NtUserEndMenu(VOID)
5879 {
5880    //PWND pWnd;
5881    TRACE("Enter NtUserEndMenu\n");
5882    UserEnterExclusive();
5883  /*  if ( gptiCurrent->pMenuState &&
5884         gptiCurrent->pMenuState->pGlobalPopupMenu )
5885    {
5886        pWnd = IntGetMSWND(gptiCurrent->pMenuState);
5887        if (pWnd)
5888        {
5889           UserPostMessage( UserHMGetHandle(pWnd), WM_CANCELMODE, 0, 0);
5890        }
5891        else
5892           gptiCurrent->pMenuState->fInsideMenuLoop = FALSE;
5893    }*/
5894    if (fInsideMenuLoop && top_popup)
5895    {
5896       fInsideMenuLoop = FALSE;
5897       UserPostMessage( top_popup, WM_CANCELMODE, 0, 0);
5898    }
5899    UserLeave();
5900    TRACE("Leave NtUserEndMenu\n");
5901    return TRUE;
5902 }
5903 
5904 /*
5905  * @implemented
5906  */
5907 BOOL APIENTRY
5908 NtUserGetMenuBarInfo(
5909    HWND hwnd,
5910    LONG idObject,
5911    LONG idItem,
5912    PMENUBARINFO pmbi)
5913 {
5914    PWND pWnd;
5915    HMENU hMenu;
5916    MENUBARINFO kmbi;
5917    BOOL Ret;
5918    PPOPUPMENU pPopupMenu;
5919    USER_REFERENCE_ENTRY Ref;
5920    NTSTATUS Status = STATUS_SUCCESS;
5921    PMENU Menu = NULL;
5922    DECLARE_RETURN(BOOL);
5923 
5924    TRACE("Enter NtUserGetMenuBarInfo\n");
5925    UserEnterShared();
5926 
5927    if (!(pWnd = UserGetWindowObject(hwnd)))
5928    {
5929         EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5930         RETURN(FALSE);
5931    }
5932 
5933    UserRefObjectCo(pWnd, &Ref);
5934 
5935    RECTL_vSetEmptyRect(&kmbi.rcBar);
5936    kmbi.hMenu = NULL;
5937    kmbi.hwndMenu = NULL;
5938    kmbi.fBarFocused = FALSE;
5939    kmbi.fFocused = FALSE;
5940 
5941    switch (idObject)
5942    {
5943     case OBJID_CLIENT:
5944         if (!pWnd->pcls->fnid)
5945             RETURN(FALSE);
5946         if (pWnd->pcls->fnid != FNID_MENU)
5947         {
5948             WARN("called on invalid window: %u\n", pWnd->pcls->fnid);
5949             EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5950             RETURN(FALSE);
5951         }
5952         // Windows does this! Wine checks for Atom and uses GetWindowLongPtrW.
5953         hMenu = (HMENU)co_IntSendMessage(hwnd, MN_GETHMENU, 0, 0);
5954         pPopupMenu = ((PMENUWND)pWnd)->ppopupmenu;
5955         if (pPopupMenu && pPopupMenu->spmenu)
5956         {
5957            if (UserHMGetHandle(pPopupMenu->spmenu) != hMenu)
5958            {
5959               ERR("Window Pop Up hMenu %p not the same as Get hMenu %p!\n",pPopupMenu->spmenu->head.h,hMenu);
5960            }
5961         }
5962         break;
5963     case OBJID_MENU:
5964         if (pWnd->style & WS_CHILD) RETURN(FALSE);
5965         hMenu = UlongToHandle(pWnd->IDMenu);
5966         TRACE("GMBI: OBJID_MENU hMenu %p\n",hMenu);
5967         break;
5968     case OBJID_SYSMENU:
5969         if (!(pWnd->style & WS_SYSMENU)) RETURN(FALSE);
5970         Menu = IntGetSystemMenu(pWnd, FALSE);
5971         hMenu = UserHMGetHandle(Menu);
5972         break;
5973     default:
5974         RETURN(FALSE);
5975    }
5976 
5977    if (!hMenu)
5978       RETURN(FALSE);
5979 
5980    _SEH2_TRY
5981    {
5982        ProbeForRead(pmbi, sizeof(MENUBARINFO), 1);
5983        kmbi.cbSize = pmbi->cbSize;
5984    }
5985    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5986    {
5987        kmbi.cbSize = 0;
5988    }
5989    _SEH2_END
5990 
5991    if (kmbi.cbSize != sizeof(MENUBARINFO))
5992    {
5993        EngSetLastError(ERROR_INVALID_PARAMETER);
5994        RETURN(FALSE);
5995    }
5996 
5997    if (!Menu) Menu = UserGetMenuObject(hMenu);
5998    if (!Menu)
5999        RETURN(FALSE);
6000 
6001    if ((idItem < 0) || ((ULONG)idItem > Menu->cItems))
6002        RETURN(FALSE);
6003 
6004    if (idItem == 0)
6005    {
6006       Ret = IntGetMenuItemRect(pWnd, Menu, 0, &kmbi.rcBar);
6007       kmbi.rcBar.right = kmbi.rcBar.left + Menu->cxMenu;
6008       kmbi.rcBar.bottom = kmbi.rcBar.top + Menu->cyMenu;
6009       TRACE("idItem a 0 %d\n",Ret);
6010    }
6011    else
6012    {
6013       Ret = IntGetMenuItemRect(pWnd, Menu, idItem-1, &kmbi.rcBar);
6014       TRACE("idItem b %d %d\n", idItem-1, Ret);
6015    }
6016 
6017    kmbi.hMenu = hMenu;
6018    kmbi.fBarFocused = top_popup_hmenu == hMenu;
6019    TRACE("GMBI: top p hm %p hMenu %p\n",top_popup_hmenu, hMenu);
6020    if (idItem)
6021    {
6022        kmbi.fFocused = Menu->iItem == idItem-1;
6023        if (kmbi.fFocused && (Menu->rgItems[idItem - 1].spSubMenu))
6024        {
6025           kmbi.hwndMenu = Menu->rgItems[idItem - 1].spSubMenu->hWnd;
6026        }
6027    }
6028    else
6029    {
6030        kmbi.fFocused = kmbi.fBarFocused;
6031    }
6032 
6033    _SEH2_TRY
6034    {
6035       ProbeForWrite(pmbi, sizeof(MENUBARINFO), 1);
6036       RtlCopyMemory(pmbi, &kmbi, sizeof(MENUBARINFO));
6037    }
6038    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6039    {
6040       Status = _SEH2_GetExceptionCode();
6041    }
6042    _SEH2_END
6043 
6044    if (!NT_SUCCESS(Status))
6045    {
6046       SetLastNtError(Status);
6047       RETURN(FALSE);
6048    }
6049 
6050    RETURN(TRUE);
6051 
6052 CLEANUP:
6053    if (pWnd) UserDerefObjectCo(pWnd);
6054    TRACE("Leave NtUserGetMenuBarInfo, ret=%i\n",_ret_);
6055    UserLeave();
6056    END_CLEANUP;
6057 }
6058 
6059 /*
6060  * @implemented
6061  */
6062 UINT APIENTRY
6063 NtUserGetMenuIndex(
6064    HMENU hMenu,
6065    HMENU hSubMenu)
6066 {
6067    PMENU Menu, SubMenu;
6068    PITEM MenuItem;
6069    UINT i;
6070    DECLARE_RETURN(UINT);
6071 
6072    TRACE("Enter NtUserGetMenuIndex\n");
6073    UserEnterShared();
6074 
6075    if ( !(Menu = UserGetMenuObject(hMenu)) ||
6076         !(SubMenu = UserGetMenuObject(hSubMenu)) )
6077       RETURN(0xFFFFFFFF);
6078 
6079    MenuItem = Menu->rgItems;
6080    for (i = 0; i < Menu->cItems; i++, MenuItem++)
6081    {
6082        if (MenuItem->spSubMenu == SubMenu)
6083           RETURN(MenuItem->wID);
6084    }
6085    RETURN(0xFFFFFFFF);
6086 
6087 CLEANUP:
6088    TRACE("Leave NtUserGetMenuIndex, ret=%u\n",_ret_);
6089    UserLeave();
6090    END_CLEANUP;
6091 }
6092 
6093 /*
6094  * @implemented
6095  */
6096 BOOL APIENTRY
6097 NtUserGetMenuItemRect(
6098    HWND hWnd,
6099    HMENU hMenu,
6100    UINT uItem,
6101    PRECTL lprcItem)
6102 {
6103    PWND ReferenceWnd;
6104    LONG XMove, YMove;
6105    RECTL Rect;
6106    PMENU Menu;
6107    PITEM MenuItem;
6108    NTSTATUS Status = STATUS_SUCCESS;
6109    DECLARE_RETURN(BOOL);
6110 
6111    TRACE("Enter NtUserGetMenuItemRect\n");
6112    UserEnterShared();
6113 
6114    if (!(Menu = UserGetMenuObject(hMenu)))
6115    {
6116       RETURN(FALSE);
6117    }
6118 
6119    if ((MenuItem = MENU_FindItem (&Menu, &uItem, MF_BYPOSITION)))
6120    {
6121       Rect.left   = MenuItem->xItem;
6122       Rect.top    = MenuItem->yItem;
6123       Rect.right  = MenuItem->cxItem; // Do this for now......
6124       Rect.bottom = MenuItem->cyItem;
6125    }
6126    else
6127       RETURN(FALSE);
6128 
6129    if(!hWnd)
6130    {
6131        hWnd = Menu->hWnd;
6132    }
6133 
6134    if (lprcItem == NULL) RETURN( FALSE);
6135 
6136    if (!(ReferenceWnd = UserGetWindowObject(hWnd))) RETURN( FALSE);
6137 
6138    if (Menu->fFlags & MNF_POPUP)
6139    {
6140      XMove = ReferenceWnd->rcClient.left;
6141      YMove = ReferenceWnd->rcClient.top;
6142    }
6143    else
6144    {
6145      XMove = ReferenceWnd->rcWindow.left;
6146      YMove = ReferenceWnd->rcWindow.top;
6147    }
6148 
6149    Rect.left   += XMove;
6150    Rect.top    += YMove;
6151    Rect.right  += XMove;
6152    Rect.bottom += YMove;
6153 
6154    _SEH2_TRY
6155    {
6156       RtlCopyMemory(lprcItem, &Rect, sizeof(RECTL));
6157    }
6158    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6159    {
6160       Status = _SEH2_GetExceptionCode();
6161    }
6162    _SEH2_END
6163 
6164    if (!NT_SUCCESS(Status))
6165    {
6166       SetLastNtError(Status);
6167       RETURN(FALSE);
6168    }
6169    RETURN(TRUE);
6170 
6171 CLEANUP:
6172    TRACE("Leave NtUserGetMenuItemRect, ret=%i\n",_ret_);
6173    UserLeave();
6174    END_CLEANUP;
6175 }
6176 
6177 /*
6178  * @implemented
6179  */
6180 BOOL APIENTRY
6181 NtUserHiliteMenuItem(
6182    HWND hWnd,
6183    HMENU hMenu,
6184    UINT uItemHilite,
6185    UINT uHilite)
6186 {
6187    PMENU Menu;
6188    PWND Window;
6189    DECLARE_RETURN(BOOLEAN);
6190 
6191    TRACE("Enter NtUserHiliteMenuItem\n");
6192    UserEnterExclusive();
6193 
6194    if(!(Window = UserGetWindowObject(hWnd)))
6195    {
6196       EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
6197       RETURN(FALSE);
6198    }
6199 
6200    if(!(Menu = UserGetMenuObject(hMenu)))
6201    {
6202       EngSetLastError(ERROR_INVALID_MENU_HANDLE);
6203       RETURN(FALSE);
6204    }
6205 
6206    RETURN( IntHiliteMenuItem(Window, Menu, uItemHilite, uHilite));
6207 
6208 CLEANUP:
6209    TRACE("Leave NtUserHiliteMenuItem, ret=%u\n",_ret_);
6210    UserLeave();
6211    END_CLEANUP;
6212 }
6213 
6214 /*
6215  * @implemented
6216  */
6217 DWORD
6218 APIENTRY
6219 NtUserDrawMenuBarTemp(
6220    HWND hWnd,
6221    HDC hDC,
6222    PRECT pRect,
6223    HMENU hMenu,
6224    HFONT hFont)
6225 {
6226    PMENU Menu;
6227    PWND Window;
6228    RECT Rect;
6229    NTSTATUS Status = STATUS_SUCCESS;
6230    DECLARE_RETURN(DWORD);
6231 
6232    ERR("Enter NtUserDrawMenuBarTemp\n");
6233    UserEnterExclusive();
6234 
6235    if(!(Window = UserGetWindowObject(hWnd)))
6236    {
6237       EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
6238       RETURN(0);
6239    }
6240 
6241    if(!(Menu = UserGetMenuObject(hMenu)))
6242    {
6243       EngSetLastError(ERROR_INVALID_MENU_HANDLE);
6244       RETURN(0);
6245    }
6246 
6247    _SEH2_TRY
6248    {
6249       ProbeForRead(pRect, sizeof(RECT), sizeof(ULONG));
6250       RtlCopyMemory(&Rect, pRect, sizeof(RECT));
6251    }
6252    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6253    {
6254       Status = _SEH2_GetExceptionCode();
6255    }
6256    _SEH2_END;
6257 
6258    if (Status != STATUS_SUCCESS)
6259    {
6260       SetLastNtError(Status);
6261       RETURN(0);
6262    }
6263 
6264    RETURN( IntDrawMenuBarTemp(Window, hDC, &Rect, Menu, hFont));
6265 
6266 CLEANUP:
6267    ERR("Leave NtUserDrawMenuBarTemp, ret=%u\n",_ret_);
6268    UserLeave();
6269    END_CLEANUP;
6270 }
6271 
6272 /*
6273  * @implemented
6274  */
6275 int APIENTRY
6276 NtUserMenuItemFromPoint(
6277    HWND hWnd,
6278    HMENU hMenu,
6279    DWORD X,
6280    DWORD Y)
6281 {
6282    PMENU Menu;
6283    PWND Window = NULL;
6284    PITEM mi;
6285    ULONG i;
6286    DECLARE_RETURN(int);
6287 
6288    TRACE("Enter NtUserMenuItemFromPoint\n");
6289    UserEnterExclusive();
6290 
6291    if (!(Menu = UserGetMenuObject(hMenu)))
6292    {
6293       RETURN( -1);
6294    }
6295 
6296    if (!(Window = UserGetWindowObject(Menu->hWnd)))
6297    {
6298       RETURN( -1);
6299    }
6300 
6301    X -= Window->rcWindow.left;
6302    Y -= Window->rcWindow.top;
6303 
6304    mi = Menu->rgItems;
6305    for (i = 0; i < Menu->cItems; i++, mi++)
6306    {
6307       RECTL Rect;
6308 
6309       Rect.left   = mi->xItem;
6310       Rect.top    = mi->yItem;
6311       Rect.right  = mi->cxItem;
6312       Rect.bottom = mi->cyItem;
6313 
6314       MENU_AdjustMenuItemRect(Menu, &Rect);
6315 
6316       if (RECTL_bPointInRect(&Rect, X, Y))
6317       {
6318          break;
6319       }
6320    }
6321 
6322    RETURN( (mi ? i : NO_SELECTED_ITEM));
6323 
6324 CLEANUP:
6325    TRACE("Leave NtUserMenuItemFromPoint, ret=%i\n",_ret_);
6326    UserLeave();
6327    END_CLEANUP;
6328 }
6329 
6330 
6331 DWORD
6332 APIENTRY
6333 NtUserPaintMenuBar(
6334     HWND hWnd,
6335     HDC hDC,
6336     ULONG leftBorder,
6337     ULONG rightBorder,
6338     ULONG top,
6339     BOOL bActive)
6340 {
6341    PWND Window;
6342    RECT Rect;
6343    DWORD ret;
6344 
6345    UserEnterExclusive();
6346 
6347    if(!(Window = UserGetWindowObject(hWnd)))
6348    {
6349       EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
6350       UserLeave();
6351       return 0;
6352    }
6353 
6354    Rect.left = leftBorder;
6355    Rect.right = Window->rcWindow.right - Window->rcWindow.left - rightBorder;
6356    Rect.top = top;
6357    Rect.bottom = 0;
6358 
6359    ret = MENU_DrawMenuBar(hDC, &Rect, Window, FALSE);
6360 
6361    UserLeave();
6362 
6363    return ret;
6364 }
6365 
6366 /*
6367  * @implemented
6368  */
6369 BOOL APIENTRY
6370 NtUserRemoveMenu(
6371    HMENU hMenu,
6372    UINT uPosition,
6373    UINT uFlags)
6374 {
6375    PMENU Menu;
6376    DECLARE_RETURN(BOOL);
6377 
6378    TRACE("Enter NtUserRemoveMenu\n");
6379    UserEnterExclusive();
6380 
6381    if(!(Menu = UserGetMenuObject(hMenu)))
6382    {
6383       RETURN( FALSE);
6384    }
6385 
6386    RETURN(IntRemoveMenuItem(Menu, uPosition, uFlags, FALSE));
6387 
6388 CLEANUP:
6389    TRACE("Leave NtUserRemoveMenu, ret=%i\n",_ret_);
6390    UserLeave();
6391    END_CLEANUP;
6392 
6393 }
6394 
6395 /*
6396  * @implemented
6397  */
6398 BOOL APIENTRY
6399 NtUserSetMenu(
6400    HWND hWnd,
6401    HMENU Menu,
6402    BOOL Repaint)
6403 {
6404    PWND Window;
6405    BOOL Changed;
6406    DECLARE_RETURN(BOOL);
6407 
6408    TRACE("Enter NtUserSetMenu\n");
6409    UserEnterExclusive();
6410 
6411    if (!(Window = UserGetWindowObject(hWnd)))
6412    {
6413       RETURN( FALSE);
6414    }
6415 
6416    if (!IntSetMenu(Window, Menu, &Changed))
6417    {
6418       RETURN( FALSE);
6419    }
6420 
6421    // Not minimized and please repaint!!!
6422    if (!(Window->style & WS_MINIMIZE) && (Repaint || Changed))
6423    {
6424       USER_REFERENCE_ENTRY Ref;
6425       UserRefObjectCo(Window, &Ref);
6426       co_WinPosSetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
6427       UserDerefObjectCo(Window);
6428    }
6429 
6430    RETURN( TRUE);
6431 
6432 CLEANUP:
6433    TRACE("Leave NtUserSetMenu, ret=%i\n",_ret_);
6434    UserLeave();
6435    END_CLEANUP;
6436 }
6437 
6438 /*
6439  * @implemented
6440  */
6441 BOOL APIENTRY
6442 NtUserSetMenuContextHelpId(
6443    HMENU hMenu,
6444    DWORD dwContextHelpId)
6445 {
6446    PMENU Menu;
6447    DECLARE_RETURN(BOOL);
6448 
6449    TRACE("Enter NtUserSetMenuContextHelpId\n");
6450    UserEnterExclusive();
6451 
6452    if(!(Menu = UserGetMenuObject(hMenu)))
6453    {
6454       RETURN( FALSE);
6455    }
6456 
6457    RETURN(IntSetMenuContextHelpId(Menu, dwContextHelpId));
6458 
6459 CLEANUP:
6460    TRACE("Leave NtUserSetMenuContextHelpId, ret=%i\n",_ret_);
6461    UserLeave();
6462    END_CLEANUP;
6463 }
6464 
6465 /*
6466  * @implemented
6467  */
6468 BOOL APIENTRY
6469 NtUserSetMenuDefaultItem(
6470    HMENU hMenu,
6471    UINT uItem,
6472    UINT fByPos)
6473 {
6474    PMENU Menu;
6475    DECLARE_RETURN(BOOL);
6476 
6477    TRACE("Enter NtUserSetMenuDefaultItem\n");
6478    UserEnterExclusive();
6479 
6480    if(!(Menu = UserGetMenuObject(hMenu)))
6481    {
6482       RETURN( FALSE);
6483    }
6484 
6485    RETURN( UserSetMenuDefaultItem(Menu, uItem, fByPos));
6486 
6487 CLEANUP:
6488    TRACE("Leave NtUserSetMenuDefaultItem, ret=%i\n",_ret_);
6489    UserLeave();
6490    END_CLEANUP;
6491 }
6492 
6493 /*
6494  * @implemented
6495  */
6496 BOOL APIENTRY
6497 NtUserSetMenuFlagRtoL(
6498    HMENU hMenu)
6499 {
6500    PMENU Menu;
6501    DECLARE_RETURN(BOOL);
6502 
6503    TRACE("Enter NtUserSetMenuFlagRtoL\n");
6504    UserEnterExclusive();
6505 
6506    if(!(Menu = UserGetMenuObject(hMenu)))
6507    {
6508       RETURN( FALSE);
6509    }
6510 
6511    RETURN(IntSetMenuFlagRtoL(Menu));
6512 
6513 CLEANUP:
6514    TRACE("Leave NtUserSetMenuFlagRtoL, ret=%i\n",_ret_);
6515    UserLeave();
6516    END_CLEANUP;
6517 }
6518 
6519 /*
6520  * @implemented
6521  */
6522 BOOL APIENTRY
6523 NtUserThunkedMenuInfo(
6524    HMENU hMenu,
6525    LPCMENUINFO lpcmi)
6526 {
6527    PMENU Menu;
6528    DECLARE_RETURN(BOOL);
6529 
6530    TRACE("Enter NtUserThunkedMenuInfo\n");
6531    UserEnterExclusive();
6532 
6533    if (!(Menu = UserGetMenuObject(hMenu)))
6534    {
6535       RETURN(FALSE);
6536    }
6537 
6538    RETURN(UserMenuInfo(Menu, (PROSMENUINFO)lpcmi, TRUE));
6539 
6540 CLEANUP:
6541    TRACE("Leave NtUserThunkedMenuInfo, ret=%i\n",_ret_);
6542    UserLeave();
6543    END_CLEANUP;
6544 }
6545 
6546 /*
6547  * @implemented
6548  */
6549 BOOL APIENTRY
6550 NtUserThunkedMenuItemInfo(
6551    HMENU hMenu,
6552    UINT uItem,
6553    BOOL fByPosition,
6554    BOOL bInsert,
6555    LPMENUITEMINFOW lpmii,
6556    PUNICODE_STRING lpszCaption)
6557 {
6558    PMENU Menu;
6559    NTSTATUS Status;
6560    UNICODE_STRING lstrCaption;
6561    DECLARE_RETURN(BOOL);
6562 
6563    TRACE("Enter NtUserThunkedMenuItemInfo\n");
6564    UserEnterExclusive();
6565 
6566    /* lpszCaption may be NULL, check for it and call RtlInitUnicodeString()
6567       if bInsert == TRUE call UserInsertMenuItem() else UserSetMenuItemInfo()   */
6568 
6569    RtlInitEmptyUnicodeString(&lstrCaption, NULL, 0);
6570 
6571    if (!(Menu = UserGetMenuObject(hMenu)))
6572    {
6573       RETURN(FALSE);
6574    }
6575 
6576    /* Check if we got a Caption */
6577    if (lpszCaption && lpszCaption->Buffer)
6578    {
6579       /* Copy the string to kernel mode */
6580       Status = ProbeAndCaptureUnicodeString( &lstrCaption,
6581                                                  UserMode,
6582                                               lpszCaption);
6583       if (!NT_SUCCESS(Status))
6584       {
6585          ERR("Failed to capture MenuItem Caption (status 0x%08x)\n",Status);
6586          SetLastNtError(Status);
6587          RETURN(FALSE);
6588       }
6589    }
6590 
6591    if (bInsert) RETURN( UserInsertMenuItem(Menu, uItem, fByPosition, lpmii, &lstrCaption));
6592 
6593    RETURN( UserMenuItemInfo(Menu, uItem, fByPosition, (PROSMENUITEMINFO)lpmii, TRUE, &lstrCaption));
6594 
6595 CLEANUP:
6596    if (lstrCaption.Buffer)
6597    {
6598       ReleaseCapturedUnicodeString(&lstrCaption, UserMode);
6599    }
6600 
6601    TRACE("Leave NtUserThunkedMenuItemInfo, ret=%i\n",_ret_);
6602    UserLeave();
6603    END_CLEANUP;
6604 }
6605 
6606 /*
6607  * @implemented
6608  */
6609 BOOL APIENTRY
6610 NtUserTrackPopupMenuEx(
6611    HMENU hMenu,
6612    UINT fuFlags,
6613    int x,
6614    int y,
6615    HWND hWnd,
6616    LPTPMPARAMS lptpm)
6617 {
6618    PMENU menu;
6619    PWND pWnd;
6620    TPMPARAMS tpm;
6621    BOOL Ret = FALSE;
6622    USER_REFERENCE_ENTRY Ref;
6623 
6624    TRACE("Enter NtUserTrackPopupMenuEx\n");
6625    UserEnterExclusive();
6626    /* Parameter check */
6627    if (!(menu = UserGetMenuObject( hMenu )))
6628    {
6629       ERR("TPME : Invalid Menu handle.\n");
6630       EngSetLastError( ERROR_INVALID_MENU_HANDLE );
6631       goto Exit;
6632    }
6633 
6634    if (!(pWnd = UserGetWindowObject(hWnd)))
6635    {
6636       ERR("TPME : Invalid Window handle.\n");
6637       goto Exit;
6638    }
6639 
6640    if (lptpm)
6641    {
6642       _SEH2_TRY
6643       {
6644          ProbeForRead(lptpm, sizeof(TPMPARAMS), sizeof(ULONG));
6645          tpm = *lptpm;
6646       }
6647       _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6648       {
6649          _SEH2_YIELD(goto Exit);
6650       }
6651       _SEH2_END
6652    }
6653    UserRefObjectCo(pWnd, &Ref);
6654    Ret = IntTrackPopupMenuEx(menu, fuFlags, x, y, pWnd, lptpm ? &tpm : NULL);
6655    UserDerefObjectCo(pWnd);
6656 
6657 Exit:
6658    TRACE("Leave NtUserTrackPopupMenuEx, ret=%i\n",Ret);
6659    UserLeave();
6660    return Ret;
6661 }
6662 
6663 /* EOF */
6664