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