a4e765114316533cd0a65dbfce009ffd079ba3ac
[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)
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
3050 /* We should first try left / right, this flag is not needed as input */
3051 UINT tryFlags = flags & ~TPM_VERTICAL;
3052
3053 UINT n;
3054 for (n = 0; n < RTL_NUMBER_OF(flag_mods); ++n)
3055 {
3056 INT tx = x;
3057 INT ty = y;
3058
3059 /* Try to move a bit around */
3060 if (MENU_MoveRect(tryFlags ^ flag_mods[n], &tx, &ty, width, height, &Cleaned, monitor) &&
3061 !RECTL_Intersect(&Cleaned, tx, ty, width, height))
3062 {
3063 x = tx;
3064 y = ty;
3065 break;
3066 }
3067 }
3068 /* If none worked, we go with the original x/y */
3069 }
3070 }
3071
3072 #if SHOW_DEBUGRECT
3073 {
3074 RECT rr = {x, y, x + width, y + height};
3075 DebugRect(&rr, RGB(0, 255, 0));
3076 }
3077 #endif
3078
3079 pWnd = ValidateHwndNoErr( menu->hWnd );
3080
3081 if (!pWnd)
3082 {
3083 ERR("menu->hWnd bad hwnd %p\n",menu->hWnd);
3084 return FALSE;
3085 }
3086
3087 if (!top_popup) {
3088 top_popup = menu->hWnd;
3089 top_popup_hmenu = UserHMGetHandle(menu);
3090 }
3091
3092 /* Display the window */
3093 UserRefObjectCo(pWnd, &Ref);
3094 co_WinPosSetWindowPos( pWnd, HWND_TOPMOST, x, y, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE);
3095
3096 co_IntUpdateWindows(pWnd, RDW_ALLCHILDREN, FALSE);
3097
3098 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, pWnd, OBJID_CLIENT, CHILDID_SELF, 0);
3099 UserDerefObjectCo(pWnd);
3100
3101 return TRUE;
3102 }
3103
3104 /***********************************************************************
3105 * MENU_EnsureMenuItemVisible
3106 */
3107 void MENU_EnsureMenuItemVisible(PMENU lppop, UINT wIndex, HDC hdc)
3108 {
3109 USER_REFERENCE_ENTRY Ref;
3110 if (lppop->dwArrowsOn)
3111 {
3112 ITEM *item = &lppop->rgItems[wIndex];
3113 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
3114 UINT nOldPos = lppop->iTop;
3115 RECT rc;
3116 UINT arrow_bitmap_height;
3117 PWND pWnd = ValidateHwndNoErr(lppop->hWnd);
3118
3119 IntGetClientRect(pWnd, &rc);
3120
3121 arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy;
3122
3123 rc.top += arrow_bitmap_height;
3124 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
3125
3126 nMaxHeight -= UserGetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
3127 UserRefObjectCo(pWnd, &Ref);
3128 if (item->cyItem > lppop->iTop + nMaxHeight)
3129 {
3130 lppop->iTop = item->cyItem - nMaxHeight;
3131 IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc);
3132 MENU_DrawScrollArrows(lppop, hdc);
3133 //ERR("Scroll Down iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
3134 }
3135 else if (item->yItem - MENU_TOP_MARGIN < lppop->iTop)
3136 {
3137 lppop->iTop = item->yItem - MENU_TOP_MARGIN;
3138 IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc);
3139 MENU_DrawScrollArrows(lppop, hdc);
3140 //ERR("Scroll Up iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
3141 }
3142 UserDerefObjectCo(pWnd);
3143 }
3144 }
3145
3146 /***********************************************************************
3147 * MenuSelectItem
3148 */
3149 static void FASTCALL MENU_SelectItem(PWND pwndOwner, PMENU menu, UINT wIndex,
3150 BOOL sendMenuSelect, PMENU topmenu)
3151 {
3152 HDC hdc;
3153 PWND pWnd;
3154
3155 TRACE("M_SI: owner=%p menu=%p index=0x%04x select=0x%04x\n", pwndOwner, menu, wIndex, sendMenuSelect);
3156
3157 if (!menu || !menu->cItems) return;
3158
3159 pWnd = ValidateHwndNoErr(menu->hWnd);
3160
3161 if (!pWnd) return;
3162
3163 if (menu->iItem == wIndex) return;
3164
3165 if (menu->fFlags & MNF_POPUP)
3166 hdc = UserGetDCEx(pWnd, 0, DCX_USESTYLE);
3167 else
3168 hdc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW);
3169
3170 if (!top_popup) {
3171 top_popup = menu->hWnd; //pPopupMenu->spwndActivePopup or
3172 //pPopupMenu->fIsTrackPopup set pPopupMenu->spwndPopupMenu;
3173 top_popup_hmenu = UserHMGetHandle(menu); //pPopupMenu->spmenu
3174 }
3175
3176 NtGdiSelectFont( hdc, ghMenuFont );
3177
3178 /* Clear previous highlighted item */
3179 if (menu->iItem != NO_SELECTED_ITEM)
3180 {
3181 menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
3182 MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc, &menu->rgItems[menu->iItem],
3183 menu->cyMenu, !(menu->fFlags & MNF_POPUP),
3184 ODA_SELECT);
3185 }
3186
3187 /* Highlight new item (if any) */
3188 menu->iItem = wIndex;
3189 if (menu->iItem != NO_SELECTED_ITEM)
3190 {
3191 if (!(menu->rgItems[wIndex].fType & MF_SEPARATOR))
3192 {
3193 menu->rgItems[wIndex].fState |= MF_HILITE;
3194 MENU_EnsureMenuItemVisible(menu, wIndex, hdc);
3195 MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc,
3196 &menu->rgItems[wIndex], menu->cyMenu, !(menu->fFlags & MNF_POPUP), ODA_SELECT);
3197 }
3198 if (sendMenuSelect)
3199 {
3200 ITEM *ip = &menu->rgItems[menu->iItem];
3201 WPARAM wParam = MAKEWPARAM( ip->spSubMenu ? wIndex : ip->wID,
3202 ip->fType | ip->fState |
3203 (ip->spSubMenu ? MF_POPUP : 0) |
3204 (menu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
3205
3206 co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(menu));
3207 }
3208 }
3209 else if (sendMenuSelect)
3210 {
3211 if (topmenu)
3212 {
3213 int pos;
3214 pos = MENU_FindSubMenu(&topmenu, menu);
3215 if (pos != NO_SELECTED_ITEM)
3216 {
3217 ITEM *ip = &topmenu->rgItems[pos];
3218 WPARAM wParam = MAKEWPARAM( Pos, ip->fType | ip->fState |
3219 (ip->spSubMenu ? MF_POPUP : 0) |
3220 (topmenu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
3221
3222 co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(topmenu));
3223 }
3224 }
3225 }
3226 UserReleaseDC(pWnd, hdc, FALSE);
3227 }
3228
3229 /***********************************************************************
3230 * MenuMoveSelection
3231 *
3232 * Moves currently selected item according to the Offset parameter.
3233 * If there is no selection then it should select the last item if
3234 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
3235 */
3236 static void FASTCALL MENU_MoveSelection(PWND pwndOwner, PMENU menu, INT offset)
3237 {
3238 INT i;
3239
3240 TRACE("pwnd=%x menu=%x off=0x%04x\n", pwndOwner, menu, offset);
3241
3242 if ((!menu) || (!menu->rgItems)) return;
3243
3244 if ( menu->iItem != NO_SELECTED_ITEM )
3245 {
3246 if ( menu->cItems == 1 )
3247 return;
3248 else
3249 for (i = menu->iItem + offset ; i >= 0 && i < menu->cItems
3250 ; i += offset)
3251 if (!(menu->rgItems[i].fType & MF_SEPARATOR))
3252 {
3253 MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 );
3254 return;
3255 }
3256 }
3257
3258 for ( i = (offset > 0) ? 0 : menu->cItems - 1;
3259 i >= 0 && i < menu->cItems ; i += offset)
3260 if (!(menu->rgItems[i].fType & MF_SEPARATOR))
3261 {
3262 MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 );
3263 return;
3264 }
3265 }
3266
3267 /***********************************************************************
3268 * MenuHideSubPopups
3269 *
3270 * Hide the sub-popup menus of this menu.
3271 */
3272 static void FASTCALL MENU_HideSubPopups(PWND pWndOwner, PMENU Menu,
3273 BOOL SendMenuSelect, UINT wFlags)
3274 {
3275 TRACE("owner=%x menu=%x 0x%04x\n", pWndOwner, Menu, SendMenuSelect);
3276
3277 if ( Menu && top_popup )
3278 {
3279 PITEM Item;
3280
3281 if (Menu->iItem != NO_SELECTED_ITEM)
3282 {
3283 Item = &Menu->rgItems[Menu->iItem];
3284 if (!(Item->spSubMenu) ||
3285 !(Item->fState & MF_MOUSESELECT)) return;
3286 Item->fState &= ~MF_MOUSESELECT;
3287 }
3288 else
3289 return;
3290
3291 if (Item->spSubMenu)
3292 {
3293 PWND pWnd;
3294 if (!VerifyMenu(Item->spSubMenu)) return;
3295 pWnd = ValidateHwndNoErr(Item->spSubMenu->hWnd);
3296 MENU_HideSubPopups(pWndOwner, Item->spSubMenu, FALSE, wFlags);
3297 MENU_SelectItem(pWndOwner, Item->spSubMenu, NO_SELECTED_ITEM, SendMenuSelect, NULL);
3298 TRACE("M_HSP top p hm %p pWndOwner IDMenu %p\n",top_popup_hmenu,pWndOwner->IDMenu);
3299 co_UserDestroyWindow(pWnd);
3300
3301 /* Native returns handle to destroyed window */
3302 if (!(wFlags & TPM_NONOTIFY))
3303 {
3304 co_IntSendMessage( UserHMGetHandle(pWndOwner), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(Item->spSubMenu),
3305 MAKELPARAM(0, IS_SYSTEM_MENU(Item->spSubMenu)) );
3306 }
3307 ////
3308 // Call WM_UNINITMENUPOPUP FIRST before destroy!!
3309 // Fixes todo_wine User32 test menu.c line 2239 GetMenuBarInfo callback....
3310 //
3311 Item->spSubMenu->hWnd = NULL;
3312 ////
3313 }
3314 }
3315 }
3316
3317 /***********************************************************************
3318 * MenuShowSubPopup
3319 *
3320 * Display the sub-menu of the selected item of this menu.
3321 * Return the handle of the submenu, or menu if no submenu to display.
3322 */
3323 static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFirst, UINT Flags)
3324 {
3325 RECT Rect, ParentRect;
3326 ITEM *Item;
3327 HDC Dc;
3328 PWND pWnd;
3329
3330 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, Menu, SelectFirst);
3331
3332 if (!Menu) return Menu;
3333
3334 if (Menu->iItem == NO_SELECTED_ITEM) return Menu;
3335
3336 Item = &Menu->rgItems[Menu->iItem];
3337 if (!(Item->spSubMenu) || (Item->fState & (MF_GRAYED | MF_DISABLED)))
3338 return Menu;
3339
3340 /* message must be sent before using item,
3341 because nearly everything may be changed by the application ! */
3342
3343 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3344 if (!(Flags & TPM_NONOTIFY))
3345 {
3346 co_IntSendMessage(UserHMGetHandle(WndOwner), WM_INITMENUPOPUP,
3347 (WPARAM) UserHMGetHandle(Item->spSubMenu),
3348 MAKELPARAM(Menu->iItem, IS_SYSTEM_MENU(Menu)));
3349 }
3350
3351 Item = &Menu->rgItems[Menu->iItem];
3352 //Rect = ItemInfo.Rect;
3353 Rect.left = Item->xItem;
3354 Rect.top = Item->yItem;
3355 Rect.right = Item->cxItem; // Do this for now......
3356 Rect.bottom = Item->cyItem;
3357
3358 pWnd = ValidateHwndNoErr(Menu->hWnd);
3359
3360 /* Grab the rect of our (entire) parent menu, so we can try to not overlap it */
3361 if (Menu->fFlags & MNF_POPUP)
3362 {
3363 if (!IntGetWindowRect(pWnd, &ParentRect))
3364 {
3365 ERR("No pWnd\n");
3366 ParentRect = Rect;
3367 }
3368
3369 /* Ensure we can slightly overlap our parent */
3370 RECTL_vInflateRect(&ParentRect, -UserGetSystemMetrics(SM_CXEDGE) * 2, 0);
3371 }
3372 else
3373 {
3374 /* Inside the menu bar, we do not want to grab the entire window... */
3375 ParentRect = Rect;
3376 if (pWnd)
3377 RECTL_vOffsetRect(&ParentRect, pWnd->rcWindow.left, pWnd->rcWindow.top);
3378 }
3379
3380 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
3381 if (!(Item->fState & MF_HILITE))
3382 {
3383 if (Menu->fFlags & MNF_POPUP) Dc = UserGetDCEx(pWnd, NULL, DCX_USESTYLE);
3384 else Dc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW);
3385
3386 NtGdiSelectFont(Dc, ghMenuFont);
3387
3388 Item->fState |= MF_HILITE;
3389 MENU_DrawMenuItem(pWnd, Menu, WndOwner, Dc, Item, Menu->cyMenu,
3390 !(Menu->fFlags & MNF_POPUP), ODA_DRAWENTIRE);
3391
3392 UserReleaseDC(pWnd, Dc, FALSE);
3393 }
3394
3395 if (!Item->yItem && !Item->xItem && !Item->cyItem && !Item->cxItem)
3396 {
3397 Item->xItem = Rect.left;
3398 Item->yItem = Rect.top;
3399 Item->cxItem = Rect.right; // Do this for now......
3400 Item->cyItem = Rect.bottom;
3401 }
3402 Item->fState |= MF_MOUSESELECT;
3403
3404 if (IS_SYSTEM_MENU(Menu))
3405 {
3406 MENU_InitSysMenuPopup(Item->spSubMenu, pWnd->style, pWnd->pcls->style, HTSYSMENU);
3407
3408 NC_GetSysPopupPos(pWnd, &Rect);
3409 if (Flags & TPM_LAYOUTRTL) Rect.left = Rect.right;
3410 Rect.top = Rect.bottom;
3411 Rect.right = UserGetSystemMetrics(SM_CXSIZE);
3412 Rect.bottom = UserGetSystemMetrics(SM_CYSIZE);
3413 }
3414 else
3415 {
3416 IntGetWindowRect(pWnd, &Rect);
3417 if (Menu->fFlags & MNF_POPUP)
3418 {
3419 RECT rc;
3420 rc.left = Item->xItem;
3421 rc.top = Item->yItem;
3422 rc.right = Item->cxItem; // Do this for now......
3423 rc.bottom = Item->cyItem;
3424
3425 MENU_AdjustMenuItemRect(Menu, &rc);
3426
3427 /* The first item in the popup menu has to be at the
3428 same y position as the focused menu item */
3429 if(Flags & TPM_LAYOUTRTL)
3430 Rect.left += UserGetSystemMetrics(SM_CXBORDER);
3431 else
3432 Rect.left += rc.right /*ItemInfo.Rect.right*/ - UserGetSystemMetrics(SM_CXBORDER);
3433 Rect.top += rc.top - MENU_TOP_MARGIN;//3;
3434 Rect.right = rc.left - rc.right + UserGetSystemMetrics(SM_CXBORDER);
3435 Rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN - MENU_BOTTOM_MARGIN/*2*/
3436 - UserGetSystemMetrics(SM_CYBORDER);
3437 }
3438 else
3439 {
3440 if(Flags & TPM_LAYOUTRTL)
3441 Rect.left += Rect.right - Item->xItem; //ItemInfo.Rect.left;
3442 else
3443 Rect.left += Item->xItem; //ItemInfo.Rect.left;
3444 Rect.top += Item->cyItem; //ItemInfo.Rect.bottom;
3445 Rect.right = Item->cxItem - Item->xItem; //ItemInfo.Rect.right - ItemInfo.Rect.left;
3446 Rect.bottom = Item->cyItem - Item->yItem; //ItemInfo.Rect.bottom - ItemInfo.Rect.top;
3447 }
3448 }
3449
3450 /* use default alignment for submenus */
3451 Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
3452
3453 MENU_InitPopup( WndOwner, Item->spSubMenu, Flags );
3454
3455 MENU_ShowPopup( WndOwner, Item->spSubMenu, Menu->iItem, Flags,
3456 Rect.left, Rect.top, &ParentRect);
3457 if (SelectFirst)
3458 {
3459 MENU_MoveSelection(WndOwner, Item->spSubMenu, ITEM_NEXT);
3460 }
3461 return Item->spSubMenu;
3462 }
3463
3464 /***********************************************************************
3465 * MenuExecFocusedItem
3466 *
3467 * Execute a menu item (for instance when user pressed Enter).
3468 * Return the wID of the executed item. Otherwise, -1 indicating
3469 * that no menu item was executed, -2 if a popup is shown;
3470 * Have to receive the flags for the TrackPopupMenu options to avoid
3471 * sending unwanted message.
3472 *
3473 */
3474 static INT FASTCALL MENU_ExecFocusedItem(MTRACKER *pmt, PMENU Menu, UINT Flags)
3475 {
3476 PITEM Item;
3477
3478 TRACE("%p menu=%p\n", pmt, Menu);
3479
3480 if (!Menu || !Menu->cItems || Menu->iItem == NO_SELECTED_ITEM)
3481 {
3482 return -1;
3483 }
3484
3485 Item = &Menu->rgItems[Menu->iItem];
3486
3487 TRACE("%p %08x %p\n", Menu, Item->wID, Item->spSubMenu);
3488
3489 if (!(Item->spSubMenu))
3490 {
3491 if (!(Item->fState & (MF_GRAYED | MF_DISABLED)) && !(Item->fType & MF_SEPARATOR))
3492 {
3493 /* If TPM_RETURNCMD is set you return the id, but
3494 do not send a message to the owner */
3495 if (!(Flags & TPM_RETURNCMD))
3496 {
3497 if (Menu->fFlags & MNF_SYSMENU)
3498 {
3499 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_SYSCOMMAND, Item->wID,
3500 MAKELPARAM((SHORT) pmt->Pt.x, (SHORT) pmt->Pt.y));
3501 }
3502 else
3503 {
3504 DWORD dwStyle = ((Menu->fFlags & MNS_STYLE_MASK) | ( pmt->TopMenu ? (pmt->TopMenu->fFlags & MNS_STYLE_MASK) : 0) );
3505
3506 if (dwStyle & MNS_NOTIFYBYPOS)
3507 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_MENUCOMMAND, Menu->iItem, (LPARAM)UserHMGetHandle(Menu));
3508 else
3509 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_COMMAND, Item->wID, 0);
3510 }
3511 }
3512 return Item->wID;
3513 }
3514 }
3515 else
3516 {
3517 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, Menu, TRUE, Flags);
3518 return -2;
3519 }
3520
3521 return -1;
3522 }
3523
3524 /***********************************************************************
3525 * MenuSwitchTracking
3526 *
3527 * Helper function for menu navigation routines.
3528 */
3529 static void FASTCALL MENU_SwitchTracking(MTRACKER* pmt, PMENU PtMenu, UINT Index, UINT wFlags)
3530 {
3531 TRACE("%x menu=%x 0x%04x\n", pmt, PtMenu, Index);
3532
3533 if ( pmt->TopMenu != PtMenu &&
3534 !((PtMenu->fFlags | pmt->TopMenu->fFlags) & MNF_POPUP) )
3535 {
3536 /* both are top level menus (system and menu-bar) */
3537 MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags);
3538 MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, NO_SELECTED_ITEM, FALSE, NULL);
3539 pmt->TopMenu = PtMenu;
3540 }
3541 else
3542 {
3543 MENU_HideSubPopups(pmt->OwnerWnd, PtMenu, FALSE, wFlags);
3544 }
3545
3546 MENU_SelectItem(pmt->OwnerWnd, PtMenu, Index, TRUE, NULL);
3547 }
3548
3549 /***********************************************************************
3550 * MenuButtonDown
3551 *
3552 * Return TRUE if we can go on with menu tracking.
3553 */
3554 static BOOL FASTCALL MENU_ButtonDown(MTRACKER* pmt, PMENU PtMenu, UINT Flags)
3555 {
3556 TRACE("%x PtMenu=%p\n", pmt, PtMenu);
3557
3558 if (PtMenu)
3559 {
3560 UINT id = 0;
3561 PITEM item;
3562 if (IS_SYSTEM_MENU(PtMenu))
3563 {
3564 item = PtMenu->rgItems;
3565 }
3566 else
3567 {
3568 item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &id );
3569 }
3570
3571 if (item)
3572 {
3573 if (PtMenu->iItem != id)
3574 MENU_SwitchTracking(pmt, PtMenu, id, Flags);
3575
3576 /* If the popup menu is not already "popped" */
3577 if (!(item->fState & MF_MOUSESELECT))
3578 {
3579 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags);
3580 }
3581
3582 return TRUE;
3583 }
3584 /* Else the click was on the menu bar, finish the tracking */
3585 }
3586 return FALSE;
3587 }
3588
3589 /***********************************************************************
3590 * MenuButtonUp
3591 *
3592 * Return the value of MenuExecFocusedItem if
3593 * the selected item was not a popup. Else open the popup.
3594 * A -1 return value indicates that we go on with menu tracking.
3595 *
3596 */
3597 static INT FASTCALL MENU_ButtonUp(MTRACKER *pmt, PMENU PtMenu, UINT Flags)
3598 {
3599 TRACE("%p pmenu=%x\n", pmt, PtMenu);
3600
3601 if (PtMenu)
3602 {
3603 UINT Id = 0;
3604 ITEM *item;
3605
3606 if ( IS_SYSTEM_MENU(PtMenu) )
3607 {
3608 item = PtMenu->rgItems;
3609 }
3610 else
3611 {
3612 item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &Id );
3613 }
3614
3615 if (item && ( PtMenu->iItem == Id))
3616 {
3617 if (!(item->spSubMenu))
3618 {
3619 INT ExecutedMenuId = MENU_ExecFocusedItem( pmt, PtMenu, Flags);
3620 if (ExecutedMenuId == -1 || ExecutedMenuId == -2) return -1;
3621 return ExecutedMenuId;
3622 }
3623
3624 /* If we are dealing with the menu bar */
3625 /* and this is a click on an already "popped" item: */
3626 /* Stop the menu tracking and close the opened submenus */
3627 if (pmt->TopMenu == PtMenu && PtMenu->TimeToHide)
3628 {
3629 return 0;
3630 }
3631 }
3632 if ( IntGetMenu(PtMenu->hWnd) == PtMenu )
3633 {
3634 PtMenu->TimeToHide = TRUE;
3635 }
3636 }
3637 return -1;
3638 }
3639
3640 /***********************************************************************
3641 * MenuPtMenu
3642 *
3643 * Walks menu chain trying to find a menu pt maps to.
3644 */
3645 static PMENU FASTCALL MENU_PtMenu(PMENU menu, POINT pt)
3646 {
3647 PITEM pItem;
3648 PMENU ret = NULL;
3649
3650 if (!menu) return NULL;
3651
3652 /* try subpopup first (if any) */
3653 if (menu->iItem != NO_SELECTED_ITEM)
3654 {
3655 pItem = menu->rgItems;
3656 if ( pItem ) pItem = &pItem[menu->iItem];
3657 if ( pItem && pItem->spSubMenu && pItem->fState & MF_MOUSESELECT)
3658 {
3659 ret = MENU_PtMenu( pItem->spSubMenu, pt);
3660 }
3661 }
3662
3663 /* check the current window (avoiding WM_HITTEST) */
3664 if (!ret)
3665 {
3666 PWND pWnd = ValidateHwndNoErr(menu->hWnd);
3667 INT ht = GetNCHitEx(pWnd, pt);
3668 if ( menu->fFlags & MNF_POPUP )
3669 {
3670 if (ht != HTNOWHERE && ht != HTERROR) ret = menu;
3671 }
3672 else if (ht == HTSYSMENU)
3673 ret = get_win_sys_menu(menu->hWnd);
3674 else if (ht == HTMENU)
3675 ret = IntGetMenu( menu->hWnd );
3676 }
3677 return ret;
3678 }
3679
3680 /***********************************************************************
3681 * MenuMouseMove
3682 *
3683 * Return TRUE if we can go on with menu tracking.
3684 */
3685 static BOOL FASTCALL MENU_MouseMove(MTRACKER *pmt, PMENU PtMenu, UINT Flags)
3686 {
3687 UINT Index = NO_SELECTED_ITEM;
3688
3689 if ( PtMenu )
3690 {
3691 if (IS_SYSTEM_MENU(PtMenu))
3692 {
3693 Index = 0;
3694 //// ReactOS only HACK: CORE-2338
3695 // Windows tracks mouse moves to the system menu but does not open it.
3696 // Only keyboard tracking can do that.
3697 //
3698 TRACE("SystemMenu\n");
3699 return TRUE; // Stay inside the Loop!
3700 }
3701 else
3702 MENU_FindItemByCoords( PtMenu, pmt->Pt, &Index );
3703 }
3704
3705 if (Index == NO_SELECTED_ITEM)
3706 {
3707 MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NO_SELECTED_ITEM, TRUE, pmt->TopMenu);
3708 }
3709 else if (PtMenu->iItem != Index)
3710 {
3711 MENU_SwitchTracking(pmt, PtMenu, Index, Flags);
3712 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags);
3713 }
3714 return TRUE;
3715 }
3716
3717 /***********************************************************************
3718 * MenuGetSubPopup
3719 *
3720 * Return the handle of the selected sub-popup menu (if any).
3721 */
3722 static PMENU MENU_GetSubPopup( PMENU menu )
3723 {
3724 ITEM *item;
3725
3726 if ((!menu) || (menu->iItem == NO_SELECTED_ITEM)) return 0;
3727
3728 item = &menu->rgItems[menu->iItem];
3729 if (item && (item->spSubMenu) && (item->fState & MF_MOUSESELECT))
3730 {
3731 return item->spSubMenu;
3732 }
3733 return 0;
3734 }
3735
3736 /***********************************************************************
3737 * MenuDoNextMenu
3738 *
3739 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
3740 */
3741 static LRESULT FASTCALL MENU_DoNextMenu(MTRACKER* pmt, UINT Vk, UINT wFlags)
3742 {
3743 BOOL atEnd = FALSE;
3744
3745 /* When skipping left, we need to do something special after the
3746 first menu. */
3747 if (Vk == VK_LEFT && pmt->TopMenu->iItem == 0)
3748 {
3749 atEnd = TRUE;
3750 }
3751 /* When skipping right, for the non-system menu, we need to
3752 handle the last non-special menu item (ie skip any window
3753 icons such as MDI maximize, restore or close) */
3754 else if ((Vk == VK_RIGHT) && !IS_SYSTEM_MENU(pmt->TopMenu))
3755 {
3756 UINT i = pmt->TopMenu->iItem + 1;
3757 while (i < pmt->TopMenu->cItems) {
3758 if ((pmt->TopMenu->rgItems[i].wID >= SC_SIZE &&
3759 pmt->TopMenu->rgItems[i].wID <= SC_RESTORE)) {
3760 i++;
3761 } else break;
3762 }
3763 if (i == pmt->TopMenu->cItems) {
3764 atEnd = TRUE;
3765 }
3766 }
3767 /* When skipping right, we need to cater for the system menu */
3768 else if ((Vk == VK_RIGHT) && IS_SYSTEM_MENU(pmt->TopMenu))
3769 {
3770 if (pmt->TopMenu->iItem == (pmt->TopMenu->cItems - 1)) {
3771 atEnd = TRUE;
3772 }
3773 }
3774
3775 if ( atEnd )
3776 {
3777 MDINEXTMENU NextMenu;
3778 PMENU MenuTmp;
3779 PWND pwndTemp;
3780 HMENU hNewMenu;
3781 HWND hNewWnd;
3782 UINT Id = 0;
3783
3784 MenuTmp = (IS_SYSTEM_MENU(pmt->TopMenu)) ? co_IntGetSubMenu(pmt->TopMenu, 0) : pmt->TopMenu;
3785 NextMenu.hmenuIn = UserHMGetHandle(MenuTmp);
3786 NextMenu.hmenuNext = NULL;
3787 NextMenu.hwndNext = NULL;
3788 co_IntSendMessage(UserHMGetHandle(pmt->OwnerWnd), WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
3789
3790 TRACE("%p [%p] -> %p [%p]\n",
3791 pmt->CurrentMenu, pmt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
3792
3793 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
3794 {
3795 hNewWnd = UserHMGetHandle(pmt->OwnerWnd);
3796 if (IS_SYSTEM_MENU(pmt->TopMenu))
3797 {
3798 /* switch to the menu bar */
3799
3800 if (pmt->OwnerWnd->style & WS_CHILD || !(MenuTmp = IntGetMenu(hNewWnd))) return FALSE;
3801
3802 if (Vk == VK_LEFT)
3803 {
3804 Id = MenuTmp->cItems - 1;
3805
3806 /* Skip backwards over any system predefined icons,
3807 eg. MDI close, restore etc icons */
3808 while ((Id > 0) &&
3809 (MenuTmp->rgItems[Id].wID >= SC_SIZE &&
3810 MenuTmp->rgItems[Id].wID <= SC_RESTORE)) Id--;
3811
3812 }
3813 hNewMenu = UserHMGetHandle(MenuTmp);
3814 }
3815 else if (pmt->OwnerWnd->style & WS_SYSMENU)
3816 {
3817 /* switch to the system menu */
3818 MenuTmp = get_win_sys_menu(hNewWnd);
3819 if (MenuTmp) hNewMenu = UserHMGetHandle(MenuTmp);
3820 }
3821 else
3822 return FALSE;
3823 }
3824 else /* application returned a new menu to switch to */
3825 {
3826 hNewMenu = NextMenu.hmenuNext;
3827 hNewWnd = NextMenu.hwndNext;
3828
3829 if ((MenuTmp = UserGetMenuObject(hNewMenu)) && (pwndTemp = ValidateHwndNoErr(hNewWnd)))
3830 {
3831 if ( pwndTemp->style & WS_SYSMENU && (get_win_sys_menu(hNewWnd) == MenuTmp) )
3832 {
3833 /* get the real system menu */
3834 MenuTmp = get_win_sys_menu(hNewWnd);
3835 hNewMenu = UserHMGetHandle(MenuTmp);
3836 }
3837 else if (pwndTemp->style & WS_CHILD || IntGetMenu(hNewWnd) != MenuTmp)
3838 {
3839 /* FIXME: Not sure what to do here;
3840 * perhaps try to track NewMenu as a popup? */
3841
3842 WARN(" -- got confused.\n");
3843 return FALSE;
3844 }
3845 }
3846 else return FALSE;
3847 }
3848
3849 if (hNewMenu != UserHMGetHandle(pmt->TopMenu))
3850 {
3851 MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3852
3853 if (pmt->CurrentMenu != pmt->TopMenu)
3854 MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags);
3855 }
3856
3857 if (hNewWnd != UserHMGetHandle(pmt->OwnerWnd))
3858 {
3859 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
3860 pmt->OwnerWnd = ValidateHwndNoErr(hNewWnd);
3861 ///// Use thread pms!!!!
3862 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, hNewWnd);
3863 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
3864 co_UserSetCapture(UserHMGetHandle(pmt->OwnerWnd));
3865 pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED;
3866 }
3867
3868 pmt->TopMenu = pmt->CurrentMenu = UserGetMenuObject(hNewMenu); /* all subpopups are hidden */
3869 MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, Id, TRUE, 0);
3870
3871 return TRUE;
3872 }
3873 return FALSE;
3874 }
3875
3876 /***********************************************************************
3877 * MenuSuspendPopup
3878 *
3879 * The idea is not to show the popup if the next input message is
3880 * going to hide it anyway.
3881 */
3882 static BOOL FASTCALL MENU_SuspendPopup(MTRACKER* pmt, UINT uMsg)
3883 {
3884 MSG msg;
3885
3886 msg.hwnd = UserHMGetHandle(pmt->OwnerWnd); ////// ? silly wine'isms?
3887
3888 co_IntGetPeekMessage( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE, FALSE);
3889 pmt->TrackFlags |= TF_SKIPREMOVE;
3890
3891 switch( uMsg )
3892 {
3893 case WM_KEYDOWN:
3894 co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE, FALSE);
3895 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
3896 {
3897 co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE, FALSE);
3898 co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE, FALSE);
3899 if( msg.message == WM_KEYDOWN &&
3900 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
3901 {
3902 pmt->TrackFlags |= TF_SUSPENDPOPUP;
3903 return TRUE;
3904 }
3905 }
3906 break;
3907 }
3908 /* failures go through this */
3909 pmt->TrackFlags &= ~TF_SUSPENDPOPUP;
3910 return FALSE;
3911 }
3912
3913 /***********************************************************************
3914 * MenuKeyEscape
3915 *
3916 * Handle a VK_ESCAPE key event in a menu.
3917 */
3918 static BOOL FASTCALL MENU_KeyEscape(MTRACKER *pmt, UINT Flags)
3919 {
3920 BOOL EndMenu = TRUE;
3921
3922 if (pmt->CurrentMenu != pmt->TopMenu)
3923 {
3924 if (pmt->CurrentMenu && (pmt->CurrentMenu->fFlags & MNF_POPUP))
3925 {
3926 PMENU MenuPrev, MenuTmp;
3927
3928 MenuPrev = MenuTmp = pmt->TopMenu;
3929
3930 /* close topmost popup */
3931 while (MenuTmp != pmt->CurrentMenu)
3932 {
3933 MenuPrev = MenuTmp;
3934 MenuTmp = MENU_GetSubPopup(MenuPrev);
3935 }
3936
3937 MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags);
3938 pmt->CurrentMenu = MenuPrev;
3939 EndMenu = FALSE;
3940 }
3941 }
3942
3943 return EndMenu;
3944 }
3945
3946 /***********************************************************************
3947 * MenuKeyLeft
3948 *
3949 * Handle a VK_LEFT key event in a menu.
3950 */
3951 static void FASTCALL MENU_KeyLeft(MTRACKER* pmt, UINT Flags, UINT msg)
3952 {
3953 PMENU MenuTmp, MenuPrev;
3954 UINT PrevCol;
3955
3956 MenuPrev = MenuTmp = pmt->TopMenu;
3957
3958 /* Try to move 1 column left (if possible) */
3959 if ( (PrevCol = MENU_GetStartOfPrevColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM)
3960 {
3961 MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, PrevCol, TRUE, 0);
3962 return;
3963 }
3964
3965 /* close topmost popup */
3966 while (MenuTmp != pmt->CurrentMenu)
3967 {
3968 MenuPrev = MenuTmp;
3969 MenuTmp = MENU_GetSubPopup(MenuPrev);
3970 }
3971
3972 MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags);
3973 pmt->CurrentMenu = MenuPrev;
3974
3975 if ((MenuPrev == pmt->TopMenu) && !(pmt->TopMenu->fFlags & MNF_POPUP))
3976 {
3977 /* move menu bar selection if no more popups are left */
3978
3979 if (!MENU_DoNextMenu(pmt, VK_LEFT, Flags))
3980 MENU_MoveSelection(pmt->OwnerWnd, pmt->TopMenu, ITEM_PREV);
3981
3982 if (MenuPrev != MenuTmp || pmt->TrackFlags & TF_SUSPENDPOPUP)
3983 {
3984 /* A sublevel menu was displayed - display the next one
3985 * unless there is another displacement coming up */
3986
3987 if (!MENU_SuspendPopup(pmt, msg))
3988 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu,
3989 TRUE, Flags);
3990 }
3991 }
3992 }
3993
3994 /***********************************************************************
3995 * MenuKeyRight
3996 *
3997 * Handle a VK_RIGHT key event in a menu.
3998 */
3999 static void FASTCALL MENU_KeyRight(MTRACKER *pmt, UINT Flags, UINT msg)
4000 {
4001 PMENU menutmp;
4002 UINT NextCol;
4003
4004 TRACE("MenuKeyRight called, cur %p, top %p.\n",
4005 pmt->CurrentMenu, pmt->TopMenu);
4006
4007 if ((pmt->TopMenu->fFlags & MNF_POPUP) || (pmt->CurrentMenu != pmt->TopMenu))
4008 {
4009 /* If already displaying a popup, try to display sub-popup */
4010
4011 menutmp = pmt->CurrentMenu;
4012 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, menutmp, TRUE, Flags);
4013
4014 /* if subpopup was displayed then we are done */
4015 if (menutmp != pmt->CurrentMenu) return;
4016 }
4017
4018 /* Check to see if there's another column */
4019 if ( (NextCol = MENU_GetStartOfNextColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM)
4020 {
4021 TRACE("Going to %d.\n", NextCol);
4022 MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NextCol, TRUE, 0);
4023 return;
4024 }
4025
4026 if (!(pmt->TopMenu->fFlags & MNF_POPUP)) /* menu bar tracking */
4027 {
4028 if (pmt->CurrentMenu != pmt->TopMenu)
4029 {
4030 MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, Flags);
4031 menutmp = pmt->CurrentMenu = pmt->TopMenu;
4032 }
4033 else
4034 {
4035 menutmp = NULL;
4036 }
4037
4038 /* try to move to the next item */
4039 if ( !MENU_DoNextMenu(pmt, VK_RIGHT, Flags))
4040 MENU_MoveSelection(pmt->OwnerWnd, pmt->TopMenu, ITEM_NEXT);
4041
4042 if ( menutmp || pmt->TrackFlags & TF_SUSPENDPOPUP )
4043 {
4044 if ( !MENU_SuspendPopup(pmt, msg) )
4045 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu, TRUE, Flags);
4046 }
4047 }
4048 }
4049
4050 /***********************************************************************
4051 * MenuTrackMenu
4052 *
4053 * Menu tracking code.
4054 */
4055 static INT FASTCALL MENU_TrackMenu(PMENU pmenu, UINT wFlags, INT x, INT y,
4056 PWND pwnd)
4057 {
4058 MSG msg;
4059 BOOL fRemove;
4060 INT executedMenuId = -1;
4061 MTRACKER mt;
4062 HWND capture_win;
4063 PMENU pmMouse;
4064 BOOL enterIdleSent = FALSE;
4065 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
4066
4067 if (pti != pwnd->head.pti)
4068 {
4069 ERR("Not the same PTI!!!!\n");
4070 }
4071
4072 mt.TrackFlags = 0;
4073 mt.CurrentMenu = pmenu;
4074 mt.TopMenu = pmenu;
4075 mt.OwnerWnd = pwnd;
4076 mt.Pt.x = x;
4077 mt.Pt.y = y;
4078
4079 TRACE("MTM : hmenu=%p flags=0x%08x (%d,%d) hwnd=%x\n",
4080 UserHMGetHandle(pmenu), wFlags, x, y, UserHMGetHandle(pwnd));
4081
4082 pti->MessageQueue->QF_flags &= ~QF_ACTIVATIONCHANGE;
4083
4084 if (wFlags & TPM_BUTTONDOWN)
4085 {
4086 /* Get the result in order to start the tracking or not */
4087 fRemove = MENU_ButtonDown( &mt, pmenu, wFlags );
4088 fInsideMenuLoop = fRemove;
4089 }
4090
4091 if (wFlags & TF_ENDMENU) fInsideMenuLoop = FALSE;
4092
4093 if (wFlags & TPM_POPUPMENU && pmenu->cItems == 0) // Tracking empty popup menu...
4094 {
4095 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL);
4096 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
4097 co_UserSetCapture(NULL); /* release the capture */
4098 return 0;
4099 }
4100
4101 capture_win = IntGetCapture();
4102
4103 while (fInsideMenuLoop)
4104 {
4105 BOOL ErrorExit = FALSE;
4106 if (!VerifyMenu( mt.CurrentMenu )) /* sometimes happens if I do a window manager close */
4107 break;
4108
4109 /* we have to keep the message in the queue until it's
4110 * clear that menu loop is not over yet. */
4111
4112 for (;;)
4113 {
4114 if (co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOREMOVE, FALSE ))
4115 {
4116 if (!IntCallMsgFilter( &msg, MSGF_MENU )) break;
4117 /* remove the message from the queue */
4118 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4119 }
4120 else
4121 {
4122 /* ReactOS Checks */
4123 if (!VerifyWnd(mt.OwnerWnd) ||
4124 !ValidateHwndNoErr(mt.CurrentMenu->hWnd) ||
4125 pti->MessageQueue->QF_flags & QF_ACTIVATIONCHANGE ||
4126 capture_win != IntGetCapture() ) // Should not happen, but this is ReactOS...
4127 {
4128 ErrorExit = TRUE; // Do not wait on dead windows, now win test_capture_4 works.
4129 break;
4130 }
4131
4132 if (!enterIdleSent)
4133 {
4134 HWND win = mt.CurrentMenu->fFlags & MNF_POPUP ? mt.CurrentMenu->hWnd : NULL;
4135 enterIdleSent = TRUE;
4136 co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_ENTERIDLE, MSGF_MENU, (LPARAM) win);
4137 }
4138 co_IntWaitMessage(NULL, 0, 0);
4139 }
4140 }
4141
4142 if (ErrorExit) break; // Gracefully dropout.
4143
4144 /* check if EndMenu() tried to cancel us, by posting this message */
4145 if (msg.message == WM_CANCELMODE)
4146 {
4147 /* we are now out of the loop */
4148 fInsideMenuLoop = FALSE;
4149
4150 /* remove the message from the queue */
4151 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4152
4153 /* break out of internal loop, ala ESCAPE */
4154 break;
4155 }
4156
4157 mt.Pt = msg.pt;
4158
4159 if ( (msg.hwnd == mt.CurrentMenu->hWnd) || ((msg.message!=WM_TIMER) && (msg.message!=WM_SYSTIMER)) )
4160 enterIdleSent=FALSE;
4161
4162 fRemove = FALSE;
4163 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
4164 {
4165 /*
4166 * Use the mouse coordinates in lParam instead of those in the MSG
4167 * struct to properly handle synthetic messages. They are already
4168 * in screen coordinates.
4169 */
4170 mt.Pt.x = (short)LOWORD(msg.lParam);
4171 mt.Pt.y = (short)HIWORD(msg.lParam);
4172
4173 /* Find a menu for this mouse event */
4174 pmMouse = MENU_PtMenu( mt.TopMenu, mt.Pt );
4175
4176 switch(msg.message)
4177 {
4178 /* no WM_NC... messages in captured state */
4179
4180 case WM_RBUTTONDBLCLK:
4181 case WM_RBUTTONDOWN:
4182 if (!(wFlags & TPM_RIGHTBUTTON))
4183 {
4184 if ( msg.message == WM_RBUTTONDBLCLK ) fInsideMenuLoop = FALSE; // Must exit or loop forever!
4185 break;
4186 }
4187 /* fall through */
4188 case WM_LBUTTONDBLCLK:
4189 case WM_LBUTTONDOWN:
4190 /* If the message belongs to the menu, removes it from the queue */
4191 /* Else, end menu tracking */
4192 fRemove = MENU_ButtonDown(&mt, pmMouse, wFlags);
4193 fInsideMenuLoop = fRemove;
4194 if ( msg.message == WM_LBUTTONDBLCLK ||
4195 msg.message == WM_RBUTTONDBLCLK ) fInsideMenuLoop = FALSE; // Must exit or loop forever!
4196 break;
4197
4198 case WM_RBUTTONUP:
4199 if (!(wFlags & TPM_RIGHTBUTTON)) break;
4200 /* fall through */
4201 case WM_LBUTTONUP:
4202 /* Check if a menu was selected by the mouse */
4203 if (pmMouse)
4204 {
4205 executedMenuId = MENU_ButtonUp( &mt, pmMouse, wFlags);
4206
4207 /* End the loop if executedMenuId is an item ID */
4208 /* or if the job was done (executedMenuId = 0). */
4209 fRemove = (executedMenuId != -1);
4210 fInsideMenuLoop = !fRemove;
4211 }
4212 /* No menu was selected by the mouse */
4213 /* if the function was called by TrackPopupMenu, continue
4214 with the menu tracking. If not, stop it */
4215 else
4216 fInsideMenuLoop = ((wFlags & TPM_POPUPMENU) ? TRUE : FALSE);
4217
4218 break;
4219
4220 case WM_MOUSEMOVE:
4221 /* the selected menu item must be changed every time */
4222 /* the mouse moves. */
4223
4224 if (pmMouse)
4225 fInsideMenuLoop |= MENU_MouseMove( &mt, pmMouse, wFlags );
4226
4227 } /* switch(msg.message) - mouse */
4228 }
4229 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
4230 {
4231 fRemove = TRUE; /* Keyboard messages are always removed */
4232 switch(msg.message)
4233 {
4234 case WM_KEYDOWN:
4235 case WM_SYSKEYDOWN:
4236 switch(msg.wParam)
4237 {
4238 case VK_MENU:
4239 case VK_F10:
4240 fInsideMenuLoop = FALSE;
4241 break;
4242
4243 case VK_HOME:
4244 case VK_END:
4245 MENU_SelectItem(mt.OwnerWnd, mt.CurrentMenu, NO_SELECTED_ITEM, FALSE, 0 );
4246 MENU_MoveSelection(mt.OwnerWnd, mt.CurrentMenu, VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV);
4247 break;
4248
4249 case VK_UP:
4250 case VK_DOWN: /* If on menu bar, pull-down the menu */
4251 if (!(mt.CurrentMenu->fFlags & MNF_POPUP))
4252 mt.CurrentMenu = MENU_ShowSubPopup(mt.OwnerWnd, mt.TopMenu, TRUE, wFlags);
4253 else /* otherwise try to move selection */
4254 MENU_MoveSelection(mt.OwnerWnd, mt.CurrentMenu, (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
4255 break;
4256
4257 case VK_LEFT:
4258 MENU_KeyLeft( &mt, wFlags, msg.message );
4259 break;
4260
4261 case VK_RIGHT:
4262 MENU_KeyRight( &mt, wFlags, msg.message );
4263 break;
4264
4265 case VK_ESCAPE:
4266 fInsideMenuLoop = !MENU_KeyEscape(&mt, wFlags);
4267 break;
4268
4269 case VK_F1:
4270 {
4271 HELPINFO hi;
4272 hi.cbSize = sizeof(HELPINFO);
4273 hi.iContextType = HELPINFO_MENUITEM;
4274 if (mt.CurrentMenu->iItem == NO_SELECTED_ITEM)
4275 hi.iCtrlId = 0;
4276 else
4277 hi.iCtrlId = pmenu->rgItems[mt.CurrentMenu->iItem].wID;
4278 hi.hItemHandle = UserHMGetHandle(mt.CurrentMenu);
4279 hi.dwContextId = pmenu->dwContextHelpId;
4280 hi.MousePos = msg.pt;
4281 co_IntSendMessage( UserHMGetHandle(pwnd), WM_HELP, 0, (LPARAM)&hi);
4282 break;
4283 }
4284
4285 default:
4286 IntTranslateKbdMessage(&msg, 0);
4287 break;
4288 }
4289 break; /* WM_KEYDOWN */
4290
4291 case WM_CHAR:
4292 case WM_SYSCHAR:
4293 {
4294 UINT pos;
4295 BOOL fEndMenu;
4296
4297 if (msg.wParam == L'\r' || msg.wParam == L' ')
4298 {
4299 executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags);
4300 fEndMenu = (executedMenuId != -2);
4301 fInsideMenuLoop = !fEndMenu;
4302 break;
4303 }
4304
4305 /* Hack to avoid control chars. */
4306 /* We will find a better way real soon... */
4307 if (msg.wParam < 32) break;
4308
4309 pos = MENU_FindItemByKey(mt.OwnerWnd, mt.CurrentMenu, LOWORD(msg.wParam), FALSE);
4310
4311 if (pos == (UINT)-2) fInsideMenuLoop = FALSE;
4312 else if (pos == (UINT)-1) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0);
4313 else
4314 {
4315 MENU_SelectItem(mt.OwnerWnd, mt.CurrentMenu, pos, TRUE, 0);
4316 executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags);
4317 fEndMenu = (executedMenuId != -2);
4318 fInsideMenuLoop = !fEndMenu;
4319 }
4320 }
4321 break;
4322 } /* switch(msg.message) - kbd */
4323 }
4324 else
4325 {
4326 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4327 IntDispatchMessage( &msg );
4328 continue;
4329 }
4330
4331 if (fInsideMenuLoop) fRemove = TRUE;
4332
4333 /* finally remove message from the queue */
4334
4335 if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) )
4336 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4337 else mt.TrackFlags &= ~TF_SKIPREMOVE;
4338 }
4339
4340 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL);
4341 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
4342 co_UserSetCapture(NULL); /* release the capture */
4343
4344 /* If dropdown is still painted and the close box is clicked on
4345 then the menu will be destroyed as part of the DispatchMessage above.
4346 This will then invalidate the menu handle in mt.hTopMenu. We should
4347 check for this first. */
4348 if ( VerifyMenu( mt.TopMenu ) )
4349 {
4350 if (VerifyWnd(mt.OwnerWnd))
4351 {
4352 MENU_HideSubPopups(mt.OwnerWnd, mt.TopMenu, FALSE, wFlags);
4353
4354 if (mt.TopMenu->fFlags & MNF_POPUP)
4355 {
4356 PWND pwndTM = ValidateHwndNoErr(mt.TopMenu->hWnd);
4357 if (pwndTM)
4358 {
4359 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, pwndTM, OBJID_CLIENT, CHILDID_SELF, 0);
4360
4361 co_UserDestroyWindow(pwndTM);
4362 }
4363 mt.TopMenu->hWnd = NULL;
4364
4365 if (!(wFlags & TPM_NONOTIFY))
4366 {
4367 co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(mt.TopMenu),
4368 MAKELPARAM(0, IS_SYSTEM_MENU(mt.TopMenu)) );
4369 }
4370 }
4371 MENU_SelectItem( mt.OwnerWnd, mt.TopMenu, NO_SELECTED_ITEM, FALSE, 0 );
4372 co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 );
4373 }
4374
4375 /* Reset the variable for hiding menu */
4376 mt.TopMenu->TimeToHide = FALSE;
4377 }
4378
4379 EngSetLastError( ERROR_SUCCESS );
4380 /* The return value is only used by TrackPopupMenu */
4381 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
4382 if (executedMenuId == -1) executedMenuId = 0;
4383 return executedMenuId;
4384 }
4385
4386 /***********************************************************************
4387 * MenuInitTracking
4388 */
4389 static BOOL FASTCALL MENU_InitTracking(PWND pWnd, PMENU Menu, BOOL bPopup, UINT wFlags)
4390 {
4391 HWND capture_win;
4392 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
4393
4394 TRACE("hwnd=%p hmenu=%p\n", UserHMGetHandle(pWnd), UserHMGetHandle(Menu));
4395
4396 co_UserHideCaret(0);
4397
4398 /* This makes the menus of applications built with Delphi work.
4399 * It also enables menus to be displayed in more than one window,
4400 * but there are some bugs left that need to be fixed in this case.
4401 */
4402 if (!bPopup)
4403 {
4404 Menu->hWnd = UserHMGetHandle(pWnd);
4405 }
4406
4407 if (!top_popup) {
4408 top_popup = Menu->hWnd;
4409 top_popup_hmenu = UserHMGetHandle(Menu);
4410 }
4411
4412 fInsideMenuLoop = TRUE;
4413 fInEndMenu = FALSE;
4414
4415 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
4416 if (!(wFlags & TPM_NONOTIFY))
4417 {
4418 co_IntSendMessage( UserHMGetHandle(pWnd), WM_ENTERMENULOOP, bPopup, 0 );
4419 }
4420
4421 //
4422 // Capture is set before calling WM_INITMENU and after WM_ENTERMENULOOP, see msg_menu.
4423 //
4424 capture_win = (wFlags & TPM_POPUPMENU) ? Menu->hWnd : UserHMGetHandle(pWnd);
4425 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, capture_win); // 1
4426 co_UserSetCapture(capture_win); // 2
4427 pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED; // Set the Q bits so noone can change this!
4428
4429 co_IntSendMessage( UserHMGetHandle(pWnd), WM_SETCURSOR, (WPARAM)UserHMGetHandle(pWnd), HTCAPTION );
4430
4431 if (!(wFlags & TPM_NONOTIFY))
4432 {
4433 co_IntSendMessage( UserHMGetHandle(pWnd), WM_INITMENU, (WPARAM)UserHMGetHandle(Menu), 0 );
4434 /* If an app changed/recreated menu bar entries in WM_INITMENU
4435 * menu sizes will be recalculated once the menu created/shown.
4436 */
4437 }
4438
4439 IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART,
4440 pWnd,
4441 Menu->fFlags & MNF_SYSMENU ? OBJID_SYSMENU : OBJID_MENU,
4442 CHILDID_SELF, 0);
4443 return TRUE;
4444 }
4445
4446 /***********************************************************************
4447 * MenuExitTracking
4448 */
4449 static BOOL FASTCALL MENU_ExitTracking(PWND pWnd, BOOL bPopup, UINT wFlags)
4450 {
4451 TRACE("Exit Track hwnd=%p bPopup %d\n", UserHMGetHandle(pWnd), bPopup);
4452
4453 IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, pWnd, OBJID_WINDOW, CHILDID_SELF, 0);
4454
4455 if (!(wFlags & TPM_NONOTIFY))
4456 co_IntSendMessage( UserHMGetHandle(pWnd), WM_EXITMENULOOP, bPopup, 0 );
4457
4458 co_UserShowCaret(0);
4459
4460 top_popup = 0;
4461 top_popup_hmenu = NULL;
4462
4463 return TRUE;
4464 }
4465
4466 /***********************************************************************
4467 * MenuTrackMouseMenuBar
4468 *
4469 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
4470 */
4471 VOID MENU_TrackMouseMenuBar( PWND pWnd, ULONG ht, POINT pt)
4472 {
4473 PMENU pMenu = (ht == HTSYSMENU) ? IntGetSystemMenu(pWnd, FALSE) : IntGetMenu( UserHMGetHandle(pWnd) ); // See 74276 and CORE-12801
4474 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
4475
4476 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", pWnd, ht, pt.x, pt.y);
4477
4478 if (pWnd->ExStyle & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
4479 if (VerifyMenu(pMenu))
4480 {
4481 /* map point to parent client coordinates */
4482 PWND Parent = UserGetAncestor(pWnd, GA_PARENT );
4483 if (Parent != UserGetDesktopWindow())
4484 {
4485 IntScreenToClient(Parent, &pt);
4486 }
4487
4488 MENU_InitTracking(pWnd, pMenu, FALSE, wFlags);
4489 /* fetch the window menu again, it may have changed */
4490 pMenu = (ht == HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pWnd) ) : IntGetMenu( UserHMGetHandle(pWnd) );
4491 MENU_TrackMenu(pMenu, wFlags, pt.x, pt.y, pWnd);
4492 MENU_ExitTracking(pWnd, FALSE, wFlags);
4493 }
4494 }
4495
4496 /***********************************************************************
4497 * MenuTrackKbdMenuBar
4498 *
4499 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
4500 */
4501 VOID MENU_TrackKbdMenuBar(PWND pwnd, UINT wParam, WCHAR wChar)
4502 {
4503 UINT uItem = NO_SELECTED_ITEM;
4504 PMENU TrackMenu;
4505 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
4506
4507 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", UserHMGetHandle(pwnd), wParam, wChar);
4508
4509 /* find window that has a menu */
4510
4511 while (!( (pwnd->style & (WS_CHILD | WS_POPUP)) != WS_CHILD ) )
4512 if (!(pwnd = UserGetAncestor( pwnd, GA_PARENT ))) return;
4513
4514 /* check if we have to track a system menu */
4515
4516 TrackMenu = IntGetMenu( UserHMGetHandle(pwnd) );
4517 if (!TrackMenu || (pwnd->style & WS_MINIMIZE) != 0 || wChar == ' ' )
4518 {
4519 if (!(pwnd->style & WS_SYSMENU)) return;
4520 TrackMenu = get_win_sys_menu( UserHMGetHandle(pwnd) );
4521 uItem = 0;
4522 wParam |= HTSYSMENU; /* prevent item lookup */
4523 }
4524
4525 if (!VerifyMenu( TrackMenu )) return;
4526
4527 MENU_InitTracking( pwnd, TrackMenu, FALSE, wFlags );
4528
4529 /* fetch the window menu again, it may have changed */
4530 TrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pwnd) ) : IntGetMenu( UserHMGetHandle(pwnd) );
4531
4532 if( wChar && wChar != ' ' )
4533 {
4534 uItem = MENU_FindItemByKey( pwnd, TrackMenu, wChar, (wParam & HTSYSMENU) );
4535 if ( uItem >= (UINT)(-2) )
4536 {
4537 if( uItem == (UINT)(-1) ) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0);
4538 /* schedule end of menu tracking */
4539 wFlags |= TF_ENDMENU;
4540 goto track_menu;
4541 }
4542 }
4543
4544 MENU_SelectItem( pwnd, TrackMenu, uItem, TRUE, 0 );
4545
4546 if (!(wParam & HTSYSMENU) || wChar == ' ')
4547 {
4548 if( uItem == NO_SELECTED_ITEM )
4549 MENU_MoveSelection( pwnd, TrackMenu, ITEM_NEXT );
4550 else
4551 UserPostMessage( UserHMGetHandle(pwnd), WM_KEYDOWN, VK_RETURN, 0 );
4552 }
4553
4554 track_menu:
4555 MENU_TrackMenu( TrackMenu, wFlags, 0, 0, pwnd );
4556 MENU_ExitTracking( pwnd, FALSE, wFlags);
4557 }
4558
4559 /**********************************************************************
4560 * TrackPopupMenuEx (USER32.@)
4561 */
4562 BOOL WINAPI IntTrackPopupMenuEx( PMENU menu, UINT wFlags, int x, int y,
4563 PWND pWnd, LPTPMPARAMS lpTpm)
4564 {
4565 BOOL ret = FALSE;
4566 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
4567
4568 if (pti != pWnd->head.pti)
4569 {
4570 ERR("Must be the same pti!\n");
4571 return ret;
4572 }
4573
4574 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p \n", //rect %s\n",
4575 UserHMGetHandle(menu), wFlags, x, y, UserHMGetHandle(pWnd), lpTpm); //,
4576 //lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
4577
4578 if (menu->hWnd && IntIsWindow(menu->hWnd))
4579 {
4580 EngSetLastError( ERROR_POPUP_ALREADY_ACTIVE );
4581 return FALSE;
4582 }
4583
4584 if (MENU_InitPopup( pWnd, menu, wFlags ))
4585 {
4586 MENU_InitTracking(pWnd, menu, TRUE, wFlags);
4587
4588 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
4589 if (!(wFlags & TPM_NONOTIFY))
4590 {
4591 co_IntSendMessage( UserHMGetHandle(pWnd), WM_INITMENUPOPUP, (WPARAM) UserHMGetHandle(menu), 0);
4592 }
4593
4594 if (menu->fFlags & MNF_SYSMENU)
4595 MENU_InitSysMenuPopup( menu, pWnd->style, pWnd->pcls->style, HTSYSMENU);
4596
4597 if (MENU_ShowPopup(pWnd, menu, 0, wFlags | TPM_POPUPMENU, x, y, lpTpm ? &lpTpm->rcExclude : NULL))
4598 ret = MENU_TrackMenu( menu, wFlags | TPM_POPUPMENU, 0, 0, pWnd);
4599 else
4600 {
4601 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL);
4602 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
4603 co_UserSetCapture(NULL); /* release the capture */
4604 }
4605
4606 MENU_ExitTracking(pWnd, TRUE, wFlags);
4607
4608 if (menu->hWnd)
4609 {
4610 PWND pwndM = ValidateHwndNoErr( menu->hWnd );
4611 if (pwndM) // wine hack around this with their destroy function.
4612 {
4613 if (!(pWnd->state & WNDS_DESTROYED))
4614 co_UserDestroyWindow( pwndM ); // Fix wrong error return.
4615 }
4616 menu->hWnd = 0;
4617
4618 if (!(wFlags & TPM_NONOTIFY))
4619 {
4620 co_IntSendMessage( UserHMGetHandle(pWnd), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(menu),
4621 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
4622 }
4623 }
4624 }
4625 return ret;
4626 }
4627
4628 //
4629 // Menu Class Proc.
4630 //
4631 BOOL WINAPI
4632 PopupMenuWndProc(
4633 PWND Wnd,
4634 UINT Message,
4635 WPARAM wParam,
4636 LPARAM lParam,
4637 LRESULT *lResult)
4638 {
4639 PPOPUPMENU pPopupMenu;
4640
4641 *lResult = 0;
4642
4643 TRACE("PMWP : pwnd=%x msg=%d wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
4644
4645 if (Wnd)
4646 {
4647 if (!Wnd->fnid)
4648 {
4649 if (Message != WM_NCCREATE)
4650 {
4651 *lResult = IntDefWindowProc(Wnd, Message, wParam, lParam, FALSE);
4652 return TRUE;
4653 }
4654 Wnd->fnid = FNID_MENU;
4655 pPopupMenu = DesktopHeapAlloc( Wnd->head.rpdesk, sizeof(POPUPMENU) );
4656 if (pPopupMenu == NULL)
4657 {
4658 return TRUE;
4659 }
4660 pPopupMenu->posSelectedItem = NO_SELECTED_ITEM;
4661 pPopupMenu->spwndPopupMenu = Wnd;
4662 ((PMENUWND)Wnd)->ppopupmenu = pPopupMenu;
4663 TRACE("Pop Up Menu is Setup! Msg %d\n",Message);
4664 *lResult = 1;
4665 return TRUE;
4666 }
4667 else
4668 {
4669 if (Wnd->fnid != FNID_MENU)
4670 {
4671 ERR("Wrong window class for Menu! fnid %x\n",Wnd->fnid);
4672 return TRUE;
4673 }
4674 pPopupMenu = ((PMENUWND)Wnd)->ppopupmenu;
4675 }
4676 }
4677
4678 switch(Message)
4679 {
4680 case WM_CREATE:
4681 {
4682 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
4683 pPopupMenu->spmenu = UserGetMenuObject(cs->lpCreateParams);
4684 if (pPopupMenu->spmenu)
4685 {
4686 UserReferenceObject(pPopupMenu->spmenu);
4687 }
4688 break;
4689 }
4690
4691 case WM_MOUSEACTIVATE: /* We don't want to be activated */
4692 *lResult = MA_NOACTIVATE;
4693 break;
4694
4695 case WM_PAINT:
4696 {
4697 PAINTSTRUCT ps;
4698 IntBeginPaint(Wnd, &ps);
4699 MENU_DrawPopupMenu(Wnd, ps.hdc, pPopupMenu->spmenu);
4700 IntEndPaint(Wnd, &ps);
4701 break;
4702 }
4703
4704 case WM_PRINTCLIENT:
4705 {
4706 MENU_DrawPopupMenu( Wnd, (HDC)wParam, pPopupMenu->spmenu);
4707 break;
4708 }
4709
4710 case WM_ERASEBKGND:
4711 *lResult = 1;
4712 break;
4713
4714 case WM_DESTROY:
4715 /* zero out global pointer in case resident popup window was destroyed. */
4716 if (pPopupMenu)
4717 {
4718 if (UserHMGetHandle(Wnd) == top_popup)
4719 {
4720 top_popup = NULL;
4721 top_popup_hmenu = NULL;
4722 }
4723 }
4724 else
4725 {
4726 ERR("No Window Pop Up!\n");
4727 }
4728 break;
4729
4730 case WM_NCDESTROY:
4731 {
4732 if (pPopupMenu->spmenu)
4733 {
4734 IntReleaseMenuObject(pPopupMenu->spmenu);
4735 }
4736 DesktopHeapFree(Wnd->head.rpdesk, pPopupMenu );
4737 ((PMENUWND)Wnd)->ppopupmenu = 0;
4738 Wnd->fnid = FNID_DESTROY;
4739 break;
4740 }
4741
4742 case MM_SETMENUHANDLE: // wine'isms
4743 case MN_SETHMENU:
4744 {
4745 PMENU pmenu = UserGetMenuObject((HMENU)wParam);
4746 if (!pmenu)
4747 {
4748 ERR("Bad Menu Handle\n");
4749 break;
4750 }
4751 UserReferenceObject(pmenu);
4752 if (pPopupMenu->spmenu)
4753 {
4754 IntReleaseMenuObject(pPopupMenu->spmenu);
4755 }
4756 pPopupMenu->spmenu = pmenu;
4757 break;
4758 }
4759
4760 case MM_GETMENUHANDLE: // wine'isms
4761 case MN_GETHMENU:
4762 *lResult = (LRESULT)(pPopupMenu ? (pPopupMenu->spmenu ? UserHMGetHandle(pPopupMenu->spmenu) : NULL) : NULL);
4763 break;
4764
4765 default:
4766 if (Message > MN_GETHMENU && Message < MN_GETHMENU+19)
4767 {
4768 ERR("Someone is passing unknown menu messages %d\n",Message);
4769 }
4770 TRACE("PMWP to IDWP %d\n",Message);
4771 *lResult = IntDefWindowProc(Wnd, Message, wParam, lParam, FALSE);
4772 break;
4773 }
4774
4775 return TRUE;
4776 }
4777
4778 BOOL FASTCALL
4779 IntHiliteMenuItem(PWND WindowObject,
4780 PMENU MenuObject,
4781 UINT uItemHilite,
4782 UINT uHilite)
4783 {
4784 PITEM MenuItem;
4785 UINT uItem = uItemHilite;
4786
4787 if (!(MenuItem = MENU_FindItem( &MenuObject, &uItem, uHilite ))) return TRUE;
4788
4789 if (uHilite & MF_HILITE)
4790 {
4791 MenuItem->fState |= MF_HILITE;
4792 }
4793 else
4794 {
4795 MenuItem->fState &= ~MF_HILITE;
4796 }
4797 if (MenuObject->iItem == uItemHilite) return TRUE;
4798 MENU_HideSubPopups( WindowObject, MenuObject, FALSE, 0 );
4799 MENU_SelectItem( WindowObject, MenuObject, uItemHilite, TRUE, 0 );
4800
4801 return TRUE; // Always returns true!!!!
4802 }
4803
4804 BOOLEAN APIENTRY
4805 intGetTitleBarInfo(PWND pWindowObject, PTITLEBARINFO bti)
4806 {
4807
4808 DWORD dwStyle = 0;
4809 DWORD dwExStyle = 0;
4810 BOOLEAN retValue = TRUE;
4811
4812 if (bti->cbSize == sizeof(TITLEBARINFO))
4813 {
4814 RtlZeroMemory(&bti->rgstate[0],sizeof(DWORD)*(CCHILDREN_TITLEBAR+1));
4815
4816 bti->rgstate[0] = STATE_SYSTEM_FOCUSABLE;
4817
4818 dwStyle = pWindowObject->style;
4819 dwExStyle = pWindowObject->ExStyle;
4820
4821 bti->rcTitleBar.top = 0;
4822 bti->rcTitleBar.left = 0;
4823 bti->rcTitleBar.right = pWindowObject->rcWindow.right - pWindowObject->rcWindow.left;
4824 bti->rcTitleBar.bottom = pWindowObject->rcWindow.bottom - pWindowObject->rcWindow.top;
4825
4826 /* Is it iconiced ? */
4827 if ((dwStyle & WS_ICONIC)!=WS_ICONIC)
4828 {
4829 /* Remove frame from rectangle */
4830 if (HAS_THICKFRAME( dwStyle, dwExStyle ))
4831 {
4832 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXFRAME) and UserGetSystemMetrics(SM_CYFRAME) */
4833 RECTL_vInflateRect( &bti->rcTitleBar, -UserGetSystemMetrics(SM_CXFRAME), -UserGetSystemMetrics(SM_CYFRAME) );
4834 }
4835 else if (HAS_DLGFRAME( dwStyle, dwExStyle ))
4836 {
4837 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXDLGFRAME) and UserGetSystemMetrics(SM_CYDLGFRAME) */
4838 RECTL_vInflateRect( &bti->rcTitleBar, -UserGetSystemMetrics(SM_CXDLGFRAME), -UserGetSystemMetrics(SM_CYDLGFRAME));
4839 }
4840 else if (HAS_THINFRAME( dwStyle, dwExStyle))
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 /* We have additional border information if the window
4847 * is a child (but not an MDI child) */
4848 if ( (dwStyle & WS_CHILD) &&
4849 ((dwExStyle & WS_EX_MDICHILD) == 0 ) )
4850 {
4851 if (dwExStyle & WS_EX_CLIENTEDGE)
4852 {
4853 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXEDGE) and UserGetSystemMetrics(SM_CYEDGE) */
4854 RECTL_vInflateRect (&bti->rcTitleBar, -UserGetSystemMetrics(SM_CXEDGE), -UserGetSystemMetrics(SM_CYEDGE));
4855 }
4856
4857 if (dwExStyle & WS_EX_STATICEDGE)
4858 {
4859 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXBORDER) and UserGetSystemMetrics(SM_CYBORDER) */
4860 RECTL_vInflateRect (&bti->rcTitleBar, -UserGetSystemMetrics(SM_CXBORDER), -UserGetSystemMetrics(SM_CYBORDER));
4861 }
4862 }
4863 }
4864
4865 bti->rcTitleBar.top += pWindowObject->rcWindow.top;
4866 bti->rcTitleBar.left += pWindowObject->rcWindow.left;
4867 bti->rcTitleBar.right += pWindowObject->rcWindow.left;
4868
4869 bti->rcTitleBar.bottom = bti->rcTitleBar.top;
4870 if (dwExStyle & WS_EX_TOOLWINDOW)
4871 {
4872 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CYSMCAPTION) */
4873 bti->rcTitleBar.bottom += UserGetSystemMetrics(SM_CYSMCAPTION);
4874 }
4875 else
4876 {
4877 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CYCAPTION) and UserGetSystemMetrics(SM_CXSIZE) */
4878 bti->rcTitleBar.bottom += UserGetSystemMetrics(SM_CYCAPTION);
4879 bti->rcTitleBar.left += UserGetSystemMetrics(SM_CXSIZE);
4880 }
4881
4882 if (dwStyle & WS_CAPTION)
4883 {
4884 bti->rgstate[1] = STATE_SYSTEM_INVISIBLE;
4885 if (dwStyle & WS_SYSMENU)
4886 {
4887 if (!(dwStyle & (WS_MINIMIZEBOX|WS_MAXIMIZEBOX)))
4888 {
4889 bti->rgstate[2] = STATE_SYSTEM_INVISIBLE;
4890 bti->rgstate[3] = STATE_SYSTEM_INVISIBLE;
4891 }
4892 else
4893 {
4894 if (!(dwStyle & WS_MINIMIZEBOX))
4895 {
4896 bti->rgstate[2] = STATE_SYSTEM_UNAVAILABLE;
4897 }
4898 if (!(dwStyle & WS_MAXIMIZEBOX))
4899 {
4900 bti->rgstate[3] = STATE_SYSTEM_UNAVAILABLE;
4901 }
4902 }
4903
4904 if (!(dwExStyle & WS_EX_CONTEXTHELP))
4905 {
4906 bti->rgstate[4] = STATE_SYSTEM_INVISIBLE;
4907 }
4908 if (pWindowObject->pcls->style & CS_NOCLOSE)
4909 {
4910 bti->rgstate[5] = STATE_SYSTEM_UNAVAILABLE;
4911 }
4912 }
4913 else
4914 {
4915 bti->rgstate[2] = STATE_SYSTEM_INVISIBLE;
4916 bti->rgstate[3] = STATE_SYSTEM_INVISIBLE;
4917 bti->rgstate[4] = STATE_SYSTEM_INVISIBLE;
4918 bti->rgstate[5] = STATE_SYSTEM_INVISIBLE;
4919 }
4920 }
4921 else
4922 {
4923 bti->rgstate[0] |= STATE_SYSTEM_INVISIBLE;
4924 }
4925 }
4926 else
4927 {
4928 EngSetLastError(ERROR_INVALID_PARAMETER);
4929 retValue = FALSE;
4930 }
4931
4932 return retValue;
4933 }
4934
4935 DWORD FASTCALL
4936 UserInsertMenuItem(
4937 PMENU Menu,
4938 UINT uItem,
4939 BOOL fByPosition,
4940 LPCMENUITEMINFOW UnsafeItemInfo,
4941 PUNICODE_STRING lpstr)
4942 {
4943 NTSTATUS Status;
4944 ROSMENUITEMINFO ItemInfo;
4945
4946 /* Try to copy the whole MENUITEMINFOW structure */
4947 Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, sizeof(MENUITEMINFOW));
4948 if (NT_SUCCESS(Status))
4949 {
4950 if (sizeof(MENUITEMINFOW) != ItemInfo.cbSize
4951 && FIELD_OFFSET(MENUITEMINFOW, hbmpItem) != ItemInfo.cbSize)
4952 {
4953 EngSetLastError(ERROR_INVALID_PARAMETER);
4954 return FALSE;
4955 }
4956 return IntInsertMenuItem(Menu, uItem, fByPosition, &ItemInfo, lpstr);
4957 }
4958
4959 /* Try to copy without last field (not present in older versions) */
4960 Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, FIELD_OFFSET(MENUITEMINFOW, hbmpItem));
4961 if (NT_SUCCESS(Status))
4962 {
4963 if (FIELD_OFFSET(MENUITEMINFOW, hbmpItem) != ItemInfo.cbSize)
4964 {
4965 EngSetLastError(ERROR_INVALID_PARAMETER);
4966 return FALSE;
4967 }
4968 ItemInfo.hbmpItem = (HBITMAP)0;
4969 return IntInsertMenuItem(Menu, uItem, fByPosition, &ItemInfo, lpstr);
4970 }
4971
4972 SetLastNtError(Status);
4973 return FALSE;
4974 }
4975
4976 UINT FASTCALL IntGetMenuState( HMENU hMenu, UINT uId, UINT uFlags)
4977 {
4978 PMENU MenuObject;
4979 PITEM pItem;
4980
4981 if (!(MenuObject = UserGetMenuObject(hMenu)))
4982 {
4983 return (UINT)-1;
4984 }
4985
4986 if (!(pItem = MENU_FindItem( &MenuObject, &uId, uFlags ))) return -1;
4987
4988 if (pItem->spSubMenu)
4989 {
4990 return (pItem->spSubMenu->cItems << 8) | ((pItem->fState|pItem->fType|MF_POPUP) & 0xff);
4991 }
4992 else
4993 return (pItem->fType | pItem->fState);
4994 }
4995
4996 HMENU FASTCALL IntGetSubMenu( HMENU hMenu, int nPos)
4997 {
4998 PMENU MenuObject;
4999 PITEM pItem;
5000
5001 if (!(MenuObject = UserGetMenuObject(hMenu)))
5002 {
5003 return NULL;
5004 }
5005
5006 if (!(pItem = MENU_FindItem( &MenuObject, (UINT*)&nPos, MF_BYPOSITION ))) return NULL;
5007
5008 if (pItem->spSubMenu)
5009 {
5010 HMENU hsubmenu = UserHMGetHandle(pItem->spSubMenu);
5011 return hsubmenu;
5012 }
5013 return NULL;
5014 }
5015
5016 UINT FASTCALL IntFindSubMenu(HMENU *hMenu, HMENU hSubTarget )
5017 {
5018 PMENU menu, pSubTarget;
5019 UINT Pos;
5020 if (((*hMenu)==(HMENU)0xffff) ||(!(menu = UserGetMenuObject(*hMenu))))
5021 return NO_SELECTED_ITEM;
5022
5023 pSubTarget = UserGetMenuObject(hSubTarget);
5024
5025 Pos = MENU_FindSubMenu(&menu, pSubTarget );
5026
5027 *hMenu = (menu ? UserHMGetHandle(menu) : NULL);
5028
5029 return Pos;
5030 }
5031
5032
5033 HMENU FASTCALL UserCreateMenu(PDESKTOP Desktop, BOOL PopupMenu)
5034 {
5035 PWINSTATION_OBJECT WinStaObject;
5036 HANDLE Handle;
5037 PMENU Menu;
5038 NTSTATUS Status;
5039 PEPROCESS CurrentProcess = PsGetCurrentProcess();
5040
5041 if (gpepCSRSS != CurrentProcess)
5042 {
5043 /*
5044 * gpepCSRSS does not have a Win32WindowStation
5045 */
5046
5047 Status = IntValidateWindowStationHandle(CurrentProcess->Win32WindowStation,
5048 UserMode,
5049 0,
5050 &WinStaObject,
5051 0);
5052
5053 if (!NT_SUCCESS(Status))
5054 {
5055 ERR("Validation of window station handle (%p) failed\n",
5056 CurrentProcess->Win32WindowStation);
5057 SetLastNtError(Status);
5058 return (HMENU)0;
5059 }
5060 Menu = IntCreateMenu(&Handle, !PopupMenu, Desktop, GetW32ProcessInfo());
5061 if (Menu && Menu->head.rpdesk->rpwinstaParent != WinStaObject)
5062 {
5063 ERR("Desktop Window Station does not match Process one!\n");
5064 }
5065 ObDereferenceObject(WinStaObject);
5066 }
5067 else
5068 {
5069 Menu = IntCreateMenu(&Handle, !PopupMenu, GetW32ThreadInfo()->rpdesk, GetW32ProcessInfo());
5070 }
5071
5072 if (Menu) UserDereferenceObject(Menu);
5073 return (HMENU)Handle;
5074 }
5075
5076 BOOL FASTCALL
5077 IntMenuItemInfo(
5078 PMENU Menu,
5079 UINT Item,
5080 BOOL ByPosition,
5081 PROSMENUITEMINFO ItemInfo,
5082 BOOL SetOrGet,
5083 PUNICODE_STRING lpstr)
5084 {
5085 PITEM MenuItem;
5086 BOOL Ret;
5087
5088 if (!(MenuItem = MENU_FindItem( &Menu, &Item, (ByPosition ? MF_BYPOSITION : MF_BYCOMMAND) )))
5089 {
5090 EngSetLastError(ERROR_MENU_ITEM_NOT_FOUND);
5091 return( FALSE);
5092 }
5093 if (SetOrGet)
5094 {
5095 Ret = IntSetMenuItemInfo(Menu, MenuItem, ItemInfo, lpstr);
5096 }
5097 else
5098 {
5099 Ret = IntGetMenuItemInfo(Menu, MenuItem, ItemInfo);
5100 }
5101 return( Ret);
5102 }
5103
5104 BOOL FASTCALL
5105 UserMenuItemInfo(
5106 PMENU Menu,
5107 UINT Item,
5108 BOOL ByPosition,
5109 PROSMENUITEMINFO UnsafeItemInfo,
5110 BOOL SetOrGet,
5111 PUNICODE_STRING lpstr)
5112 {
5113 PITEM MenuItem;
5114 ROSMENUITEMINFO ItemInfo;
5115 NTSTATUS Status;
5116 UINT Size;
5117 BOOL Ret;
5118
5119 Status = MmCopyFromCaller(&Size, &UnsafeItemInfo->cbSize, sizeof(UINT));
5120 if (! NT_SUCCESS(Status))
5121 {
5122 SetLastNtError(Status);
5123 return( FALSE);
5124 }
5125 if ( Size != sizeof(MENUITEMINFOW) &&
5126 Size != FIELD_OFFSET(MENUITEMINFOW, hbmpItem) &&
5127 Size != sizeof(ROSMENUITEMINFO) )
5128 {
5129 EngSetLastError(ERROR_INVALID_PARAMETER);
5130 return( FALSE);
5131 }
5132 Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, Size);
5133 if (! NT_SUCCESS(Status))
5134 {
5135 SetLastNtError(Status);
5136 return( FALSE);
5137 }
5138 /* If this is a pre-0x0500 _WIN32_WINNT MENUITEMINFOW, you can't
5139 set/get hbmpItem */
5140 if (FIELD_OFFSET(MENUITEMINFOW, hbmpItem) == Size
5141 && 0 != (ItemInfo.fMask & MIIM_BITMAP))
5142 {
5143 EngSetLastError(ERROR_INVALID_PARAMETER);
5144 return( FALSE);
5145 }
5146
5147 if (!(MenuItem = MENU_FindItem( &Menu, &Item, (ByPosition ? MF_BYPOSITION : MF_BYCOMMAND) )))
5148 {
5149 /* workaround for Word 95: pretend that SC_TASKLIST item exists. */
5150 if ( SetOrGet && Item == SC_TASKLIST && !ByPosition )
5151 return TRUE;
5152
5153 EngSetLastError(ERROR_MENU_ITEM_NOT_FOUND);
5154 return( FALSE);
5155 }
5156
5157 if (SetOrGet)
5158 {
5159 Ret = IntSetMenuItemInfo(Menu, MenuItem, &ItemInfo, lpstr);
5160 }
5161 else
5162 {
5163 Ret = IntGetMenuItemInfo(Menu, MenuItem, &ItemInfo);
5164 if (Ret)
5165 {
5166 Status = MmCopyToCaller(UnsafeItemInfo, &ItemInfo, Size);
5167 if (! NT_SUCCESS(Status))
5168 {
5169 SetLastNtError(Status);
5170 return( FALSE);
5171 }
5172 }
5173 }
5174
5175 return( Ret);
5176 }
5177
5178 BOOL FASTCALL
5179 UserMenuInfo(
5180 PMENU Menu,
5181 PROSMENUINFO UnsafeMenuInfo,
5182 BOOL SetOrGet)
5183 {
5184 BOOL Res;
5185 DWORD Size;
5186 NTSTATUS Status;
5187 ROSMENUINFO MenuInfo;
5188
5189 Status = MmCopyFromCaller(&Size, &UnsafeMenuInfo->cbSize, sizeof(DWORD));
5190 if (! NT_SUCCESS(Status))
5191 {
5192 SetLastNtError(Status);
5193 return( FALSE);
5194 }
5195 if ( Size < sizeof(MENUINFO) || Size > sizeof(ROSMENUINFO) )
5196 {
5197 EngSetLastError(ERROR_INVALID_PARAMETER);
5198 return( FALSE);
5199 }
5200 Status = MmCopyFromCaller(&MenuInfo, UnsafeMenuInfo, Size);
5201 if (! NT_SUCCESS(Status))
5202 {
5203 SetLastNtError(Status);
5204 return( FALSE);
5205 }
5206
5207 if(SetOrGet)
5208 {
5209 /* Set MenuInfo */
5210 Res = IntSetMenuInfo(Menu, &MenuInfo);
5211 }
5212 else
5213 {
5214 /* Get MenuInfo */
5215 Res = IntGetMenuInfo(Menu, &MenuInfo);
5216 if (Res)
5217 {
5218 Status = MmCopyToCaller(UnsafeMenuInfo, &MenuInfo, Size);
5219 if (! NT_SUCCESS(Status))
5220 {
5221 SetLastNtError(Status);
5222 return( FALSE);
5223 }
5224 }
5225 }
5226
5227 return( Res);
5228 }
5229
5230 BOOL FASTCALL
5231 IntGetMenuItemRect(
5232 PWND pWnd,
5233 PMENU Menu,
5234 UINT uItem,
5235 PRECTL Rect)
5236 {
5237 LONG XMove, YMove;
5238 PITEM MenuItem;
5239 UINT I = uItem;
5240
5241 if ((MenuItem = MENU_FindItem (&Menu, &I, MF_BYPOSITION)))
5242 {
5243 Rect->left = MenuItem->xItem;
5244 Rect->top = MenuItem->yItem;
5245 Rect->right = MenuItem->cxItem; // Do this for now......
5246 Rect->bottom = MenuItem->cyItem;
5247 }
5248 else
5249 {
5250 ERR("Failed Item Lookup! %u\n", uItem);
5251 return FALSE;
5252 }
5253
5254 if (!pWnd)
5255 {
5256 HWND hWnd = Menu->hWnd;
5257 if (!(pWnd = UserGetWindowObject(hWnd))) return FALSE;
5258 }
5259
5260 if (Menu->fFlags & MNF_POPUP)
5261 {
5262 XMove = pWnd->rcClient.left;
5263 YMove = pWnd->rcClient.top;
5264 }
5265 else
5266 {
5267 XMove = pWnd->rcWindow.left;
5268 YMove = pWnd->rcWindow.top;
5269 }
5270
5271 Rect->left += XMove;
5272 Rect->top += YMove;
5273 Rect->right += XMove;
5274 Rect->bottom += YMove;
5275
5276 return TRUE;
5277 }
5278
5279 PMENU FASTCALL MENU_GetSystemMenu(PWND Window, PMENU Popup)
5280 {
5281 PMENU Menu, NewMenu = NULL, SysMenu = NULL;
5282 HMENU hSysMenu, hNewMenu = NULL;
5283 ROSMENUITEMINFO ItemInfoSet = {0};
5284 ROSMENUITEMINFO ItemInfo = {0};
5285 UNICODE_STRING MenuName;
5286
5287 hSysMenu = UserCreateMenu(Window->head.rpdesk, FALSE);
5288 if (NULL == hSysMenu)
5289 {
5290 return NULL;
5291 }
5292 SysMenu = UserGetMenuObject(hSysMenu);
5293 if (NULL == SysMenu)
5294 {
5295 UserDestroyMenu(hSysMenu);
5296 return NULL;
5297 }
5298
5299 SysMenu->fFlags |= MNF_SYSMENU;
5300 SysMenu->hWnd = UserHMGetHandle(Window);
5301
5302 if (!Popup)
5303 {
5304 //hNewMenu = co_IntLoadSysMenuTemplate();
5305 if ( Window->ExStyle & WS_EX_MDICHILD )
5306 {
5307 RtlInitUnicodeString( &MenuName, L"SYSMENUMDI");
5308 hNewMenu = co_IntCallLoadMenu( hModClient, &MenuName);
5309 }
5310 else
5311 {
5312 RtlInitUnicodeString( &MenuName, L"SYSMENU");
5313 hNewMenu = co_IntCallLoadMenu( hModClient, &MenuName);
5314 //ERR("%wZ\n",&MenuName);
5315 }
5316 if (!hNewMenu)
5317 {
5318 ERR("No Menu!!\n");
5319 IntDestroyMenuObject(SysMenu, FALSE);
5320 return NULL;
5321 }
5322 Menu = UserGetMenuObject(hNewMenu);
5323 if (!Menu)
5324 {
5325 IntDestroyMenuObject(SysMenu, FALSE);
5326 return NULL;
5327 }
5328
5329 // Do the rest in here.
5330
5331 Menu->fFlags |= MNS_CHECKORBMP | MNF_SYSMENU | MNF_POPUP;
5332
5333 ItemInfoSet.cbSize = sizeof( MENUITEMINFOW);
5334 ItemInfoSet.fMask = MIIM_BITMAP;
5335 ItemInfoSet.hbmpItem = HBMMENU_POPUP_CLOSE;
5336 IntMenuItemInfo(Menu, SC_CLOSE, FALSE, &ItemInfoSet, TRUE, NULL);
5337 ItemInfoSet.hbmpItem = HBMMENU_POPUP_RESTORE;
5338 IntMenuItemInfo(Menu, SC_RESTORE, FALSE, &ItemInfoSet, TRUE, NULL);
5339 ItemInfoSet.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
5340 IntMenuItemInfo(Menu, SC_MAXIMIZE, FALSE, &ItemInfoSet, TRUE, NULL);
5341 ItemInfoSet.hbmpItem = HBMMENU_POPUP_MINIMIZE;
5342 IntMenuItemInfo(Menu, SC_MINIMIZE, FALSE, &ItemInfoSet, TRUE, NULL);
5343
5344 NewMenu = IntCloneMenu(Menu);
5345 if (NewMenu == NULL)
5346 {
5347 IntDestroyMenuObject(Menu, FALSE);
5348 IntDestroyMenuObject(SysMenu, FALSE);
5349 return NULL;
5350 }
5351
5352 IntReleaseMenuObject(NewMenu);
5353 UserSetMenuDefaultItem(NewMenu, SC_CLOSE, FALSE);
5354
5355 IntDestroyMenuObject(Menu, FALSE);
5356 }
5357 else
5358 {
5359 NewMenu = Popup;
5360 }
5361 if (NewMenu)
5362 {
5363 NewMenu->fFlags |= MNF_SYSMENU | MNF_POPUP;
5364
5365 if (Window->pcls->style & CS_NOCLOSE)
5366 IntRemoveMenuItem(NewMenu, SC_CLOSE, MF_BYCOMMAND, TRUE);
5367
5368 ItemInfo.cbSize = sizeof(MENUITEMINFOW);
5369 ItemInfo.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_SUBMENU;
5370 ItemInfo.fType = MF_POPUP;
5371 ItemInfo.fState = MFS_ENABLED;
5372 ItemInfo.dwTypeData = NULL;
5373 ItemInfo.cch = 0;
5374 ItemInfo.hSubMenu = UserHMGetHandle(NewMenu);
5375 IntInsertMenuItem(SysMenu, (UINT) -1, TRUE, &ItemInfo, NULL);
5376
5377 return SysMenu;
5378 }
5379 ERR("failed to load system menu!\n");
5380 return NULL;
5381 }
5382
5383 PMENU FASTCALL
5384 IntGetSystemMenu(PWND Window, BOOL bRevert)
5385 {
5386 PMENU Menu;
5387
5388 if (bRevert)
5389 {
5390 if (Window->SystemMenu)
5391 {
5392 Menu = UserGetMenuObject(Window->SystemMenu);
5393 if (Menu && !(Menu->fFlags & MNF_SYSDESKMN))
5394 {
5395 IntDestroyMenuObject(Menu, TRUE);
5396 Window->SystemMenu = NULL;
5397 }
5398 }
5399 }
5400 else
5401 {
5402 Menu = Window->SystemMenu ? UserGetMenuObject(Window->SystemMenu) : NULL;
5403 if ((!Menu || Menu->fFlags & MNF_SYSDESKMN) && Window->style & WS_SYSMENU)
5404 {
5405 Menu = MENU_GetSystemMenu(Window, NULL);
5406 Window->SystemMenu = Menu ? UserHMGetHandle(Menu) : NULL;
5407 }
5408 }
5409
5410 if (Window->SystemMenu)
5411 {
5412 HMENU hMenu = IntGetSubMenu( Window->SystemMenu, 0);
5413 /* Store the dummy sysmenu handle to facilitate the refresh */
5414 /* of the close button if the SC_CLOSE item change */
5415 Menu = UserGetMenuObject(hMenu);
5416 if (Menu)
5417 {
5418 Menu->spwndNotify = Window;
5419 Menu->fFlags |= MNF_SYSSUBMENU;
5420 }
5421 return Menu;
5422 }
5423 return NULL;
5424 }
5425
5426 BOOL FASTCALL
5427 IntSetSystemMenu(PWND Window, PMENU Menu)
5428 {
5429 PMENU OldMenu;
5430
5431 if (!(Window->style & WS_SYSMENU)) return FALSE;
5432
5433 if (Window->SystemMenu)
5434 {
5435 OldMenu = UserGetMenuObject(Window->SystemMenu);
5436 if (OldMenu)
5437 {
5438 OldMenu->fFlags &= ~MNF_SYSMENU;
5439 IntDestroyMenuObject(OldMenu, TRUE);
5440 }
5441 }
5442
5443 OldMenu = MENU_GetSystemMenu(Window, Menu);
5444 if (OldMenu)
5445 { // Use spmenuSys too!
5446 Window->SystemMenu = UserHMGetHandle(OldMenu);
5447 }
5448 else
5449 Window->SystemMenu = NULL;
5450
5451 if (Menu && Window != Menu->spwndNotify)
5452 {
5453 Menu->spwndNotify = Window;
5454 }
5455
5456 return TRUE;
5457 }
5458
5459 BOOL FASTCALL
5460 IntSetMenu(
5461 PWND Wnd,
5462 HMENU Menu,
5463 BOOL *Changed)
5464 {
5465 PMENU OldMenu, NewMenu = NULL;
5466
5467 if ((Wnd->style & (WS_CHILD | WS_POPUP)) == WS_CHILD)
5468 {
5469 ERR("SetMenu: Window is a Child 0x%p!\n",UserHMGetHandle(Wnd));
5470 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5471 return FALSE;
5472 }
5473
5474 *Changed = (UlongToHandle(Wnd->IDMenu) != Menu);
5475 if (! *Changed)
5476 {
5477 return TRUE;
5478 }
5479
5480 if (Wnd->IDMenu)
5481 {
5482 OldMenu = IntGetMenuObject(UlongToHandle(Wnd->IDMenu));
5483 ASSERT(NULL == OldMenu || OldMenu->hWnd == UserHMGetHandle(Wnd));
5484 }
5485 else
5486 {
5487 OldMenu = NULL;
5488 }
5489
5490 if (NULL != Menu)
5491 {
5492 NewMenu = IntGetMenuObject(Menu);
5493 if (NULL == NewMenu)
5494 {
5495 if (NULL != OldMenu)
5496 {
5497 IntReleaseMenuObject(OldMenu);
5498 }
5499 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5500 return FALSE;
5501 }
5502 if (NULL != NewMenu->hWnd)
5503 {
5504 /* Can't use the same menu for two windows */
5505 if (NULL != OldMenu)
5506 {
5507 IntReleaseMenuObject(OldMenu);
5508 }
5509 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5510 return FALSE;
5511 }
5512
5513 }
5514
5515 Wnd->IDMenu = (UINT_PTR) Menu;
5516 if (NULL != NewMenu)
5517 {
5518 NewMenu->hWnd = UserHMGetHandle(Wnd);
5519 IntReleaseMenuObject(NewMenu);
5520 }
5521 if (NULL != OldMenu)
5522 {
5523 OldMenu->hWnd = NULL;
5524 IntReleaseMenuObject(OldMenu);
5525 }
5526
5527 return TRUE;
5528 }
5529
5530
5531 /* FUNCTIONS *****************************************************************/
5532
5533 /*
5534 * @implemented
5535 */
5536 /* http://www.cyber-ta.org/releases/malware-analysis/public/SOURCES/b47155634ccb2c30630da7e3666d3d07/b47155634ccb2c30630da7e3666d3d07.trace.html#NtUserGetIconSize */
5537 DWORD
5538 APIENTRY
5539 NtUserCalcMenuBar(
5540 HWND hwnd,
5541 DWORD leftBorder,
5542 DWORD rightBorder,
5543 DWORD top,
5544 LPRECT prc )
5545 {
5546 HDC hdc;
5547 PWND Window;
5548 RECT Rect;
5549 DWORD ret;
5550
5551 UserEnterExclusive();
5552
5553 if(!(Window = UserGetWindowObject(hwnd)))
5554 {
5555 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5556 UserLeave();
5557 return 0;
5558 }
5559
5560 hdc = UserGetDCEx(NULL, NULL, DCX_CACHE);
5561 if (!hdc)
5562 {
5563 UserLeave();
5564 return 0;
5565 }
5566
5567 Rect.left = leftBorder;
5568 Rect.right = Window->rcWindow.right - Window->rcWindow.left - rightBorder;
5569 Rect.top = top;
5570 Rect.bottom = 0;
5571
5572 ret = MENU_DrawMenuBar(hdc, &Rect, Window, TRUE);
5573
5574 UserReleaseDC( 0, hdc, FALSE );
5575
5576 UserLeave();
5577
5578 return ret;
5579 }
5580
5581 /*
5582 * @implemented
5583 */
5584 DWORD APIENTRY
5585 NtUserCheckMenuItem(
5586 HMENU hMenu,
5587 UINT uIDCheckItem,
5588 UINT uCheck)
5589 {
5590 PMENU Menu;
5591 DECLARE_RETURN(DWORD);
5592
5593 TRACE("Enter NtUserCheckMenuItem\n");
5594 UserEnterExclusive();
5595
5596 if(!(Menu = UserGetMenuObject(hMenu)))
5597 {
5598 RETURN( (DWORD)-1);
5599 }
5600
5601 RETURN( IntCheckMenuItem(Menu, uIDCheckItem, uCheck));
5602
5603 CLEANUP:
5604 TRACE("Leave NtUserCheckMenuItem, ret=%lu\n",_ret_);
5605 UserLeave();
5606 END_CLEANUP;
5607 }
5608
5609 /*
5610 * @implemented
5611 */
5612 BOOL APIENTRY
5613 NtUserDeleteMenu(
5614 HMENU hMenu,
5615 UINT uPosition,
5616 UINT uFlags)
5617 {
5618 PMENU Menu;
5619 DECLARE_RETURN(BOOL);
5620
5621 TRACE("Enter NtUserDeleteMenu\n");
5622 UserEnterExclusive();
5623
5624 if(!(Menu = UserGetMenuObject(hMenu)))
5625 {
5626 RETURN( FALSE);
5627 }
5628
5629 RETURN( IntRemoveMenuItem(Menu, uPosition, uFlags, TRUE));
5630
5631 CLEANUP:
5632 TRACE("Leave NtUserDeleteMenu, ret=%i\n",_ret_);
5633 UserLeave();
5634 END_CLEANUP;
5635 }
5636
5637 /*
5638 * NtUserGetSystemMenu
5639 *
5640 * The NtUserGetSystemMenu function allows the application to access the
5641 * window menu (also known as the system menu or the control menu) for
5642 * copying and modifying.
5643 *
5644 * Parameters
5645 * hWnd
5646 * Handle to the window that will own a copy of the window menu.
5647 * bRevert
5648 * Specifies the action to be taken. If this parameter is FALSE,
5649 * NtUserGetSystemMenu returns a handle to the copy of the window menu
5650 * currently in use. The copy is initially identical to the window menu
5651 * but it can be modified.
5652 * If this parameter is TRUE, GetSystemMenu resets the window menu back
5653 * to the default state. The previous window menu, if any, is destroyed.
5654 *
5655 * Return Value
5656 * If the bRevert parameter is FALSE, the return value is a handle to a
5657 * copy of the window menu. If the bRevert parameter is TRUE, the return
5658 * value is NULL.
5659 *
5660 * Status
5661 * @implemented
5662 */
5663
5664 HMENU APIENTRY
5665 NtUserGetSystemMenu(HWND hWnd, BOOL bRevert)
5666 {
5667 PWND Window;
5668 PMENU Menu;
5669 DECLARE_RETURN(HMENU);
5670
5671 TRACE("Enter NtUserGetSystemMenu\n");
5672 UserEnterExclusive();
5673
5674 if (!(Window = UserGetWindowObject(hWnd)))
5675 {
5676 RETURN(NULL);
5677 }
5678
5679 if (!(Menu = IntGetSystemMenu(Window, bRevert)))
5680 {
5681 RETURN(NULL);
5682 }
5683
5684 RETURN(Menu->head.h);
5685
5686 CLEANUP:
5687 TRACE("Leave NtUserGetSystemMenu, ret=%p\n", _ret_);
5688 UserLeave();
5689 END_CLEANUP;
5690 }
5691
5692 /*
5693 * NtUserSetSystemMenu
5694 *
5695 * Status
5696 * @implemented
5697 */
5698
5699 BOOL APIENTRY
5700 NtUserSetSystemMenu(HWND hWnd, HMENU hMenu)
5701 {
5702 BOOL Result = FALSE;
5703 PWND Window;
5704 PMENU Menu;
5705 DECLARE_RETURN(BOOL);
5706
5707 TRACE("Enter NtUserSetSystemMenu\n");
5708 UserEnterExclusive();
5709
5710 if (!(Window = UserGetWindowObject(hWnd)))
5711 {
5712 RETURN( FALSE);
5713 }
5714
5715 if (hMenu)
5716 {
5717 /*
5718 * Assign new menu handle and Up the Lock Count.
5719 */
5720 if (!(Menu = IntGetMenuObject(hMenu)))
5721 {
5722 RETURN( FALSE);
5723 }
5724
5725 Result = IntSetSystemMenu(Window, Menu);
5726 }
5727 else
5728 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5729
5730 RETURN( Result);
5731
5732 CLEANUP:
5733 TRACE("Leave NtUserSetSystemMenu, ret=%i\n",_ret_);
5734 UserLeave();
5735 END_CLEANUP;
5736 }
5737
5738 /*
5739 * @implemented
5740 */
5741 BOOLEAN APIENTRY
5742 NtUserGetTitleBarInfo(
5743 HWND hwnd,
5744 PTITLEBARINFO bti)
5745 {
5746 PWND WindowObject;
5747 TITLEBARINFO bartitleinfo;
5748 DECLARE_RETURN(BOOLEAN);
5749 BOOLEAN retValue = TRUE;
5750
5751 TRACE("Enter NtUserGetTitleBarInfo\n");
5752 UserEnterExclusive();
5753
5754 /* Vaildate the windows handle */
5755 if (!(WindowObject = UserGetWindowObject(hwnd)))
5756 {
5757 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5758 retValue = FALSE;
5759 }
5760
5761 _SEH2_TRY
5762 {
5763 /* Copy our usermode buffer bti to local buffer bartitleinfo */
5764 ProbeForRead(bti, sizeof(TITLEBARINFO), 1);
5765 RtlCopyMemory(&bartitleinfo, bti, sizeof(TITLEBARINFO));
5766 }
5767 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5768 {
5769 /* Fail copy the data */
5770 EngSetLastError(ERROR_INVALID_PARAMETER);
5771 retValue = FALSE;
5772 }
5773 _SEH2_END
5774
5775 /* Get the tile bar info */
5776 if (retValue)
5777 {
5778 retValue = intGetTitleBarInfo(WindowObject, &bartitleinfo);
5779 if (retValue)
5780 {
5781 _SEH2_TRY
5782 {
5783 /* Copy our buffer to user mode buffer bti */
5784 ProbeForWrite(bti, sizeof(TITLEBARINFO), 1);
5785 RtlCopyMemory(bti, &bartitleinfo, sizeof(TITLEBARINFO));
5786 }
5787 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5788 {
5789 /* Fail copy the data */
5790 EngSetLastError(ERROR_INVALID_PARAMETER);
5791 retValue = FALSE;
5792 }
5793 _SEH2_END
5794 }
5795 }
5796
5797 RETURN( retValue );
5798
5799 CLEANUP:
5800 TRACE("Leave NtUserGetTitleBarInfo, ret=%u\n",_ret_);
5801 UserLeave();
5802 END_CLEANUP;
5803 }
5804
5805 /*
5806 * @implemented
5807 */
5808 BOOL FASTCALL UserDestroyMenu(HMENU hMenu)
5809 {
5810 PMENU Menu;
5811 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
5812
5813 if(!(Menu = UserGetMenuObject(hMenu)))
5814 {
5815 return FALSE;
5816 }
5817
5818 if (Menu->head.rpdesk != pti->rpdesk)
5819 {
5820 EngSetLastError(ERROR_ACCESS_DENIED);
5821 return FALSE;
5822 }
5823 return IntDestroyMenuObject(Menu, FALSE);
5824 }
5825
5826 /*
5827 * @implemented
5828 */
5829 BOOL APIENTRY
5830 NtUserDestroyMenu(
5831 HMENU hMenu)
5832 {
5833 PMENU Menu;
5834 DECLARE_RETURN(BOOL);
5835
5836 TRACE("Enter NtUserDestroyMenu\n");
5837 UserEnterExclusive();
5838
5839 if(!(Menu = UserGetMenuObject(hMenu)))
5840 {
5841 RETURN( FALSE);
5842 }
5843 if (Menu->head.rpdesk != gptiCurrent->rpdesk)
5844 {
5845 EngSetLastError(ERROR_ACCESS_DENIED);
5846 RETURN( FALSE);
5847 }
5848 RETURN( IntDestroyMenuObject(Menu, TRUE));
5849
5850 CLEANUP:
5851 TRACE("Leave NtUserDestroyMenu, ret=%i\n",_ret_);
5852 UserLeave();
5853 END_CLEANUP;
5854 }
5855
5856 /*
5857 * @implemented
5858 */
5859 UINT APIENTRY
5860 NtUserEnableMenuItem(
5861 HMENU hMenu,
5862 UINT uIDEnableItem,
5863 UINT uEnable)
5864 {
5865 PMENU Menu;
5866 DECLARE_RETURN(UINT);
5867
5868 TRACE("Enter NtUserEnableMenuItem\n");
5869 UserEnterExclusive();
5870
5871 if(!(Menu = UserGetMenuObject(hMenu)))
5872 {
5873 RETURN(-1);
5874 }
5875
5876 RETURN( IntEnableMenuItem(Menu, uIDEnableItem, uEnable));
5877
5878 CLEANUP:
5879 TRACE("Leave NtUserEnableMenuItem, ret=%u\n",_ret_);
5880 UserLeave();
5881 END_CLEANUP;
5882 }
5883
5884 /*
5885 * @implemented
5886 */
5887 BOOL APIENTRY
5888 NtUserEndMenu(VOID)
5889 {
5890 //PWND pWnd;
5891 TRACE("Enter NtUserEndMenu\n");
5892 UserEnterExclusive();
5893 /* if ( gptiCurrent->pMenuState &&
5894 gptiCurrent->pMenuState->pGlobalPopupMenu )
5895 {
5896 pWnd = IntGetMSWND(gptiCurrent->pMenuState);
5897 if (pWnd)
5898 {
5899 UserPostMessage( UserHMGetHandle(pWnd), WM_CANCELMODE, 0, 0);
5900 }
5901 else
5902 gptiCurrent->pMenuState->fInsideMenuLoop = FALSE;
5903 }*/
5904 if (fInsideMenuLoop && top_popup)
5905 {
5906 fInsideMenuLoop = FALSE;
5907 UserPostMessage( top_popup, WM_CANCELMODE, 0, 0);
5908 }
5909 UserLeave();
5910 TRACE("Leave NtUserEndMenu\n");
5911 return TRUE;
5912 }
5913
5914 /*
5915 * @implemented
5916 */
5917 BOOL APIENTRY
5918 NtUserGetMenuBarInfo(
5919 HWND hwnd,
5920 LONG idObject,
5921 LONG idItem,
5922 PMENUBARINFO pmbi)
5923 {
5924 PWND pWnd;
5925 HMENU hMenu;
5926 MENUBARINFO kmbi;
5927 BOOL Ret;
5928 PPOPUPMENU pPopupMenu;
5929 USER_REFERENCE_ENTRY Ref;
5930 NTSTATUS Status = STATUS_SUCCESS;
5931 PMENU Menu = NULL;
5932 DECLARE_RETURN(BOOL);
5933
5934 TRACE("Enter NtUserGetMenuBarInfo\n");
5935 UserEnterShared();
5936
5937 if (!(pWnd = UserGetWindowObject(hwnd)))
5938 {
5939 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5940 RETURN(FALSE);
5941 }
5942
5943 UserRefObjectCo(pWnd, &Ref);
5944
5945 RECTL_vSetEmptyRect(&kmbi.rcBar);
5946 kmbi.hMenu = NULL;
5947 kmbi.hwndMenu = NULL;
5948 kmbi.fBarFocused = FALSE;
5949 kmbi.fFocused = FALSE;
5950
5951 switch (idObject)
5952 {
5953 case OBJID_CLIENT:
5954 if (!pWnd->pcls->fnid)
5955 RETURN(FALSE);
5956 if (pWnd->pcls->fnid != FNID_MENU)
5957 {
5958 WARN("called on invalid window: %u\n", pWnd->pcls->fnid);
5959 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5960 RETURN(FALSE);
5961 }
5962 // Windows does this! Wine checks for Atom and uses GetWindowLongPtrW.
5963 hMenu = (HMENU)co_IntSendMessage(hwnd, MN_GETHMENU, 0, 0);
5964 pPopupMenu = ((PMENUWND)pWnd)->ppopupmenu;
5965 if (pPopupMenu && pPopupMenu->spmenu)
5966 {
5967 if (UserHMGetHandle(pPopupMenu->spmenu) != hMenu)
5968 {
5969 ERR("Window Pop Up hMenu %p not the same as Get hMenu %p!\n",pPopupMenu->spmenu->head.h,hMenu);
5970 }
5971 }
5972 break;
5973 case OBJID_MENU:
5974 if (pWnd->style & WS_CHILD) RETURN(FALSE);
5975 hMenu = UlongToHandle(pWnd->IDMenu);
5976 TRACE("GMBI: OBJID_MENU hMenu %p\n",hMenu);
5977 break;
5978 case OBJID_SYSMENU:
5979 if (!(pWnd->style & WS_SYSMENU)) RETURN(FALSE);
5980 Menu = IntGetSystemMenu(pWnd, FALSE);
5981 hMenu = UserHMGetHandle(Menu);
5982 break;
5983 default:
5984 RETURN(FALSE);
5985 }
5986
5987 if (!hMenu)
5988 RETURN(FALSE);
5989
5990 _SEH2_TRY
5991 {
5992 ProbeForRead(pmbi, sizeof(MENUBARINFO), 1);
5993 kmbi.cbSize = pmbi->cbSize;
5994 }
5995 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5996 {
5997 kmbi.cbSize = 0;
5998 }
5999 _SEH2_END
6000
6001 if (kmbi.cbSize != sizeof(MENUBARINFO))
6002 {
6003 EngSetLastError(ERROR_INVALID_PARAMETER);
6004 RETURN(FALSE);
6005 }
6006
6007 if (!Menu) Menu = UserGetMenuObject(hMenu);
6008 if (!Menu)
6009 RETURN(FALSE);
6010
6011 if ((idItem < 0) || ((ULONG)idItem > Menu->cItems))
6012 RETURN(FALSE);
6013
6014 if (idItem == 0)
6015 {
6016 Ret = IntGetMenuItemRect(pWnd, Menu, 0, &kmbi.rcBar);
6017 kmbi.rcBar.right = kmbi.rcBar.left + Menu->cxMenu;
6018 kmbi.rcBar.bottom = kmbi.rcBar.top + Menu->cyMenu;
6019 TRACE("idItem a 0 %d\n",Ret);
6020 }
6021 else
6022 {
6023 Ret = IntGetMenuItemRect(pWnd, Menu, idItem-1, &kmbi.rcBar);
6024 TRACE("idItem b %d %d\n", idItem-1, Ret);
6025 }
6026
6027 kmbi.hMenu = hMenu;
6028 kmbi.fBarFocused = top_popup_hmenu == hMenu;
6029 TRACE("GMBI: top p hm %p hMenu %p\n",top_popup_hmenu, hMenu);
6030 if (idItem)
6031 {
6032 kmbi.fFocused = Menu->iItem == idItem-1;
6033 if (kmbi.fFocused && (Menu->rgItems[idItem - 1].spSubMenu))
6034 {
6035 kmbi.hwndMenu = Menu->rgItems[idItem - 1].spSubMenu->hWnd;
6036 }
6037 }
6038 else
6039 {
6040 kmbi.fFocused = kmbi.fBarFocused;
6041 }
6042
6043 _SEH2_TRY
6044 {
6045 ProbeForWrite(pmbi, sizeof(MENUBARINFO), 1);
6046 RtlCopyMemory(pmbi, &kmbi, sizeof(MENUBARINFO));
6047 }
6048 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6049 {
6050 Status = _SEH2_GetExceptionCode();
6051 }
6052 _SEH2_END
6053
6054 if (!NT_SUCCESS(Status))
6055 {
6056 SetLastNtError(Status);
6057 RETURN(FALSE);
6058 }
6059
6060 RETURN(TRUE);
6061
6062 CLEANUP:
6063 if (pWnd) UserDerefObjectCo(pWnd);
6064 TRACE("Leave NtUserGetMenuBarInfo, ret=%i\n",_ret_);
6065 UserLeave();
6066 END_CLEANUP;
6067 }
6068
6069 /*
6070 * @implemented
6071 */
6072 UINT APIENTRY
6073 NtUserGetMenuIndex(
6074 HMENU hMenu,
6075 HMENU hSubMenu)
6076 {
6077 PMENU Menu, SubMenu;
6078 PITEM MenuItem;
6079 UINT i;
6080 DECLARE_RETURN(UINT);
6081
6082 TRACE("Enter NtUserGetMenuIndex\n");
6083 UserEnterShared();
6084
6085 if ( !(Menu = UserGetMenuObject(hMenu)) ||
6086 !(SubMenu = UserGetMenuObject(hSubMenu)) )
6087 RETURN(0xFFFFFFFF);
6088
6089 MenuItem = Menu->rgItems;
6090 for (i = 0; i < Menu->cItems; i++, MenuItem++)
6091 {
6092 if (MenuItem->spSubMenu == SubMenu)
6093 RETURN(MenuItem->wID);
6094 }
6095 RETURN(0xFFFFFFFF);
6096
6097 CLEANUP:
6098 TRACE("Leave NtUserGetMenuIndex, ret=%u\n",_ret_);
6099 UserLeave();
6100 END_CLEANUP;
6101 }
6102
6103 /*
6104 * @implemented
6105 */
6106 BOOL APIENTRY
6107 NtUserGetMenuItemRect(
6108 HWND hWnd,
6109 HMENU hMenu,
6110 UINT uItem,
6111 PRECTL lprcItem)
6112 {
6113 PWND ReferenceWnd;
6114 LONG XMove, YMove;
6115 RECTL Rect;
6116 PMENU Menu;
6117 PITEM MenuItem;
6118 NTSTATUS Status = STATUS_SUCCESS;
6119 DECLARE_RETURN(BOOL);
6120
6121 TRACE("Enter NtUserGetMenuItemRect\n");
6122 UserEnterShared();
6123
6124 if (!(Menu = UserGetMenuObject(hMenu)))
6125 {
6126 RETURN(FALSE);
6127 }
6128
6129 if ((MenuItem = MENU_FindItem (&Menu, &uItem, MF_BYPOSITION)))
6130 {
6131 Rect.left = MenuItem->xItem;
6132 Rect.top = MenuItem->yItem;
6133 Rect.right = MenuItem->cxItem; // Do this for now......
6134 Rect.bottom = MenuItem->cyItem;
6135 }
6136 else
6137 RETURN(FALSE);
6138
6139 if(!hWnd)
6140 {
6141 hWnd = Menu->hWnd;
6142 }
6143
6144 if (lprcItem == NULL) RETURN( FALSE);
6145
6146 if (!(ReferenceWnd = UserGetWindowObject(hWnd))) RETURN( FALSE);
6147
6148 if (Menu->fFlags & MNF_POPUP)
6149 {
6150 XMove = ReferenceWnd->rcClient.left;
6151 YMove = ReferenceWnd->rcClient.top;
6152 }
6153 else
6154 {
6155 XMove = ReferenceWnd->rcWindow.left;
6156 YMove = ReferenceWnd->rcWindow.top;
6157 }
6158
6159 Rect.left += XMove;
6160 Rect.top += YMove;
6161 Rect.right += XMove;
6162 Rect.bottom += YMove;
6163
6164 _SEH2_TRY
6165 {
6166 RtlCopyMemory(lprcItem, &Rect, sizeof(RECTL));
6167 }
6168 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6169 {
6170 Status = _SEH2_GetExceptionCode();
6171 }
6172 _SEH2_END
6173
6174 if (!NT_SUCCESS(Status))
6175 {
6176 SetLastNtError(Status);
6177 RETURN(FALSE);
6178 }
6179 RETURN(TRUE);
6180
6181 CLEANUP:
6182 TRACE("Leave NtUserGetMenuItemRect, ret=%i\n",_ret_);
6183 UserLeave();
6184 END_CLEANUP;
6185 }
6186
6187 /*
6188 * @implemented
6189 */
6190 BOOL APIENTRY
6191 NtUserHiliteMenuItem(
6192 HWND hWnd,
6193 HMENU hMenu,
6194 UINT uItemHilite,
6195 UINT uHilite)
6196 {
6197 PMENU Menu;
6198 PWND Window;
6199 DECLARE_RETURN(BOOLEAN);
6200
6201 TRACE("Enter NtUserHiliteMenuItem\n");
6202 UserEnterExclusive();
6203
6204 if(!(Window = UserGetWindowObject(hWnd)))
6205 {
6206 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
6207 RETURN(FALSE);
6208 }
6209
6210 if(!(Menu = UserGetMenuObject(hMenu)))
6211 {
6212 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
6213 RETURN(FALSE);
6214 }
6215
6216 RETURN( IntHiliteMenuItem(Window, Menu, uItemHilite, uHilite));
6217
6218 CLEANUP:
6219 TRACE("Leave NtUserHiliteMenuItem, ret=%u\n",_ret_);
6220 UserLeave();
6221 END_CLEANUP;
6222 }
6223
6224 /*
6225 * @implemented
6226 */
6227 DWORD
6228 APIENTRY
6229 NtUserDrawMenuBarTemp(
6230 HWND hWnd,
6231 HDC hDC,
6232 PRECT pRect,
6233 HMENU hMenu,
6234 HFONT hFont)
6235 {
6236 PMENU Menu;
6237 PWND Window;
6238 RECT Rect;
6239 NTSTATUS Status = STATUS_SUCCESS;
6240 DECLARE_RETURN(DWORD);
6241
6242 ERR("Enter NtUserDrawMenuBarTemp\n");
6243 UserEnterExclusive();
6244
6245 if(!(Window = UserGetWindowObject(hWnd)))
6246 {
6247 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
6248 RETURN(0);
6249 }
6250
6251 if(!(Menu = UserGetMenuObject(hMenu)))
6252 {
6253 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
6254 RETURN(0);
6255 }
6256
6257 _SEH2_TRY
6258 {
6259 ProbeForRead(pRect, sizeof(RECT), sizeof(ULONG));
6260 RtlCopyMemory(&Rect, pRect, sizeof(RECT));
6261 }
6262 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6263 {
6264 Status = _SEH2_GetExceptionCode();
6265 }
6266 _SEH2_END;
6267
6268 if (Status != STATUS_SUCCESS)
6269 {
6270 SetLastNtError(Status);
6271 RETURN(0);
6272 }
6273
6274 RETURN( IntDrawMenuBarTemp(Window, hDC, &Rect, Menu, hFont));
6275
6276 CLEANUP:
6277 ERR("Leave NtUserDrawMenuBarTemp, ret=%u\n",_ret_);
6278 UserLeave();
6279 END_CLEANUP;
6280 }
6281
6282 /*
6283 * @implemented
6284 */
6285 int APIENTRY
6286 NtUserMenuItemFromPoint(
6287 HWND hWnd,
6288 HMENU hMenu,
6289 DWORD X,
6290 DWORD Y)
6291 {
6292 PMENU Menu;
6293 PWND Window = NULL;
6294 PITEM mi;
6295 ULONG i;
6296 DECLARE_RETURN(int);
6297
6298 TRACE("Enter NtUserMenuItemFromPoint\n");
6299 UserEnterExclusive();
6300
6301 if (!(Menu = UserGetMenuObject(hMenu)))
6302 {
6303 RETURN( -1);
6304 }
6305
6306 if (!(Window = UserGetWindowObject(Menu->hWnd)))
6307 {
6308 RETURN( -1);
6309 }
6310
6311 X -= Window->rcWindow.left;
6312 Y -= Window->rcWindow.top;
6313
6314 mi = Menu->rgItems;
6315 for (i = 0; i < Menu->cItems; i++, mi++)
6316 {
6317 RECTL Rect;
6318
6319 Rect.left = mi->xItem;
6320 Rect.top = mi->yItem;
6321 Rect.right = mi->cxItem;
6322 Rect.bottom = mi->cyItem;
6323
6324 MENU_AdjustMenuItemRect(Menu, &Rect);
6325
6326 if (RECTL_bPointInRect(&Rect, X, Y))
6327 {
6328 break;
6329 }
6330 }
6331
6332 RETURN( (mi ? i : NO_SELECTED_ITEM));
6333
6334 CLEANUP:
6335 TRACE("Leave NtUserMenuItemFromPoint, ret=%i\n",_ret_);
6336 UserLeave();
6337 END_CLEANUP;
6338 }
6339
6340
6341 DWORD
6342 APIENTRY
6343 NtUserPaintMenuBar(
6344 HWND hWnd,
6345 HDC hDC,
6346 ULONG leftBorder,
6347 ULONG rightBorder,
6348 ULONG top,
6349 BOOL bActive)
6350 {
6351 PWND Window;
6352 RECT Rect;
6353 DWORD ret;
6354
6355 UserEnterExclusive();
6356
6357 if(!(Window = UserGetWindowObject(hWnd)))
6358 {
6359 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
6360 UserLeave();
6361 return 0;
6362 }
6363
6364 Rect.left = leftBorder;
6365 Rect.right = Window->rcWindow.right - Window->rcWindow.left - rightBorder;
6366 Rect.top = top;
6367 Rect.bottom = 0;
6368
6369 ret = MENU_DrawMenuBar(hDC, &Rect, Window, FALSE);
6370
6371 UserLeave();
6372
6373 return ret;
6374 }
6375
6376 /*
6377 * @implemented
6378 */
6379 BOOL APIENTRY
6380 NtUserRemoveMenu(
6381 HMENU hMenu,
6382 UINT uPosition,
6383 UINT uFlags)
6384 {
6385 PMENU Menu;
6386 DECLARE_RETURN(BOOL);
6387
6388 TRACE("Enter NtUserRemoveMenu\n");
6389 UserEnterExclusive();
6390
6391 if(!(Menu = UserGetMenuObject(hMenu)))
6392 {
6393 RETURN( FALSE);
6394 }
6395
6396 RETURN(IntRemoveMenuItem(Menu, uPosition, uFlags, FALSE));
6397
6398 CLEANUP:
6399 TRACE("Leave NtUserRemoveMenu, ret=%i\n",_ret_);
6400 UserLeave();
6401 END_CLEANUP;
6402
6403 }
6404
6405 /*
6406 * @implemented
6407 */
6408 BOOL APIENTRY
6409 NtUserSetMenu(
6410 HWND hWnd,
6411 HMENU Menu,
6412 BOOL Repaint)
6413 {
6414 PWND Window;
6415 BOOL Changed;
6416 DECLARE_RETURN(BOOL);
6417
6418 TRACE("Enter NtUserSetMenu\n");
6419 UserEnterExclusive();
6420
6421 if (!(Window = UserGetWindowObject(hWnd)))
6422 {
6423 RETURN( FALSE);
6424 }
6425
6426 if (!IntSetMenu(Window, Menu, &Changed))
6427 {
6428 RETURN( FALSE);
6429 }
6430
6431 // Not minimized and please repaint!!!
6432 if (!(Window->style & WS_MINIMIZE) && (Repaint || Changed))
6433 {
6434 USER_REFERENCE_ENTRY Ref;
6435 UserRefObjectCo(Window, &Ref);
6436 co_WinPosSetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
6437 UserDerefObjectCo(Window);
6438 }
6439
6440 RETURN( TRUE);
6441
6442 CLEANUP:
6443 TRACE("Leave NtUserSetMenu, ret=%i\n",_ret_);
6444 UserLeave();
6445 END_CLEANUP;
6446 }
6447
6448 /*
6449 * @implemented
6450 */
6451 BOOL APIENTRY
6452 NtUserSetMenuContextHelpId(
6453 HMENU hMenu,
6454 DWORD dwContextHelpId)
6455 {
6456 PMENU Menu;
6457 DECLARE_RETURN(BOOL);
6458
6459 TRACE("Enter NtUserSetMenuContextHelpId\n");
6460 UserEnterExclusive();
6461
6462 if(!(Menu = UserGetMenuObject(hMenu)))
6463 {
6464 RETURN( FALSE);
6465 }
6466
6467 RETURN(IntSetMenuContextHelpId(Menu, dwContextHelpId));
6468
6469 CLEANUP:
6470 TRACE("Leave NtUserSetMenuContextHelpId, ret=%i\n",_ret_);
6471 UserLeave();
6472 END_CLEANUP;
6473 }
6474
6475 /*
6476 * @implemented
6477 */
6478 BOOL APIENTRY
6479 NtUserSetMenuDefaultItem(
6480 HMENU hMenu,
6481 UINT uItem,
6482 UINT fByPos)
6483 {
6484 PMENU Menu;
6485 DECLARE_RETURN(BOOL);
6486
6487 TRACE("Enter NtUserSetMenuDefaultItem\n");
6488 UserEnterExclusive();
6489
6490 if(!(Menu = UserGetMenuObject(hMenu)))
6491 {
6492 RETURN( FALSE);
6493 }
6494
6495 RETURN( UserSetMenuDefaultItem(Menu, uItem, fByPos));
6496
6497 CLEANUP:
6498 TRACE("Leave NtUserSetMenuDefaultItem, ret=%i\n",_ret_);
6499 UserLeave();
6500 END_CLEANUP;
6501 }
6502
6503 /*
6504 * @implemented
6505 */
6506 BOOL APIENTRY
6507 NtUserSetMenuFlagRtoL(
6508 HMENU hMenu)
6509 {
6510 PMENU Menu;
6511 DECLARE_RETURN(BOOL);
6512
6513 TRACE("Enter NtUserSetMenuFlagRtoL\n");
6514 UserEnterExclusive();
6515
6516 if(!(Menu = UserGetMenuObject(hMenu)))
6517 {
6518 RETURN( FALSE);
6519 }
6520
6521 RETURN(IntSetMenuFlagRtoL(Menu));
6522
6523 CLEANUP:
6524 TRACE("Leave NtUserSetMenuFlagRtoL, ret=%i\n",_ret_);
6525 UserLeave();
6526 END_CLEANUP;
6527 }
6528
6529 /*
6530 * @implemented
6531 */
6532 BOOL APIENTRY
6533 NtUserThunkedMenuInfo(
6534 HMENU hMenu,
6535 LPCMENUINFO lpcmi)
6536 {
6537 PMENU Menu;
6538 DECLARE_RETURN(BOOL);
6539
6540 TRACE("Enter NtUserThunkedMenuInfo\n");
6541 UserEnterExclusive();
6542
6543 if (!(Menu = UserGetMenuObject(hMenu)))
6544 {
6545 RETURN(FALSE);
6546 }
6547
6548 RETURN(UserMenuInfo(Menu, (PROSMENUINFO)lpcmi, TRUE));
6549
6550 CLEANUP:
6551 TRACE("Leave NtUserThunkedMenuInfo, ret=%i\n",_ret_);
6552 UserLeave();
6553 END_CLEANUP;
6554 }
6555
6556 /*
6557 * @implemented
6558 */
6559 BOOL APIENTRY
6560 NtUserThunkedMenuItemInfo(
6561 HMENU hMenu,
6562 UINT uItem,
6563 BOOL fByPosition,
6564 BOOL bInsert,
6565 LPMENUITEMINFOW lpmii,
6566 PUNICODE_STRING lpszCaption)
6567 {
6568 PMENU Menu;
6569 NTSTATUS Status;
6570 UNICODE_STRING lstrCaption;
6571 DECLARE_RETURN(BOOL);
6572
6573 TRACE("Enter NtUserThunkedMenuItemInfo\n");
6574 UserEnterExclusive();
6575
6576 /* lpszCaption may be NULL, check for it and call RtlInitUnicodeString()
6577 if bInsert == TRUE call UserInsertMenuItem() else UserSetMenuItemInfo() */
6578
6579 RtlInitEmptyUnicodeString(&lstrCaption, NULL, 0);
6580
6581 if (!(Menu = UserGetMenuObject(hMenu)))
6582 {
6583 RETURN(FALSE);
6584 }
6585
6586 /* Check if we got a Caption */
6587 if (lpszCaption && lpszCaption->Buffer)
6588 {
6589 /* Copy the string to kernel mode */
6590 Status = ProbeAndCaptureUnicodeString( &lstrCaption,
6591 UserMode,
6592 lpszCaption);
6593 if (!NT_SUCCESS(Status))
6594 {
6595 ERR("Failed to capture MenuItem Caption (status 0x%08x)\n",Status);
6596 SetLastNtError(Status);
6597 RETURN(FALSE);
6598 }
6599 }
6600
6601 if (bInsert) RETURN( UserInsertMenuItem(Menu, uItem, fByPosition, lpmii, &lstrCaption));
6602
6603 RETURN( UserMenuItemInfo(Menu, uItem, fByPosition, (PROSMENUITEMINFO)lpmii, TRUE, &lstrCaption));
6604
6605 CLEANUP:
6606 if (lstrCaption.Buffer)
6607 {
6608 ReleaseCapturedUnicodeString(&lstrCaption, UserMode);
6609 }
6610
6611 TRACE("Leave NtUserThunkedMenuItemInfo, ret=%i\n",_ret_);
6612 UserLeave();
6613 END_CLEANUP;
6614 }
6615
6616 /*
6617 * @implemented
6618 */
6619 BOOL APIENTRY
6620 NtUserTrackPopupMenuEx(
6621 HMENU hMenu,
6622 UINT fuFlags,
6623 int x,
6624 int y,
6625 HWND hWnd,
6626 LPTPMPARAMS lptpm)
6627 {
6628 PMENU menu;
6629 PWND pWnd;
6630 TPMPARAMS tpm;
6631 BOOL Ret = FALSE;
6632 USER_REFERENCE_ENTRY Ref;
6633
6634 TRACE("Enter NtUserTrackPopupMenuEx\n");
6635 UserEnterExclusive();
6636 /* Parameter check */
6637 if (!(menu = UserGetMenuObject( hMenu )))
6638 {
6639 ERR("TPME : Invalid Menu handle.\n");
6640 EngSetLastError( ERROR_INVALID_MENU_HANDLE );
6641 goto Exit;
6642 }
6643
6644 if (!(pWnd = UserGetWindowObject(hWnd)))
6645 {
6646 ERR("TPME : Invalid Window handle.\n");
6647 goto Exit;
6648 }
6649
6650 if (lptpm)
6651 {
6652 _SEH2_TRY
6653 {
6654 ProbeForRead(lptpm, sizeof(TPMPARAMS), sizeof(ULONG));
6655 tpm = *lptpm;
6656 }
6657 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6658 {
6659 _SEH2_YIELD(goto Exit);
6660 }
6661 _SEH2_END
6662 }
6663 UserRefObjectCo(pWnd, &Ref);
6664 Ret = IntTrackPopupMenuEx(menu, fuFlags, x, y, pWnd, lptpm ? &tpm : NULL);
6665 UserDerefObjectCo(pWnd);
6666
6667 Exit:
6668 TRACE("Leave NtUserTrackPopupMenuEx, ret=%i\n",Ret);
6669 UserLeave();
6670 return Ret;
6671 }
6672
6673 /* EOF */