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