[SHELL32_APITEST] Follow-up to #6796 (25e2f5f)
[reactos.git] / win32ss / user / ntuser / menu.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Menus
5 * FILE: win32ss/user/ntuser/menu.c
6 * PROGRAMER: Thomas Weidenmueller (w3seek@users.sourceforge.net)
7 */
8
9 #include <win32k.h>
10 DBG_DEFAULT_CHANNEL(UserMenu);
11
12 /* INTERNAL ******************************************************************/
13
14 HFONT ghMenuFont = NULL;
15 HFONT ghMenuFontBold = NULL;
16 static SIZE MenuCharSize;
17
18 /* Use global popup window because there's no way 2 menus can
19 * be tracked at the same time. */
20 static HWND top_popup = NULL;
21 static HMENU top_popup_hmenu = NULL;
22
23 BOOL fInsideMenuLoop = FALSE;
24 BOOL fInEndMenu = FALSE;
25
26 /* internal popup menu window messages */
27
28 #define MM_SETMENUHANDLE (WM_USER + 0)
29 #define MM_GETMENUHANDLE (WM_USER + 1)
30
31 /* internal flags for menu tracking */
32
33 #define TF_ENDMENU 0x10000
34 #define TF_SUSPENDPOPUP 0x20000
35 #define TF_SKIPREMOVE 0x40000
36
37
38 /* maximum allowed depth of any branch in the menu tree.
39 * This value is slightly larger than in windows (25) to
40 * stay on the safe side. */
41 #define MAXMENUDEPTH 30
42
43 #define MNS_STYLE_MASK (MNS_NOCHECK|MNS_MODELESS|MNS_DRAGDROP|MNS_AUTODISMISS|MNS_NOTIFYBYPOS|MNS_CHECKORBMP)
44
45 #define MENUITEMINFO_TYPE_MASK \
46 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
47 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
48 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
49
50 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
51
52 #define STATE_MASK (~TYPE_MASK)
53
54 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
55
56 #define MII_STATE_MASK (MFS_GRAYED|MFS_CHECKED|MFS_HILITE|MFS_DEFAULT)
57
58 #define IS_SYSTEM_MENU(MenuInfo) \
59 (!((MenuInfo)->fFlags & MNF_POPUP) && ((MenuInfo)->fFlags & MNF_SYSMENU))
60
61 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
62
63 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
64 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
65
66 /* Maximum number of menu items a menu can contain */
67 #define MAX_MENU_ITEMS (0x4000)
68 #define MAX_GOINTOSUBMENU (0x10)
69
70 /* Space between 2 columns */
71 #define MENU_COL_SPACE 4
72
73 #define MENU_ITEM_HBMP_SPACE (5)
74 #define MENU_BAR_ITEMS_SPACE (12)
75 #define SEPARATOR_HEIGHT (5)
76 #define MENU_TAB_SPACE (8)
77
78 typedef struct
79 {
80 UINT TrackFlags;
81 PMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/
82 PMENU TopMenu; /* initial menu */
83 PWND OwnerWnd; /* where notifications are sent */
84 POINT Pt;
85 } MTRACKER;
86
87 /* Internal MenuTrackMenu() flags */
88 #define TPM_INTERNAL 0xF0000000
89 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
90 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
91
92 #define ITEM_PREV -1
93 #define ITEM_NEXT 1
94
95 #define UpdateMenuItemState(state, change) \
96 {\
97 if((change) & MF_GRAYED) { \
98 (state) |= MF_GRAYED; \
99 } else { \
100 (state) &= ~MF_GRAYED; \
101 } /* Separate the two for test_menu_resource_layout.*/ \
102 if((change) & MF_DISABLED) { \
103 (state) |= MF_DISABLED; \
104 } else { \
105 (state) &= ~MF_DISABLED; \
106 } \
107 if((change) & MFS_CHECKED) { \
108 (state) |= MFS_CHECKED; \
109 } else { \
110 (state) &= ~MFS_CHECKED; \
111 } \
112 if((change) & MFS_HILITE) { \
113 (state) |= MFS_HILITE; \
114 } else { \
115 (state) &= ~MFS_HILITE; \
116 } \
117 if((change) & MFS_DEFAULT) { \
118 (state) |= MFS_DEFAULT; \
119 } else { \
120 (state) &= ~MFS_DEFAULT; \
121 } \
122 if((change) & MF_MOUSESELECT) { \
123 (state) |= MF_MOUSESELECT; \
124 } else { \
125 (state) &= ~MF_MOUSESELECT; \
126 } \
127 }
128
129 #if 0
130 void FASTCALL
131 DumpMenuItemList(PMENU Menu, PITEM MenuItem)
132 {
133 UINT cnt = 0, i = Menu->cItems;
134 while(i)
135 {
136 if(MenuItem->lpstr.Length)
137 DbgPrint(" %d. %wZ\n", ++cnt, &MenuItem->lpstr);
138 else
139 DbgPrint(" %d. NO TEXT dwTypeData==%d\n", ++cnt, (DWORD)MenuItem->lpstr.Buffer);
140 DbgPrint(" fType=");
141 if(MFT_BITMAP & MenuItem->fType)
142 DbgPrint("MFT_BITMAP ");
143 if(MFT_MENUBARBREAK & MenuItem->fType)
144 DbgPrint("MFT_MENUBARBREAK ");
145 if(MFT_MENUBREAK & MenuItem->fType)
146 DbgPrint("MFT_MENUBREAK ");
147 if(MFT_OWNERDRAW & MenuItem->fType)
148 DbgPrint("MFT_OWNERDRAW ");
149 if(MFT_RADIOCHECK & MenuItem->fType)
150 DbgPrint("MFT_RADIOCHECK ");
151 if(MFT_RIGHTJUSTIFY & MenuItem->fType)
152 DbgPrint("MFT_RIGHTJUSTIFY ");
153 if(MFT_SEPARATOR & MenuItem->fType)
154 DbgPrint("MFT_SEPARATOR ");
155 if(MFT_STRING & MenuItem->fType)
156 DbgPrint("MFT_STRING ");
157 DbgPrint("\n fState=");
158 if(MFS_DISABLED & MenuItem->fState)
159 DbgPrint("MFS_DISABLED ");
160 else
161 DbgPrint("MFS_ENABLED ");
162 if(MFS_CHECKED & MenuItem->fState)
163 DbgPrint("MFS_CHECKED ");
164 else
165 DbgPrint("MFS_UNCHECKED ");
166 if(MFS_HILITE & MenuItem->fState)
167 DbgPrint("MFS_HILITE ");
168 else
169 DbgPrint("MFS_UNHILITE ");
170 if(MFS_DEFAULT & MenuItem->fState)
171 DbgPrint("MFS_DEFAULT ");
172 if(MFS_GRAYED & MenuItem->fState)
173 DbgPrint("MFS_GRAYED ");
174 DbgPrint("\n wId=%d\n", MenuItem->wID);
175 MenuItem++;
176 i--;
177 }
178 DbgPrint("Entries: %d\n", cnt);
179 return;
180 }
181 #endif
182
183 #define FreeMenuText(Menu,MenuItem) \
184 { \
185 if((MENU_ITEM_TYPE((MenuItem)->fType) == MF_STRING) && \
186 (MenuItem)->lpstr.Length) { \
187 DesktopHeapFree(((PMENU)Menu)->head.rpdesk, (MenuItem)->lpstr.Buffer); \
188 } \
189 }
190
191 PMENU FASTCALL
192 IntGetMenuObject(HMENU hMenu)
193 {
194 PMENU Menu = UserGetMenuObject(hMenu);
195 if (Menu)
196 Menu->head.cLockObj++;
197
198 return Menu;
199 }
200
201 PMENU FASTCALL VerifyMenu(PMENU pMenu)
202 {
203 HMENU hMenu;
204 PITEM pItem;
205 ULONG Error;
206 UINT i;
207 if (!pMenu) return NULL;
208
209 Error = EngGetLastError();
210
211 _SEH2_TRY
212 {
213 hMenu = UserHMGetHandle(pMenu);
214 pItem = pMenu->rgItems;
215 if (pItem)
216 {
217 i = pItem[0].wID;
218 pItem[0].wID = i;
219 }
220 }
221 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
222 {
223 ERR("Run away LOOP!\n");
224 EngSetLastError(Error);
225 _SEH2_YIELD(return NULL);
226 }
227 _SEH2_END
228
229 if ( UserObjectInDestroy(hMenu))
230 {
231 ERR("Menu is marked for destruction!\n");
232 pMenu = NULL;
233 }
234 EngSetLastError(Error);
235 return pMenu;
236 }
237
238 BOOL
239 FASTCALL
240 IntIsMenu(HMENU Menu)
241 {
242 if (UserGetMenuObject(Menu)) return TRUE;
243 return FALSE;
244 }
245
246
247 PMENU WINAPI
248 IntGetMenu(HWND hWnd)
249 {
250 PWND Wnd = ValidateHwndNoErr(hWnd);
251
252 if (!Wnd)
253 return NULL;
254
255 return UserGetMenuObject(UlongToHandle(Wnd->IDMenu));
256 }
257
258 PMENU get_win_sys_menu( HWND hwnd )
259 {
260 PMENU ret = 0;
261 WND *win = ValidateHwndNoErr( hwnd );
262 if (win)
263 {
264 ret = UserGetMenuObject(win->SystemMenu);
265 }
266 return ret;
267 }
268
269 BOOL IntDestroyMenu( PMENU pMenu, BOOL bRecurse)
270 {
271 PMENU SubMenu;
272
273 ASSERT(UserIsEnteredExclusive());
274 if (pMenu->rgItems) /* recursively destroy submenus */
275 {
276 int i;
277 ITEM *item = pMenu->rgItems;
278 for (i = pMenu->cItems; i > 0; i--, item++)
279 {
280 SubMenu = item->spSubMenu;
281 item->spSubMenu = NULL;
282
283 /* Remove Item Text */
284 FreeMenuText(pMenu,item);
285
286 /* Remove Item Bitmap and set it for this process */
287 if (item->hbmp && !(item->fState & MFS_HBMMENUBMP))
288 {
289 GreSetObjectOwner(item->hbmp, GDI_OBJ_HMGR_POWNED);
290 item->hbmp = NULL;
291 }
292
293 /* Remove Item submenu */
294 if (bRecurse && SubMenu)//VerifyMenu(SubMenu))
295 {
296 /* Release submenu since it was referenced when inserted */
297 IntReleaseMenuObject(SubMenu);
298 IntDestroyMenuObject(SubMenu, bRecurse);
299 }
300 }
301 /* Free the Item */
302 DesktopHeapFree(pMenu->head.rpdesk, pMenu->rgItems );
303 pMenu->rgItems = NULL;
304 pMenu->cItems = 0;
305 }
306 return TRUE;
307 }
308
309 /* Callback for the object manager */
310 BOOLEAN
311 UserDestroyMenuObject(PVOID Object)
312 {
313 return IntDestroyMenuObject(Object, TRUE);
314 }
315
316 BOOL FASTCALL
317 IntDestroyMenuObject(PMENU Menu, BOOL bRecurse)
318 {
319 ASSERT(UserIsEnteredExclusive());
320 if (Menu)
321 {
322 PWND Window;
323
324 if (PsGetCurrentProcessSessionId() == Menu->head.rpdesk->rpwinstaParent->dwSessionId)
325 {
326 BOOL ret;
327 if (Menu->hWnd)
328 {
329 Window = ValidateHwndNoErr(Menu->hWnd);
330 if (Window)
331 {
332 //Window->IDMenu = 0; Only in Win9x!! wine win test_SetMenu test...
333
334 /* DestroyMenu should not destroy system menu popup owner */
335 if ((Menu->fFlags & (MNF_POPUP | MNF_SYSSUBMENU)) == MNF_POPUP)
336 {
337 // Should we check it to see if it has Class?
338 ERR("FIXME Pop up menu window thing'ie\n");
339 //co_UserDestroyWindow( Window );
340 //Menu->hWnd = 0;
341 }
342 }
343 }
344
345 if (!UserMarkObjectDestroy(Menu)) return TRUE;
346
347 /* Remove all menu items */
348 IntDestroyMenu( Menu, bRecurse);
349
350 ret = UserDeleteObject(Menu->head.h, TYPE_MENU);
351 TRACE("IntDestroyMenuObject %d\n",ret);
352 return ret;
353 }
354 }
355 return FALSE;
356 }
357
358 BOOL
359 MenuInit(VOID)
360 {
361 NONCLIENTMETRICSW ncm;
362
363 /* get the menu font */
364 if (!ghMenuFont || !ghMenuFontBold)
365 {
366 ncm.cbSize = sizeof(ncm);
367 if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
368 {
369 ERR("MenuInit(): SystemParametersInfo(SPI_GETNONCLIENTMETRICS) failed!\n");
370 return FALSE;
371 }
372
373 ghMenuFont = GreCreateFontIndirectW(&ncm.lfMenuFont);
374 if (ghMenuFont == NULL)
375 {
376 ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
377 return FALSE;
378 }
379 ncm.lfMenuFont.lfWeight = min(ncm.lfMenuFont.lfWeight + (FW_BOLD - FW_NORMAL), FW_HEAVY);
380 ghMenuFontBold = GreCreateFontIndirectW(&ncm.lfMenuFont);
381 if (ghMenuFontBold == NULL)
382 {
383 ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
384 GreDeleteObject(ghMenuFont);
385 ghMenuFont = NULL;
386 return FALSE;
387 }
388
389 GreSetObjectOwner(ghMenuFont, GDI_OBJ_HMGR_PUBLIC);
390 GreSetObjectOwner(ghMenuFontBold, GDI_OBJ_HMGR_PUBLIC);
391
392 co_IntSetupOBM();
393 }
394
395 return TRUE;
396 }
397
398
399 /**********************************************************************
400 * MENU_depth
401 *
402 * detect if there are loops in the menu tree (or the depth is too large)
403 */
404 int FASTCALL MENU_depth( PMENU pmenu, int depth)
405 {
406 UINT i;
407 ITEM *item;
408 int subdepth;
409
410 if (!pmenu) return depth;
411
412 depth++;
413 if( depth > MAXMENUDEPTH) return depth;
414 item = pmenu->rgItems;
415 subdepth = depth;
416 for( i = 0; i < pmenu->cItems && subdepth <= MAXMENUDEPTH; i++, item++)
417 {
418 if( item->spSubMenu)//VerifyMenu(item->spSubMenu))
419 {
420 int bdepth = MENU_depth( item->spSubMenu, depth);
421 if( bdepth > subdepth) subdepth = bdepth;
422 }
423 if( subdepth > MAXMENUDEPTH)
424 TRACE("<- hmenu %p\n", item->spSubMenu);
425 }
426 return subdepth;
427 }
428
429
430 /******************************************************************************
431 *
432 * UINT MenuGetStartOfNextColumn(
433 * PMENU Menu)
434 *
435 *****************************************************************************/
436
437 static UINT MENU_GetStartOfNextColumn(
438 PMENU menu )
439 {
440 PITEM pItem;
441 UINT i;
442
443 if(!menu)
444 return NO_SELECTED_ITEM;
445
446 i = menu->iItem + 1;
447 if( i == NO_SELECTED_ITEM )
448 return i;
449
450 pItem = menu->rgItems;
451 if (!pItem) return NO_SELECTED_ITEM;
452
453 for( ; i < menu->cItems; ++i ) {
454 if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
455 return i;
456 }
457
458 return NO_SELECTED_ITEM;
459 }
460
461 /******************************************************************************
462 *
463 * UINT MenuGetStartOfPrevColumn(
464 * PMENU Menu)
465 *
466 *****************************************************************************/
467 static UINT MENU_GetStartOfPrevColumn(
468 PMENU menu )
469 {
470 UINT i;
471 PITEM pItem;
472
473 if( !menu )
474 return NO_SELECTED_ITEM;
475
476 if( menu->iItem == 0 || menu->iItem == NO_SELECTED_ITEM )
477 return NO_SELECTED_ITEM;
478
479 pItem = menu->rgItems;
480 if (!pItem) return NO_SELECTED_ITEM;
481
482 /* Find the start of the column */
483
484 for(i = menu->iItem; i != 0 &&
485 !(pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
486 --i); /* empty */
487
488 if(i == 0)
489 return NO_SELECTED_ITEM;
490
491 for(--i; i != 0; --i) {
492 if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
493 break;
494 }
495
496 TRACE("ret %d.\n", i );
497
498 return i;
499 }
500
501 /***********************************************************************
502 * MENU_FindItem
503 *
504 * Find a menu item. Return a pointer on the item, and modifies *hmenu
505 * in case the item was in a sub-menu.
506 */
507 PITEM FASTCALL MENU_FindItem( PMENU *pmenu, UINT *nPos, UINT wFlags )
508 {
509 MENU *menu = *pmenu;
510 ITEM *fallback = NULL;
511 UINT fallback_pos = 0;
512 UINT i;
513
514 if (!menu) return NULL;
515
516 if (wFlags & MF_BYPOSITION)
517 {
518 if (!menu->cItems) return NULL;
519 if (*nPos >= menu->cItems) return NULL;
520 return &menu->rgItems[*nPos];
521 }
522 else
523 {
524 PITEM item = menu->rgItems;
525 for (i = 0; i < menu->cItems; i++, item++)
526 {
527 if (item->spSubMenu)
528 {
529 PMENU psubmenu = item->spSubMenu;//VerifyMenu(item->spSubMenu);
530 PITEM subitem = MENU_FindItem( &psubmenu, nPos, wFlags );
531 if (subitem)
532 {
533 *pmenu = psubmenu;
534 return subitem;
535 }
536 else if (item->wID == *nPos)
537 {
538 /* fallback to this item if nothing else found */
539 fallback_pos = i;
540 fallback = item;
541 }
542 }
543 else if (item->wID == *nPos)
544 {
545 *nPos = i;
546 return item;
547 }
548 }
549 }
550
551 if (fallback)
552 *nPos = fallback_pos;
553
554 return fallback;
555 }
556
557 /***********************************************************************
558 * MenuFindSubMenu
559 *
560 * Find a Sub menu. Return the position of the submenu, and modifies
561 * *hmenu in case it is found in another sub-menu.
562 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
563 */
564 static UINT FASTCALL MENU_FindSubMenu(PMENU *menu, PMENU SubTarget )
565 {
566 UINT i;
567 PITEM item;
568
569 item = ((PMENU)*menu)->rgItems;
570 for (i = 0; i < ((PMENU)*menu)->cItems; i++, item++)
571 {
572 if (!item->spSubMenu)
573 continue;
574 else
575 {
576 if (item->spSubMenu == SubTarget)
577 {
578 return i;
579 }
580 else
581 {
582 PMENU pSubMenu = item->spSubMenu;
583 UINT pos = MENU_FindSubMenu( &pSubMenu, SubTarget );
584 if (pos != NO_SELECTED_ITEM)
585 {
586 *menu = pSubMenu;
587 return pos;
588 }
589 }
590 }
591 }
592 return NO_SELECTED_ITEM;
593 }
594
595 BOOL FASTCALL
596 IntRemoveMenuItem( PMENU pMenu, UINT nPos, UINT wFlags, BOOL bRecurse )
597 {
598 PITEM item;
599 PITEM newItems;
600
601 TRACE("(menu=%p pos=%04x flags=%04x)\n",pMenu, nPos, wFlags);
602 if (!(item = MENU_FindItem( &pMenu, &nPos, wFlags ))) return FALSE;
603
604 /* Remove item */
605
606 FreeMenuText(pMenu,item);
607 if (bRecurse && item->spSubMenu)
608 {
609 IntDestroyMenuObject(item->spSubMenu, bRecurse);
610 }
611 ////// Use cAlloced with inc's of 8's....
612 if (--pMenu->cItems == 0)
613 {
614 DesktopHeapFree(pMenu->head.rpdesk, pMenu->rgItems );
615 pMenu->rgItems = NULL;
616 }
617 else
618 {
619 while (nPos < pMenu->cItems)
620 {
621 *item = *(item+1);
622 item++;
623 nPos++;
624 }
625 newItems = DesktopHeapReAlloc(pMenu->head.rpdesk, pMenu->rgItems, pMenu->cItems * sizeof(ITEM));
626 if (newItems)
627 {
628 pMenu->rgItems = newItems;
629 }
630 }
631 return TRUE;
632 }
633
634 /**********************************************************************
635 * MENU_InsertItem
636 *
637 * Insert (allocate) a new item into a menu.
638 */
639 ITEM *MENU_InsertItem( PMENU menu, UINT pos, UINT flags, PMENU *submenu, UINT *npos )
640 {
641 ITEM *newItems;
642
643 /* Find where to insert new item */
644
645 if (flags & MF_BYPOSITION) {
646 if (pos > menu->cItems)
647 pos = menu->cItems;
648 } else {
649 if (!MENU_FindItem( &menu, &pos, flags ))
650 {
651 if (submenu) *submenu = menu;
652 if (npos) *npos = pos;
653 pos = menu->cItems;
654 }
655 }
656
657 /* Make sure that MDI system buttons stay on the right side.
658 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
659 * regardless of their id.
660 */
661 while ( pos > 0 &&
662 (INT_PTR)menu->rgItems[pos - 1].hbmp >= (INT_PTR)HBMMENU_SYSTEM &&
663 (INT_PTR)menu->rgItems[pos - 1].hbmp <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
664 pos--;
665
666 TRACE("inserting at %u flags %x\n", pos, flags);
667
668 /* Create new items array */
669
670 newItems = DesktopHeapAlloc(menu->head.rpdesk, sizeof(ITEM) * (menu->cItems+1) );
671 if (!newItems)
672 {
673 WARN("allocation failed\n" );
674 return NULL;
675 }
676 if (menu->cItems > 0)
677 {
678 /* Copy the old array into the new one */
679 if (pos > 0) RtlCopyMemory( newItems, menu->rgItems, pos * sizeof(ITEM) );
680 if (pos < menu->cItems) RtlCopyMemory( &newItems[pos+1], &menu->rgItems[pos], (menu->cItems-pos)*sizeof(ITEM) );
681 DesktopHeapFree(menu->head.rpdesk, menu->rgItems );
682 }
683 menu->rgItems = newItems;
684 menu->cItems++;
685 RtlZeroMemory( &newItems[pos], sizeof(*newItems) );
686 menu->cyMenu = 0; /* force size recalculate */
687 return &newItems[pos];
688 }
689
690 BOOL FASTCALL
691 IntInsertMenuItem(
692 _In_ PMENU MenuObject,
693 UINT uItem,
694 BOOL fByPosition,
695 PROSMENUITEMINFO ItemInfo,
696 PUNICODE_STRING lpstr)
697 {
698 PITEM MenuItem;
699 PMENU SubMenu = NULL;
700
701 NT_ASSERT(MenuObject != NULL);
702
703 if (MAX_MENU_ITEMS <= MenuObject->cItems)
704 {
705 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
706 return FALSE;
707 }
708
709 SubMenu = MenuObject;
710
711 if(!(MenuItem = MENU_InsertItem( SubMenu, uItem, fByPosition ? MF_BYPOSITION : MF_BYCOMMAND, &SubMenu, &uItem ))) return FALSE;
712
713 if(!IntSetMenuItemInfo(SubMenu, MenuItem, ItemInfo, lpstr))
714 {
715 IntRemoveMenuItem(SubMenu, uItem, fByPosition ? MF_BYPOSITION : MF_BYCOMMAND, FALSE);
716 return FALSE;
717 }
718
719 /* Force size recalculation! */
720 SubMenu->cyMenu = 0;
721 MenuItem->hbmpChecked = MenuItem->hbmpUnchecked = 0;
722
723 TRACE("IntInsertMenuItemToList = %u %i\n", uItem, (BOOL)((INT)uItem >= 0));
724
725 return TRUE;
726 }
727
728 PMENU FASTCALL
729 IntCreateMenu(
730 _Out_ PHANDLE Handle,
731 _In_ BOOL IsMenuBar,
732 _In_ PDESKTOP Desktop,
733 _In_ PPROCESSINFO ppi)
734 {
735 PMENU Menu;
736
737 Menu = (PMENU)UserCreateObject( gHandleTable,
738 Desktop,
739 ppi->ptiList,
740 Handle,
741 TYPE_MENU,
742 sizeof(MENU));
743 if(!Menu)
744 {
745 *Handle = 0;
746 return NULL;
747 }
748
749 Menu->cyMax = 0; /* Default */
750 Menu->hbrBack = NULL; /* No brush */
751 Menu->dwContextHelpId = 0; /* Default */
752 Menu->dwMenuData = 0; /* Default */
753 Menu->iItem = NO_SELECTED_ITEM; // Focused item
754 Menu->fFlags = (IsMenuBar ? 0 : MNF_POPUP);
755 Menu->spwndNotify = NULL;
756 Menu->cyMenu = 0; // Height
757 Menu->cxMenu = 0; // Width
758 Menu->cItems = 0; // Item count
759 Menu->iTop = 0;
760 Menu->iMaxTop = 0;
761 Menu->cxTextAlign = 0;
762 Menu->rgItems = NULL;
763
764 Menu->hWnd = NULL;
765 Menu->TimeToHide = FALSE;
766
767 return Menu;
768 }
769
770 BOOL FASTCALL
771 IntCloneMenuItems(PMENU Destination, PMENU Source)
772 {
773 PITEM MenuItem, NewMenuItem = NULL;
774 UINT i;
775
776 if(!Source->cItems)
777 return FALSE;
778
779 NewMenuItem = DesktopHeapAlloc(Destination->head.rpdesk, Source->cItems * sizeof(ITEM));
780 if(!NewMenuItem) return FALSE;
781
782 RtlZeroMemory(NewMenuItem, Source->cItems * sizeof(ITEM));
783
784 Destination->rgItems = NewMenuItem;
785
786 MenuItem = Source->rgItems;
787 for (i = 0; i < Source->cItems; i++, MenuItem++, NewMenuItem++)
788 {
789 NewMenuItem->fType = MenuItem->fType;
790 NewMenuItem->fState = MenuItem->fState;
791 NewMenuItem->wID = MenuItem->wID;
792 NewMenuItem->spSubMenu = MenuItem->spSubMenu;
793 NewMenuItem->hbmpChecked = MenuItem->hbmpChecked;
794 NewMenuItem->hbmpUnchecked = MenuItem->hbmpUnchecked;
795 NewMenuItem->dwItemData = MenuItem->dwItemData;
796 if (MenuItem->lpstr.Length)
797 {
798 NewMenuItem->lpstr.Length = 0;
799 NewMenuItem->lpstr.MaximumLength = MenuItem->lpstr.MaximumLength;
800 NewMenuItem->lpstr.Buffer = DesktopHeapAlloc(Destination->head.rpdesk, MenuItem->lpstr.MaximumLength);
801 if (!NewMenuItem->lpstr.Buffer)
802 {
803 DesktopHeapFree(Destination->head.rpdesk, NewMenuItem);
804 break;
805 }
806 RtlCopyUnicodeString(&NewMenuItem->lpstr, &MenuItem->lpstr);
807 NewMenuItem->lpstr.Buffer[MenuItem->lpstr.Length / sizeof(WCHAR)] = 0;
808 NewMenuItem->Xlpstr = NewMenuItem->lpstr.Buffer;
809 }
810 else
811 {
812 NewMenuItem->lpstr.Buffer = MenuItem->lpstr.Buffer;
813 NewMenuItem->Xlpstr = NewMenuItem->lpstr.Buffer;
814 }
815 NewMenuItem->hbmp = MenuItem->hbmp;
816 Destination->cItems = i + 1;
817 }
818 return TRUE;
819 }
820
821 PMENU FASTCALL
822 IntCloneMenu(PMENU Source)
823 {
824 HANDLE hMenu;
825 PMENU Menu;
826
827 if(!Source)
828 return NULL;
829
830 /* A menu is valid process wide. We can pass to the object manager any thread ptr */
831 Menu = (PMENU)UserCreateObject( gHandleTable,
832 Source->head.rpdesk,
833 ((PPROCESSINFO)Source->head.hTaskWow)->ptiList,
834 &hMenu,
835 TYPE_MENU,
836 sizeof(MENU));
837 if(!Menu)
838 return NULL;
839
840 Menu->fFlags = Source->fFlags;
841 Menu->cyMax = Source->cyMax;
842 Menu->hbrBack = Source->hbrBack;
843 Menu->dwContextHelpId = Source->dwContextHelpId;
844 Menu->dwMenuData = Source->dwMenuData;
845 Menu->iItem = NO_SELECTED_ITEM;
846 Menu->spwndNotify = NULL;
847 Menu->cyMenu = 0;
848 Menu->cxMenu = 0;
849 Menu->cItems = 0;
850 Menu->iTop = 0;
851 Menu->iMaxTop = 0;
852 Menu->cxTextAlign = 0;
853 Menu->rgItems = NULL;
854
855 Menu->hWnd = NULL;
856 Menu->TimeToHide = FALSE;
857
858 IntCloneMenuItems(Menu, Source);
859
860 return Menu;
861 }
862
863 BOOL FASTCALL
864 IntSetMenuFlagRtoL(PMENU Menu)
865 {
866 ERR("SetMenuFlagRtoL\n");
867 Menu->fFlags |= MNF_RTOL;
868 return TRUE;
869 }
870
871 BOOL FASTCALL
872 IntSetMenuContextHelpId(PMENU Menu, DWORD dwContextHelpId)
873 {
874 Menu->dwContextHelpId = dwContextHelpId;
875 return TRUE;
876 }
877
878 BOOL FASTCALL
879 IntGetMenuInfo(PMENU Menu, PROSMENUINFO lpmi)
880 {
881 if(lpmi->fMask & MIM_BACKGROUND)
882 lpmi->hbrBack = Menu->hbrBack;
883 if(lpmi->fMask & MIM_HELPID)
884 lpmi->dwContextHelpID = Menu->dwContextHelpId;
885 if(lpmi->fMask & MIM_MAXHEIGHT)
886 lpmi->cyMax = Menu->cyMax;
887 if(lpmi->fMask & MIM_MENUDATA)
888 lpmi->dwMenuData = Menu->dwMenuData;
889 if(lpmi->fMask & MIM_STYLE)
890 lpmi->dwStyle = Menu->fFlags & MNS_STYLE_MASK;
891
892 if (sizeof(MENUINFO) < lpmi->cbSize)
893 {
894 lpmi->cItems = Menu->cItems;
895
896 lpmi->iItem = Menu->iItem;
897 lpmi->cxMenu = Menu->cxMenu;
898 lpmi->cyMenu = Menu->cyMenu;
899 lpmi->spwndNotify = Menu->spwndNotify;
900 lpmi->cxTextAlign = Menu->cxTextAlign;
901 lpmi->iTop = Menu->iTop;
902 lpmi->iMaxTop = Menu->iMaxTop;
903 lpmi->dwArrowsOn = Menu->dwArrowsOn;
904
905 lpmi->fFlags = Menu->fFlags;
906 lpmi->Self = Menu->head.h;
907 lpmi->TimeToHide = Menu->TimeToHide;
908 lpmi->Wnd = Menu->hWnd;
909 }
910 return TRUE;
911 }
912
913 BOOL FASTCALL
914 IntSetMenuInfo(PMENU Menu, PROSMENUINFO lpmi)
915 {
916 if(lpmi->fMask & MIM_BACKGROUND)
917 Menu->hbrBack = lpmi->hbrBack;
918 if(lpmi->fMask & MIM_HELPID)
919 Menu->dwContextHelpId = lpmi->dwContextHelpID;
920 if(lpmi->fMask & MIM_MAXHEIGHT)
921 Menu->cyMax = lpmi->cyMax;
922 if(lpmi->fMask & MIM_MENUDATA)
923 Menu->dwMenuData = lpmi->dwMenuData;
924 if(lpmi->fMask & MIM_STYLE)
925 Menu->fFlags ^= (Menu->fFlags ^ lpmi->dwStyle) & MNS_STYLE_MASK;
926 if(lpmi->fMask & MIM_APPLYTOSUBMENUS)
927 {
928 int i;
929 PITEM item = Menu->rgItems;
930 for ( i = Menu->cItems; i; i--, item++)
931 {
932 if ( item->spSubMenu )
933 {
934 IntSetMenuInfo( item->spSubMenu, lpmi);
935 }
936 }
937 }
938 if (sizeof(MENUINFO) < lpmi->cbSize)
939 {
940 Menu->iItem = lpmi->iItem;
941 Menu->cyMenu = lpmi->cyMenu;
942 Menu->cxMenu = lpmi->cxMenu;
943 Menu->spwndNotify = lpmi->spwndNotify;
944 Menu->cxTextAlign = lpmi->cxTextAlign;
945 Menu->iTop = lpmi->iTop;
946 Menu->iMaxTop = lpmi->iMaxTop;
947 Menu->dwArrowsOn = lpmi->dwArrowsOn;
948
949 Menu->TimeToHide = lpmi->TimeToHide;
950 Menu->hWnd = lpmi->Wnd;
951 }
952 if ( lpmi->fMask & MIM_STYLE)
953 {
954 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented wine\n");
955 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented wine\n");
956 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented wine\n");
957 }
958 return TRUE;
959 }
960
961 BOOL FASTCALL
962 IntGetMenuItemInfo(PMENU Menu, /* UNUSED PARAM!! */
963 PITEM MenuItem, PROSMENUITEMINFO lpmii)
964 {
965 NTSTATUS Status;
966
967 if(lpmii->fMask & (MIIM_FTYPE | MIIM_TYPE))
968 {
969 lpmii->fType = MenuItem->fType;
970 }
971 if(lpmii->fMask & MIIM_BITMAP)
972 {
973 lpmii->hbmpItem = MenuItem->hbmp;
974 }
975 if(lpmii->fMask & MIIM_CHECKMARKS)
976 {
977 lpmii->hbmpChecked = MenuItem->hbmpChecked;
978 lpmii->hbmpUnchecked = MenuItem->hbmpUnchecked;
979 }
980 if(lpmii->fMask & MIIM_DATA)
981 {
982 lpmii->dwItemData = MenuItem->dwItemData;
983 }
984 if(lpmii->fMask & MIIM_ID)
985 {
986 lpmii->wID = MenuItem->wID;
987 }
988 if(lpmii->fMask & MIIM_STATE)
989 {
990 lpmii->fState = MenuItem->fState;
991 }
992 if(lpmii->fMask & MIIM_SUBMENU)
993 {
994 lpmii->hSubMenu = MenuItem->spSubMenu ? MenuItem->spSubMenu->head.h : NULL;
995 }
996
997 if ((lpmii->fMask & MIIM_STRING) ||
998 ((lpmii->fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(lpmii->fType) == MF_STRING)))
999 {
1000 if (lpmii->dwTypeData == NULL)
1001 {
1002 lpmii->cch = MenuItem->lpstr.Length / sizeof(WCHAR);
1003 }
1004 else
1005 { //// lpmii->lpstr can be read in user mode!!!!
1006 Status = MmCopyToCaller(lpmii->dwTypeData, MenuItem->lpstr.Buffer,
1007 min(lpmii->cch * sizeof(WCHAR),
1008 MenuItem->lpstr.MaximumLength));
1009 if (! NT_SUCCESS(Status))
1010 {
1011 SetLastNtError(Status);
1012 return FALSE;
1013 }
1014 }
1015 }
1016
1017 if (sizeof(ROSMENUITEMINFO) == lpmii->cbSize)
1018 {
1019 lpmii->Rect.left = MenuItem->xItem;
1020 lpmii->Rect.top = MenuItem->yItem;
1021 lpmii->Rect.right = MenuItem->cxItem; // Do this for now......
1022 lpmii->Rect.bottom = MenuItem->cyItem;
1023 lpmii->dxTab = MenuItem->dxTab;
1024 lpmii->lpstr = MenuItem->lpstr.Buffer;
1025 lpmii->maxBmpSize.cx = MenuItem->cxBmp;
1026 lpmii->maxBmpSize.cy = MenuItem->cyBmp;
1027 }
1028
1029 return TRUE;
1030 }
1031
1032 BOOL FASTCALL
1033 IntSetMenuItemInfo(PMENU MenuObject, PITEM MenuItem, PROSMENUITEMINFO lpmii, PUNICODE_STRING lpstr)
1034 {
1035 PMENU SubMenuObject;
1036 BOOL circref = FALSE;
1037
1038 if(!MenuItem || !MenuObject || !lpmii)
1039 {
1040 return FALSE;
1041 }
1042 if ( lpmii->fMask & MIIM_FTYPE )
1043 {
1044 MenuItem->fType &= ~MENUITEMINFO_TYPE_MASK;
1045 MenuItem->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
1046 }
1047 if (lpmii->fMask & MIIM_TYPE)
1048 {
1049 #if 0 //// Done in User32.
1050 if (lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP))
1051 {
1052 ERR("IntSetMenuItemInfo: Invalid combination of fMask bits used\n");
1053 KeRosDumpStackFrames(NULL, 20);
1054 /* This does not happen on Win9x/ME */
1055 SetLastNtError( ERROR_INVALID_PARAMETER);
1056 return FALSE;
1057 }
1058 #endif
1059 /*
1060 * Delete the menu item type when changing type from
1061 * MF_STRING.
1062 */
1063 if (MenuItem->fType != lpmii->fType &&
1064 MENU_ITEM_TYPE(MenuItem->fType) == MFT_STRING)
1065 {
1066 FreeMenuText(MenuObject,MenuItem);
1067 RtlInitUnicodeString(&MenuItem->lpstr, NULL);
1068 MenuItem->Xlpstr = NULL;
1069 }
1070 if(lpmii->fType & MFT_BITMAP)
1071 {
1072 if(lpmii->hbmpItem)
1073 MenuItem->hbmp = lpmii->hbmpItem;
1074 else
1075 { /* Win 9x/Me stuff */
1076 MenuItem->hbmp = (HBITMAP)((ULONG_PTR)(LOWORD(lpmii->dwTypeData)));
1077 }
1078 lpmii->dwTypeData = 0;
1079 }
1080 }
1081 if(lpmii->fMask & MIIM_BITMAP)
1082 {
1083 MenuItem->hbmp = lpmii->hbmpItem;
1084 if (MenuItem->hbmp <= HBMMENU_POPUP_MINIMIZE && MenuItem->hbmp >= HBMMENU_CALLBACK)
1085 MenuItem->fState |= MFS_HBMMENUBMP;
1086 else
1087 MenuItem->fState &= ~MFS_HBMMENUBMP;
1088 }
1089 if(lpmii->fMask & MIIM_CHECKMARKS)
1090 {
1091 MenuItem->hbmpChecked = lpmii->hbmpChecked;
1092 MenuItem->hbmpUnchecked = lpmii->hbmpUnchecked;
1093 }
1094 if(lpmii->fMask & MIIM_DATA)
1095 {
1096 MenuItem->dwItemData = lpmii->dwItemData;
1097 }
1098 if(lpmii->fMask & MIIM_ID)
1099 {
1100 MenuItem->wID = lpmii->wID;
1101 }
1102 if(lpmii->fMask & MIIM_STATE)
1103 {
1104 /* Remove MFS_DEFAULT flag from all other menu items if this item
1105 has the MFS_DEFAULT state */
1106 if(lpmii->fState & MFS_DEFAULT)
1107 UserSetMenuDefaultItem(MenuObject, -1, 0);
1108 /* Update the menu item state flags */
1109 UpdateMenuItemState(MenuItem->fState, lpmii->fState);
1110 }
1111
1112 if(lpmii->fMask & MIIM_SUBMENU)
1113 {
1114 if (lpmii->hSubMenu)
1115 {
1116 SubMenuObject = UserGetMenuObject(lpmii->hSubMenu);
1117 if ( SubMenuObject && !(UserObjectInDestroy(lpmii->hSubMenu)) )
1118 {
1119 //// wine Bug 12171 : Adding Popup Menu to itself! Could create endless loops.
1120 //// CORE-7967.
1121 if (MenuObject == SubMenuObject)
1122 {
1123 HANDLE hMenu;
1124 ERR("Pop Up Menu Double Trouble!\n");
1125 SubMenuObject = IntCreateMenu(&hMenu,
1126 FALSE,
1127 MenuObject->head.rpdesk,
1128 (PPROCESSINFO)MenuObject->head.hTaskWow); // It will be marked.
1129 if (!SubMenuObject) return FALSE;
1130 IntReleaseMenuObject(SubMenuObject); // This will be referenced again after insertion.
1131 circref = TRUE;
1132 }
1133 if ( MENU_depth( SubMenuObject, 0) > MAXMENUDEPTH )
1134 {
1135 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
1136 if (circref) IntDestroyMenuObject(SubMenuObject, FALSE);
1137 return FALSE;
1138 }
1139 /* Make sure the submenu is marked as a popup menu */
1140 SubMenuObject->fFlags |= MNF_POPUP;
1141 // Now fix the test_subpopup_locked_by_menu tests....
1142 if (MenuItem->spSubMenu) IntReleaseMenuObject(MenuItem->spSubMenu);
1143 MenuItem->spSubMenu = SubMenuObject;
1144 UserReferenceObject(SubMenuObject);
1145 }
1146 else
1147 {
1148 EngSetLastError( ERROR_INVALID_PARAMETER);
1149 return FALSE;
1150 }
1151 }
1152 else
1153 { // If submenu just dereference it.
1154 if (MenuItem->spSubMenu) IntReleaseMenuObject(MenuItem->spSubMenu);
1155 MenuItem->spSubMenu = NULL;
1156 }
1157 }
1158
1159 if ((lpmii->fMask & MIIM_STRING) ||
1160 ((lpmii->fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(lpmii->fType) == MF_STRING)))
1161 {
1162 /* free the string when used */
1163 FreeMenuText(MenuObject,MenuItem);
1164 RtlInitUnicodeString(&MenuItem->lpstr, NULL);
1165 MenuItem->Xlpstr = NULL;
1166
1167 if(lpmii->dwTypeData && lpmii->cch && lpstr && lpstr->Buffer)
1168 {
1169 UNICODE_STRING Source;
1170
1171 if (!NT_VERIFY(lpmii->cch <= UNICODE_STRING_MAX_CHARS))
1172 {
1173 return FALSE;
1174 }
1175
1176 Source.Length = Source.MaximumLength = (USHORT)(lpmii->cch * sizeof(WCHAR));
1177 Source.Buffer = lpmii->dwTypeData;
1178
1179 MenuItem->lpstr.Buffer = DesktopHeapAlloc( MenuObject->head.rpdesk, Source.Length + sizeof(WCHAR));
1180 if(MenuItem->lpstr.Buffer != NULL)
1181 {
1182 MenuItem->lpstr.Length = 0;
1183 MenuItem->lpstr.MaximumLength = Source.Length + sizeof(WCHAR);
1184 RtlCopyUnicodeString(&MenuItem->lpstr, &Source);
1185 MenuItem->lpstr.Buffer[MenuItem->lpstr.Length / sizeof(WCHAR)] = 0;
1186
1187 MenuItem->cch = MenuItem->lpstr.Length / sizeof(WCHAR);
1188 MenuItem->Xlpstr = (USHORT*)MenuItem->lpstr.Buffer;
1189 }
1190 }
1191 }
1192
1193 if( !(MenuObject->fFlags & MNF_SYSMENU) &&
1194 !MenuItem->Xlpstr &&
1195 !lpmii->dwTypeData &&
1196 !(MenuItem->fType & MFT_OWNERDRAW) &&
1197 !MenuItem->hbmp)
1198 MenuItem->fType |= MFT_SEPARATOR;
1199
1200 if (sizeof(ROSMENUITEMINFO) == lpmii->cbSize)
1201 {
1202 MenuItem->xItem = lpmii->Rect.left;
1203 MenuItem->yItem = lpmii->Rect.top;
1204 MenuItem->cxItem = lpmii->Rect.right; // Do this for now......
1205 MenuItem->cyItem = lpmii->Rect.bottom;
1206 MenuItem->dxTab = lpmii->dxTab;
1207 lpmii->lpstr = MenuItem->lpstr.Buffer; /* Send back new allocated string or zero */
1208 MenuItem->cxBmp = lpmii->maxBmpSize.cx;
1209 MenuItem->cyBmp = lpmii->maxBmpSize.cy;
1210 }
1211
1212 return TRUE;
1213 }
1214
1215
1216 UINT FASTCALL
1217 IntEnableMenuItem(PMENU MenuObject, UINT uIDEnableItem, UINT uEnable)
1218 {
1219 PITEM MenuItem;
1220 UINT res;
1221
1222 if (!(MenuItem = MENU_FindItem( &MenuObject, &uIDEnableItem, uEnable ))) return (UINT)-1;
1223
1224 res = MenuItem->fState & (MF_GRAYED | MF_DISABLED);
1225
1226 MenuItem->fState ^= (res ^ uEnable) & (MF_GRAYED | MF_DISABLED);
1227
1228 /* If the close item in the system menu change update the close button */
1229 if (res != uEnable)
1230 {
1231 switch (MenuItem->wID) // More than just close.
1232 {
1233 case SC_CLOSE:
1234 case SC_MAXIMIZE:
1235 case SC_MINIMIZE:
1236 case SC_MOVE:
1237 case SC_RESTORE:
1238 case SC_SIZE:
1239 if (MenuObject->fFlags & MNF_SYSSUBMENU && MenuObject->spwndNotify != 0)
1240 {
1241 //RECTL rc = MenuObject->spwndNotify->rcWindow;
1242
1243 /* Refresh the frame to reflect the change */
1244 //IntMapWindowPoints(0, MenuObject->spwndNotify, (POINT *)&rc, 2);
1245 //rc.bottom = 0;
1246 //co_UserRedrawWindow(MenuObject->spwndNotify, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
1247
1248 // Allow UxTheme!
1249 UserPaintCaption(MenuObject->spwndNotify, DC_BUTTONS);
1250 }
1251 default:
1252 break;
1253 }
1254 }
1255 return res;
1256 }
1257
1258 DWORD FASTCALL
1259 IntCheckMenuItem(PMENU MenuObject, UINT uIDCheckItem, UINT uCheck)
1260 {
1261 PITEM MenuItem;
1262 DWORD res;
1263
1264 if (!(MenuItem = MENU_FindItem( &MenuObject, &uIDCheckItem, uCheck ))) return -1;
1265
1266 res = (DWORD)(MenuItem->fState & MF_CHECKED);
1267
1268 MenuItem->fState ^= (res ^ uCheck) & MF_CHECKED;
1269
1270 return res;
1271 }
1272
1273 BOOL FASTCALL
1274 UserSetMenuDefaultItem(PMENU MenuObject, UINT uItem, UINT fByPos)
1275 {
1276 UINT i;
1277 PITEM MenuItem = MenuObject->rgItems;
1278
1279 if (!MenuItem) return FALSE;
1280
1281 /* reset all default-item flags */
1282 for (i = 0; i < MenuObject->cItems; i++, MenuItem++)
1283 {
1284 MenuItem->fState &= ~MFS_DEFAULT;
1285 }
1286
1287 /* no default item */
1288 if(uItem == (UINT)-1)
1289 {
1290 return TRUE;
1291 }
1292 MenuItem = MenuObject->rgItems;
1293 if ( fByPos )
1294 {
1295 if ( uItem >= MenuObject->cItems ) return FALSE;
1296 MenuItem[uItem].fState |= MFS_DEFAULT;
1297 return TRUE;
1298 }
1299 else
1300 {
1301 for (i = 0; i < MenuObject->cItems; i++, MenuItem++)
1302 {
1303 if (MenuItem->wID == uItem)
1304 {
1305 MenuItem->fState |= MFS_DEFAULT;
1306 return TRUE;
1307 }
1308 }
1309
1310 }
1311 return FALSE;
1312 }
1313
1314 UINT FASTCALL
1315 IntGetMenuDefaultItem(PMENU MenuObject, UINT fByPos, UINT gmdiFlags, DWORD *gismc)
1316 {
1317 UINT i = 0;
1318 PITEM MenuItem = MenuObject->rgItems;
1319
1320 /* empty menu */
1321 if (!MenuItem) return -1;
1322
1323 while ( !( MenuItem->fState & MFS_DEFAULT ) )
1324 {
1325 i++; MenuItem++;
1326 if (i >= MenuObject->cItems ) return -1;
1327 }
1328
1329 /* default: don't return disabled items */
1330 if ( (!(GMDI_USEDISABLED & gmdiFlags)) && (MenuItem->fState & MFS_DISABLED )) return -1;
1331
1332 /* search rekursiv when needed */
1333 if ( (gmdiFlags & GMDI_GOINTOPOPUPS) && MenuItem->spSubMenu )
1334 {
1335 UINT ret;
1336 (*gismc)++;
1337 ret = IntGetMenuDefaultItem( MenuItem->spSubMenu, fByPos, gmdiFlags, gismc );
1338 (*gismc)--;
1339 if ( -1 != ret ) return ret;
1340
1341 /* when item not found in submenu, return the popup item */
1342 }
1343 return ( fByPos ) ? i : MenuItem->wID;
1344 }
1345
1346 PMENU
1347 FASTCALL
1348 co_IntGetSubMenu(
1349 PMENU pMenu,
1350 int nPos)
1351 {
1352 PITEM pItem;
1353 if (!(pItem = MENU_FindItem( &pMenu, (UINT*)&nPos, MF_BYPOSITION ))) return NULL;
1354 return pItem->spSubMenu;
1355 }
1356
1357 /***********************************************************************
1358 * MenuInitSysMenuPopup
1359 *
1360 * Grey the appropriate items in System menu.
1361 */
1362 void FASTCALL MENU_InitSysMenuPopup(PMENU menu, DWORD style, DWORD clsStyle, LONG HitTest )
1363 {
1364 BOOL gray;
1365 UINT DefItem;
1366
1367 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
1368 IntEnableMenuItem( menu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
1369 gray = ((style & WS_MAXIMIZE) != 0);
1370 IntEnableMenuItem( menu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
1371 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
1372 IntEnableMenuItem( menu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
1373 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
1374 IntEnableMenuItem( menu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
1375 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
1376 IntEnableMenuItem( menu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
1377 gray = (clsStyle & CS_NOCLOSE) != 0;
1378
1379 /* The menu item must keep its state if it's disabled */
1380 if(gray)
1381 IntEnableMenuItem( menu, SC_CLOSE, MF_GRAYED);
1382
1383 /* Set default menu item */
1384 if(style & WS_MINIMIZE) DefItem = SC_RESTORE;
1385 else if(HitTest == HTCAPTION) DefItem = ((style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE);
1386 else DefItem = SC_CLOSE;
1387
1388 UserSetMenuDefaultItem(menu, DefItem, MF_BYCOMMAND);
1389 }
1390
1391
1392 /***********************************************************************
1393 * MenuDrawPopupGlyph
1394 *
1395 * Draws popup magic glyphs (can be found in system menu).
1396 */
1397 static void FASTCALL
1398 MENU_DrawPopupGlyph(HDC dc, LPRECT r, INT_PTR popupMagic, BOOL inactive, BOOL hilite)
1399 {
1400 LOGFONTW lf;
1401 HFONT hFont, hOldFont;
1402 COLORREF clrsave;
1403 INT bkmode;
1404 WCHAR symbol;
1405 switch (popupMagic)
1406 {
1407 case (INT_PTR) HBMMENU_POPUP_RESTORE:
1408 symbol = '2';
1409 break;
1410 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
1411 symbol = '0';
1412 break;
1413 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
1414 symbol = '1';
1415 break;
1416 case (INT_PTR) HBMMENU_POPUP_CLOSE:
1417 symbol = 'r';
1418 break;
1419 default:
1420 ERR("Invalid popup magic bitmap %d\n", (int)popupMagic);
1421 return;
1422 }
1423 RtlZeroMemory(&lf, sizeof(LOGFONTW));
1424 RECTL_vInflateRect(r, -2, -2);
1425 lf.lfHeight = r->bottom - r->top;
1426 lf.lfWidth = 0;
1427 lf.lfWeight = FW_NORMAL;
1428 lf.lfCharSet = DEFAULT_CHARSET;
1429 RtlCopyMemory(lf.lfFaceName, L"Marlett", sizeof(L"Marlett"));
1430 hFont = GreCreateFontIndirectW(&lf);
1431 /* save font and text color */
1432 hOldFont = NtGdiSelectFont(dc, hFont);
1433 clrsave = GreGetTextColor(dc);
1434 bkmode = GreGetBkMode(dc);
1435 /* set color and drawing mode */
1436 IntGdiSetBkMode(dc, TRANSPARENT);
1437 if (inactive)
1438 {
1439 /* draw shadow */
1440 if (!hilite)
1441 {
1442 IntGdiSetTextColor(dc, IntGetSysColor(COLOR_HIGHLIGHTTEXT));
1443 GreTextOutW(dc, r->left + 1, r->top + 1, &symbol, 1);
1444 }
1445 }
1446 IntGdiSetTextColor(dc, IntGetSysColor(inactive ? COLOR_GRAYTEXT : (hilite ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)));
1447 /* draw selected symbol */
1448 GreTextOutW(dc, r->left, r->top, &symbol, 1);
1449 /* restore previous settings */
1450 IntGdiSetTextColor(dc, clrsave);
1451 NtGdiSelectFont(dc, hOldFont);
1452 IntGdiSetBkMode(dc, bkmode);
1453 GreDeleteObject(hFont);
1454 }
1455
1456 /***********************************************************************
1457 * MENU_AdjustMenuItemRect
1458 *
1459 * Adjust menu item rectangle according to scrolling state.
1460 */
1461 VOID FASTCALL
1462 MENU_AdjustMenuItemRect(PMENU menu, PRECTL rect)
1463 {
1464 if (menu->dwArrowsOn)
1465 {
1466 UINT arrow_bitmap_height;
1467 arrow_bitmap_height = gpsi->oembmi[OBI_UPARROW].cy; ///// Menu up arrow! OBM_UPARROW
1468 rect->top += arrow_bitmap_height - menu->iTop;
1469 rect->bottom += arrow_bitmap_height - menu->iTop;
1470 }
1471 }
1472
1473 /***********************************************************************
1474 * MENU_FindItemByCoords
1475 *
1476 * Find the item at the specified coordinates (screen coords). Does
1477 * not work for child windows and therefore should not be called for
1478 * an arbitrary system menu.
1479 */
1480 static ITEM *MENU_FindItemByCoords( MENU *menu, POINT pt, UINT *pos )
1481 {
1482 ITEM *item;
1483 UINT i;
1484 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
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 */
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 */
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 */
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
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 */
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
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 */
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 */
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 */
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 DrawFrameControl(hdc, &rect, DFC_MENU, (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 DrawFrameControl(hdc, &rect, DFC_MENU, Flags|DFCS_MENUARROWDOWN);
2197 }
2198
2199 /***********************************************************************
2200 * MenuDrawMenuItem
2201 *
2202 * Draw a single menu item.
2203 */
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 DrawFrameControl(hdc, &rectTemp, DFC_MENU, 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 DrawFrameControl( hdc, &r, DFC_MENU,
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 DrawFrameControl(hdc, &rectTemp, DFC_MENU, 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 */
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 */
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 */
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
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
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 */
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
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
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
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
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 */
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 if ((x - width) < monitor->rcMonitor.left || x >= monitor->rcMonitor.right)
2996 x = monitor->rcMonitor.right - width;
2997 else
2998 x -= width;
2999 }
3000
3001 /* We are off the left side of the screen */
3002 if (x < monitor->rcMonitor.left)
3003 {
3004 /* Re-orient the menu around the x-axis */
3005 x += width;
3006
3007 if (x < monitor->rcMonitor.left || x >= monitor->rcMonitor.right || bIsPopup)
3008 x = monitor->rcMonitor.left;
3009 }
3010
3011 /* Same here, but then the top */
3012 if (y < monitor->rcMonitor.top)
3013 {
3014 y += height;
3015
3016 if (y < monitor->rcMonitor.top || y >= monitor->rcMonitor.bottom || bIsPopup)
3017 y = monitor->rcMonitor.top;
3018 }
3019
3020 /* And the bottom */
3021 if (y + height > monitor->rcMonitor.bottom)
3022 {
3023 if ((y - height) < monitor->rcMonitor.top || y >= monitor->rcMonitor.bottom)
3024 y = monitor->rcMonitor.bottom - height;
3025 else
3026 y -= height;
3027 }
3028
3029 if (pExclude)
3030 {
3031 RECT Cleaned;
3032
3033 if (RECTL_bIntersectRect(&Cleaned, pExclude, &monitor->rcMonitor) &&
3034 RECTL_Intersect(&Cleaned, x, y, width, height))
3035 {
3036 UINT flag_mods[] = {
3037 0, /* First try the 'normal' way */
3038 TPM_BOTTOMALIGN | TPM_RIGHTALIGN, /* Then try the opposite side */
3039 TPM_VERTICAL, /* Then swap horizontal / vertical */
3040 TPM_BOTTOMALIGN | TPM_RIGHTALIGN | TPM_VERTICAL, /* Then the other side again (still swapped hor/ver) */
3041 };
3042
3043 UINT n;
3044 for (n = 0; n < RTL_NUMBER_OF(flag_mods); ++n)
3045 {
3046 INT tx = x;
3047 INT ty = y;
3048
3049 /* Try to move a bit around */
3050 if (MENU_MoveRect(flags ^ flag_mods[n], &tx, &ty, width, height, &Cleaned, monitor) &&
3051 !RECTL_Intersect(&Cleaned, tx, ty, width, height))
3052 {
3053 x = tx;
3054 y = ty;
3055 break;
3056 }
3057 }
3058 /* If none worked, we go with the original x/y */
3059 }
3060 }
3061
3062 #if SHOW_DEBUGRECT
3063 {
3064 RECT rr = {x, y, x + width, y + height};
3065 DebugRect(&rr, RGB(0, 255, 0));
3066 }
3067 #endif
3068
3069 pWnd = ValidateHwndNoErr( menu->hWnd );
3070
3071 if (!pWnd)
3072 {
3073 ERR("menu->hWnd bad hwnd %p\n",menu->hWnd);
3074 return FALSE;
3075 }
3076
3077 if (!top_popup) {
3078 top_popup = menu->hWnd;
3079 top_popup_hmenu = UserHMGetHandle(menu);
3080 }
3081
3082 /* Display the window */
3083 UserRefObjectCo(pWnd, &Ref);
3084 co_WinPosSetWindowPos( pWnd, HWND_TOPMOST, x, y, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE);
3085
3086 co_IntUpdateWindows(pWnd, RDW_ALLCHILDREN, FALSE);
3087
3088 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, pWnd, OBJID_CLIENT, CHILDID_SELF, 0);
3089 UserDerefObjectCo(pWnd);
3090
3091 return TRUE;
3092 }
3093
3094 /***********************************************************************
3095 * MENU_EnsureMenuItemVisible
3096 */
3097 void MENU_EnsureMenuItemVisible(PMENU lppop, UINT wIndex, HDC hdc)
3098 {
3099 USER_REFERENCE_ENTRY Ref;
3100 if (lppop->dwArrowsOn)
3101 {
3102 ITEM *item = &lppop->rgItems[wIndex];
3103 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
3104 UINT nOldPos = lppop->iTop;
3105 RECT rc;
3106 UINT arrow_bitmap_height;
3107 PWND pWnd = ValidateHwndNoErr(lppop->hWnd);
3108
3109 IntGetClientRect(pWnd, &rc);
3110
3111 arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy;
3112
3113 rc.top += arrow_bitmap_height;
3114 rc.bottom -= arrow_bitmap_height;
3115
3116 nMaxHeight -= UserGetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
3117 UserRefObjectCo(pWnd, &Ref);
3118 if (item->cyItem > lppop->iTop + nMaxHeight)
3119 {
3120 lppop->iTop = item->cyItem - nMaxHeight;
3121 IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc);
3122 MENU_DrawScrollArrows(lppop, hdc);
3123 //ERR("Scroll Down iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
3124 }
3125 else if (item->yItem < lppop->iTop)
3126 {
3127 lppop->iTop = item->yItem;
3128 IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc);
3129 MENU_DrawScrollArrows(lppop, hdc);
3130 //ERR("Scroll Up iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
3131 }
3132 UserDerefObjectCo(pWnd);
3133 }
3134 }
3135
3136 /***********************************************************************
3137 * MenuSelectItem
3138 */
3139 static void FASTCALL MENU_SelectItem(PWND pwndOwner, PMENU menu, UINT wIndex,
3140 BOOL sendMenuSelect, PMENU topmenu)
3141 {
3142 HDC hdc;
3143 PWND pWnd;
3144
3145 TRACE("M_SI: owner=%p menu=%p index=0x%04x select=0x%04x\n", pwndOwner, menu, wIndex, sendMenuSelect);
3146
3147 if (!menu || !menu->cItems) return;
3148
3149 pWnd = ValidateHwndNoErr(menu->hWnd);
3150
3151 if (!pWnd) return;
3152
3153 if (menu->iItem == wIndex) return;
3154
3155 if (menu->fFlags & MNF_POPUP)
3156 hdc = UserGetDCEx(pWnd, 0, DCX_USESTYLE);
3157 else
3158 hdc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW);
3159
3160 if (!top_popup) {
3161 top_popup = menu->hWnd; //pPopupMenu->spwndActivePopup or
3162 //pPopupMenu->fIsTrackPopup set pPopupMenu->spwndPopupMenu;
3163 top_popup_hmenu = UserHMGetHandle(menu); //pPopupMenu->spmenu
3164 }
3165
3166 NtGdiSelectFont( hdc, ghMenuFont );
3167
3168 /* Clear previous highlighted item */
3169 if (menu->iItem != NO_SELECTED_ITEM)
3170 {
3171 menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
3172 MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc, &menu->rgItems[menu->iItem],
3173 menu->cyMenu, !(menu->fFlags & MNF_POPUP),
3174 ODA_SELECT);
3175 }
3176
3177 /* Highlight new item (if any) */
3178 menu->iItem = wIndex;
3179 if (menu->iItem != NO_SELECTED_ITEM)
3180 {
3181 if (!(menu->rgItems[wIndex].fType & MF_SEPARATOR))
3182 {
3183 menu->rgItems[wIndex].fState |= MF_HILITE;
3184 MENU_EnsureMenuItemVisible(menu, wIndex, hdc);
3185 MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc,
3186 &menu->rgItems[wIndex], menu->cyMenu, !(menu->fFlags & MNF_POPUP), ODA_SELECT);
3187 }
3188 if (sendMenuSelect)
3189 {
3190 ITEM *ip = &menu->rgItems[menu->iItem];
3191 WPARAM wParam = MAKEWPARAM( ip->spSubMenu ? wIndex : ip->wID,
3192 ip->fType | ip->fState |
3193 (ip->spSubMenu ? MF_POPUP : 0) |
3194 (menu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
3195
3196 co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(menu));
3197 }
3198 }
3199 else if (sendMenuSelect)
3200 {
3201 if (topmenu)
3202 {
3203 int pos;
3204 pos = MENU_FindSubMenu(&topmenu, menu);
3205 if (pos != NO_SELECTED_ITEM)
3206 {
3207 ITEM *ip = &topmenu->rgItems[pos];
3208 WPARAM wParam = MAKEWPARAM( Pos, ip->fType | ip->fState |
3209 (ip->spSubMenu ? MF_POPUP : 0) |
3210 (topmenu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
3211
3212 co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(topmenu));
3213 }
3214 }
3215 }
3216 UserReleaseDC(pWnd, hdc, FALSE);
3217 }
3218
3219 /***********************************************************************
3220 * MenuMoveSelection
3221 *
3222 * Moves currently selected item according to the Offset parameter.
3223 * If there is no selection then it should select the last item if
3224 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
3225 */
3226 static void FASTCALL MENU_MoveSelection(PWND pwndOwner, PMENU menu, INT offset)
3227 {
3228 INT i;
3229
3230 TRACE("pwnd=%x menu=%x off=0x%04x\n", pwndOwner, menu, offset);
3231
3232 if ((!menu) || (!menu->rgItems)) return;
3233
3234 if ( menu->iItem != NO_SELECTED_ITEM )
3235 {
3236 if ( menu->cItems == 1 )
3237 return;
3238 else
3239 for (i = menu->iItem + offset ; i >= 0 && i < menu->cItems
3240 ; i += offset)
3241 if (!(menu->rgItems[i].fType & MF_SEPARATOR))
3242 {
3243 MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 );
3244 return;
3245 }
3246 }
3247
3248 for ( i = (offset > 0) ? 0 : menu->cItems - 1;
3249 i >= 0 && i < menu->cItems ; i += offset)
3250 if (!(menu->rgItems[i].fType & MF_SEPARATOR))
3251 {
3252 MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 );
3253 return;
3254 }
3255 }
3256
3257 /***********************************************************************
3258 * MenuHideSubPopups
3259 *
3260 * Hide the sub-popup menus of this menu.
3261 */
3262 static void FASTCALL MENU_HideSubPopups(PWND pWndOwner, PMENU Menu,
3263 BOOL SendMenuSelect, UINT wFlags)
3264 {
3265 TRACE("owner=%x menu=%x 0x%04x\n", pWndOwner, Menu, SendMenuSelect);
3266
3267 if ( Menu && top_popup )
3268 {
3269 PITEM Item;
3270
3271 if (Menu->iItem != NO_SELECTED_ITEM)
3272 {
3273 Item = &Menu->rgItems[Menu->iItem];
3274 if (!(Item->spSubMenu) ||
3275 !(Item->fState & MF_MOUSESELECT)) return;
3276 Item->fState &= ~MF_MOUSESELECT;
3277 }
3278 else
3279 return;
3280
3281 if (Item->spSubMenu)
3282 {
3283 PWND pWnd;
3284 if (!VerifyMenu(Item->spSubMenu)) return;
3285 pWnd = ValidateHwndNoErr(Item->spSubMenu->hWnd);
3286 MENU_HideSubPopups(pWndOwner, Item->spSubMenu, FALSE, wFlags);
3287 MENU_SelectItem(pWndOwner, Item->spSubMenu, NO_SELECTED_ITEM, SendMenuSelect, NULL);
3288 TRACE("M_HSP top p hm %p pWndOwner IDMenu %p\n",top_popup_hmenu,pWndOwner->IDMenu);
3289 co_UserDestroyWindow(pWnd);
3290
3291 /* Native returns handle to destroyed window */
3292 if (!(wFlags & TPM_NONOTIFY))
3293 {
3294 co_IntSendMessage( UserHMGetHandle(pWndOwner), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(Item->spSubMenu),
3295 MAKELPARAM(0, IS_SYSTEM_MENU(Item->spSubMenu)) );
3296 }
3297 ////
3298 // Call WM_UNINITMENUPOPUP FIRST before destroy!!
3299 // Fixes todo_wine User32 test menu.c line 2239 GetMenuBarInfo callback....
3300 //
3301 Item->spSubMenu->hWnd = NULL;
3302 ////
3303 }
3304 }
3305 }
3306
3307 /***********************************************************************
3308 * MenuShowSubPopup
3309 *
3310 * Display the sub-menu of the selected item of this menu.
3311 * Return the handle of the submenu, or menu if no submenu to display.
3312 */
3313 static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFirst, UINT Flags)
3314 {
3315 RECT Rect, ParentRect;
3316 ITEM *Item;
3317 HDC Dc;
3318 PWND pWnd;
3319
3320 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, Menu, SelectFirst);
3321
3322 if (!Menu) return Menu;
3323
3324 if (Menu->iItem == NO_SELECTED_ITEM) return Menu;
3325
3326 Item = &Menu->rgItems[Menu->iItem];
3327 if (!(Item->spSubMenu) || (Item->fState & (MF_GRAYED | MF_DISABLED)))
3328 return Menu;
3329
3330 /* message must be sent before using item,
3331 because nearly everything may be changed by the application ! */
3332
3333 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3334 if (!(Flags & TPM_NONOTIFY))
3335 {
3336 co_IntSendMessage(UserHMGetHandle(WndOwner), WM_INITMENUPOPUP,
3337 (WPARAM) UserHMGetHandle(Item->spSubMenu),
3338 MAKELPARAM(Menu->iItem, IS_SYSTEM_MENU(Menu)));
3339 }
3340
3341 Item = &Menu->rgItems[Menu->iItem];
3342 //Rect = ItemInfo.Rect;
3343 Rect.left = Item->xItem;
3344 Rect.top = Item->yItem;
3345 Rect.right = Item->cxItem; // Do this for now......
3346 Rect.bottom = Item->cyItem;
3347
3348 pWnd = ValidateHwndNoErr(Menu->hWnd);
3349
3350 /* Grab the rect of our (entire) parent menu, so we can try to not overlap it */
3351 if (Menu->fFlags & MNF_POPUP)
3352 {
3353 if (!IntGetWindowRect(pWnd, &ParentRect))
3354 {
3355 ERR("No pWnd\n");
3356 ParentRect = Rect;
3357 }
3358
3359 /* Ensure we can slightly overlap our parent */
3360 RECTL_vInflateRect(&ParentRect, -UserGetSystemMetrics(SM_CXEDGE) * 2, 0);
3361 }
3362 else
3363 {
3364 /* Inside the menu bar, we do not want to grab the entire window... */
3365 ParentRect = Rect;
3366 if (pWnd)
3367 RECTL_vOffsetRect(&ParentRect, pWnd->rcWindow.left, pWnd->rcWindow.top);
3368 }
3369
3370 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
3371 if (!(Item->fState & MF_HILITE))
3372 {
3373 if (Menu->fFlags & MNF_POPUP) Dc = UserGetDCEx(pWnd, NULL, DCX_USESTYLE);
3374 else Dc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW);
3375
3376 NtGdiSelectFont(Dc, ghMenuFont);
3377
3378 Item->fState |= MF_HILITE;
3379 MENU_DrawMenuItem(pWnd, Menu, WndOwner, Dc, Item, Menu->cyMenu,
3380 !(Menu->fFlags & MNF_POPUP), ODA_DRAWENTIRE);
3381
3382 UserReleaseDC(pWnd, Dc, FALSE);
3383 }
3384
3385 if (!Item->yItem && !Item->xItem && !Item->cyItem && !Item->cxItem)
3386 {
3387 Item->xItem = Rect.left;
3388 Item->yItem = Rect.top;
3389 Item->cxItem = Rect.right; // Do this for now......
3390 Item->cyItem = Rect.bottom;
3391 }
3392 Item->fState |= MF_MOUSESELECT;
3393
3394 if (IS_SYSTEM_MENU(Menu))
3395 {
3396 MENU_InitSysMenuPopup(Item->spSubMenu, pWnd->style, pWnd->pcls->style, HTSYSMENU);
3397
3398 NC_GetSysPopupPos(pWnd, &Rect);
3399 /* Ensure we do not overlap this */
3400 ParentRect = Rect;
3401 if (Flags & TPM_LAYOUTRTL) Rect.left = Rect.right;
3402 Rect.top = Rect.bottom;
3403 Rect.right = UserGetSystemMetrics(SM_CXSIZE);
3404 Rect.bottom = UserGetSystemMetrics(SM_CYSIZE);
3405 }
3406 else
3407 {
3408 IntGetWindowRect(pWnd, &Rect);
3409 if (Menu->fFlags & MNF_POPUP)
3410 {
3411 RECT rc;
3412 rc.left = Item->xItem;
3413 rc.top = Item->yItem;
3414 rc.right = Item->cxItem;
3415 rc.bottom = Item->cyItem;
3416
3417 MENU_AdjustMenuItemRect(Menu, &rc);
3418
3419 /* The first item in the popup menu has to be at the
3420 same y position as the focused menu item */
3421 if(Flags & TPM_LAYOUTRTL)
3422 Rect.left += UserGetSystemMetrics(SM_CXDLGFRAME);
3423 else
3424 Rect.left += rc.right - UserGetSystemMetrics(SM_CXDLGFRAME);
3425
3426 Rect.top += rc.top;
3427 }
3428 else
3429 {
3430 if(Flags & TPM_LAYOUTRTL)
3431 Rect.left += Rect.right - Item->xItem; //ItemInfo.Rect.left;
3432 else
3433 Rect.left += Item->xItem; //ItemInfo.Rect.left;
3434 Rect.top += Item->cyItem; //ItemInfo.Rect.bottom;
3435 Rect.right = Item->cxItem - Item->xItem; //ItemInfo.Rect.right - ItemInfo.Rect.left;
3436 Rect.bottom = Item->cyItem - Item->yItem; //ItemInfo.Rect.bottom - ItemInfo.Rect.top;
3437 }
3438 }
3439
3440 /* Next menu does not need to be shown vertical anymore */
3441 if (Menu->fFlags & MNF_POPUP)
3442 Flags &= (~TPM_VERTICAL);
3443
3444
3445
3446 /* use default alignment for submenus */
3447 Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
3448
3449 MENU_InitPopup( WndOwner, Item->spSubMenu, Flags );
3450
3451 MENU_ShowPopup( WndOwner, Item->spSubMenu, Menu->iItem, Flags,
3452 Rect.left, Rect.top, &ParentRect);
3453 if (SelectFirst)
3454 {
3455 MENU_MoveSelection(WndOwner, Item->spSubMenu, ITEM_NEXT);
3456 }
3457 return Item->spSubMenu;
3458 }
3459
3460 /***********************************************************************
3461 * MenuExecFocusedItem
3462 *
3463 * Execute a menu item (for instance when user pressed Enter).
3464 * Return the wID of the executed item. Otherwise, -1 indicating
3465 * that no menu item was executed, -2 if a popup is shown;
3466 * Have to receive the flags for the TrackPopupMenu options to avoid
3467 * sending unwanted message.
3468 *
3469 */
3470 static INT FASTCALL MENU_ExecFocusedItem(MTRACKER *pmt, PMENU Menu, UINT Flags)
3471 {
3472 PITEM Item;
3473
3474 TRACE("%p menu=%p\n", pmt, Menu);
3475
3476 if (!Menu || !Menu->cItems || Menu->iItem == NO_SELECTED_ITEM)
3477 {
3478 return -1;
3479 }
3480
3481 Item = &Menu->rgItems[Menu->iItem];
3482
3483 TRACE("%p %08x %p\n", Menu, Item->wID, Item->spSubMenu);
3484
3485 if (!(Item->spSubMenu))
3486 {
3487 if (!(Item->fState & (MF_GRAYED | MF_DISABLED)) && !(Item->fType & MF_SEPARATOR))
3488 {
3489 /* If TPM_RETURNCMD is set you return the id, but
3490 do not send a message to the owner */
3491 if (!(Flags & TPM_RETURNCMD))
3492 {
3493 if (Menu->fFlags & MNF_SYSMENU)
3494 {
3495 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_SYSCOMMAND, Item->wID,
3496 MAKELPARAM((SHORT) pmt->Pt.x, (SHORT) pmt->Pt.y));
3497 }
3498 else
3499 {
3500 DWORD dwStyle = ((Menu->fFlags & MNS_STYLE_MASK) | ( pmt->TopMenu ? (pmt->TopMenu->fFlags & MNS_STYLE_MASK) : 0) );
3501
3502 if (dwStyle & MNS_NOTIFYBYPOS)
3503 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_MENUCOMMAND, Menu->iItem, (LPARAM)UserHMGetHandle(Menu));
3504 else
3505 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_COMMAND, Item->wID, 0);
3506 }
3507 }
3508 return Item->wID;
3509 }
3510 }
3511 else
3512 {
3513 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, Menu, TRUE, Flags);
3514 return -2;
3515 }
3516
3517 return -1;
3518 }
3519
3520 /***********************************************************************
3521 * MenuSwitchTracking
3522 *
3523 * Helper function for menu navigation routines.
3524 */
3525 static void FASTCALL MENU_SwitchTracking(MTRACKER* pmt, PMENU PtMenu, UINT Index, UINT wFlags)
3526 {
3527 TRACE("%x menu=%x 0x%04x\n", pmt, PtMenu, Index);
3528
3529 if ( pmt->TopMenu != PtMenu &&
3530 !((PtMenu->fFlags | pmt->TopMenu->fFlags) & MNF_POPUP) )
3531 {
3532 /* both are top level menus (system and menu-bar) */
3533 MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags);
3534 MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, NO_SELECTED_ITEM, FALSE, NULL);
3535 pmt->TopMenu = PtMenu;
3536 }
3537 else
3538 {
3539 MENU_HideSubPopups(pmt->OwnerWnd, PtMenu, FALSE, wFlags);
3540 }
3541
3542 MENU_SelectItem(pmt->OwnerWnd, PtMenu, Index, TRUE, NULL);
3543 }
3544
3545 /***********************************************************************
3546 * MenuButtonDown
3547 *
3548 * Return TRUE if we can go on with menu tracking.
3549 */
3550 static BOOL FASTCALL MENU_ButtonDown(MTRACKER* pmt, PMENU PtMenu, UINT Flags)
3551 {
3552 TRACE("%x PtMenu=%p\n", pmt, PtMenu);
3553
3554 if (PtMenu)
3555 {
3556 UINT id = 0;
3557 PITEM item;
3558 if (IS_SYSTEM_MENU(PtMenu))
3559 {
3560 item = PtMenu->rgItems;
3561 }
3562 else
3563 {
3564 item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &id );
3565 }
3566
3567 if (item)
3568 {
3569 if (PtMenu->iItem != id)
3570 MENU_SwitchTracking(pmt, PtMenu, id, Flags);
3571
3572 /* If the popup menu is not already "popped" */
3573 if (!(item->fState & MF_MOUSESELECT))
3574 {
3575 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags);
3576 }
3577
3578 return TRUE;
3579 }
3580 /* Else the click was on the menu bar, finish the tracking */
3581 }
3582 return FALSE;
3583 }
3584
3585 /***********************************************************************
3586 * MenuButtonUp
3587 *
3588 * Return the value of MenuExecFocusedItem if
3589 * the selected item was not a popup. Else open the popup.
3590 * A -1 return value indicates that we go on with menu tracking.
3591 *
3592 */
3593 static INT FASTCALL MENU_ButtonUp(MTRACKER *pmt, PMENU PtMenu, UINT Flags)
3594 {
3595 TRACE("%p pmenu=%x\n", pmt, PtMenu);
3596
3597 if (PtMenu)
3598 {
3599 UINT Id = 0;
3600 ITEM *item;
3601
3602 if ( IS_SYSTEM_MENU(PtMenu) )
3603 {
3604 item = PtMenu->rgItems;
3605 }
3606 else
3607 {
3608 item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &Id );
3609 }
3610
3611 if (item && ( PtMenu->iItem == Id))
3612 {
3613 if (!(item->spSubMenu))
3614 {
3615 INT ExecutedMenuId = MENU_ExecFocusedItem( pmt, PtMenu, Flags);
3616 if (ExecutedMenuId == -1 || ExecutedMenuId == -2) return -1;
3617 return ExecutedMenuId;
3618 }
3619
3620 /* If we are dealing with the menu bar */
3621 /* and this is a click on an already "popped" item: */
3622 /* Stop the menu tracking and close the opened submenus */
3623 if (pmt->TopMenu == PtMenu && PtMenu->TimeToHide)
3624 {
3625 return 0;
3626 }
3627 }
3628 if ( IntGetMenu(PtMenu->hWnd) == PtMenu )
3629 {
3630 PtMenu->TimeToHide = TRUE;
3631 }
3632 }
3633 return -1;
3634 }
3635
3636 /***********************************************************************
3637 * MenuPtMenu
3638 *
3639 * Walks menu chain trying to find a menu pt maps to.
3640 */
3641 static PMENU FASTCALL MENU_PtMenu(PMENU menu, POINT pt)
3642 {
3643 PITEM pItem;
3644 PMENU ret = NULL;
3645
3646 if (!menu) return NULL;
3647
3648 /* try subpopup first (if any) */
3649 if (menu->iItem != NO_SELECTED_ITEM)
3650 {
3651 pItem = menu->rgItems;
3652 if ( pItem ) pItem = &pItem[menu->iItem];
3653 if ( pItem && pItem->spSubMenu && pItem->fState & MF_MOUSESELECT)
3654 {
3655 ret = MENU_PtMenu( pItem->spSubMenu, pt);
3656 }
3657 }
3658
3659 /* check the current window (avoiding WM_HITTEST) */
3660 if (!ret)
3661 {
3662 PWND pWnd = ValidateHwndNoErr(menu->hWnd);
3663 INT ht = GetNCHitEx(pWnd, pt);
3664 if ( menu->fFlags & MNF_POPUP )
3665 {
3666 if (ht != HTNOWHERE && ht != HTERROR) ret = menu;
3667 }
3668 else if (ht == HTSYSMENU)
3669 ret = get_win_sys_menu(menu->hWnd);
3670 else if (ht == HTMENU)
3671 ret = IntGetMenu( menu->hWnd );
3672 }
3673 return ret;
3674 }
3675
3676 /***********************************************************************
3677 * MenuMouseMove
3678 *
3679 * Return TRUE if we can go on with menu tracking.
3680 */
3681 static BOOL FASTCALL MENU_MouseMove(MTRACKER *pmt, PMENU PtMenu, UINT Flags)
3682 {
3683 UINT Index = NO_SELECTED_ITEM;
3684
3685 if ( PtMenu )
3686 {
3687 if (IS_SYSTEM_MENU(PtMenu))
3688 {
3689 Index = 0;
3690 //// ReactOS only HACK: CORE-2338
3691 // Windows tracks mouse moves to the system menu but does not open it.
3692 // Only keyboard tracking can do that.
3693 //
3694 TRACE("SystemMenu\n");
3695 return TRUE; // Stay inside the Loop!
3696 }
3697 else
3698 MENU_FindItemByCoords( PtMenu, pmt->Pt, &Index );
3699 }
3700
3701 if (Index == NO_SELECTED_ITEM)
3702 {
3703 MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NO_SELECTED_ITEM, TRUE, pmt->TopMenu);
3704 }
3705 else if (PtMenu->iItem != Index)
3706 {
3707 MENU_SwitchTracking(pmt, PtMenu, Index, Flags);
3708 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags);
3709 }
3710 return TRUE;
3711 }
3712
3713 /***********************************************************************
3714 * MenuGetSubPopup
3715 *
3716 * Return the handle of the selected sub-popup menu (if any).
3717 */
3718 static PMENU MENU_GetSubPopup( PMENU menu )
3719 {
3720 ITEM *item;
3721
3722 if ((!menu) || (menu->iItem == NO_SELECTED_ITEM)) return 0;
3723
3724 item = &menu->rgItems[menu->iItem];
3725 if (item && (item->spSubMenu) && (item->fState & MF_MOUSESELECT))
3726 {
3727 return item->spSubMenu;
3728 }
3729 return 0;
3730 }
3731
3732 /***********************************************************************
3733 * MenuDoNextMenu
3734 *
3735 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
3736 */
3737 static LRESULT FASTCALL MENU_DoNextMenu(MTRACKER* pmt, UINT Vk, UINT wFlags)
3738 {
3739 BOOL atEnd = FALSE;
3740
3741 /* When skipping left, we need to do something special after the
3742 first menu. */
3743 if (Vk == VK_LEFT && pmt->TopMenu->iItem == 0)
3744 {
3745 atEnd = TRUE;
3746 }
3747 /* When skipping right, for the non-system menu, we need to
3748 handle the last non-special menu item (ie skip any window
3749 icons such as MDI maximize, restore or close) */
3750 else if ((Vk == VK_RIGHT) && !IS_SYSTEM_MENU(pmt->TopMenu))
3751 {
3752 UINT i = pmt->TopMenu->iItem + 1;
3753 while (i < pmt->TopMenu->cItems) {
3754 if ((pmt->TopMenu->rgItems[i].wID >= SC_SIZE &&
3755 pmt->TopMenu->rgItems[i].wID <= SC_RESTORE)) {
3756 i++;
3757 } else break;
3758 }
3759 if (i == pmt->TopMenu->cItems) {
3760 atEnd = TRUE;
3761 }
3762 }
3763 /* When skipping right, we need to cater for the system menu */
3764 else if ((Vk == VK_RIGHT) && IS_SYSTEM_MENU(pmt->TopMenu))
3765 {
3766 if (pmt->TopMenu->iItem == (pmt->TopMenu->cItems - 1)) {
3767 atEnd = TRUE;
3768 }
3769 }
3770
3771 if ( atEnd )
3772 {
3773 MDINEXTMENU NextMenu;
3774 PMENU MenuTmp;
3775 PWND pwndTemp;
3776 HMENU hNewMenu;
3777 HWND hNewWnd;
3778 UINT Id = 0;
3779
3780 MenuTmp = (IS_SYSTEM_MENU(pmt->TopMenu)) ? co_IntGetSubMenu(pmt->TopMenu, 0) : pmt->TopMenu;
3781 NextMenu.hmenuIn = UserHMGetHandle(MenuTmp);
3782 NextMenu.hmenuNext = NULL;
3783 NextMenu.hwndNext = NULL;
3784 co_IntSendMessage(UserHMGetHandle(pmt->OwnerWnd), WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
3785
3786 TRACE("%p [%p] -> %p [%p]\n",
3787 pmt->CurrentMenu, pmt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
3788
3789 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
3790 {
3791 hNewWnd = UserHMGetHandle(pmt->OwnerWnd);
3792 if (IS_SYSTEM_MENU(pmt->TopMenu))
3793 {
3794 /* switch to the menu bar */
3795
3796 if (pmt->OwnerWnd->style & WS_CHILD || !(MenuTmp = IntGetMenu(hNewWnd))) return FALSE;
3797
3798 if (Vk == VK_LEFT)
3799 {
3800 Id = MenuTmp->cItems - 1;
3801
3802 /* Skip backwards over any system predefined icons,
3803 eg. MDI close, restore etc icons */
3804 while ((Id > 0) &&
3805 (MenuTmp->rgItems[Id].wID >= SC_SIZE &&
3806 MenuTmp->rgItems[Id].wID <= SC_RESTORE)) Id--;
3807
3808 }
3809 hNewMenu = UserHMGetHandle(MenuTmp);
3810 }
3811 else if (pmt->OwnerWnd->style & WS_SYSMENU)
3812 {
3813 /* switch to the system menu */
3814 MenuTmp = get_win_sys_menu(hNewWnd);
3815 if (MenuTmp) hNewMenu = UserHMGetHandle(MenuTmp);
3816 }
3817 else
3818 return FALSE;
3819 }
3820 else /* application returned a new menu to switch to */
3821 {
3822 hNewMenu = NextMenu.hmenuNext;
3823 hNewWnd = NextMenu.hwndNext;
3824
3825 if ((MenuTmp = UserGetMenuObject(hNewMenu)) && (pwndTemp = ValidateHwndNoErr(hNewWnd)))
3826 {
3827 if ( pwndTemp->style & WS_SYSMENU && (get_win_sys_menu(hNewWnd) == MenuTmp) )
3828 {
3829 /* get the real system menu */
3830 MenuTmp = get_win_sys_menu(hNewWnd);
3831 hNewMenu = UserHMGetHandle(MenuTmp);
3832 }
3833 else if (pwndTemp->style & WS_CHILD || IntGetMenu(hNewWnd) != MenuTmp)
3834 {
3835 /* FIXME: Not sure what to do here;
3836 * perhaps try to track NewMenu as a popup? */
3837
3838 WARN(" -- got confused.\n");
3839 return FALSE;
3840 }
3841 }
3842 else return FALSE;
3843 }
3844
3845 if (hNewMenu != UserHMGetHandle(pmt->TopMenu))
3846 {
3847 MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3848
3849 if (pmt->CurrentMenu != pmt->TopMenu)
3850 MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags);
3851 }
3852
3853 if (hNewWnd != UserHMGetHandle(pmt->OwnerWnd))
3854 {
3855 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
3856 pmt->OwnerWnd = ValidateHwndNoErr(hNewWnd);
3857 ///// Use thread pms!!!!
3858 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, hNewWnd);
3859 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
3860 co_UserSetCapture(UserHMGetHandle(pmt->OwnerWnd));
3861 pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED;
3862 }
3863
3864 pmt->TopMenu = pmt->CurrentMenu = UserGetMenuObject(hNewMenu); /* all subpopups are hidden */
3865 MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, Id, TRUE, 0);
3866
3867 return TRUE;
3868 }
3869 return FALSE;
3870 }
3871
3872 /***********************************************************************
3873 * MenuSuspendPopup
3874 *
3875 * The idea is not to show the popup if the next input message is
3876 * going to hide it anyway.
3877 */
3878 static BOOL FASTCALL MENU_SuspendPopup(MTRACKER* pmt, UINT uMsg)
3879 {
3880 MSG msg;
3881
3882 msg.hwnd = UserHMGetHandle(pmt->OwnerWnd); ////// ? silly wine'isms?
3883
3884 co_IntGetPeekMessage( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE, FALSE);
3885 pmt->TrackFlags |= TF_SKIPREMOVE;
3886
3887 switch( uMsg )
3888 {
3889 case WM_KEYDOWN:
3890 co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE, FALSE);
3891 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
3892 {
3893 co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE, FALSE);
3894 co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE, FALSE);
3895 if( msg.message == WM_KEYDOWN &&
3896 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
3897 {
3898 pmt->TrackFlags |= TF_SUSPENDPOPUP;
3899 return TRUE;
3900 }
3901 }
3902 break;
3903 }
3904 /* failures go through this */
3905 pmt->TrackFlags &= ~TF_SUSPENDPOPUP;
3906 return FALSE;
3907 }
3908
3909 /***********************************************************************
3910 * MenuKeyEscape
3911 *
3912 * Handle a VK_ESCAPE key event in a menu.
3913 */
3914 static BOOL FASTCALL MENU_KeyEscape(MTRACKER *pmt, UINT Flags)
3915 {
3916 BOOL EndMenu = TRUE;
3917
3918 if (pmt->CurrentMenu != pmt->TopMenu)
3919 {
3920 if (pmt->CurrentMenu && (pmt->CurrentMenu->fFlags & MNF_POPUP))
3921 {
3922 PMENU MenuPrev, MenuTmp;
3923
3924 MenuPrev = MenuTmp = pmt->TopMenu;
3925
3926 /* close topmost popup */
3927 while (MenuTmp != pmt->CurrentMenu)
3928 {
3929 MenuPrev = MenuTmp;
3930 MenuTmp = MENU_GetSubPopup(MenuPrev);
3931 }
3932
3933 MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags);
3934 pmt->CurrentMenu = MenuPrev;
3935 EndMenu = FALSE;
3936 }
3937 }
3938
3939 return EndMenu;
3940 }
3941
3942 /***********************************************************************
3943 * MenuKeyLeft
3944 *
3945 * Handle a VK_LEFT key event in a menu.
3946 */
3947 static void FASTCALL MENU_KeyLeft(MTRACKER* pmt, UINT Flags, UINT msg)
3948 {
3949 PMENU MenuTmp, MenuPrev;
3950 UINT PrevCol;
3951
3952 MenuPrev = MenuTmp = pmt->TopMenu;
3953
3954 /* Try to move 1 column left (if possible) */
3955 if ( (PrevCol = MENU_GetStartOfPrevColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM)
3956 {
3957 MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, PrevCol, TRUE, 0);
3958 return;
3959 }
3960
3961 /* close topmost popup */
3962 while (MenuTmp != pmt->CurrentMenu)
3963 {
3964 MenuPrev = MenuTmp;
3965 MenuTmp = MENU_GetSubPopup(MenuPrev);
3966 }
3967
3968 MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags);
3969 pmt->CurrentMenu = MenuPrev;
3970
3971 if ((MenuPrev == pmt->TopMenu) && !(pmt->TopMenu->fFlags & MNF_POPUP))
3972 {
3973 /* move menu bar selection if no more popups are left */
3974
3975 if (!MENU_DoNextMenu(pmt, VK_LEFT, Flags))
3976 MENU_MoveSelection(pmt->OwnerWnd, pmt->TopMenu, ITEM_PREV);
3977
3978 if (MenuPrev != MenuTmp || pmt->TrackFlags & TF_SUSPENDPOPUP)
3979 {
3980 /* A sublevel menu was displayed - display the next one
3981 * unless there is another displacement coming up */
3982
3983 if (!MENU_SuspendPopup(pmt, msg))
3984 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu,
3985 TRUE, Flags);
3986 }
3987 }
3988 }
3989
3990 /***********************************************************************
3991 * MenuKeyRight
3992 *
3993 * Handle a VK_RIGHT key event in a menu.
3994 */
3995 static void FASTCALL MENU_KeyRight(MTRACKER *pmt, UINT Flags, UINT msg)
3996 {
3997 PMENU menutmp;
3998 UINT NextCol;
3999
4000 TRACE("MenuKeyRight called, cur %p, top %p.\n",
4001 pmt->CurrentMenu, pmt->TopMenu);
4002
4003 if ((pmt->TopMenu->fFlags & MNF_POPUP) || (pmt->CurrentMenu != pmt->TopMenu))
4004 {
4005 /* If already displaying a popup, try to display sub-popup */
4006
4007 menutmp = pmt->CurrentMenu;
4008 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, menutmp, TRUE, Flags);
4009
4010 /* if subpopup was displayed then we are done */
4011 if (menutmp != pmt->CurrentMenu) return;
4012 }
4013
4014 /* Check to see if there's another column */
4015 if ( (NextCol = MENU_GetStartOfNextColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM)
4016 {
4017 TRACE("Going to %d.\n", NextCol);
4018 MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NextCol, TRUE, 0);
4019 return;
4020 }
4021
4022 if (!(pmt->TopMenu->fFlags & MNF_POPUP)) /* menu bar tracking */
4023 {
4024 if (pmt->CurrentMenu != pmt->TopMenu)
4025 {
4026 MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, Flags);
4027 menutmp = pmt->CurrentMenu = pmt->TopMenu;
4028 }
4029 else
4030 {
4031 menutmp = NULL;
4032 }
4033
4034 /* try to move to the next item */
4035 if ( !MENU_DoNextMenu(pmt, VK_RIGHT, Flags))
4036 MENU_MoveSelection(pmt->OwnerWnd, pmt->TopMenu, ITEM_NEXT);
4037
4038 if ( menutmp || pmt->TrackFlags & TF_SUSPENDPOPUP )
4039 {
4040 if ( !MENU_SuspendPopup(pmt, msg) )
4041 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu, TRUE, Flags);
4042 }
4043 }
4044 }
4045
4046 /***********************************************************************
4047 * MenuTrackMenu
4048 *
4049 * Menu tracking code.
4050 */
4051 static INT FASTCALL MENU_TrackMenu(PMENU pmenu, UINT wFlags, INT x, INT y,
4052 PWND pwnd)
4053 {
4054 MSG msg;
4055 BOOL fRemove;
4056 INT executedMenuId = -1;
4057 MTRACKER mt;
4058 HWND capture_win;
4059 PMENU pmMouse;
4060 BOOL enterIdleSent = FALSE;
4061 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
4062
4063 if (pti != pwnd->head.pti)
4064 {
4065 ERR("Not the same PTI!!!!\n");
4066 }
4067
4068 mt.TrackFlags = 0;
4069 mt.CurrentMenu = pmenu;
4070 mt.TopMenu = pmenu;
4071 mt.OwnerWnd = pwnd;
4072 mt.Pt.x = x;
4073 mt.Pt.y = y;
4074
4075 TRACE("MTM : hmenu=%p flags=0x%08x (%d,%d) hwnd=%x\n",
4076 UserHMGetHandle(pmenu), wFlags, x, y, UserHMGetHandle(pwnd));
4077
4078 pti->MessageQueue->QF_flags &= ~QF_ACTIVATIONCHANGE;
4079
4080 if (wFlags & TPM_BUTTONDOWN)
4081 {
4082 /* Get the result in order to start the tracking or not */
4083 fRemove = MENU_ButtonDown( &mt, pmenu, wFlags );
4084 fInsideMenuLoop = fRemove;
4085 }
4086
4087 if (wFlags & TF_ENDMENU) fInsideMenuLoop = FALSE;
4088
4089 if (wFlags & TPM_POPUPMENU && pmenu->cItems == 0) // Tracking empty popup menu...
4090 {
4091 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL);
4092 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
4093 co_UserSetCapture(NULL); /* release the capture */
4094 return 0;
4095 }
4096
4097 capture_win = IntGetCapture();
4098
4099 while (fInsideMenuLoop)
4100 {
4101 BOOL ErrorExit = FALSE;
4102 if (!VerifyMenu( mt.CurrentMenu )) /* sometimes happens if I do a window manager close */
4103 break;
4104
4105 /* we have to keep the message in the queue until it's
4106 * clear that menu loop is not over yet. */
4107
4108 for (;;)
4109 {
4110 if (co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOREMOVE, FALSE ))
4111 {
4112 if (!IntCallMsgFilter( &msg, MSGF_MENU )) break;
4113 /* remove the message from the queue */
4114 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4115 }
4116 else
4117 {
4118 /* ReactOS Checks */
4119 if (!VerifyWnd(mt.OwnerWnd) ||
4120 !ValidateHwndNoErr(mt.CurrentMenu->hWnd) ||
4121 pti->MessageQueue->QF_flags & QF_ACTIVATIONCHANGE ||
4122 capture_win != IntGetCapture() ) // Should not happen, but this is ReactOS...
4123 {
4124 ErrorExit = TRUE; // Do not wait on dead windows, now win test_capture_4 works.
4125 break;
4126 }
4127
4128 if (!enterIdleSent)
4129 {
4130 HWND win = mt.CurrentMenu->fFlags & MNF_POPUP ? mt.CurrentMenu->hWnd : NULL;
4131 enterIdleSent = TRUE;
4132 co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_ENTERIDLE, MSGF_MENU, (LPARAM) win);
4133 }
4134 co_IntWaitMessage(NULL, 0, 0);
4135 }
4136 }
4137
4138 if (ErrorExit) break; // Gracefully dropout.
4139
4140 /* check if EndMenu() tried to cancel us, by posting this message */
4141 if (msg.message == WM_CANCELMODE)
4142 {
4143 /* we are now out of the loop */
4144 fInsideMenuLoop = FALSE;
4145
4146 /* remove the message from the queue */
4147 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4148
4149 /* break out of internal loop, ala ESCAPE */
4150 break;
4151 }
4152
4153 mt.Pt = msg.pt;
4154
4155 if ( (msg.hwnd == mt.CurrentMenu->hWnd) || ((msg.message!=WM_TIMER) && (msg.message!=WM_SYSTIMER)) )
4156 enterIdleSent=FALSE;
4157
4158 fRemove = FALSE;
4159 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
4160 {
4161 /*
4162 * Use the mouse coordinates in lParam instead of those in the MSG
4163 * struct to properly handle synthetic messages. They are already
4164 * in screen coordinates.
4165 */
4166 mt.Pt.x = (short)LOWORD(msg.lParam);
4167 mt.Pt.y = (short)HIWORD(msg.lParam);
4168
4169 /* Find a menu for this mouse event */
4170 pmMouse = MENU_PtMenu( mt.TopMenu, mt.Pt );
4171
4172 switch(msg.message)
4173 {
4174 /* no WM_NC... messages in captured state */
4175
4176 case WM_RBUTTONDBLCLK:
4177 case WM_RBUTTONDOWN:
4178 if (!(wFlags & TPM_RIGHTBUTTON))
4179 {
4180 if ( msg.message == WM_RBUTTONDBLCLK ) fInsideMenuLoop = FALSE; // Must exit or loop forever!
4181 break;
4182 }
4183 /* fall through */
4184 case WM_LBUTTONDBLCLK:
4185 case WM_LBUTTONDOWN:
4186 /* If the message belongs to the menu, removes it from the queue */
4187 /* Else, end menu tracking */
4188 fRemove = MENU_ButtonDown(&mt, pmMouse, wFlags);
4189 fInsideMenuLoop = fRemove;
4190 if ( msg.message == WM_LBUTTONDBLCLK ||
4191 msg.message == WM_RBUTTONDBLCLK ) fInsideMenuLoop = FALSE; // Must exit or loop forever!
4192 break;
4193
4194 case WM_RBUTTONUP:
4195 if (!(wFlags & TPM_RIGHTBUTTON)) break;
4196 /* fall through */
4197 case WM_LBUTTONUP:
4198 /* Check if a menu was selected by the mouse */
4199 if (pmMouse)
4200 {
4201 executedMenuId = MENU_ButtonUp( &mt, pmMouse, wFlags);
4202
4203 /* End the loop if executedMenuId is an item ID */
4204 /* or if the job was done (executedMenuId = 0). */
4205 fRemove = (executedMenuId != -1);
4206 fInsideMenuLoop = !fRemove;
4207 }
4208 /* No menu was selected by the mouse */
4209 /* if the function was called by TrackPopupMenu, continue
4210 with the menu tracking. If not, stop it */
4211 else
4212 fInsideMenuLoop = ((wFlags & TPM_POPUPMENU) ? TRUE : FALSE);
4213
4214 break;
4215
4216 case WM_MOUSEMOVE:
4217 /* the selected menu item must be changed every time */
4218 /* the mouse moves. */
4219
4220 if (pmMouse)
4221 fInsideMenuLoop |= MENU_MouseMove( &mt, pmMouse, wFlags );
4222
4223 } /* switch(msg.message) - mouse */
4224 }
4225 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
4226 {
4227 fRemove = TRUE; /* Keyboard messages are always removed */
4228 switch(msg.message)
4229 {
4230 case WM_KEYDOWN:
4231 case WM_SYSKEYDOWN:
4232 switch(msg.wParam)
4233 {
4234 case VK_MENU:
4235 case VK_F10:
4236 fInsideMenuLoop = FALSE;
4237 break;
4238
4239 case VK_HOME:
4240 case VK_END:
4241 MENU_SelectItem(mt.OwnerWnd, mt.CurrentMenu, NO_SELECTED_ITEM, FALSE, 0 );
4242 MENU_MoveSelection(mt.OwnerWnd, mt.CurrentMenu, VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV);
4243 break;
4244
4245 case VK_UP:
4246 case VK_DOWN: /* If on menu bar, pull-down the menu */
4247 if (!(mt.CurrentMenu->fFlags & MNF_POPUP))
4248 mt.CurrentMenu = MENU_ShowSubPopup(mt.OwnerWnd, mt.TopMenu, TRUE, wFlags);
4249 else /* otherwise try to move selection */
4250 MENU_MoveSelection(mt.OwnerWnd, mt.CurrentMenu, (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
4251 break;
4252
4253 case VK_LEFT:
4254 MENU_KeyLeft( &mt, wFlags, msg.message );
4255 break;
4256
4257 case VK_RIGHT:
4258 MENU_KeyRight( &mt, wFlags, msg.message );
4259 break;
4260
4261 case VK_ESCAPE:
4262 fInsideMenuLoop = !MENU_KeyEscape(&mt, wFlags);
4263 break;
4264
4265 case VK_F1:
4266 {
4267 HELPINFO hi;
4268 hi.cbSize = sizeof(HELPINFO);
4269 hi.iContextType = HELPINFO_MENUITEM;
4270 if (mt.CurrentMenu->iItem == NO_SELECTED_ITEM)
4271 hi.iCtrlId = 0;
4272 else
4273 hi.iCtrlId = pmenu->rgItems[mt.CurrentMenu->iItem].wID;
4274 hi.hItemHandle = UserHMGetHandle(mt.CurrentMenu);
4275 hi.dwContextId = pmenu->dwContextHelpId;
4276 hi.MousePos = msg.pt;
4277 co_IntSendMessage( UserHMGetHandle(pwnd), WM_HELP, 0, (LPARAM)&hi);
4278 break;
4279 }
4280
4281 default:
4282 IntTranslateKbdMessage(&msg, 0);
4283 break;
4284 }
4285 break; /* WM_KEYDOWN */
4286
4287 case WM_CHAR:
4288 case WM_SYSCHAR:
4289 {
4290 UINT pos;
4291 BOOL fEndMenu;
4292
4293 if (msg.wParam == L'\r' || msg.wParam == L' ')
4294 {
4295 executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags);
4296 fEndMenu = (executedMenuId != -2);
4297 fInsideMenuLoop = !fEndMenu;
4298 break;
4299 }
4300
4301 /* Hack to avoid control chars. */
4302 /* We will find a better way real soon... */
4303 if (msg.wParam < 32) break;
4304
4305 pos = MENU_FindItemByKey(mt.OwnerWnd, mt.CurrentMenu, LOWORD(msg.wParam), FALSE);
4306
4307 if (pos == (UINT)-2) fInsideMenuLoop = FALSE;
4308 else if (pos == (UINT)-1) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0);
4309 else
4310 {
4311 MENU_SelectItem(mt.OwnerWnd, mt.CurrentMenu, pos, TRUE, 0);
4312 executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags);
4313 fEndMenu = (executedMenuId != -2);
4314 fInsideMenuLoop = !fEndMenu;
4315 }
4316 }
4317 break;
4318 } /* switch(msg.message) - kbd */
4319 }
4320 else
4321 {
4322 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4323 IntDispatchMessage( &msg );
4324 continue;
4325 }
4326
4327 if (fInsideMenuLoop) fRemove = TRUE;
4328
4329 /* finally remove message from the queue */
4330
4331 if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) )
4332 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4333 else mt.TrackFlags &= ~TF_SKIPREMOVE;
4334 }
4335
4336 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL);
4337 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
4338 co_UserSetCapture(NULL); /* release the capture */
4339
4340 /* If dropdown is still painted and the close box is clicked on
4341 then the menu will be destroyed as part of the DispatchMessage above.
4342 This will then invalidate the menu handle in mt.hTopMenu. We should
4343 check for this first. */
4344 if ( VerifyMenu( mt.TopMenu ) )
4345 {
4346 if (VerifyWnd(mt.OwnerWnd))
4347 {
4348 MENU_HideSubPopups(mt.OwnerWnd, mt.TopMenu, FALSE, wFlags);
4349
4350 if (mt.TopMenu->fFlags & MNF_POPUP)
4351 {
4352 PWND pwndTM = ValidateHwndNoErr(mt.TopMenu->hWnd);
4353 if (pwndTM)
4354 {
4355 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, pwndTM, OBJID_CLIENT, CHILDID_SELF, 0);
4356
4357 co_UserDestroyWindow(pwndTM);
4358 }
4359 mt.TopMenu->hWnd = NULL;
4360
4361 if (!(wFlags & TPM_NONOTIFY))
4362 {
4363 co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(mt.TopMenu),
4364 MAKELPARAM(0, IS_SYSTEM_MENU(mt.TopMenu)) );
4365 }
4366 }
4367 MENU_SelectItem( mt.OwnerWnd, mt.TopMenu, NO_SELECTED_ITEM, FALSE, 0 );
4368 co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 );
4369 }
4370
4371 /* Reset the variable for hiding menu */
4372 mt.TopMenu->TimeToHide = FALSE;
4373 }
4374
4375 EngSetLastError( ERROR_SUCCESS );
4376 /* The return value is only used by TrackPopupMenu */
4377 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
4378 if (executedMenuId == -1) executedMenuId = 0;
4379 return executedMenuId;
4380 }
4381
4382 /***********************************************************************
4383 * MenuInitTracking
4384 */
4385 static BOOL FASTCALL MENU_InitTracking(PWND pWnd, PMENU Menu, BOOL bPopup, UINT wFlags)
4386 {
4387 HWND capture_win;
4388 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
4389
4390 TRACE("hwnd=%p hmenu=%p\n", UserHMGetHandle(pWnd), UserHMGetHandle(Menu));
4391
4392 co_UserHideCaret(0);
4393
4394 /* This makes the menus of applications built with Delphi work.
4395 * It also enables menus to be displayed in more than one window,
4396 * but there are some bugs left that need to be fixed in this case.
4397 */
4398 if (!bPopup)
4399 {
4400 Menu->hWnd = UserHMGetHandle(pWnd);
4401 }
4402
4403 if (!top_popup) {
4404 top_popup = Menu->hWnd;
4405 top_popup_hmenu = UserHMGetHandle(Menu);
4406 }
4407
4408 fInsideMenuLoop = TRUE;
4409 fInEndMenu = FALSE;
4410
4411 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
4412 if (!(wFlags & TPM_NONOTIFY))
4413 {
4414 co_IntSendMessage( UserHMGetHandle(pWnd), WM_ENTERMENULOOP, bPopup, 0 );
4415 }
4416
4417 //
4418 // Capture is set before calling WM_INITMENU and after WM_ENTERMENULOOP, see msg_menu.
4419 //
4420 capture_win = (wFlags & TPM_POPUPMENU) ? Menu->hWnd : UserHMGetHandle(pWnd);
4421 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, capture_win); // 1
4422 co_UserSetCapture(capture_win); // 2
4423 pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED; // Set the Q bits so noone can change this!
4424
4425 co_IntSendMessage( UserHMGetHandle(pWnd), WM_SETCURSOR, (WPARAM)UserHMGetHandle(pWnd), HTCAPTION );
4426
4427 if (!(wFlags & TPM_NONOTIFY))
4428 {
4429 co_IntSendMessage( UserHMGetHandle(pWnd), WM_INITMENU, (WPARAM)UserHMGetHandle(Menu), 0 );
4430 /* If an app changed/recreated menu bar entries in WM_INITMENU
4431 * menu sizes will be recalculated once the menu created/shown.
4432 */
4433 }
4434
4435 IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART,
4436 pWnd,
4437 Menu->fFlags & MNF_SYSMENU ? OBJID_SYSMENU : OBJID_MENU,
4438 CHILDID_SELF, 0);
4439 return TRUE;
4440 }
4441
4442 /***********************************************************************
4443 * MenuExitTracking
4444 */
4445 static BOOL FASTCALL MENU_ExitTracking(PWND pWnd, BOOL bPopup, UINT wFlags)
4446 {
4447 TRACE("Exit Track hwnd=%p bPopup %d\n", UserHMGetHandle(pWnd), bPopup);
4448
4449 IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, pWnd, OBJID_WINDOW, CHILDID_SELF, 0);
4450
4451 if (!(wFlags & TPM_NONOTIFY))
4452 co_IntSendMessage( UserHMGetHandle(pWnd), WM_EXITMENULOOP, bPopup, 0 );
4453
4454 co_UserShowCaret(0);
4455
4456 top_popup = 0;
4457 top_popup_hmenu = NULL;
4458
4459 return TRUE;
4460 }
4461
4462 /***********************************************************************
4463 * MenuTrackMouseMenuBar
4464 *
4465 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
4466 */
4467 VOID MENU_TrackMouseMenuBar( PWND pWnd, ULONG ht, POINT pt)
4468 {
4469 PMENU pMenu = (ht == HTSYSMENU) ? IntGetSystemMenu(pWnd, FALSE) : IntGetMenu( UserHMGetHandle(pWnd) ); // See 74276 and CORE-12801
4470 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL;
4471
4472 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", pWnd, ht, pt.x, pt.y);
4473
4474 if (pWnd->ExStyle & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
4475 if (VerifyMenu(pMenu))
4476 {
4477 /* map point to parent client coordinates */
4478 PWND Parent = UserGetAncestor(pWnd, GA_PARENT );
4479 if (Parent != UserGetDesktopWindow())
4480 {
4481 IntScreenToClient(Parent, &pt);
4482 }
4483
4484 MENU_InitTracking(pWnd, pMenu, FALSE, wFlags);
4485 /* fetch the window menu again, it may have changed */
4486 pMenu = (ht == HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pWnd) ) : IntGetMenu( UserHMGetHandle(pWnd) );
4487 MENU_TrackMenu(pMenu, wFlags, pt.x, pt.y, pWnd);
4488 MENU_ExitTracking(pWnd, FALSE, wFlags);
4489 }
4490 }
4491
4492 /***********************************************************************
4493 * MenuTrackKbdMenuBar
4494 *
4495 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
4496 */
4497 VOID MENU_TrackKbdMenuBar(PWND pwnd, UINT wParam, WCHAR wChar)
4498 {
4499 UINT uItem = NO_SELECTED_ITEM;
4500 PMENU TrackMenu;
4501 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
4502
4503 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", UserHMGetHandle(pwnd), wParam, wChar);
4504
4505 /* find window that has a menu */
4506
4507 while (!( (pwnd->style & (WS_CHILD | WS_POPUP)) != WS_CHILD ) )
4508 if (!(pwnd = UserGetAncestor( pwnd, GA_PARENT ))) return;
4509
4510 /* check if we have to track a system menu */
4511
4512 TrackMenu = IntGetMenu( UserHMGetHandle(pwnd) );
4513 if (!TrackMenu || (pwnd->style & WS_MINIMIZE) != 0 || wChar == ' ' )
4514 {
4515 if (!(pwnd->style & WS_SYSMENU)) return;
4516 TrackMenu = get_win_sys_menu( UserHMGetHandle(pwnd) );
4517 uItem = 0;
4518 wParam |= HTSYSMENU; /* prevent item lookup */
4519 }
4520
4521 if (!VerifyMenu( TrackMenu )) return;
4522
4523 MENU_InitTracking( pwnd, TrackMenu, FALSE, wFlags );
4524
4525 /* fetch the window menu again, it may have changed */
4526 TrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pwnd) ) : IntGetMenu( UserHMGetHandle(pwnd) );
4527
4528 if( wChar && wChar != ' ' )
4529 {
4530 uItem = MENU_FindItemByKey( pwnd, TrackMenu, wChar, (wParam & HTSYSMENU) );
4531 if ( uItem >= (UINT)(-2) )
4532 {
4533 if( uItem == (UINT)(-1) ) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0);
4534 /* schedule end of menu tracking */
4535 wFlags |= TF_ENDMENU;
4536 goto track_menu;
4537 }
4538 }
4539
4540 MENU_SelectItem( pwnd, TrackMenu, uItem, TRUE, 0 );
4541
4542 if (!(wParam & HTSYSMENU) || wChar == ' ')
4543 {
4544 if( uItem == NO_SELECTED_ITEM )
4545 MENU_MoveSelection( pwnd, TrackMenu, ITEM_NEXT );
4546 else
4547 UserPostMessage( UserHMGetHandle(pwnd), WM_KEYDOWN, VK_RETURN, 0 );
4548 }
4549
4550 track_menu:
4551 MENU_TrackMenu( TrackMenu, wFlags, 0, 0, pwnd );
4552 MENU_ExitTracking( pwnd, FALSE, wFlags);
4553 }
4554
4555 /**********************************************************************
4556 * TrackPopupMenuEx (USER32.@)
4557 */
4558 BOOL WINAPI IntTrackPopupMenuEx( PMENU menu, UINT wFlags, int x, int y,
4559 PWND pWnd, LPTPMPARAMS lpTpm)
4560 {
4561 BOOL ret = FALSE;
4562 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
4563
4564 if (pti != pWnd->head.pti)
4565 {
4566 ERR("Must be the same pti!\n");
4567 return ret;
4568 }
4569
4570 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p \n", //rect %s\n",
4571 UserHMGetHandle(menu), wFlags, x, y, UserHMGetHandle(pWnd), lpTpm); //,
4572 //lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
4573
4574 if (menu->hWnd && IntIsWindow(menu->hWnd))
4575 {
4576 EngSetLastError( ERROR_POPUP_ALREADY_ACTIVE );
4577 return FALSE;
4578 }
4579
4580 if (MENU_InitPopup( pWnd, menu, wFlags ))
4581 {
4582 MENU_InitTracking(pWnd, menu, TRUE, wFlags);
4583
4584 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
4585 if (!(wFlags & TPM_NONOTIFY))
4586 {
4587 co_IntSendMessage( UserHMGetHandle(pWnd), WM_INITMENUPOPUP, (WPARAM) UserHMGetHandle(menu), 0);
4588 }
4589
4590 if (menu->fFlags & MNF_SYSMENU)
4591 MENU_InitSysMenuPopup( menu, pWnd->style, pWnd->pcls->style, HTSYSMENU);
4592
4593 if (MENU_ShowPopup(pWnd, menu, 0, wFlags | TPM_POPUPMENU, x, y, lpTpm ? &lpTpm->rcExclude : NULL))
4594 ret = MENU_TrackMenu( menu, wFlags | TPM_POPUPMENU, 0, 0, pWnd);
4595 else
4596 {
4597 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL);
4598 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
4599 co_UserSetCapture(NULL); /* release the capture */
4600 }
4601
4602 MENU_ExitTracking(pWnd, TRUE, wFlags);
4603
4604 if (menu->hWnd)
4605 {
4606 PWND pwndM = ValidateHwndNoErr( menu->hWnd );
4607 if (pwndM) // wine hack around this with their destroy function.
4608 {
4609 if (!(pWnd->state & WNDS_DESTROYED))
4610 co_UserDestroyWindow( pwndM ); // Fix wrong error return.
4611 }
4612 menu->hWnd = 0;
4613
4614 if (!(wFlags & TPM_NONOTIFY))
4615 {
4616 co_IntSendMessage( UserHMGetHandle(pWnd), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(menu),
4617 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
4618 }
4619 }
4620 }
4621 return ret;
4622 }
4623
4624 //
4625 // Menu Class Proc.
4626 //
4627 BOOL WINAPI
4628 PopupMenuWndProc(
4629 PWND Wnd,
4630 UINT Message,
4631 WPARAM wParam,
4632 LPARAM lParam,
4633 LRESULT *lResult)
4634 {
4635 PPOPUPMENU pPopupMenu;
4636
4637 *lResult = 0;
4638
4639 TRACE("PMWP : pwnd=%x msg=%d wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
4640
4641 if (Wnd)
4642 {
4643 if (!Wnd->fnid)
4644 {
4645 if (Message != WM_NCCREATE)
4646 {
4647 *lResult = IntDefWindowProc(Wnd, Message, wParam, lParam, FALSE);
4648 return TRUE;
4649 }
4650 Wnd->fnid = FNID_MENU;
4651 pPopupMenu = DesktopHeapAlloc( Wnd->head.rpdesk, sizeof(POPUPMENU) );
4652 if (pPopupMenu == NULL)
4653 {
4654 return TRUE;
4655 }
4656 pPopupMenu->posSelectedItem = NO_SELECTED_ITEM;
4657 pPopupMenu->spwndPopupMenu = Wnd;
4658 ((PMENUWND)Wnd)->ppopupmenu = pPopupMenu;
4659 TRACE("Pop Up Menu is Setup! Msg %d\n",Message);
4660 *lResult = 1;
4661 return TRUE;
4662 }
4663 else
4664 {
4665 if (Wnd->fnid != FNID_MENU)
4666 {
4667 ERR("Wrong window class for Menu! fnid %x\n",Wnd->fnid);
4668 return TRUE;
4669 }
4670 pPopupMenu = ((PMENUWND)Wnd)->ppopupmenu;
4671 }
4672 }
4673
4674 switch(Message)
4675 {
4676 case WM_CREATE:
4677 {
4678 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
4679 pPopupMenu->spmenu = UserGetMenuObject(cs->lpCreateParams);
4680 if (pPopupMenu->spmenu)
4681 {
4682 UserReferenceObject(pPopupMenu->spmenu);
4683 }
4684 break;
4685 }
4686
4687 case WM_MOUSEACTIVATE: /* We don't want to be activated */
4688 *lResult = MA_NOACTIVATE;
4689 break;
4690
4691 case WM_PAINT:
4692 {
4693 PAINTSTRUCT ps;
4694 IntBeginPaint(Wnd, &ps);
4695 MENU_DrawPopupMenu(Wnd, ps.hdc, pPopupMenu->spmenu);
4696 IntEndPaint(Wnd, &ps);
4697 break;
4698 }
4699
4700 case WM_PRINTCLIENT:
4701 {
4702 MENU_DrawPopupMenu( Wnd, (HDC)wParam, pPopupMenu->spmenu);
4703 break;
4704 }
4705
4706 case WM_ERASEBKGND:
4707 *lResult = 1;
4708 break;
4709
4710 case WM_DESTROY:
4711 /* zero out global pointer in case resident popup window was destroyed. */
4712 if (pPopupMenu)
4713 {
4714 if (UserHMGetHandle(Wnd) == top_popup)
4715 {
4716 top_popup = NULL;
4717 top_popup_hmenu = NULL;
4718 }
4719 }
4720 else
4721 {
4722 ERR("No Window Pop Up!\n");
4723 }
4724 break;
4725
4726 case WM_NCDESTROY:
4727 {
4728 if (pPopupMenu->spmenu)
4729 {
4730 IntReleaseMenuObject(pPopupMenu->spmenu);
4731 }
4732 DesktopHeapFree(Wnd->head.rpdesk, pPopupMenu );
4733 ((PMENUWND)Wnd)->ppopupmenu = 0;
4734 Wnd->fnid = FNID_DESTROY;
4735 break;
4736 }
4737
4738 case MM_SETMENUHANDLE: // wine'isms
4739 case MN_SETHMENU:
4740 {
4741 PMENU pmenu = UserGetMenuObject((HMENU)wParam);
4742 if (!pmenu)
4743 {
4744 ERR("Bad Menu Handle\n");
4745 break;
4746 }
4747 UserReferenceObject(pmenu);
4748 if (pPopupMenu->spmenu)
4749 {
4750 IntReleaseMenuObject(pPopupMenu->spmenu);
4751 }
4752 pPopupMenu->spmenu = pmenu;
4753 break;
4754 }
4755
4756 case MM_GETMENUHANDLE: // wine'isms
4757 case MN_GETHMENU:
4758 *lResult = (LRESULT)(pPopupMenu ? (pPopupMenu->spmenu ? UserHMGetHandle(pPopupMenu->spmenu) : NULL) : NULL);
4759 break;
4760
4761 default:
4762 if (Message > MN_GETHMENU && Message < MN_GETHMENU+19)
4763 {
4764 ERR("Someone is passing unknown menu messages %d\n",Message);
4765 }
4766 TRACE("PMWP to IDWP %d\n",Message);
4767 *lResult = IntDefWindowProc(Wnd, Message, wParam, lParam, FALSE);
4768 break;
4769 }
4770
4771 return TRUE;
4772 }
4773
4774 BOOL FASTCALL
4775 IntHiliteMenuItem(PWND WindowObject,
4776 PMENU MenuObject,
4777 UINT uItemHilite,
4778 UINT uHilite)
4779 {
4780 PITEM MenuItem;
4781 UINT uItem = uItemHilite;
4782
4783 if (!(MenuItem = MENU_FindItem( &MenuObject, &uItem, uHilite ))) return TRUE;
4784
4785 if (uHilite & MF_HILITE)
4786 {
4787 MenuItem->fState |= MF_HILITE;
4788 }
4789 else
4790 {
4791 MenuItem->fState &= ~MF_HILITE;
4792 }
4793 if (MenuObject->iItem == uItemHilite) return TRUE;
4794 MENU_HideSubPopups( WindowObject, MenuObject, FALSE, 0 );
4795 MENU_SelectItem( WindowObject, MenuObject, uItemHilite, TRUE, 0 );
4796
4797 return TRUE; // Always returns true!!!!
4798 }
4799
4800 BOOLEAN APIENTRY
4801 intGetTitleBarInfo(PWND pWindowObject, PTITLEBARINFO bti)
4802 {
4803
4804 DWORD dwStyle = 0;
4805 DWORD dwExStyle = 0;
4806 BOOLEAN retValue = TRUE;
4807
4808 if (bti->cbSize == sizeof(TITLEBARINFO))
4809 {
4810 RtlZeroMemory(&bti->rgstate[0],sizeof(DWORD)*(CCHILDREN_TITLEBAR+1));
4811
4812 bti->rgstate[0] = STATE_SYSTEM_FOCUSABLE;
4813
4814 dwStyle = pWindowObject->style;
4815 dwExStyle = pWindowObject->ExStyle;
4816
4817 bti->rcTitleBar.top = 0;
4818 bti->rcTitleBar.left = 0;
4819 bti->rcTitleBar.right = pWindowObject->rcWindow.right - pWindowObject->rcWindow.left;
4820 bti->rcTitleBar.bottom = pWindowObject->rcWindow.bottom - pWindowObject->rcWindow.top;
4821
4822 /* Is it iconiced ? */
4823 if ((dwStyle & WS_ICONIC)!=WS_ICONIC)
4824 {
4825 /* Remove frame from rectangle */
4826 if (HAS_THICKFRAME( dwStyle, dwExStyle ))
4827 {
4828 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXFRAME) and UserGetSystemMetrics(SM_CYFRAME) */
4829 RECTL_vInflateRect( &bti->rcTitleBar, -UserGetSystemMetrics(SM_CXFRAME), -UserGetSystemMetrics(SM_CYFRAME) );
4830 }
4831 else if (HAS_DLGFRAME( dwStyle, dwExStyle ))
4832 {
4833 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXDLGFRAME) and UserGetSystemMetrics(SM_CYDLGFRAME) */
4834 RECTL_vInflateRect( &bti->rcTitleBar, -UserGetSystemMetrics(SM_CXDLGFRAME), -UserGetSystemMetrics(SM_CYDLGFRAME));
4835 }
4836 else if (HAS_THINFRAME( dwStyle, dwExStyle))
4837 {
4838 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXBORDER) and UserGetSystemMetrics(SM_CYBORDER) */
4839 RECTL_vInflateRect( &bti->rcTitleBar, -UserGetSystemMetrics(SM_CXBORDER), -UserGetSystemMetrics(SM_CYBORDER) );
4840 }
4841
4842 /* We have additional border information if the window
4843 * is a child (but not an MDI child) */
4844 if ( (dwStyle & WS_CHILD) &&
4845 ((dwExStyle & WS_EX_MDICHILD) == 0 ) )
4846 {
4847 if (dwExStyle & WS_EX_CLIENTEDGE)
4848 {
4849 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXEDGE) and UserGetSystemMetrics(SM_CYEDGE) */
4850 RECTL_vInflateRect (&bti->rcTitleBar, -UserGetSystemMetrics(SM_CXEDGE), -UserGetSystemMetrics(SM_CYEDGE));
4851 }
4852
4853 if (dwExStyle & WS_EX_STATICEDGE)
4854 {
4855 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXBORDER) and UserGetSystemMetrics(SM_CYBORDER) */
4856 RECTL_vInflateRect (&bti->rcTitleBar, -UserGetSystemMetrics(SM_CXBORDER), -UserGetSystemMetrics(SM_CYBORDER));
4857 }
4858 }
4859 }
4860
4861 bti->rcTitleBar.top += pWindowObject->rcWindow.top;
4862 bti->rcTitleBar.left += pWindowObject->rcWindow.left;
4863 bti->rcTitleBar.right += pWindowObject->rcWindow.left;
4864
4865 bti->rcTitleBar.bottom = bti->rcTitleBar.top;
4866 if (dwExStyle & WS_EX_TOOLWINDOW)
4867 {
4868 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CYSMCAPTION) */
4869 bti->rcTitleBar.bottom += UserGetSystemMetrics(SM_CYSMCAPTION);
4870 }
4871 else
4872 {
4873 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CYCAPTION) and UserGetSystemMetrics(SM_CXSIZE) */
4874 bti->rcTitleBar.bottom += UserGetSystemMetrics(SM_CYCAPTION);
4875 bti->rcTitleBar.left += UserGetSystemMetrics(SM_CXSIZE);
4876 }
4877
4878 if (dwStyle & WS_CAPTION)
4879 {
4880 bti->rgstate[1] = STATE_SYSTEM_INVISIBLE;
4881 if (dwStyle & WS_SYSMENU)
4882 {
4883 if (!(dwStyle & (WS_MINIMIZEBOX|WS_MAXIMIZEBOX)))
4884 {
4885 bti->rgstate[2] = STATE_SYSTEM_INVISIBLE;
4886 bti->rgstate[3] = STATE_SYSTEM_INVISIBLE;
4887 }
4888 else
4889 {
4890 if (!(dwStyle & WS_MINIMIZEBOX))
4891 {
4892 bti->rgstate[2] = STATE_SYSTEM_UNAVAILABLE;
4893 }
4894 if (!(dwStyle & WS_MAXIMIZEBOX))
4895 {
4896 bti->rgstate[3] = STATE_SYSTEM_UNAVAILABLE;
4897 }
4898 }
4899
4900 if (!(dwExStyle & WS_EX_CONTEXTHELP))
4901 {
4902 bti->rgstate[4] = STATE_SYSTEM_INVISIBLE;
4903 }
4904 if (pWindowObject->pcls->style & CS_NOCLOSE)
4905 {
4906 bti->rgstate[5] = STATE_SYSTEM_UNAVAILABLE;
4907 }
4908 }
4909 else
4910 {
4911 bti->rgstate[2] = STATE_SYSTEM_INVISIBLE;
4912 bti->rgstate[3] = STATE_SYSTEM_INVISIBLE;
4913 bti->rgstate[4] = STATE_SYSTEM_INVISIBLE;
4914 bti->rgstate[5] = STATE_SYSTEM_INVISIBLE;
4915 }
4916 }
4917 else
4918 {
4919 bti->rgstate[0] |= STATE_SYSTEM_INVISIBLE;
4920 }
4921 }
4922 else
4923 {
4924 EngSetLastError(ERROR_INVALID_PARAMETER);
4925 retValue = FALSE;
4926 }
4927
4928 return retValue;
4929 }
4930
4931 DWORD FASTCALL
4932 UserInsertMenuItem(
4933 PMENU Menu,
4934 UINT uItem,
4935 BOOL fByPosition,
4936 LPCMENUITEMINFOW UnsafeItemInfo,
4937 PUNICODE_STRING lpstr)
4938 {
4939 NTSTATUS Status;
4940 ROSMENUITEMINFO ItemInfo;
4941
4942 /* Try to copy the whole MENUITEMINFOW structure */
4943 Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, sizeof(MENUITEMINFOW));
4944 if (NT_SUCCESS(Status))
4945 {
4946 if (sizeof(MENUITEMINFOW) != ItemInfo.cbSize
4947 && FIELD_OFFSET(MENUITEMINFOW, hbmpItem) != ItemInfo.cbSize)
4948 {
4949 EngSetLastError(ERROR_INVALID_PARAMETER);
4950 return FALSE;
4951 }
4952 return IntInsertMenuItem(Menu, uItem, fByPosition, &ItemInfo, lpstr);
4953 }
4954
4955 /* Try to copy without last field (not present in older versions) */
4956 Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, FIELD_OFFSET(MENUITEMINFOW, hbmpItem));
4957 if (NT_SUCCESS(Status))
4958 {
4959 if (FIELD_OFFSET(MENUITEMINFOW, hbmpItem) != ItemInfo.cbSize)
4960 {
4961 EngSetLastError(ERROR_INVALID_PARAMETER);
4962 return FALSE;
4963 }
4964 ItemInfo.hbmpItem = (HBITMAP)0;
4965 return IntInsertMenuItem(Menu, uItem, fByPosition, &ItemInfo, lpstr);
4966 }
4967
4968 SetLastNtError(Status);
4969 return FALSE;
4970 }
4971
4972 UINT FASTCALL IntGetMenuState( HMENU hMenu, UINT uId, UINT uFlags)
4973 {
4974 PMENU MenuObject;
4975 PITEM pItem;
4976
4977 if (!(MenuObject = UserGetMenuObject(hMenu)))
4978 {
4979 return (UINT)-1;
4980 }
4981
4982 if (!(pItem = MENU_FindItem( &MenuObject, &uId, uFlags ))) return -1;
4983
4984 if (pItem->spSubMenu)
4985 {
4986 return (pItem->spSubMenu->cItems << 8) | ((pItem->fState|pItem->fType|MF_POPUP) & 0xff);
4987 }
4988 else
4989 return (pItem->fType | pItem->fState);
4990 }
4991
4992 HMENU FASTCALL IntGetSubMenu( HMENU hMenu, int nPos)
4993 {
4994 PMENU MenuObject;
4995 PITEM pItem;
4996
4997 if (!(MenuObject = UserGetMenuObject(hMenu)))
4998 {
4999 return NULL;
5000 }
5001
5002 if (!(pItem = MENU_FindItem( &MenuObject, (UINT*)&nPos, MF_BYPOSITION ))) return NULL;
5003
5004 if (pItem->spSubMenu)
5005 {
5006 HMENU hsubmenu = UserHMGetHandle(pItem->spSubMenu);
5007 return hsubmenu;
5008 }
5009 return NULL;
5010 }
5011
5012 UINT FASTCALL IntFindSubMenu(HMENU *hMenu, HMENU hSubTarget )
5013 {
5014 PMENU menu, pSubTarget;
5015 UINT Pos;
5016 if (((*hMenu)==(HMENU)0xffff) ||(!(menu = UserGetMenuObject(*hMenu))))
5017 return NO_SELECTED_ITEM;
5018
5019 pSubTarget = UserGetMenuObject(hSubTarget);
5020
5021 Pos = MENU_FindSubMenu(&menu, pSubTarget );
5022
5023 *hMenu = (menu ? UserHMGetHandle(menu) : NULL);
5024
5025 return Pos;
5026 }
5027
5028
5029 HMENU FASTCALL UserCreateMenu(PDESKTOP Desktop, BOOL PopupMenu)
5030 {
5031 PWINSTATION_OBJECT WinStaObject;
5032 HANDLE Handle;
5033 PMENU Menu;
5034 NTSTATUS Status;
5035 PEPROCESS CurrentProcess = PsGetCurrentProcess();
5036
5037 if (gpepCSRSS != CurrentProcess)
5038 {
5039 /*
5040 * gpepCSRSS does not have a Win32WindowStation
5041 */
5042
5043 Status = IntValidateWindowStationHandle(CurrentProcess->Win32WindowStation,
5044 UserMode,
5045 0,
5046 &WinStaObject,
5047 0);
5048
5049 if (!NT_SUCCESS(Status))
5050 {
5051 ERR("Validation of window station handle (%p) failed\n",
5052 CurrentProcess->Win32WindowStation);
5053 SetLastNtError(Status);
5054 return (HMENU)0;
5055 }
5056 Menu = IntCreateMenu(&Handle, !PopupMenu, Desktop, GetW32ProcessInfo());
5057 if (Menu && Menu->head.rpdesk->rpwinstaParent != WinStaObject)
5058 {
5059 ERR("Desktop Window Station does not match Process one!\n");
5060 }
5061 ObDereferenceObject(WinStaObject);
5062 }
5063 else
5064 {
5065 Menu = IntCreateMenu(&Handle, !PopupMenu, GetW32ThreadInfo()->rpdesk, GetW32ProcessInfo());
5066 }
5067
5068 if (Menu) UserDereferenceObject(Menu);
5069 return (HMENU)Handle;
5070 }
5071
5072 BOOL FASTCALL
5073 IntMenuItemInfo(
5074 PMENU Menu,
5075 UINT Item,
5076 BOOL ByPosition,
5077 PROSMENUITEMINFO ItemInfo,
5078 BOOL SetOrGet,
5079 PUNICODE_STRING lpstr)
5080 {
5081 PITEM MenuItem;
5082 BOOL Ret;
5083
5084 if (!(MenuItem = MENU_FindItem( &Menu, &Item, (ByPosition ? MF_BYPOSITION : MF_BYCOMMAND) )))
5085 {
5086 EngSetLastError(ERROR_MENU_ITEM_NOT_FOUND);
5087 return( FALSE);
5088 }
5089 if (SetOrGet)
5090 {
5091 Ret = IntSetMenuItemInfo(Menu, MenuItem, ItemInfo, lpstr);
5092 }
5093 else
5094 {
5095 Ret = IntGetMenuItemInfo(Menu, MenuItem, ItemInfo);
5096 }
5097 return( Ret);
5098 }
5099
5100 BOOL FASTCALL
5101 UserMenuItemInfo(
5102 PMENU Menu,
5103 UINT Item,
5104 BOOL ByPosition,
5105 PROSMENUITEMINFO UnsafeItemInfo,
5106 BOOL SetOrGet,
5107 PUNICODE_STRING lpstr)
5108 {
5109 PITEM MenuItem;
5110 ROSMENUITEMINFO ItemInfo;
5111 NTSTATUS Status;
5112 UINT Size;
5113 BOOL Ret;
5114
5115 Status = MmCopyFromCaller(&Size, &UnsafeItemInfo->cbSize, sizeof(UINT));
5116 if (! NT_SUCCESS(Status))
5117 {
5118 SetLastNtError(Status);
5119 return( FALSE);
5120 }
5121 if ( Size != sizeof(MENUITEMINFOW) &&
5122 Size != FIELD_OFFSET(MENUITEMINFOW, hbmpItem) &&
5123 Size != sizeof(ROSMENUITEMINFO) )
5124 {
5125 EngSetLastError(ERROR_INVALID_PARAMETER);
5126 return( FALSE);
5127 }
5128 Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, Size);
5129 if (! NT_SUCCESS(Status))
5130 {
5131 SetLastNtError(Status);
5132 return( FALSE);
5133 }
5134 /* If this is a pre-0x0500 _WIN32_WINNT MENUITEMINFOW, you can't
5135 set/get hbmpItem */
5136 if (FIELD_OFFSET(MENUITEMINFOW, hbmpItem) == Size
5137 && 0 != (ItemInfo.fMask & MIIM_BITMAP))
5138 {
5139 EngSetLastError(ERROR_INVALID_PARAMETER);
5140 return( FALSE);
5141 }
5142
5143 if (!(MenuItem = MENU_FindItem( &Menu, &Item, (ByPosition ? MF_BYPOSITION : MF_BYCOMMAND) )))
5144 {
5145 /* workaround for Word 95: pretend that SC_TASKLIST item exists. */
5146 if ( SetOrGet && Item == SC_TASKLIST && !ByPosition )
5147 return TRUE;
5148
5149 EngSetLastError(ERROR_MENU_ITEM_NOT_FOUND);
5150 return( FALSE);
5151 }
5152
5153 if (SetOrGet)
5154 {
5155 Ret = IntSetMenuItemInfo(Menu, MenuItem, &ItemInfo, lpstr);
5156 }
5157 else
5158 {
5159 Ret = IntGetMenuItemInfo(Menu, MenuItem, &ItemInfo);
5160 if (Ret)
5161 {
5162 Status = MmCopyToCaller(UnsafeItemInfo, &ItemInfo, Size);
5163 if (! NT_SUCCESS(Status))
5164 {
5165 SetLastNtError(Status);
5166 return( FALSE);
5167 }
5168 }
5169 }
5170
5171 return( Ret);
5172 }
5173
5174 BOOL FASTCALL
5175 UserMenuInfo(
5176 PMENU Menu,
5177 PROSMENUINFO UnsafeMenuInfo,
5178 BOOL SetOrGet)
5179 {
5180 BOOL Res;
5181 DWORD Size;
5182 NTSTATUS Status;
5183 ROSMENUINFO MenuInfo;
5184
5185 Status = MmCopyFromCaller(&Size, &UnsafeMenuInfo->cbSize, sizeof(DWORD));
5186 if (! NT_SUCCESS(Status))
5187 {
5188 SetLastNtError(Status);
5189 return( FALSE);
5190 }
5191 if ( Size < sizeof(MENUINFO) || Size > sizeof(ROSMENUINFO) )
5192 {
5193 EngSetLastError(ERROR_INVALID_PARAMETER);
5194 return( FALSE);
5195 }
5196 Status = MmCopyFromCaller(&MenuInfo, UnsafeMenuInfo, Size);
5197 if (! NT_SUCCESS(Status))
5198 {
5199 SetLastNtError(Status);
5200 return( FALSE);
5201 }
5202
5203 if(SetOrGet)
5204 {
5205 /* Set MenuInfo */
5206 Res = IntSetMenuInfo(Menu, &MenuInfo);
5207 }
5208 else
5209 {
5210 /* Get MenuInfo */
5211 Res = IntGetMenuInfo(Menu, &MenuInfo);
5212 if (Res)
5213 {
5214 Status = MmCopyToCaller(UnsafeMenuInfo, &MenuInfo, Size);
5215 if (! NT_SUCCESS(Status))
5216 {
5217 SetLastNtError(Status);
5218 return( FALSE);
5219 }
5220 }
5221 }
5222
5223 return( Res);
5224 }
5225
5226 BOOL FASTCALL
5227 IntGetMenuItemRect(
5228 PWND pWnd,
5229 PMENU Menu,
5230 UINT uItem,
5231 PRECTL Rect)
5232 {
5233 LONG XMove, YMove;
5234 PITEM MenuItem;
5235 UINT I = uItem;
5236
5237 if ((MenuItem = MENU_FindItem (&Menu, &I, MF_BYPOSITION)))
5238 {
5239 Rect->left = MenuItem->xItem;
5240 Rect->top = MenuItem->yItem;
5241 Rect->right = MenuItem->cxItem; // Do this for now......
5242 Rect->bottom = MenuItem->cyItem;
5243 }
5244 else
5245 {
5246 ERR("Failed Item Lookup! %u\n", uItem);
5247 return FALSE;
5248 }
5249
5250 if (!pWnd)
5251 {
5252 HWND hWnd = Menu->hWnd;
5253 if (!(pWnd = UserGetWindowObject(hWnd))) return FALSE;
5254 }
5255
5256 if (Menu->fFlags & MNF_POPUP)
5257 {
5258 XMove = pWnd->rcClient.left;
5259 YMove = pWnd->rcClient.top;
5260 }
5261 else
5262 {
5263 XMove = pWnd->rcWindow.left;
5264 YMove = pWnd->rcWindow.top;
5265 }
5266
5267 Rect->left += XMove;
5268 Rect->top += YMove;
5269 Rect->right += XMove;
5270 Rect->bottom += YMove;
5271
5272 return TRUE;
5273 }
5274
5275 PMENU FASTCALL MENU_GetSystemMenu(PWND Window, PMENU Popup)
5276 {
5277 PMENU Menu, NewMenu = NULL, SysMenu = NULL;
5278 HMENU hSysMenu, hNewMenu = NULL;
5279 ROSMENUITEMINFO ItemInfoSet = {0};
5280 ROSMENUITEMINFO ItemInfo = {0};
5281 UNICODE_STRING MenuName;
5282
5283 hSysMenu = UserCreateMenu(Window->head.rpdesk, FALSE);
5284 if (NULL == hSysMenu)
5285 {
5286 return NULL;
5287 }
5288 SysMenu = UserGetMenuObject(hSysMenu);
5289 if (NULL == SysMenu)
5290 {
5291 UserDestroyMenu(hSysMenu);
5292 return NULL;
5293 }
5294
5295 SysMenu->fFlags |= MNF_SYSMENU;
5296 SysMenu->hWnd = UserHMGetHandle(Window);
5297
5298 if (!Popup)
5299 {
5300 //hNewMenu = co_IntLoadSysMenuTemplate();
5301 if ( Window->ExStyle & WS_EX_MDICHILD )
5302 {
5303 RtlInitUnicodeString( &MenuName, L"SYSMENUMDI");
5304 hNewMenu = co_IntCallLoadMenu( hModClient, &MenuName);
5305 }
5306 else
5307 {
5308 RtlInitUnicodeString( &MenuName, L"SYSMENU");
5309 hNewMenu = co_IntCallLoadMenu( hModClient, &MenuName);
5310 //ERR("%wZ\n",&MenuName);
5311 }
5312 if (!hNewMenu)
5313 {
5314 ERR("No Menu!!\n");
5315 IntDestroyMenuObject(SysMenu, FALSE);
5316 return NULL;
5317 }
5318 Menu = UserGetMenuObject(hNewMenu);
5319 if (!Menu)
5320 {
5321 IntDestroyMenuObject(SysMenu, FALSE);
5322 return NULL;
5323 }
5324
5325 // Do the rest in here.
5326
5327 Menu->fFlags |= MNS_CHECKORBMP | MNF_SYSMENU | MNF_POPUP;
5328
5329 ItemInfoSet.cbSize = sizeof( MENUITEMINFOW);
5330 ItemInfoSet.fMask = MIIM_BITMAP;
5331 ItemInfoSet.hbmpItem = HBMMENU_POPUP_CLOSE;
5332 IntMenuItemInfo(Menu, SC_CLOSE, FALSE, &ItemInfoSet, TRUE, NULL);
5333 ItemInfoSet.hbmpItem = HBMMENU_POPUP_RESTORE;
5334 IntMenuItemInfo(Menu, SC_RESTORE, FALSE, &ItemInfoSet, TRUE, NULL);
5335 ItemInfoSet.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
5336 IntMenuItemInfo(Menu, SC_MAXIMIZE, FALSE, &ItemInfoSet, TRUE, NULL);
5337 ItemInfoSet.hbmpItem = HBMMENU_POPUP_MINIMIZE;
5338 IntMenuItemInfo(Menu, SC_MINIMIZE, FALSE, &ItemInfoSet, TRUE, NULL);
5339
5340 NewMenu = IntCloneMenu(Menu);
5341 if (NewMenu == NULL)
5342 {
5343 IntDestroyMenuObject(Menu, FALSE);
5344 IntDestroyMenuObject(SysMenu, FALSE);
5345 return NULL;
5346 }
5347
5348 IntReleaseMenuObject(NewMenu);
5349 UserSetMenuDefaultItem(NewMenu, SC_CLOSE, FALSE);
5350
5351 IntDestroyMenuObject(Menu, FALSE);
5352 }
5353 else
5354 {
5355 NewMenu = Popup;
5356 }
5357 if (NewMenu)
5358 {
5359 NewMenu->fFlags |= MNF_SYSMENU | MNF_POPUP;
5360
5361 if (Window->pcls->style & CS_NOCLOSE)
5362 IntRemoveMenuItem(NewMenu, SC_CLOSE, MF_BYCOMMAND, TRUE);
5363
5364 ItemInfo.cbSize = sizeof(MENUITEMINFOW);
5365 ItemInfo.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_SUBMENU;
5366 ItemInfo.fType = MF_POPUP;
5367 ItemInfo.fState = MFS_ENABLED;
5368 ItemInfo.dwTypeData = NULL;
5369 ItemInfo.cch = 0;
5370 ItemInfo.hSubMenu = UserHMGetHandle(NewMenu);
5371 IntInsertMenuItem(SysMenu, (UINT) -1, TRUE, &ItemInfo, NULL);
5372
5373 return SysMenu;
5374 }
5375 ERR("failed to load system menu!\n");
5376 return NULL;
5377 }
5378
5379 PMENU FASTCALL
5380 IntGetSystemMenu(PWND Window, BOOL bRevert)
5381 {
5382 PMENU Menu;
5383
5384 if (bRevert)
5385 {
5386 if (Window->SystemMenu)
5387 {
5388 Menu = UserGetMenuObject(Window->SystemMenu);
5389 if (Menu && !(Menu->fFlags & MNF_SYSDESKMN))
5390 {
5391 IntDestroyMenuObject(Menu, TRUE);
5392 Window->SystemMenu = NULL;
5393 }
5394 }
5395 }
5396 else
5397 {
5398 Menu = Window->SystemMenu ? UserGetMenuObject(Window->SystemMenu) : NULL;
5399 if ((!Menu || Menu->fFlags & MNF_SYSDESKMN) && Window->style & WS_SYSMENU)
5400 {
5401 Menu = MENU_GetSystemMenu(Window, NULL);
5402 Window->SystemMenu = Menu ? UserHMGetHandle(Menu) : NULL;
5403 }
5404 }
5405
5406 if (Window->SystemMenu)
5407 {
5408 HMENU hMenu = IntGetSubMenu( Window->SystemMenu, 0);
5409 /* Store the dummy sysmenu handle to facilitate the refresh */
5410 /* of the close button if the SC_CLOSE item change */
5411 Menu = UserGetMenuObject(hMenu);
5412 if (Menu)
5413 {
5414 Menu->spwndNotify = Window;
5415 Menu->fFlags |= MNF_SYSSUBMENU;
5416 }
5417 return Menu;
5418 }
5419 return NULL;
5420 }
5421
5422 BOOL FASTCALL
5423 IntSetSystemMenu(PWND Window, PMENU Menu)
5424 {
5425 PMENU OldMenu;
5426
5427 if (!(Window->style & WS_SYSMENU)) return FALSE;
5428
5429 if (Window->SystemMenu)
5430 {
5431 OldMenu = UserGetMenuObject(Window->SystemMenu);
5432 if (OldMenu)
5433 {
5434 OldMenu->fFlags &= ~MNF_SYSMENU;
5435 IntDestroyMenuObject(OldMenu, TRUE);
5436 }
5437 }
5438
5439 OldMenu = MENU_GetSystemMenu(Window, Menu);
5440 if (OldMenu)
5441 { // Use spmenuSys too!
5442 Window->SystemMenu = UserHMGetHandle(OldMenu);
5443 }
5444 else
5445 Window->SystemMenu = NULL;
5446
5447 if (Menu && Window != Menu->spwndNotify)
5448 {
5449 Menu->spwndNotify = Window;
5450 }
5451
5452 return TRUE;
5453 }
5454
5455 BOOL FASTCALL
5456 IntSetMenu(
5457 PWND Wnd,
5458 HMENU Menu,
5459 BOOL *Changed)
5460 {
5461 PMENU OldMenu, NewMenu = NULL;
5462
5463 if ((Wnd->style & (WS_CHILD | WS_POPUP)) == WS_CHILD)
5464 {
5465 ERR("SetMenu: Window is a Child 0x%p!\n",UserHMGetHandle(Wnd));
5466 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5467 return FALSE;
5468 }
5469
5470 *Changed = (UlongToHandle(Wnd->IDMenu) != Menu);
5471 if (! *Changed)
5472 {
5473 return TRUE;
5474 }
5475
5476 if (Wnd->IDMenu)
5477 {
5478 OldMenu = IntGetMenuObject(UlongToHandle(Wnd->IDMenu));
5479 ASSERT(NULL == OldMenu || OldMenu->hWnd == UserHMGetHandle(Wnd));
5480 }
5481 else
5482 {
5483 OldMenu = NULL;
5484 }
5485
5486 if (NULL != Menu)
5487 {
5488 NewMenu = IntGetMenuObject(Menu);
5489 if (NULL == NewMenu)
5490 {
5491 if (NULL != OldMenu)
5492 {
5493 IntReleaseMenuObject(OldMenu);
5494 }
5495 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5496 return FALSE;
5497 }
5498 if (NULL != NewMenu->hWnd)
5499 {
5500 /* Can't use the same menu for two windows */
5501 if (NULL != OldMenu)
5502 {
5503 IntReleaseMenuObject(OldMenu);
5504 }
5505 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5506 return FALSE;
5507 }
5508
5509 }
5510
5511 Wnd->IDMenu = (UINT_PTR) Menu;
5512 if (NULL != NewMenu)
5513 {
5514 NewMenu->hWnd = UserHMGetHandle(Wnd);
5515 IntReleaseMenuObject(NewMenu);
5516 }
5517 if (NULL != OldMenu)
5518 {
5519 OldMenu->hWnd = NULL;
5520 IntReleaseMenuObject(OldMenu);
5521 }
5522
5523 return TRUE;
5524 }
5525
5526
5527 /* FUNCTIONS *****************************************************************/
5528
5529 /*
5530 * @implemented
5531 */
5532 /* http://www.cyber-ta.org/releases/malware-analysis/public/SOURCES/b47155634ccb2c30630da7e3666d3d07/b47155634ccb2c30630da7e3666d3d07.trace.html#NtUserGetIconSize */
5533 DWORD
5534 APIENTRY
5535 NtUserCalcMenuBar(
5536 HWND hwnd,
5537 DWORD leftBorder,
5538 DWORD rightBorder,
5539 DWORD top,
5540 LPRECT prc )
5541 {
5542 HDC hdc;
5543 PWND Window;
5544 RECT Rect;
5545 DWORD ret;
5546
5547 UserEnterExclusive();
5548
5549 if(!(Window = UserGetWindowObject(hwnd)))
5550 {
5551 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5552 UserLeave();
5553 return 0;
5554 }
5555
5556 hdc = UserGetDCEx(NULL, NULL, DCX_CACHE);
5557 if (!hdc)
5558 {
5559 UserLeave();
5560 return 0;
5561 }
5562
5563 Rect.left = leftBorder;
5564 Rect.right = Window->rcWindow.right - Window->rcWindow.left - rightBorder;
5565 Rect.top = top;
5566 Rect.bottom = 0;
5567
5568 ret = MENU_DrawMenuBar(hdc, &Rect, Window, TRUE);
5569
5570 UserReleaseDC( 0, hdc, FALSE );
5571
5572 UserLeave();
5573
5574 return ret;
5575 }
5576
5577 /*
5578 * @implemented
5579 */
5580 DWORD APIENTRY
5581 NtUserCheckMenuItem(
5582 HMENU hMenu,
5583 UINT uIDCheckItem,
5584 UINT uCheck)
5585 {
5586 PMENU Menu;
5587 DECLARE_RETURN(DWORD);
5588
5589 TRACE("Enter NtUserCheckMenuItem\n");
5590 UserEnterExclusive();
5591
5592 if(!(Menu = UserGetMenuObject(hMenu)))
5593 {
5594 RETURN( (DWORD)-1);
5595 }
5596
5597 RETURN( IntCheckMenuItem(Menu, uIDCheckItem, uCheck));
5598
5599 CLEANUP:
5600 TRACE("Leave NtUserCheckMenuItem, ret=%lu\n",_ret_);
5601 UserLeave();
5602 END_CLEANUP;
5603 }
5604
5605 /*
5606 * @implemented
5607 */
5608 BOOL APIENTRY
5609 NtUserDeleteMenu(
5610 HMENU hMenu,
5611 UINT uPosition,
5612 UINT uFlags)
5613 {
5614 PMENU Menu;
5615 DECLARE_RETURN(BOOL);
5616
5617 TRACE("Enter NtUserDeleteMenu\n");
5618 UserEnterExclusive();
5619
5620 if(!(Menu = UserGetMenuObject(hMenu)))
5621 {
5622 RETURN( FALSE);
5623 }
5624
5625 RETURN( IntRemoveMenuItem(Menu, uPosition, uFlags, TRUE));
5626
5627 CLEANUP:
5628 TRACE("Leave NtUserDeleteMenu, ret=%i\n",_ret_);
5629 UserLeave();
5630 END_CLEANUP;
5631 }
5632
5633 /*
5634 * NtUserGetSystemMenu
5635 *
5636 * The NtUserGetSystemMenu function allows the application to access the
5637 * window menu (also known as the system menu or the control menu) for
5638 * copying and modifying.
5639 *
5640 * Parameters
5641 * hWnd
5642 * Handle to the window that will own a copy of the window menu.
5643 * bRevert
5644 * Specifies the action to be taken. If this parameter is FALSE,
5645 * NtUserGetSystemMenu returns a handle to the copy of the window menu
5646 * currently in use. The copy is initially identical to the window menu
5647 * but it can be modified.
5648 * If this parameter is TRUE, GetSystemMenu resets the window menu back
5649 * to the default state. The previous window menu, if any, is destroyed.
5650 *
5651 * Return Value
5652 * If the bRevert parameter is FALSE, the return value is a handle to a
5653 * copy of the window menu. If the bRevert parameter is TRUE, the return
5654 * value is NULL.
5655 *
5656 * Status
5657 * @implemented
5658 */
5659
5660 HMENU APIENTRY
5661 NtUserGetSystemMenu(HWND hWnd, BOOL bRevert)
5662 {
5663 PWND Window;
5664 PMENU Menu;
5665 DECLARE_RETURN(HMENU);
5666
5667 TRACE("Enter NtUserGetSystemMenu\n");
5668 UserEnterExclusive();
5669
5670 if (!(Window = UserGetWindowObject(hWnd)))
5671 {
5672 RETURN(NULL);
5673 }
5674
5675 if (!(Menu = IntGetSystemMenu(Window, bRevert)))
5676 {
5677 RETURN(NULL);
5678 }
5679
5680 RETURN(Menu->head.h);
5681
5682 CLEANUP:
5683 TRACE("Leave NtUserGetSystemMenu, ret=%p\n", _ret_);
5684 UserLeave();
5685 END_CLEANUP;
5686 }
5687
5688 /*
5689 * NtUserSetSystemMenu
5690 *
5691 * Status
5692 * @implemented
5693 */
5694
5695 BOOL APIENTRY
5696 NtUserSetSystemMenu(HWND hWnd, HMENU hMenu)
5697 {
5698 BOOL Result = FALSE;
5699 PWND Window;
5700 PMENU Menu;
5701 DECLARE_RETURN(BOOL);
5702
5703 TRACE("Enter NtUserSetSystemMenu\n");
5704 UserEnterExclusive();
5705
5706 if (!(Window = UserGetWindowObject(hWnd)))
5707 {
5708 RETURN( FALSE);
5709 }
5710
5711 if (hMenu)
5712 {
5713 /*
5714 * Assign new menu handle and Up the Lock Count.
5715 */
5716 if (!(Menu = IntGetMenuObject(hMenu)))
5717 {
5718 RETURN( FALSE);
5719 }
5720
5721 Result = IntSetSystemMenu(Window, Menu);
5722 }
5723 else
5724 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5725
5726 RETURN( Result);
5727
5728 CLEANUP:
5729 TRACE("Leave NtUserSetSystemMenu, ret=%i\n",_ret_);
5730 UserLeave();
5731 END_CLEANUP;
5732 }
5733
5734 /*
5735 * @implemented
5736 */
5737 BOOLEAN APIENTRY
5738 NtUserGetTitleBarInfo(
5739 HWND hwnd,
5740 PTITLEBARINFO bti)
5741 {
5742 PWND WindowObject;
5743 TITLEBARINFO bartitleinfo;
5744 DECLARE_RETURN(BOOLEAN);
5745 BOOLEAN retValue = TRUE;
5746
5747 TRACE("Enter NtUserGetTitleBarInfo\n");
5748 UserEnterExclusive();
5749
5750 /* Vaildate the windows handle */
5751 if (!(WindowObject = UserGetWindowObject(hwnd)))
5752 {
5753 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5754 retValue = FALSE;
5755 }
5756
5757 _SEH2_TRY
5758 {
5759 /* Copy our usermode buffer bti to local buffer bartitleinfo */
5760 ProbeForRead(bti, sizeof(TITLEBARINFO), 1);
5761 RtlCopyMemory(&bartitleinfo, bti, sizeof(TITLEBARINFO));
5762 }
5763 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5764 {
5765 /* Fail copy the data */
5766 EngSetLastError(ERROR_INVALID_PARAMETER);
5767 retValue = FALSE;
5768 }
5769 _SEH2_END
5770
5771 /* Get the tile bar info */
5772 if (retValue)
5773 {
5774 retValue = intGetTitleBarInfo(WindowObject, &bartitleinfo);
5775 if (retValue)
5776 {
5777 _SEH2_TRY
5778 {
5779 /* Copy our buffer to user mode buffer bti */
5780 ProbeForWrite(bti, sizeof(TITLEBARINFO), 1);
5781 RtlCopyMemory(bti, &bartitleinfo, sizeof(TITLEBARINFO));
5782 }
5783 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5784 {
5785 /* Fail copy the data */
5786 EngSetLastError(ERROR_INVALID_PARAMETER);
5787 retValue = FALSE;
5788 }
5789 _SEH2_END
5790 }
5791 }
5792
5793 RETURN( retValue );
5794
5795 CLEANUP:
5796 TRACE("Leave NtUserGetTitleBarInfo, ret=%u\n",_ret_);
5797 UserLeave();
5798 END_CLEANUP;
5799 }
5800
5801 /*
5802 * @implemented
5803 */
5804 BOOL FASTCALL UserDestroyMenu(HMENU hMenu)
5805 {
5806 PMENU Menu;
5807 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
5808
5809 if(!(Menu = UserGetMenuObject(hMenu)))
5810 {
5811 return FALSE;
5812 }
5813
5814 if (Menu->head.rpdesk != pti->rpdesk)
5815 {
5816 EngSetLastError(ERROR_ACCESS_DENIED);
5817 return FALSE;
5818 }
5819 return IntDestroyMenuObject(Menu, FALSE);
5820 }
5821
5822 /*
5823 * @implemented
5824 */
5825 BOOL APIENTRY
5826 NtUserDestroyMenu(
5827 HMENU hMenu)
5828 {
5829 PMENU Menu;
5830 DECLARE_RETURN(BOOL);
5831
5832 TRACE("Enter NtUserDestroyMenu\n");
5833 UserEnterExclusive();
5834
5835 if(!(Menu = UserGetMenuObject(hMenu)))
5836 {
5837 RETURN( FALSE);
5838 }
5839 if (Menu->head.rpdesk != gptiCurrent->rpdesk)
5840 {
5841 EngSetLastError(ERROR_ACCESS_DENIED);
5842 RETURN( FALSE);
5843 }
5844 RETURN( IntDestroyMenuObject(Menu, TRUE));
5845
5846 CLEANUP:
5847 TRACE("Leave NtUserDestroyMenu, ret=%i\n",_ret_);
5848 UserLeave();
5849 END_CLEANUP;
5850 }
5851
5852 /*
5853 * @implemented
5854 */
5855 UINT APIENTRY
5856 NtUserEnableMenuItem(
5857 HMENU hMenu,
5858 UINT uIDEnableItem,
5859 UINT uEnable)
5860 {
5861 PMENU Menu;
5862 DECLARE_RETURN(UINT);
5863
5864 TRACE("Enter NtUserEnableMenuItem\n");
5865 UserEnterExclusive();
5866
5867 if(!(Menu = UserGetMenuObject(hMenu)))
5868 {
5869 RETURN(-1);
5870 }
5871
5872 RETURN( IntEnableMenuItem(Menu, uIDEnableItem, uEnable));
5873
5874 CLEANUP:
5875 TRACE("Leave NtUserEnableMenuItem, ret=%u\n",_ret_);
5876 UserLeave();
5877 END_CLEANUP;
5878 }
5879
5880 /*
5881 * @implemented
5882 */
5883 BOOL APIENTRY
5884 NtUserEndMenu(VOID)
5885 {
5886 //PWND pWnd;
5887 TRACE("Enter NtUserEndMenu\n");
5888 UserEnterExclusive();
5889 /* if ( gptiCurrent->pMenuState &&
5890 gptiCurrent->pMenuState->pGlobalPopupMenu )
5891 {
5892 pWnd = IntGetMSWND(gptiCurrent->pMenuState);
5893 if (pWnd)
5894 {
5895 UserPostMessage( UserHMGetHandle(pWnd), WM_CANCELMODE, 0, 0);
5896 }
5897 else
5898 gptiCurrent->pMenuState->fInsideMenuLoop = FALSE;
5899 }*/
5900 if (fInsideMenuLoop && top_popup)
5901 {
5902 fInsideMenuLoop = FALSE;
5903 UserPostMessage( top_popup, WM_CANCELMODE, 0, 0);
5904 }
5905 UserLeave();
5906 TRACE("Leave NtUserEndMenu\n");
5907 return TRUE;
5908 }
5909
5910 /*
5911 * @implemented
5912 */
5913 BOOL APIENTRY
5914 NtUserGetMenuBarInfo(
5915 HWND hwnd,
5916 LONG idObject,
5917 LONG idItem,
5918 PMENUBARINFO pmbi)
5919 {
5920 PWND pWnd;
5921 HMENU hMenu;
5922 MENUBARINFO kmbi;
5923 BOOL Ret;
5924 PPOPUPMENU pPopupMenu;
5925 USER_REFERENCE_ENTRY Ref;
5926 NTSTATUS Status = STATUS_SUCCESS;
5927 PMENU Menu = NULL;
5928 DECLARE_RETURN(BOOL);
5929
5930 TRACE("Enter NtUserGetMenuBarInfo\n");
5931 UserEnterShared();
5932
5933 if (!(pWnd = UserGetWindowObject(hwnd)))
5934 {
5935 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5936 RETURN(FALSE);
5937 }
5938
5939 UserRefObjectCo(pWnd, &Ref);
5940
5941 RECTL_vSetEmptyRect(&kmbi.rcBar);
5942 kmbi.hMenu = NULL;
5943 kmbi.hwndMenu = NULL;
5944 kmbi.fBarFocused = FALSE;
5945 kmbi.fFocused = FALSE;
5946
5947 switch (idObject)
5948 {
5949 case OBJID_CLIENT:
5950 if (!pWnd->pcls->fnid)
5951 RETURN(FALSE);
5952 if (pWnd->pcls->fnid != FNID_MENU)
5953 {
5954 WARN("called on invalid window: %u\n", pWnd->pcls->fnid);
5955 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5956 RETURN(FALSE);
5957 }
5958 // Windows does this! Wine checks for Atom and uses GetWindowLongPtrW.
5959 hMenu = (HMENU)co_IntSendMessage(hwnd, MN_GETHMENU, 0, 0);
5960 pPopupMenu = ((PMENUWND)pWnd)->ppopupmenu;
5961 if (pPopupMenu && pPopupMenu->spmenu)
5962 {
5963 if (UserHMGetHandle(pPopupMenu->spmenu) != hMenu)
5964 {
5965 ERR("Window Pop Up hMenu %p not the same as Get hMenu %p!\n",pPopupMenu->spmenu->head.h,hMenu);
5966 }
5967 }
5968 break;
5969 case OBJID_MENU:
5970 if (pWnd->style & WS_CHILD) RETURN(FALSE);
5971 hMenu = UlongToHandle(pWnd->IDMenu);
5972 TRACE("GMBI: OBJID_MENU hMenu %p\n",hMenu);
5973 break;
5974 case OBJID_SYSMENU:
5975 if (!(pWnd->style & WS_SYSMENU)) RETURN(FALSE);
5976 Menu = IntGetSystemMenu(pWnd, FALSE);
5977 hMenu = UserHMGetHandle(Menu);
5978 break;
5979 default:
5980 RETURN(FALSE);
5981 }
5982
5983 if (!hMenu)
5984 RETURN(FALSE);
5985
5986 _SEH2_TRY
5987 {
5988 ProbeForRead(pmbi, sizeof(MENUBARINFO), 1);
5989 kmbi.cbSize = pmbi->cbSize;
5990 }
5991 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5992 {
5993 kmbi.cbSize = 0;
5994 }
5995 _SEH2_END
5996
5997 if (kmbi.cbSize != sizeof(MENUBARINFO))
5998 {
5999 EngSetLastError(ERROR_INVALID_PARAMETER);
6000 RETURN(FALSE);
6001 }
6002
6003 if (!Menu) Menu = UserGetMenuObject(hMenu);
6004 if (!Menu)
6005 RETURN(FALSE);
6006
6007 if ((idItem < 0) || ((ULONG)idItem > Menu->cItems))
6008 RETURN(FALSE);
6009
6010 if (idItem == 0)
6011 {
6012 Ret = IntGetMenuItemRect(pWnd, Menu, 0, &kmbi.rcBar);
6013 kmbi.rcBar.right = kmbi.rcBar.left + Menu->cxMenu;
6014 kmbi.rcBar.bottom = kmbi.rcBar.top + Menu->cyMenu;
6015 TRACE("idItem a 0 %d\n",Ret);
6016 }
6017 else
6018 {
6019 Ret = IntGetMenuItemRect(pWnd, Menu, idItem-1, &kmbi.rcBar);
6020 TRACE("idItem b %d %d\n", idItem-1, Ret);
6021 }
6022
6023 kmbi.hMenu = hMenu;
6024 kmbi.fBarFocused = top_popup_hmenu == hMenu;
6025 TRACE("GMBI: top p hm %p hMenu %p\n",top_popup_hmenu, hMenu);
6026 if (idItem)
6027 {
6028 kmbi.fFocused = Menu->iItem == idItem-1;
6029 if (kmbi.fFocused && (Menu->rgItems[idItem - 1].spSubMenu))
6030 {
6031 kmbi.hwndMenu = Menu->rgItems[idItem - 1].spSubMenu->hWnd;
6032 }
6033 }
6034 else
6035 {
6036 kmbi.fFocused = kmbi.fBarFocused;
6037 }
6038
6039 _SEH2_TRY
6040 {
6041 ProbeForWrite(pmbi, sizeof(MENUBARINFO), 1);
6042 RtlCopyMemory(pmbi, &kmbi, sizeof(MENUBARINFO));
6043 }
6044 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6045 {
6046 Status = _SEH2_GetExceptionCode();
6047 }
6048 _SEH2_END
6049
6050 if (!NT_SUCCESS(Status))
6051 {
6052 SetLastNtError(Status);
6053 RETURN(FALSE);
6054 }
6055
6056 RETURN(TRUE);
6057
6058 CLEANUP:
6059 if (pWnd) UserDerefObjectCo(pWnd);
6060 TRACE("Leave NtUserGetMenuBarInfo, ret=%i\n",_ret_);
6061 UserLeave();
6062 END_CLEANUP;
6063 }
6064
6065 /*
6066 * @implemented
6067 */
6068 UINT APIENTRY
6069 NtUserGetMenuIndex(
6070 HMENU hMenu,
6071 HMENU hSubMenu)
6072 {
6073 PMENU Menu, SubMenu;
6074 PITEM MenuItem;
6075 UINT i;
6076 DECLARE_RETURN(UINT);
6077
6078 TRACE("Enter NtUserGetMenuIndex\n");
6079 UserEnterShared();
6080
6081 if ( !(Menu = UserGetMenuObject(hMenu)) ||
6082 !(SubMenu = UserGetMenuObject(hSubMenu)) )
6083 RETURN(0xFFFFFFFF);
6084
6085 MenuItem = Menu->rgItems;
6086 for (i = 0; i < Menu->cItems; i++, MenuItem++)
6087 {
6088 if (MenuItem->spSubMenu == SubMenu)
6089 RETURN(MenuItem->wID);
6090 }
6091 RETURN(0xFFFFFFFF);
6092
6093 CLEANUP:
6094 TRACE("Leave NtUserGetMenuIndex, ret=%u\n",_ret_);
6095 UserLeave();
6096 END_CLEANUP;
6097 }
6098
6099 /*
6100 * @implemented
6101 */
6102 BOOL APIENTRY
6103 NtUserGetMenuItemRect(
6104 HWND hWnd,
6105 HMENU hMenu,
6106 UINT uItem,
6107 PRECTL lprcItem)
6108 {
6109 PWND ReferenceWnd;
6110 LONG XMove, YMove;
6111 RECTL Rect;
6112 PMENU Menu;
6113 PITEM MenuItem;
6114 NTSTATUS Status = STATUS_SUCCESS;
6115 DECLARE_RETURN(BOOL);
6116
6117 TRACE("Enter NtUserGetMenuItemRect\n");
6118 UserEnterShared();
6119
6120 if (!(Menu = UserGetMenuObject(hMenu)))
6121 {
6122 RETURN(FALSE);
6123 }
6124
6125 if ((MenuItem = MENU_FindItem (&Menu, &uItem, MF_BYPOSITION)))
6126 {
6127 Rect.left = MenuItem->xItem;
6128 Rect.top = MenuItem->yItem;
6129 Rect.right = MenuItem->cxItem; // Do this for now......
6130 Rect.bottom = MenuItem->cyItem;
6131 }
6132 else
6133 RETURN(FALSE);
6134
6135 if(!hWnd)
6136 {
6137 hWnd = Menu->hWnd;
6138 }
6139
6140 if (lprcItem == NULL) RETURN( FALSE);
6141
6142 if (!(ReferenceWnd = UserGetWindowObject(hWnd))) RETURN( FALSE);
6143
6144 if (Menu->fFlags & MNF_POPUP)
6145 {
6146 XMove = ReferenceWnd->rcClient.left;
6147 YMove = ReferenceWnd->rcClient.top;
6148 }
6149 else
6150 {
6151 XMove = ReferenceWnd->rcWindow.left;
6152 YMove = ReferenceWnd->rcWindow.top;
6153 }
6154
6155 Rect.left += XMove;
6156 Rect.top += YMove;
6157 Rect.right += XMove;
6158 Rect.bottom += YMove;
6159
6160 _SEH2_TRY
6161 {
6162 RtlCopyMemory(lprcItem, &Rect, sizeof(RECTL));
6163 }
6164 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6165 {
6166 Status = _SEH2_GetExceptionCode();
6167 }
6168 _SEH2_END
6169
6170 if (!NT_SUCCESS(Status))
6171 {
6172 SetLastNtError(Status);
6173 RETURN(FALSE);
6174 }
6175 RETURN(TRUE);
6176
6177 CLEANUP:
6178 TRACE("Leave NtUserGetMenuItemRect, ret=%i\n",_ret_);
6179 UserLeave();
6180 END_CLEANUP;
6181 }
6182
6183 /*
6184 * @implemented
6185 */
6186 BOOL APIENTRY
6187 NtUserHiliteMenuItem(
6188 HWND hWnd,
6189 HMENU hMenu,
6190 UINT uItemHilite,
6191 UINT uHilite)
6192 {
6193 PMENU Menu;
6194 PWND Window;
6195 DECLARE_RETURN(BOOLEAN);
6196
6197 TRACE("Enter NtUserHiliteMenuItem\n");
6198 UserEnterExclusive();
6199
6200 if(!(Window = UserGetWindowObject(hWnd)))
6201 {
6202 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
6203 RETURN(FALSE);
6204 }
6205
6206 if(!(Menu = UserGetMenuObject(hMenu)))
6207 {
6208 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
6209 RETURN(FALSE);
6210 }
6211
6212 RETURN( IntHiliteMenuItem(Window, Menu, uItemHilite, uHilite));
6213
6214 CLEANUP:
6215 TRACE("Leave NtUserHiliteMenuItem, ret=%u\n",_ret_);
6216 UserLeave();
6217 END_CLEANUP;
6218 }
6219
6220 /*
6221 * @implemented
6222 */
6223 DWORD
6224 APIENTRY
6225 NtUserDrawMenuBarTemp(
6226 HWND hWnd,
6227 HDC hDC,
6228 PRECT pRect,
6229 HMENU hMenu,
6230 HFONT hFont)
6231 {
6232 PMENU Menu;
6233 PWND Window;
6234 RECT Rect;
6235 NTSTATUS Status = STATUS_SUCCESS;
6236 DECLARE_RETURN(DWORD);
6237
6238 ERR("Enter NtUserDrawMenuBarTemp\n");
6239 UserEnterExclusive();
6240
6241 if(!(Window = UserGetWindowObject(hWnd)))
6242 {
6243 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
6244 RETURN(0);
6245 }
6246
6247 if(!(Menu = UserGetMenuObject(hMenu)))
6248 {
6249 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
6250 RETURN(0);
6251 }
6252
6253 _SEH2_TRY
6254 {
6255 ProbeForRead(pRect, sizeof(RECT), sizeof(ULONG));
6256 RtlCopyMemory(&Rect, pRect, sizeof(RECT));
6257 }
6258 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6259 {
6260 Status = _SEH2_GetExceptionCode();
6261 }
6262 _SEH2_END;
6263
6264 if (Status != STATUS_SUCCESS)
6265 {
6266 SetLastNtError(Status);
6267 RETURN(0);
6268 }
6269
6270 RETURN( IntDrawMenuBarTemp(Window, hDC, &Rect, Menu, hFont));
6271
6272 CLEANUP:
6273 ERR("Leave NtUserDrawMenuBarTemp, ret=%u\n",_ret_);
6274 UserLeave();
6275 END_CLEANUP;
6276 }
6277
6278 /*
6279 * @implemented
6280 */
6281 int APIENTRY
6282 NtUserMenuItemFromPoint(
6283 HWND hWnd,
6284 HMENU hMenu,
6285 DWORD X,
6286 DWORD Y)
6287 {
6288 PMENU Menu;
6289 PWND Window = NULL;
6290 PITEM mi;
6291 ULONG i;
6292 DECLARE_RETURN(int);
6293
6294 TRACE("Enter NtUserMenuItemFromPoint\n");
6295 UserEnterExclusive();
6296
6297 if (!(Menu = UserGetMenuObject(hMenu)))
6298 {
6299 RETURN( -1);
6300 }
6301
6302 if (!(Window = UserGetWindowObject(Menu->hWnd)))
6303 {
6304 RETURN( -1);
6305 }
6306
6307 X -= Window->rcWindow.left;
6308 Y -= Window->rcWindow.top;
6309
6310 mi = Menu->rgItems;
6311 for (i = 0; i < Menu->cItems; i++, mi++)
6312 {
6313 RECTL Rect;
6314
6315 Rect.left = mi->xItem;
6316 Rect.top = mi->yItem;
6317 Rect.right = mi->cxItem;
6318 Rect.bottom = mi->cyItem;
6319
6320 MENU_AdjustMenuItemRect(Menu, &Rect);
6321
6322 if (RECTL_bPointInRect(&Rect, X, Y))
6323 {
6324 break;
6325 }
6326 }
6327
6328 RETURN( (mi ? i : NO_SELECTED_ITEM));
6329
6330 CLEANUP:
6331 TRACE("Leave NtUserMenuItemFromPoint, ret=%i\n",_ret_);
6332 UserLeave();
6333 END_CLEANUP;
6334 }
6335
6336
6337 DWORD
6338 APIENTRY
6339 NtUserPaintMenuBar(
6340 HWND hWnd,
6341 HDC hDC,
6342 ULONG leftBorder,
6343 ULONG rightBorder,
6344 ULONG top,
6345 BOOL bActive)
6346 {
6347 PWND Window;
6348 RECT Rect;
6349 DWORD ret;
6350
6351 UserEnterExclusive();
6352
6353 if(!(Window = UserGetWindowObject(hWnd)))
6354 {
6355 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
6356 UserLeave();
6357 return 0;
6358 }
6359
6360 Rect.left = leftBorder;
6361 Rect.right = Window->rcWindow.right - Window->rcWindow.left - rightBorder;
6362 Rect.top = top;
6363 Rect.bottom = 0;
6364
6365 ret = MENU_DrawMenuBar(hDC, &Rect, Window, FALSE);
6366
6367 UserLeave();
6368
6369 return ret;
6370 }
6371
6372 /*
6373 * @implemented
6374 */
6375 BOOL APIENTRY
6376 NtUserRemoveMenu(
6377 HMENU hMenu,
6378 UINT uPosition,
6379 UINT uFlags)
6380 {
6381 PMENU Menu;
6382 DECLARE_RETURN(BOOL);
6383
6384 TRACE("Enter NtUserRemoveMenu\n");
6385 UserEnterExclusive();
6386
6387 if(!(Menu = UserGetMenuObject(hMenu)))
6388 {
6389 RETURN( FALSE);
6390 }
6391
6392 RETURN(IntRemoveMenuItem(Menu, uPosition, uFlags, FALSE));
6393
6394 CLEANUP:
6395 TRACE("Leave NtUserRemoveMenu, ret=%i\n",_ret_);
6396 UserLeave();
6397 END_CLEANUP;
6398
6399 }
6400
6401 /*
6402 * @implemented
6403 */
6404 BOOL APIENTRY
6405 NtUserSetMenu(
6406 HWND hWnd,
6407 HMENU Menu,
6408 BOOL Repaint)
6409 {
6410 PWND Window;
6411 BOOL Changed;
6412 DECLARE_RETURN(BOOL);
6413
6414 TRACE("Enter NtUserSetMenu\n");
6415 UserEnterExclusive();
6416
6417 if (!(Window = UserGetWindowObject(hWnd)))
6418 {
6419 RETURN( FALSE);
6420 }
6421
6422 if (!IntSetMenu(Window, Menu, &Changed))
6423 {
6424 RETURN( FALSE);
6425 }
6426
6427 // Not minimized and please repaint!!!
6428 if (!(Window->style & WS_MINIMIZE) && (Repaint || Changed))
6429 {
6430 USER_REFERENCE_ENTRY Ref;
6431 UserRefObjectCo(Window, &Ref);
6432 co_WinPosSetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
6433 UserDerefObjectCo(Window);
6434 }
6435
6436 RETURN( TRUE);
6437
6438 CLEANUP:
6439 TRACE("Leave NtUserSetMenu, ret=%i\n",_ret_);
6440 UserLeave();
6441 END_CLEANUP;
6442 }
6443
6444 /*
6445 * @implemented
6446 */
6447 BOOL APIENTRY
6448 NtUserSetMenuContextHelpId(
6449 HMENU hMenu,
6450 DWORD dwContextHelpId)
6451 {
6452 PMENU Menu;
6453 DECLARE_RETURN(BOOL);
6454
6455 TRACE("Enter NtUserSetMenuContextHelpId\n");
6456 UserEnterExclusive();
6457
6458 if(!(Menu = UserGetMenuObject(hMenu)))
6459 {
6460 RETURN( FALSE);
6461 }
6462
6463 RETURN(IntSetMenuContextHelpId(Menu, dwContextHelpId));
6464
6465 CLEANUP:
6466 TRACE("Leave NtUserSetMenuContextHelpId, ret=%i\n",_ret_);
6467 UserLeave();
6468 END_CLEANUP;
6469 }
6470
6471 /*
6472 * @implemented
6473 */
6474 BOOL APIENTRY
6475 NtUserSetMenuDefaultItem(
6476 HMENU hMenu,
6477 UINT uItem,
6478 UINT fByPos)
6479 {
6480 PMENU Menu;
6481 DECLARE_RETURN(BOOL);
6482
6483 TRACE("Enter NtUserSetMenuDefaultItem\n");
6484 UserEnterExclusive();
6485
6486 if(!(Menu = UserGetMenuObject(hMenu)))
6487 {
6488 RETURN( FALSE);
6489 }
6490
6491 RETURN( UserSetMenuDefaultItem(Menu, uItem, fByPos));
6492
6493 CLEANUP:
6494 TRACE("Leave NtUserSetMenuDefaultItem, ret=%i\n",_ret_);
6495 UserLeave();
6496 END_CLEANUP;
6497 }
6498
6499 /*
6500 * @implemented
6501 */
6502 BOOL APIENTRY
6503 NtUserSetMenuFlagRtoL(
6504 HMENU hMenu)
6505 {
6506 PMENU Menu;
6507 DECLARE_RETURN(BOOL);
6508
6509 TRACE("Enter NtUserSetMenuFlagRtoL\n");
6510 UserEnterExclusive();
6511
6512 if(!(Menu = UserGetMenuObject(hMenu)))
6513 {
6514 RETURN( FALSE);
6515 }
6516
6517 RETURN(IntSetMenuFlagRtoL(Menu));
6518
6519 CLEANUP:
6520 TRACE("Leave NtUserSetMenuFlagRtoL, ret=%i\n",_ret_);
6521 UserLeave();
6522 END_CLEANUP;
6523 }
6524
6525 /*
6526 * @implemented
6527 */
6528 BOOL APIENTRY
6529 NtUserThunkedMenuInfo(
6530 HMENU hMenu,
6531 LPCMENUINFO lpcmi)
6532 {
6533 PMENU Menu;
6534 DECLARE_RETURN(BOOL);
6535
6536 TRACE("Enter NtUserThunkedMenuInfo\n");
6537 UserEnterExclusive();
6538
6539 if (!(Menu = UserGetMenuObject(hMenu)))
6540 {
6541 RETURN(FALSE);
6542 }
6543
6544 RETURN(UserMenuInfo(Menu, (PROSMENUINFO)lpcmi, TRUE));
6545
6546 CLEANUP:
6547 TRACE("Leave NtUserThunkedMenuInfo, ret=%i\n",_ret_);
6548 UserLeave();
6549 END_CLEANUP;
6550 }
6551
6552 /*
6553 * @implemented
6554 */
6555 BOOL APIENTRY
6556 NtUserThunkedMenuItemInfo(
6557 HMENU hMenu,
6558 UINT uItem,
6559 BOOL fByPosition,
6560 BOOL bInsert,
6561 LPMENUITEMINFOW lpmii,
6562 PUNICODE_STRING lpszCaption)
6563 {
6564 PMENU Menu;
6565 NTSTATUS Status;
6566 UNICODE_STRING lstrCaption;
6567 DECLARE_RETURN(BOOL);
6568
6569 TRACE("Enter NtUserThunkedMenuItemInfo\n");
6570 UserEnterExclusive();
6571
6572 /* lpszCaption may be NULL, check for it and call RtlInitUnicodeString()
6573 if bInsert == TRUE call UserInsertMenuItem() else UserSetMenuItemInfo() */
6574
6575 RtlInitEmptyUnicodeString(&lstrCaption, NULL, 0);
6576
6577 if (!(Menu = UserGetMenuObject(hMenu)))
6578 {
6579 RETURN(FALSE);
6580 }
6581
6582 /* Check if we got a Caption */
6583 if (lpszCaption && lpszCaption->Buffer)
6584 {
6585 /* Copy the string to kernel mode */
6586 Status = ProbeAndCaptureUnicodeString( &lstrCaption,
6587 UserMode,
6588 lpszCaption);
6589 if (!NT_SUCCESS(Status))
6590 {
6591 ERR("Failed to capture MenuItem Caption (status 0x%08x)\n",Status);
6592 SetLastNtError(Status);
6593 RETURN(FALSE);
6594 }
6595 }
6596
6597 if (bInsert) RETURN( UserInsertMenuItem(Menu, uItem, fByPosition, lpmii, &lstrCaption));
6598
6599 RETURN( UserMenuItemInfo(Menu, uItem, fByPosition, (PROSMENUITEMINFO)lpmii, TRUE, &lstrCaption));
6600
6601 CLEANUP:
6602 if (lstrCaption.Buffer)
6603 {
6604 ReleaseCapturedUnicodeString(&lstrCaption, UserMode);
6605 }
6606
6607 TRACE("Leave NtUserThunkedMenuItemInfo, ret=%i\n",_ret_);
6608 UserLeave();
6609 END_CLEANUP;
6610 }
6611
6612 /*
6613 * @implemented
6614 */
6615 BOOL APIENTRY
6616 NtUserTrackPopupMenuEx(
6617 HMENU hMenu,
6618 UINT fuFlags,
6619 int x,
6620 int y,
6621 HWND hWnd,
6622 LPTPMPARAMS lptpm)
6623 {
6624 PMENU menu;
6625 PWND pWnd;
6626 TPMPARAMS tpm;
6627 BOOL Ret = FALSE;
6628 USER_REFERENCE_ENTRY Ref;
6629
6630 TRACE("Enter NtUserTrackPopupMenuEx\n");
6631 UserEnterExclusive();
6632 /* Parameter check */
6633 if (!(menu = UserGetMenuObject( hMenu )))
6634 {
6635 ERR("TPME : Invalid Menu handle.\n");
6636 EngSetLastError( ERROR_INVALID_MENU_HANDLE );
6637 goto Exit;
6638 }
6639
6640 if (!(pWnd = UserGetWindowObject(hWnd)))
6641 {
6642 ERR("TPME : Invalid Window handle.\n");
6643 goto Exit;
6644 }
6645
6646 if (lptpm)
6647 {
6648 _SEH2_TRY
6649 {
6650 ProbeForRead(lptpm, sizeof(TPMPARAMS), sizeof(ULONG));
6651 tpm = *lptpm;
6652 }
6653 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6654 {
6655 _SEH2_YIELD(goto Exit);
6656 }
6657 _SEH2_END
6658 }
6659 UserRefObjectCo(pWnd, &Ref);
6660 Ret = IntTrackPopupMenuEx(menu, fuFlags, x, y, pWnd, lptpm ? &tpm : NULL);
6661 UserDerefObjectCo(pWnd);
6662
6663 Exit:
6664 TRACE("Leave NtUserTrackPopupMenuEx, ret=%i\n",Ret);
6665 UserLeave();
6666 return Ret;
6667 }
6668
6669 /* EOF */