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