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