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