[WIN32K:NTUSER] Do not try to reposition an off-screen menu as if it were a popup...
[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 = max(ncm.lfMenuFont.lfWeight + 300, 1000);
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, - lpitem->xItem, - 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_width, arrow_bitmap_height;
2181 RECT rect, dfcrc;
2182 UINT Flags = 0;
2183
2184 arrow_bitmap_width = gpsi->oembmi[OBI_DNARROW].cx;
2185 arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy;
2186
2187 rect.left = 0;
2188 rect.top = 0;
2189 rect.right = lppop->cxMenu;
2190 rect.bottom = arrow_bitmap_height;
2191 FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENU));
2192 dfcrc.left = (lppop->cxMenu - arrow_bitmap_width) / 2;
2193 dfcrc.top = 0;
2194 dfcrc.right = arrow_bitmap_width;
2195 dfcrc.bottom = arrow_bitmap_height;
2196 DrawFrameControl(hdc, &dfcrc, DFC_MENU, (lppop->iTop ? 0 : DFCS_INACTIVE)|DFCS_MENUARROWUP);
2197
2198 rect.top = lppop->cyMenu - arrow_bitmap_height;
2199 rect.bottom = lppop->cyMenu;
2200 FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENU));
2201 if (!(lppop->iTop < lppop->iMaxTop - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height)))
2202 Flags = DFCS_INACTIVE;
2203 dfcrc.left = (lppop->cxMenu - arrow_bitmap_width) / 2;
2204 dfcrc.top = lppop->cyMenu - arrow_bitmap_height;
2205 dfcrc.right = arrow_bitmap_width;
2206 dfcrc.bottom = lppop->cyMenu;
2207 DrawFrameControl(hdc, &dfcrc, DFC_MENU, Flags|DFCS_MENUARROWDOWN);
2208 }
2209
2210 /***********************************************************************
2211 * MenuDrawMenuItem
2212 *
2213 * Draw a single menu item.
2214 */
2215 static void FASTCALL MENU_DrawMenuItem(PWND Wnd, PMENU Menu, PWND WndOwner, HDC hdc,
2216 PITEM lpitem, UINT Height, BOOL menuBar, UINT odaction)
2217 {
2218 RECT rect;
2219 PWCHAR Text;
2220 BOOL flat_menu = FALSE;
2221 int bkgnd;
2222 UINT arrow_bitmap_width = 0;
2223 //RECT bmprc;
2224
2225 if (!menuBar) {
2226 arrow_bitmap_width = gpsi->oembmi[OBI_MNARROW].cx;
2227 }
2228
2229 if (lpitem->fType & MF_SYSMENU)
2230 {
2231 if (!(Wnd->style & WS_MINIMIZE))
2232 {
2233 NC_GetInsideRect(Wnd, &rect);
2234 UserDrawSysMenuButton(Wnd, hdc, &rect, lpitem->fState & (MF_HILITE | MF_MOUSESELECT));
2235 }
2236 return;
2237 }
2238
2239 UserSystemParametersInfo (SPI_GETFLATMENU, 0, &flat_menu, 0);
2240 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
2241
2242 /* Setup colors */
2243
2244 if (lpitem->fState & MF_HILITE)
2245 {
2246 if(menuBar && !flat_menu) {
2247 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_MENUTEXT));
2248 IntGdiSetBkColor(hdc, IntGetSysColor(COLOR_MENU));
2249 } else {
2250 if (lpitem->fState & MF_GRAYED)
2251 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_GRAYTEXT));
2252 else
2253 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_HIGHLIGHTTEXT));
2254 IntGdiSetBkColor(hdc, IntGetSysColor(COLOR_HIGHLIGHT));
2255 }
2256 }
2257 else
2258 {
2259 if (lpitem->fState & MF_GRAYED)
2260 IntGdiSetTextColor( hdc, IntGetSysColor( COLOR_GRAYTEXT ) );
2261 else
2262 IntGdiSetTextColor( hdc, IntGetSysColor( COLOR_MENUTEXT ) );
2263 IntGdiSetBkColor( hdc, IntGetSysColor( bkgnd ) );
2264 }
2265
2266 //TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->Rect));
2267 //rect = lpitem->Rect;
2268 rect.left = lpitem->xItem;
2269 rect.top = lpitem->yItem;
2270 rect.right = lpitem->cxItem; // Do this for now......
2271 rect.bottom = lpitem->cyItem;
2272
2273 MENU_AdjustMenuItemRect(Menu, &rect);
2274
2275 if (lpitem->fType & MF_OWNERDRAW)
2276 {
2277 /*
2278 ** Experimentation under Windows reveals that an owner-drawn
2279 ** menu is given the rectangle which includes the space it requested
2280 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
2281 ** and a popup-menu arrow. This is the value of lpitem->rect.
2282 ** Windows will leave all drawing to the application except for
2283 ** the popup-menu arrow. Windows always draws that itself, after
2284 ** the menu owner has finished drawing.
2285 */
2286 DRAWITEMSTRUCT dis;
2287 COLORREF old_bk, old_text;
2288
2289 dis.CtlType = ODT_MENU;
2290 dis.CtlID = 0;
2291 dis.itemID = lpitem->wID;
2292 dis.itemData = (DWORD)lpitem->dwItemData;
2293 dis.itemState = 0;
2294 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
2295 if (lpitem->fState & MF_DEFAULT) dis.itemState |= ODS_DEFAULT;
2296 if (lpitem->fState & MF_DISABLED) dis.itemState |= ODS_DISABLED;
2297 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED | ODS_DISABLED;
2298 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
2299 if (!(Menu->fFlags & MNF_UNDERLINE)) dis.itemState |= ODS_NOACCEL;
2300 if (Menu->fFlags & MNF_INACTIVE) dis.itemState |= ODS_INACTIVE;
2301 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
2302 dis.hwndItem = (HWND) UserHMGetHandle(Menu);
2303 dis.hDC = hdc;
2304 dis.rcItem = rect;
2305 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
2306 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", Wnd,
2307 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
2308 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
2309 dis.rcItem.bottom);
2310 TRACE("Ownerdraw: Width %d Height %d\n", dis.rcItem.right-dis.rcItem.left, dis.rcItem.bottom-dis.rcItem.top);
2311 old_bk = GreGetBkColor(hdc);
2312 old_text = GreGetTextColor(hdc);
2313 co_IntSendMessage(UserHMGetHandle(WndOwner), WM_DRAWITEM, 0, (LPARAM) &dis);
2314 IntGdiSetBkColor(hdc, old_bk);
2315 IntGdiSetTextColor(hdc, old_text);
2316 /* Draw the popup-menu arrow */
2317 if (!menuBar && lpitem->spSubMenu)
2318 {
2319 RECT rectTemp;
2320 RtlCopyMemory(&rectTemp, &rect, sizeof(RECT));
2321 rectTemp.left = rectTemp.right - UserGetSystemMetrics(SM_CXMENUCHECK);
2322 DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
2323 }
2324 return;
2325 }
2326
2327 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
2328
2329 if (lpitem->fState & MF_HILITE)
2330 {
2331 if (flat_menu)
2332 {
2333 RECTL_vInflateRect (&rect, -1, -1);
2334 FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENUHILIGHT));
2335 RECTL_vInflateRect (&rect, 1, 1);
2336 FrameRect(hdc, &rect, IntGetSysColorBrush(COLOR_HIGHLIGHT));
2337 }
2338 else
2339 {
2340 if (menuBar)
2341 {
2342 FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENU));
2343 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
2344 }
2345 else
2346 {
2347 FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_HIGHLIGHT));
2348 }
2349 }
2350 }
2351 else
2352 FillRect( hdc, &rect, IntGetSysColorBrush(bkgnd) );
2353
2354 IntGdiSetBkMode( hdc, TRANSPARENT );
2355
2356 /* vertical separator */
2357 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
2358 {
2359 HPEN oldPen;
2360 RECT rc = rect;
2361
2362 rc.left -= 3;//MENU_COL_SPACE / 2 + 1; == 3!!
2363 rc.top = 3;
2364 rc.bottom = Height - 3;
2365 if (flat_menu)
2366 {
2367 oldPen = NtGdiSelectPen( hdc, NtGdiGetStockObject(DC_PEN) );
2368 IntSetDCPenColor(hdc, IntGetSysColor(COLOR_BTNSHADOW));
2369 GreMoveTo( hdc, rc.left, rc.top, NULL );
2370 NtGdiLineTo( hdc, rc.left, rc.bottom );
2371 NtGdiSelectPen( hdc, oldPen );
2372 }
2373 else
2374 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
2375 }
2376
2377 /* horizontal separator */
2378 if (lpitem->fType & MF_SEPARATOR)
2379 {
2380 HPEN oldPen;
2381 RECT rc = rect;
2382
2383 rc.left++;
2384 rc.right--;
2385 rc.top = (rc.top + rc.bottom) / 2 - 1;
2386 if (flat_menu)
2387 {
2388 oldPen = NtGdiSelectPen( hdc, NtGdiGetStockObject(DC_PEN) );
2389 IntSetDCPenColor( hdc, IntGetSysColor(COLOR_BTNSHADOW));
2390 GreMoveTo( hdc, rc.left, rc.top, NULL );
2391 NtGdiLineTo( hdc, rc.right, rc.top );
2392 NtGdiSelectPen( hdc, oldPen );
2393 }
2394 else
2395 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
2396 return;
2397 }
2398 #if 0
2399 /* helper lines for debugging */
2400 /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
2401 FrameRect(hdc, &rect, NtGdiGetStockObject(BLACK_BRUSH));
2402 NtGdiSelectPen(hdc, NtGdiGetStockObject(DC_PEN));
2403 IntSetDCPenColor(hdc, IntGetSysColor(COLOR_WINDOWFRAME));
2404 GreMoveTo(hdc, rect.left, (rect.top + rect.bottom) / 2, NULL);
2405 NtGdiLineTo(hdc, rect.right, (rect.top + rect.bottom) / 2);
2406 #endif
2407 #if 0 // breaks mdi menu bar icons.
2408 if (lpitem->hbmp) {
2409 /* calculate the bitmap rectangle in coordinates relative
2410 * to the item rectangle */
2411 if( menuBar) {
2412 if( lpitem->hbmp == HBMMENU_CALLBACK)
2413 bmprc.left = 3;
2414 else
2415 bmprc.left = lpitem->Xlpstr ? MenuCharSize.cx : 0;
2416 }
2417 else if ((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)
2418 bmprc.left = 4;
2419 else if ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)
2420 bmprc.left = 2;
2421 else
2422 bmprc.left = 4 + UserGetSystemMetrics(SM_CXMENUCHECK);
2423
2424 bmprc.right = bmprc.left + lpitem->cxBmp;
2425
2426 if( menuBar && !(lpitem->hbmp == HBMMENU_CALLBACK))
2427 bmprc.top = 0;
2428 else
2429 bmprc.top = (rect.bottom - rect.top - lpitem->cyBmp) / 2;
2430
2431 bmprc.bottom = bmprc.top + lpitem->cyBmp;
2432 }
2433 #endif
2434 if (!menuBar)
2435 {
2436 HBITMAP bm;
2437 INT y = rect.top + rect.bottom;
2438 RECT rc = rect;
2439 BOOL checked = FALSE;
2440 UINT check_bitmap_width = UserGetSystemMetrics( SM_CXMENUCHECK );
2441 UINT check_bitmap_height = UserGetSystemMetrics( SM_CYMENUCHECK );
2442 /* Draw the check mark
2443 *
2444 * FIXME:
2445 * Custom checkmark bitmaps are monochrome but not always 1bpp.
2446 */
2447 if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)) {
2448 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hbmpChecked :
2449 lpitem->hbmpUnchecked;
2450 if (bm) /* we have a custom bitmap */
2451 {
2452 HDC hdcMem = NtGdiCreateCompatibleDC( hdc );
2453
2454 NtGdiSelectBitmap( hdcMem, bm );
2455 NtGdiBitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
2456 check_bitmap_width, check_bitmap_height,
2457 hdcMem, 0, 0, SRCCOPY, 0,0);
2458 IntGdiDeleteDC( hdcMem, FALSE );
2459 checked = TRUE;
2460 }
2461 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
2462 {
2463 RECT r;
2464 r = rect;
2465 r.right = r.left + check_bitmap_width;
2466 DrawFrameControl( hdc, &r, DFC_MENU,
2467 (lpitem->fType & MFT_RADIOCHECK) ?
2468 DFCS_MENUBULLET : DFCS_MENUCHECK);
2469 checked = TRUE;
2470 }
2471 }
2472 if ( lpitem->hbmp )//&& !( checked && ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)))
2473 {
2474 RECT bmpRect = rect;
2475 if (!((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP) && !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
2476 bmpRect.left += check_bitmap_width + 2;
2477 if (!(checked && ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)))
2478 {
2479 bmpRect.right = bmpRect.left + lpitem->cxBmp;
2480 MENU_DrawBitmapItem(hdc, lpitem, &bmpRect, Menu, WndOwner, odaction, menuBar);
2481 }
2482 }
2483 /* Draw the popup-menu arrow */
2484 if (lpitem->spSubMenu)
2485 {
2486 RECT rectTemp;
2487 RtlCopyMemory(&rectTemp, &rect, sizeof(RECT));
2488 rectTemp.left = rectTemp.right - check_bitmap_width;
2489 DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
2490 }
2491 rect.left += 4;
2492 if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
2493 rect.left += check_bitmap_width;
2494 rect.right -= arrow_bitmap_width;
2495 }
2496 else if( lpitem->hbmp)
2497 { /* Draw the bitmap */
2498 MENU_DrawBitmapItem(hdc, lpitem, &rect/*bmprc*/, Menu, WndOwner, odaction, menuBar);
2499 }
2500
2501 /* process text if present */
2502 if (lpitem->Xlpstr)
2503 {
2504 int i = 0;
2505 HFONT hfontOld = 0;
2506
2507 UINT uFormat = menuBar ?
2508 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
2509 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
2510
2511 if (((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP))
2512 rect.left += max(0, (int)(Menu->cxTextAlign - UserGetSystemMetrics(SM_CXMENUCHECK)));
2513 else
2514 rect.left += Menu->cxTextAlign;
2515
2516 if ( lpitem->fState & MFS_DEFAULT )
2517 {
2518 hfontOld = NtGdiSelectFont(hdc, ghMenuFontBold);
2519 }
2520
2521 if (menuBar) {
2522 if( lpitem->hbmp)
2523 rect.left += lpitem->cxBmp;
2524 if( !(lpitem->hbmp == HBMMENU_CALLBACK))
2525 rect.left += MenuCharSize.cx;
2526 rect.right -= MenuCharSize.cx;
2527 }
2528
2529 Text = lpitem->Xlpstr;
2530 if(Text)
2531 {
2532 for (i = 0; Text[i]; i++)
2533 if (Text[i] == L'\t' || Text[i] == L'\b')
2534 break;
2535 }
2536
2537 if (menuBar &&
2538 !flat_menu &&
2539 (lpitem->fState & (MF_HILITE | MF_GRAYED)) == MF_HILITE)
2540 {
2541 RECTL_vOffsetRect(&rect, +1, +1);
2542 }
2543
2544 if (!menuBar)
2545 --rect.bottom;
2546
2547 if(lpitem->fState & MF_GRAYED)
2548 {
2549 if (!(lpitem->fState & MF_HILITE) )
2550 {
2551 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
2552 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_BTNHIGHLIGHT));
2553 DrawTextW( hdc, Text, i, &rect, uFormat );
2554 --rect.left; --rect.top; --rect.right; --rect.bottom;
2555 }
2556 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_BTNSHADOW));
2557 }
2558 DrawTextW( hdc, Text, i, &rect, uFormat);
2559
2560 /* paint the shortcut text */
2561 if (!menuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */
2562 {
2563 if (L'\t' == Text[i])
2564 {
2565 rect.left = lpitem->dxTab;
2566 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
2567 }
2568 else
2569 {
2570 rect.right = lpitem->dxTab;
2571 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
2572 }
2573
2574 if (lpitem->fState & MF_GRAYED)
2575 {
2576 if (!(lpitem->fState & MF_HILITE) )
2577 {
2578 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
2579 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_BTNHIGHLIGHT));
2580 DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat);
2581 --rect.left; --rect.top; --rect.right; --rect.bottom;
2582 }
2583 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_BTNSHADOW));
2584 }
2585 DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat );
2586 }
2587
2588 if (!menuBar)
2589 ++rect.bottom;
2590
2591 if (menuBar &&
2592 !flat_menu &&
2593 (lpitem->fState & (MF_HILITE | MF_GRAYED)) == MF_HILITE)
2594 {
2595 RECTL_vOffsetRect(&rect, -1, -1);
2596 }
2597
2598 if (hfontOld)
2599 {
2600 NtGdiSelectFont (hdc, hfontOld);
2601 }
2602 }
2603 }
2604
2605 /***********************************************************************
2606 * MenuDrawPopupMenu
2607 *
2608 * Paint a popup menu.
2609 */
2610 static void FASTCALL MENU_DrawPopupMenu(PWND wnd, HDC hdc, PMENU menu )
2611 {
2612 HBRUSH hPrevBrush = 0, brush = IntGetSysColorBrush(COLOR_MENU);
2613 RECT rect;
2614
2615 TRACE("DPM wnd=%p dc=%p menu=%p\n", wnd, hdc, menu);
2616
2617 IntGetClientRect( wnd, &rect );
2618
2619 if (menu && menu->hbrBack) brush = menu->hbrBack;
2620 if((hPrevBrush = NtGdiSelectBrush( hdc, brush ))
2621 && (NtGdiSelectFont( hdc, ghMenuFont)))
2622 {
2623 HPEN hPrevPen;
2624
2625 NtGdiRectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
2626
2627 hPrevPen = NtGdiSelectPen( hdc, NtGdiGetStockObject( NULL_PEN ) );
2628 if ( hPrevPen )
2629 {
2630 BOOL flat_menu = FALSE;
2631
2632 UserSystemParametersInfo (SPI_GETFLATMENU, 0, &flat_menu, 0);
2633 if (flat_menu)
2634 FrameRect(hdc, &rect, IntGetSysColorBrush(COLOR_BTNSHADOW));
2635 else
2636 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
2637
2638 TRACE("hmenu %p Style %08x\n", UserHMGetHandle(menu), (menu->fFlags & MNS_STYLE_MASK));
2639 /* draw menu items */
2640 if (menu && menu->cItems)
2641 {
2642 ITEM *item;
2643 UINT u;
2644
2645 item = menu->rgItems;
2646 for( u = menu->cItems; u > 0; u--, item++)
2647 {
2648 MENU_DrawMenuItem(wnd, menu, menu->spwndNotify, hdc, item,
2649 menu->cyMenu, FALSE, ODA_DRAWENTIRE);
2650 }
2651 /* draw scroll arrows */
2652 if (menu->dwArrowsOn)
2653 {
2654 MENU_DrawScrollArrows(menu, hdc);
2655 }
2656 }
2657 }
2658 else
2659 {
2660 NtGdiSelectBrush( hdc, hPrevBrush );
2661 }
2662 }
2663 }
2664
2665 /**********************************************************************
2666 * MENU_IsMenuActive
2667 */
2668 PWND MENU_IsMenuActive(VOID)
2669 {
2670 return ValidateHwndNoErr(top_popup);
2671 }
2672
2673 /**********************************************************************
2674 * MENU_EndMenu
2675 *
2676 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2677 *
2678 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2679 */
2680 void MENU_EndMenu( PWND pwnd )
2681 {
2682 PMENU menu = NULL;
2683 menu = UserGetMenuObject(top_popup_hmenu);
2684 if ( menu && ( UserHMGetHandle(pwnd) == menu->hWnd || pwnd == menu->spwndNotify ) )
2685 {
2686 if (fInsideMenuLoop && top_popup)
2687 {
2688 fInsideMenuLoop = FALSE;
2689
2690 if (fInEndMenu)
2691 {
2692 ERR("Already in End loop\n");
2693 return;
2694 }
2695
2696 fInEndMenu = TRUE;
2697 UserPostMessage( top_popup, WM_CANCELMODE, 0, 0);
2698 }
2699 }
2700 }
2701
2702 DWORD WINAPI
2703 IntDrawMenuBarTemp(PWND pWnd, HDC hDC, LPRECT Rect, PMENU pMenu, HFONT Font)
2704 {
2705 UINT i;
2706 HFONT FontOld = NULL;
2707 BOOL flat_menu = FALSE;
2708
2709 UserSystemParametersInfo(SPI_GETFLATMENU, 0, &flat_menu, 0);
2710
2711 if (!pMenu)
2712 {
2713 pMenu = UserGetMenuObject(UlongToHandle(pWnd->IDMenu));
2714 }
2715
2716 if (!Font)
2717 {
2718 Font = ghMenuFont;
2719 }
2720
2721 if (Rect == NULL || !pMenu)
2722 {
2723 return UserGetSystemMetrics(SM_CYMENU);
2724 }
2725
2726 TRACE("(%x, %x, %p, %x, %x)\n", pWnd, hDC, Rect, pMenu, Font);
2727
2728 FontOld = NtGdiSelectFont(hDC, Font);
2729
2730 if (pMenu->cyMenu == 0)
2731 {
2732 MENU_MenuBarCalcSize(hDC, Rect, pMenu, pWnd);
2733 }
2734
2735 Rect->bottom = Rect->top + pMenu->cyMenu;
2736
2737 FillRect(hDC, Rect, IntGetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2738
2739 NtGdiSelectPen(hDC, NtGdiGetStockObject(DC_PEN));
2740 IntSetDCPenColor(hDC, IntGetSysColor(COLOR_3DFACE));
2741 GreMoveTo(hDC, Rect->left, Rect->bottom - 1, NULL);
2742 NtGdiLineTo(hDC, Rect->right, Rect->bottom - 1);
2743
2744 if (pMenu->cItems == 0)
2745 {
2746 NtGdiSelectFont(hDC, FontOld);
2747 return UserGetSystemMetrics(SM_CYMENU);
2748 }
2749
2750 for (i = 0; i < pMenu->cItems; i++)
2751 {
2752 MENU_DrawMenuItem(pWnd, pMenu, pWnd, hDC, &pMenu->rgItems[i], pMenu->cyMenu, TRUE, ODA_DRAWENTIRE);
2753 }
2754
2755 NtGdiSelectFont(hDC, FontOld);
2756
2757 return pMenu->cyMenu;
2758 }
2759
2760 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, PWND pWnd, BOOL suppress_draw )
2761 {
2762 HFONT hfontOld = 0;
2763 PMENU lppop = UserGetMenuObject(UlongToHandle(pWnd->IDMenu));
2764
2765 if (lppop == NULL)
2766 {
2767 // No menu. Do not reserve any space
2768 return 0;
2769 }
2770
2771 if (lprect == NULL)
2772 {
2773 return UserGetSystemMetrics(SM_CYMENU);
2774 }
2775
2776 if (suppress_draw)
2777 {
2778 hfontOld = NtGdiSelectFont(hDC, ghMenuFont);
2779
2780 MENU_MenuBarCalcSize(hDC, lprect, lppop, pWnd);
2781
2782 lprect->bottom = lprect->top + lppop->cyMenu;
2783
2784 if (hfontOld) NtGdiSelectFont( hDC, hfontOld);
2785
2786 return lppop->cyMenu;
2787 }
2788 else
2789 {
2790 return IntDrawMenuBarTemp(pWnd, hDC, lprect, lppop, NULL);
2791 }
2792 }
2793
2794 /***********************************************************************
2795 * MENU_InitPopup
2796 *
2797 * Popup menu initialization before WM_ENTERMENULOOP.
2798 */
2799 static BOOL MENU_InitPopup( PWND pWndOwner, PMENU menu, UINT flags )
2800 {
2801 PWND pWndCreated;
2802 PPOPUPMENU pPopupMenu;
2803 CREATESTRUCTW Cs;
2804 LARGE_STRING WindowName;
2805 UNICODE_STRING ClassName;
2806 DWORD ex_style = WS_EX_TOOLWINDOW;
2807
2808 TRACE("owner=%p hmenu=%p\n", pWndOwner, menu);
2809
2810 menu->spwndNotify = pWndOwner;
2811
2812 if (flags & TPM_LAYOUTRTL || pWndOwner->ExStyle & WS_EX_LAYOUTRTL)
2813 ex_style = WS_EX_LAYOUTRTL;
2814
2815 ClassName.Buffer = WC_MENU;
2816 ClassName.Length = 0;
2817
2818 RtlZeroMemory(&WindowName, sizeof(WindowName));
2819 RtlZeroMemory(&Cs, sizeof(Cs));
2820 Cs.style = WS_POPUP;
2821 Cs.dwExStyle = ex_style;
2822 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
2823 Cs.lpszName = (LPCWSTR) &WindowName;
2824 Cs.lpszClass = (LPCWSTR) &ClassName;
2825 Cs.lpCreateParams = UserHMGetHandle(menu);
2826 Cs.hwndParent = UserHMGetHandle(pWndOwner);
2827
2828 /* NOTE: In Windows, top menu popup is not owned. */
2829 pWndCreated = co_UserCreateWindowEx( &Cs, &ClassName, &WindowName, NULL);
2830
2831 if( !pWndCreated ) return FALSE;
2832
2833 //
2834 // Setup pop up menu structure.
2835 //
2836 menu->hWnd = UserHMGetHandle(pWndCreated);
2837
2838 pPopupMenu = ((PMENUWND)pWndCreated)->ppopupmenu;
2839
2840 pPopupMenu->spwndActivePopup = pWndCreated; // top_popup = MenuInfo.Wnd or menu->hWnd
2841 pPopupMenu->spwndNotify = pWndOwner; // Same as MenuInfo.spwndNotify(which could be wrong) or menu->hwndOwner
2842 //pPopupMenu->spmenu = menu; Should be set up already from WM_CREATE!
2843
2844 pPopupMenu->fIsTrackPopup = !!(flags & TPM_POPUPMENU);
2845 pPopupMenu->fIsSysMenu = !!(flags & TPM_SYSTEM_MENU);
2846 pPopupMenu->fNoNotify = !!(flags & TPM_NONOTIFY);
2847 pPopupMenu->fRightButton = !!(flags & TPM_RIGHTBUTTON);
2848 pPopupMenu->fSynchronous = !!(flags & TPM_RETURNCMD);
2849
2850 if (pPopupMenu->fRightButton)
2851 pPopupMenu->fFirstClick = !!(UserGetKeyState(VK_RBUTTON) & 0x8000);
2852 else
2853 pPopupMenu->fFirstClick = !!(UserGetKeyState(VK_LBUTTON) & 0x8000);
2854
2855 if (gpsi->aiSysMet[SM_MENUDROPALIGNMENT] ||
2856 menu->fFlags & MNF_RTOL)
2857 {
2858 pPopupMenu->fDroppedLeft = TRUE;
2859 }
2860 return TRUE;
2861 }
2862
2863 /***********************************************************************
2864 * MenuShowPopup
2865 *
2866 * Display a popup menu.
2867 */
2868 static BOOL FASTCALL MENU_ShowPopup(PWND pwndOwner, PMENU menu, UINT id, UINT flags,
2869 INT x, INT y)
2870 {
2871 UINT width, height;
2872 POINT pt;
2873 PMONITOR monitor;
2874 PWND pWnd;
2875 USER_REFERENCE_ENTRY Ref;
2876 BOOL bIsPopup = (flags & TPM_POPUPMENU) != 0;
2877
2878 TRACE("owner=%p menu=%p id=0x%04x x=0x%04x y=0x%04x\n",
2879 pwndOwner, menu, id, x, y);
2880
2881 if (menu->iItem != NO_SELECTED_ITEM)
2882 {
2883 menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
2884 menu->iItem = NO_SELECTED_ITEM;
2885 }
2886
2887 menu->dwArrowsOn = 0;
2888 MENU_PopupMenuCalcSize(menu, pwndOwner);
2889
2890 /* adjust popup menu pos so that it fits within the desktop */
2891
2892 width = menu->cxMenu + UserGetSystemMetrics(SM_CXBORDER);
2893 height = menu->cyMenu + UserGetSystemMetrics(SM_CYBORDER);
2894
2895 /* FIXME: should use item rect */
2896 pt.x = x;
2897 pt.y = y;
2898 monitor = UserMonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
2899
2900 if (flags & TPM_LAYOUTRTL)
2901 flags ^= TPM_RIGHTALIGN;
2902
2903 if( flags & TPM_RIGHTALIGN ) x -= width;
2904 if( flags & TPM_CENTERALIGN ) x -= width / 2;
2905
2906 if( flags & TPM_BOTTOMALIGN ) y -= height;
2907 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
2908
2909 if( x + width > monitor->rcMonitor.right)
2910 {
2911 if( x + width > monitor->rcMonitor.right)
2912 {
2913 /* If we would flip around our origin, would we go off screen on the other side?
2914 Or is our origin itself too far to the right already? */
2915 if (!bIsPopup || x - width < monitor->rcMonitor.left || x > monitor->rcMonitor.right)
2916 x = monitor->rcMonitor.right - width;
2917 else
2918 x -= width;
2919 }
2920 }
2921 if( x < monitor->rcMonitor.left )
2922 {
2923 /* If we would flip around our origin, would we go off screen on the other side? */
2924 if (!bIsPopup || x + width > monitor->rcMonitor.right)
2925 x = monitor->rcMonitor.left;
2926 else
2927 x += width;
2928 }
2929
2930 if( y + height > monitor->rcMonitor.bottom)
2931 {
2932 if( y + height > monitor->rcMonitor.bottom)
2933 {
2934 /* If we would flip around our origin, would we go off screen on the other side?
2935 Or is our origin itself too far to the bottom already? */
2936 if (!bIsPopup || y - height < monitor->rcMonitor.top || y > monitor->rcMonitor.bottom)
2937 y = monitor->rcMonitor.bottom - height;
2938 else
2939 y -= height;
2940 }
2941 }
2942 if( y < monitor->rcMonitor.top )
2943 {
2944 /* If we would flip around our origin, would we go off screen on the other side? */
2945 if (!bIsPopup || y + height > monitor->rcMonitor.bottom)
2946 y = monitor->rcMonitor.top;
2947 else
2948 y += height;
2949 }
2950
2951 pWnd = ValidateHwndNoErr( menu->hWnd );
2952
2953 if (!pWnd)
2954 {
2955 ERR("menu->hWnd bad hwnd %p\n",menu->hWnd);
2956 return FALSE;
2957 }
2958
2959 if (!top_popup) {
2960 top_popup = menu->hWnd;
2961 top_popup_hmenu = UserHMGetHandle(menu);
2962 }
2963
2964 /* Display the window */
2965 UserRefObjectCo(pWnd, &Ref);
2966 co_WinPosSetWindowPos( pWnd, HWND_TOPMOST, x, y, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE);
2967
2968 co_IntUpdateWindows(pWnd, RDW_ALLCHILDREN, FALSE);
2969
2970 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, pWnd, OBJID_CLIENT, CHILDID_SELF, 0);
2971 UserDerefObjectCo(pWnd);
2972
2973 return TRUE;
2974 }
2975
2976 /***********************************************************************
2977 * MENU_EnsureMenuItemVisible
2978 */
2979 void MENU_EnsureMenuItemVisible(PMENU lppop, UINT wIndex, HDC hdc)
2980 {
2981 USER_REFERENCE_ENTRY Ref;
2982 if (lppop->dwArrowsOn)
2983 {
2984 ITEM *item = &lppop->rgItems[wIndex];
2985 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
2986 UINT nOldPos = lppop->iTop;
2987 RECT rc;
2988 UINT arrow_bitmap_height;
2989 PWND pWnd = ValidateHwndNoErr(lppop->hWnd);
2990
2991 IntGetClientRect(pWnd, &rc);
2992
2993 arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy;
2994
2995 rc.top += arrow_bitmap_height;
2996 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
2997
2998 nMaxHeight -= UserGetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
2999 UserRefObjectCo(pWnd, &Ref);
3000 if (item->cyItem > lppop->iTop + nMaxHeight)
3001 {
3002 lppop->iTop = item->cyItem - nMaxHeight;
3003 IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc);
3004 MENU_DrawScrollArrows(lppop, hdc);
3005 //ERR("Scroll Down iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
3006 }
3007 else if (item->yItem - MENU_TOP_MARGIN < lppop->iTop)
3008 {
3009 lppop->iTop = item->yItem - MENU_TOP_MARGIN;
3010 IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc);
3011 MENU_DrawScrollArrows(lppop, hdc);
3012 //ERR("Scroll Up iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
3013 }
3014 UserDerefObjectCo(pWnd);
3015 }
3016 }
3017
3018 /***********************************************************************
3019 * MenuSelectItem
3020 */
3021 static void FASTCALL MENU_SelectItem(PWND pwndOwner, PMENU menu, UINT wIndex,
3022 BOOL sendMenuSelect, PMENU topmenu)
3023 {
3024 HDC hdc;
3025 PWND pWnd;
3026
3027 TRACE("M_SI: owner=%p menu=%p index=0x%04x select=0x%04x\n", pwndOwner, menu, wIndex, sendMenuSelect);
3028
3029 if (!menu || !menu->cItems) return;
3030
3031 pWnd = ValidateHwndNoErr(menu->hWnd);
3032
3033 if (!pWnd) return;
3034
3035 if (menu->iItem == wIndex) return;
3036
3037 if (menu->fFlags & MNF_POPUP)
3038 hdc = UserGetDCEx(pWnd, 0, DCX_USESTYLE);
3039 else
3040 hdc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW);
3041
3042 if (!top_popup) {
3043 top_popup = menu->hWnd; //pPopupMenu->spwndActivePopup or
3044 //pPopupMenu->fIsTrackPopup set pPopupMenu->spwndPopupMenu;
3045 top_popup_hmenu = UserHMGetHandle(menu); //pPopupMenu->spmenu
3046 }
3047
3048 NtGdiSelectFont( hdc, ghMenuFont );
3049
3050 /* Clear previous highlighted item */
3051 if (menu->iItem != NO_SELECTED_ITEM)
3052 {
3053 menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
3054 MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc, &menu->rgItems[menu->iItem],
3055 menu->cyMenu, !(menu->fFlags & MNF_POPUP),
3056 ODA_SELECT);
3057 }
3058
3059 /* Highlight new item (if any) */
3060 menu->iItem = wIndex;
3061 if (menu->iItem != NO_SELECTED_ITEM)
3062 {
3063 if (!(menu->rgItems[wIndex].fType & MF_SEPARATOR))
3064 {
3065 menu->rgItems[wIndex].fState |= MF_HILITE;
3066 MENU_EnsureMenuItemVisible(menu, wIndex, hdc);
3067 MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc,
3068 &menu->rgItems[wIndex], menu->cyMenu, !(menu->fFlags & MNF_POPUP), ODA_SELECT);
3069 }
3070 if (sendMenuSelect)
3071 {
3072 ITEM *ip = &menu->rgItems[menu->iItem];
3073 WPARAM wParam = MAKEWPARAM( ip->spSubMenu ? wIndex : ip->wID,
3074 ip->fType | ip->fState |
3075 (ip->spSubMenu ? MF_POPUP : 0) |
3076 (menu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
3077
3078 co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(menu));
3079 }
3080 }
3081 else if (sendMenuSelect)
3082 {
3083 if (topmenu)
3084 {
3085 int pos;
3086 pos = MENU_FindSubMenu(&topmenu, menu);
3087 if (pos != NO_SELECTED_ITEM)
3088 {
3089 ITEM *ip = &topmenu->rgItems[pos];
3090 WPARAM wParam = MAKEWPARAM( Pos, ip->fType | ip->fState |
3091 (ip->spSubMenu ? MF_POPUP : 0) |
3092 (topmenu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
3093
3094 co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(topmenu));
3095 }
3096 }
3097 }
3098 UserReleaseDC(pWnd, hdc, FALSE);
3099 }
3100
3101 /***********************************************************************
3102 * MenuMoveSelection
3103 *
3104 * Moves currently selected item according to the Offset parameter.
3105 * If there is no selection then it should select the last item if
3106 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
3107 */
3108 static void FASTCALL MENU_MoveSelection(PWND pwndOwner, PMENU menu, INT offset)
3109 {
3110 INT i;
3111
3112 TRACE("pwnd=%x menu=%x off=0x%04x\n", pwndOwner, menu, offset);
3113
3114 if ((!menu) || (!menu->rgItems)) return;
3115
3116 if ( menu->iItem != NO_SELECTED_ITEM )
3117 {
3118 if ( menu->cItems == 1 )
3119 return;
3120 else
3121 for (i = menu->iItem + offset ; i >= 0 && i < menu->cItems
3122 ; i += offset)
3123 if (!(menu->rgItems[i].fType & MF_SEPARATOR))
3124 {
3125 MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 );
3126 return;
3127 }
3128 }
3129
3130 for ( i = (offset > 0) ? 0 : menu->cItems - 1;
3131 i >= 0 && i < menu->cItems ; i += offset)
3132 if (!(menu->rgItems[i].fType & MF_SEPARATOR))
3133 {
3134 MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 );
3135 return;
3136 }
3137 }
3138
3139 /***********************************************************************
3140 * MenuHideSubPopups
3141 *
3142 * Hide the sub-popup menus of this menu.
3143 */
3144 static void FASTCALL MENU_HideSubPopups(PWND pWndOwner, PMENU Menu,
3145 BOOL SendMenuSelect, UINT wFlags)
3146 {
3147 TRACE("owner=%x menu=%x 0x%04x\n", pWndOwner, Menu, SendMenuSelect);
3148
3149 if ( Menu && top_popup )
3150 {
3151 PITEM Item;
3152
3153 if (Menu->iItem != NO_SELECTED_ITEM)
3154 {
3155 Item = &Menu->rgItems[Menu->iItem];
3156 if (!(Item->spSubMenu) ||
3157 !(Item->fState & MF_MOUSESELECT)) return;
3158 Item->fState &= ~MF_MOUSESELECT;
3159 }
3160 else
3161 return;
3162
3163 if (Item->spSubMenu)
3164 {
3165 PWND pWnd;
3166 if (!VerifyMenu(Item->spSubMenu)) return;
3167 pWnd = ValidateHwndNoErr(Item->spSubMenu->hWnd);
3168 MENU_HideSubPopups(pWndOwner, Item->spSubMenu, FALSE, wFlags);
3169 MENU_SelectItem(pWndOwner, Item->spSubMenu, NO_SELECTED_ITEM, SendMenuSelect, NULL);
3170 TRACE("M_HSP top p hm %p pWndOwner IDMenu %p\n",top_popup_hmenu,pWndOwner->IDMenu);
3171 co_UserDestroyWindow(pWnd);
3172
3173 /* Native returns handle to destroyed window */
3174 if (!(wFlags & TPM_NONOTIFY))
3175 {
3176 co_IntSendMessage( UserHMGetHandle(pWndOwner), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(Item->spSubMenu),
3177 MAKELPARAM(0, IS_SYSTEM_MENU(Item->spSubMenu)) );
3178 }
3179 ////
3180 // Call WM_UNINITMENUPOPUP FIRST before destroy!!
3181 // Fixes todo_wine User32 test menu.c line 2239 GetMenuBarInfo callback....
3182 //
3183 Item->spSubMenu->hWnd = NULL;
3184 ////
3185 }
3186 }
3187 }
3188
3189 /***********************************************************************
3190 * MenuShowSubPopup
3191 *
3192 * Display the sub-menu of the selected item of this menu.
3193 * Return the handle of the submenu, or menu if no submenu to display.
3194 */
3195 static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFirst, UINT Flags)
3196 {
3197 RECT Rect;
3198 ITEM *Item;
3199 HDC Dc;
3200 PWND pWnd;
3201
3202 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, Menu, SelectFirst);
3203
3204 if (!Menu) return Menu;
3205
3206 if (Menu->iItem == NO_SELECTED_ITEM) return Menu;
3207
3208 Item = &Menu->rgItems[Menu->iItem];
3209 if (!(Item->spSubMenu) || (Item->fState & (MF_GRAYED | MF_DISABLED)))
3210 return Menu;
3211
3212 /* message must be sent before using item,
3213 because nearly everything may be changed by the application ! */
3214
3215 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3216 if (!(Flags & TPM_NONOTIFY))
3217 {
3218 co_IntSendMessage(UserHMGetHandle(WndOwner), WM_INITMENUPOPUP,
3219 (WPARAM) UserHMGetHandle(Item->spSubMenu),
3220 MAKELPARAM(Menu->iItem, IS_SYSTEM_MENU(Menu)));
3221 }
3222
3223 Item = &Menu->rgItems[Menu->iItem];
3224 //Rect = ItemInfo.Rect;
3225 Rect.left = Item->xItem;
3226 Rect.top = Item->yItem;
3227 Rect.right = Item->cxItem; // Do this for now......
3228 Rect.bottom = Item->cyItem;
3229
3230 pWnd = ValidateHwndNoErr(Menu->hWnd);
3231
3232 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
3233 if (!(Item->fState & MF_HILITE))
3234 {
3235 if (Menu->fFlags & MNF_POPUP) Dc = UserGetDCEx(pWnd, NULL, DCX_USESTYLE);
3236 else Dc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW);
3237
3238 NtGdiSelectFont(Dc, ghMenuFont);
3239
3240 Item->fState |= MF_HILITE;
3241 MENU_DrawMenuItem(pWnd, Menu, WndOwner, Dc, Item, Menu->cyMenu,
3242 !(Menu->fFlags & MNF_POPUP), ODA_DRAWENTIRE);
3243
3244 UserReleaseDC(pWnd, Dc, FALSE);
3245 }
3246
3247 if (!Item->yItem && !Item->xItem && !Item->cyItem && !Item->cxItem)
3248 {
3249 Item->xItem = Rect.left;
3250 Item->yItem = Rect.top;
3251 Item->cxItem = Rect.right; // Do this for now......
3252 Item->cyItem = Rect.bottom;
3253 }
3254 Item->fState |= MF_MOUSESELECT;
3255
3256 if (IS_SYSTEM_MENU(Menu))
3257 {
3258 MENU_InitSysMenuPopup(Item->spSubMenu, pWnd->style, pWnd->pcls->style, HTSYSMENU);
3259
3260 NC_GetSysPopupPos(pWnd, &Rect);
3261 if (Flags & TPM_LAYOUTRTL) Rect.left = Rect.right;
3262 Rect.top = Rect.bottom;
3263 Rect.right = UserGetSystemMetrics(SM_CXSIZE);
3264 Rect.bottom = UserGetSystemMetrics(SM_CYSIZE);
3265 }
3266 else
3267 {
3268 IntGetWindowRect(pWnd, &Rect);
3269 if (Menu->fFlags & MNF_POPUP)
3270 {
3271 RECT rc;
3272 rc.left = Item->xItem;
3273 rc.top = Item->yItem;
3274 rc.right = Item->cxItem; // Do this for now......
3275 rc.bottom = Item->cyItem;
3276
3277 MENU_AdjustMenuItemRect(Menu, &rc);
3278
3279 /* The first item in the popup menu has to be at the
3280 same y position as the focused menu item */
3281 if(Flags & TPM_LAYOUTRTL)
3282 Rect.left += UserGetSystemMetrics(SM_CXBORDER);
3283 else
3284 Rect.left += rc.right /*ItemInfo.Rect.right*/ - UserGetSystemMetrics(SM_CXBORDER);
3285 Rect.top += rc.top - MENU_TOP_MARGIN;//3;
3286 Rect.right = rc.left - rc.right + UserGetSystemMetrics(SM_CXBORDER);
3287 Rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN - MENU_BOTTOM_MARGIN/*2*/
3288 - UserGetSystemMetrics(SM_CYBORDER);
3289 }
3290 else
3291 {
3292 if(Flags & TPM_LAYOUTRTL)
3293 Rect.left += Rect.right - Item->xItem; //ItemInfo.Rect.left;
3294 else
3295 Rect.left += Item->xItem; //ItemInfo.Rect.left;
3296 Rect.top += Item->cyItem; //ItemInfo.Rect.bottom;
3297 Rect.right = Item->cxItem - Item->xItem; //ItemInfo.Rect.right - ItemInfo.Rect.left;
3298 Rect.bottom = Item->cyItem - Item->yItem; //ItemInfo.Rect.bottom - ItemInfo.Rect.top;
3299 }
3300 }
3301
3302 /* use default alignment for submenus */
3303 Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
3304
3305 MENU_InitPopup( WndOwner, Item->spSubMenu, Flags );
3306
3307 MENU_ShowPopup( WndOwner, Item->spSubMenu, Menu->iItem, Flags,
3308 Rect.left, Rect.top);
3309 if (SelectFirst)
3310 {
3311 MENU_MoveSelection(WndOwner, Item->spSubMenu, ITEM_NEXT);
3312 }
3313 return Item->spSubMenu;
3314 }
3315
3316 /***********************************************************************
3317 * MenuExecFocusedItem
3318 *
3319 * Execute a menu item (for instance when user pressed Enter).
3320 * Return the wID of the executed item. Otherwise, -1 indicating
3321 * that no menu item was executed, -2 if a popup is shown;
3322 * Have to receive the flags for the TrackPopupMenu options to avoid
3323 * sending unwanted message.
3324 *
3325 */
3326 static INT FASTCALL MENU_ExecFocusedItem(MTRACKER *pmt, PMENU Menu, UINT Flags)
3327 {
3328 PITEM Item;
3329
3330 TRACE("%p menu=%p\n", pmt, Menu);
3331
3332 if (!Menu || !Menu->cItems || Menu->iItem == NO_SELECTED_ITEM)
3333 {
3334 return -1;
3335 }
3336
3337 Item = &Menu->rgItems[Menu->iItem];
3338
3339 TRACE("%p %08x %p\n", Menu, Item->wID, Item->spSubMenu);
3340
3341 if (!(Item->spSubMenu))
3342 {
3343 if (!(Item->fState & (MF_GRAYED | MF_DISABLED)) && !(Item->fType & MF_SEPARATOR))
3344 {
3345 /* If TPM_RETURNCMD is set you return the id, but
3346 do not send a message to the owner */
3347 if (!(Flags & TPM_RETURNCMD))
3348 {
3349 if (Menu->fFlags & MNF_SYSMENU)
3350 {
3351 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_SYSCOMMAND, Item->wID,
3352 MAKELPARAM((SHORT) pmt->Pt.x, (SHORT) pmt->Pt.y));
3353 }
3354 else
3355 {
3356 DWORD dwStyle = ((Menu->fFlags & MNS_STYLE_MASK) | ( pmt->TopMenu ? (pmt->TopMenu->fFlags & MNS_STYLE_MASK) : 0) );
3357
3358 if (dwStyle & MNS_NOTIFYBYPOS)
3359 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_MENUCOMMAND, Menu->iItem, (LPARAM)UserHMGetHandle(Menu));
3360 else
3361 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_COMMAND, Item->wID, 0);
3362 }
3363 }
3364 return Item->wID;
3365 }
3366 }
3367 else
3368 {
3369 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, Menu, TRUE, Flags);
3370 return -2;
3371 }
3372
3373 return -1;
3374 }
3375
3376 /***********************************************************************
3377 * MenuSwitchTracking
3378 *
3379 * Helper function for menu navigation routines.
3380 */
3381 static void FASTCALL MENU_SwitchTracking(MTRACKER* pmt, PMENU PtMenu, UINT Index, UINT wFlags)
3382 {
3383 TRACE("%x menu=%x 0x%04x\n", pmt, PtMenu, Index);
3384
3385 if ( pmt->TopMenu != PtMenu &&
3386 !((PtMenu->fFlags | pmt->TopMenu->fFlags) & MNF_POPUP) )
3387 {
3388 /* both are top level menus (system and menu-bar) */
3389 MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags);
3390 MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, NO_SELECTED_ITEM, FALSE, NULL);
3391 pmt->TopMenu = PtMenu;
3392 }
3393 else
3394 {
3395 MENU_HideSubPopups(pmt->OwnerWnd, PtMenu, FALSE, wFlags);
3396 }
3397
3398 MENU_SelectItem(pmt->OwnerWnd, PtMenu, Index, TRUE, NULL);
3399 }
3400
3401 /***********************************************************************
3402 * MenuButtonDown
3403 *
3404 * Return TRUE if we can go on with menu tracking.
3405 */
3406 static BOOL FASTCALL MENU_ButtonDown(MTRACKER* pmt, PMENU PtMenu, UINT Flags)
3407 {
3408 TRACE("%x PtMenu=%p\n", pmt, PtMenu);
3409
3410 if (PtMenu)
3411 {
3412 UINT id = 0;
3413 PITEM item;
3414 if (IS_SYSTEM_MENU(PtMenu))
3415 {
3416 item = PtMenu->rgItems;
3417 }
3418 else
3419 {
3420 item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &id );
3421 }
3422
3423 if (item)
3424 {
3425 if (PtMenu->iItem != id)
3426 MENU_SwitchTracking(pmt, PtMenu, id, Flags);
3427
3428 /* If the popup menu is not already "popped" */
3429 if (!(item->fState & MF_MOUSESELECT))
3430 {
3431 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags);
3432 }
3433
3434 return TRUE;
3435 }
3436 /* Else the click was on the menu bar, finish the tracking */
3437 }
3438 return FALSE;
3439 }
3440
3441 /***********************************************************************
3442 * MenuButtonUp
3443 *
3444 * Return the value of MenuExecFocusedItem if
3445 * the selected item was not a popup. Else open the popup.
3446 * A -1 return value indicates that we go on with menu tracking.
3447 *
3448 */
3449 static INT FASTCALL MENU_ButtonUp(MTRACKER *pmt, PMENU PtMenu, UINT Flags)
3450 {
3451 TRACE("%p pmenu=%x\n", pmt, PtMenu);
3452
3453 if (PtMenu)
3454 {
3455 UINT Id = 0;
3456 ITEM *item;
3457
3458 if ( IS_SYSTEM_MENU(PtMenu) )
3459 {
3460 item = PtMenu->rgItems;
3461 }
3462 else
3463 {
3464 item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &Id );
3465 }
3466
3467 if (item && ( PtMenu->iItem == Id))
3468 {
3469 if (!(item->spSubMenu))
3470 {
3471 INT ExecutedMenuId = MENU_ExecFocusedItem( pmt, PtMenu, Flags);
3472 if (ExecutedMenuId == -1 || ExecutedMenuId == -2) return -1;
3473 return ExecutedMenuId;
3474 }
3475
3476 /* If we are dealing with the menu bar */
3477 /* and this is a click on an already "popped" item: */
3478 /* Stop the menu tracking and close the opened submenus */
3479 if (pmt->TopMenu == PtMenu && PtMenu->TimeToHide)
3480 {
3481 return 0;
3482 }
3483 }
3484 if ( IntGetMenu(PtMenu->hWnd) == PtMenu )
3485 {
3486 PtMenu->TimeToHide = TRUE;
3487 }
3488 }
3489 return -1;
3490 }
3491
3492 /***********************************************************************
3493 * MenuPtMenu
3494 *
3495 * Walks menu chain trying to find a menu pt maps to.
3496 */
3497 static PMENU FASTCALL MENU_PtMenu(PMENU menu, POINT pt)
3498 {
3499 PITEM pItem;
3500 PMENU ret = NULL;
3501
3502 if (!menu) return NULL;
3503
3504 /* try subpopup first (if any) */
3505 if (menu->iItem != NO_SELECTED_ITEM)
3506 {
3507 pItem = menu->rgItems;
3508 if ( pItem ) pItem = &pItem[menu->iItem];
3509 if ( pItem && pItem->spSubMenu && pItem->fState & MF_MOUSESELECT)
3510 {
3511 ret = MENU_PtMenu( pItem->spSubMenu, pt);
3512 }
3513 }
3514
3515 /* check the current window (avoiding WM_HITTEST) */
3516 if (!ret)
3517 {
3518 PWND pWnd = ValidateHwndNoErr(menu->hWnd);
3519 INT ht = GetNCHitEx(pWnd, pt);
3520 if ( menu->fFlags & MNF_POPUP )
3521 {
3522 if (ht != HTNOWHERE && ht != HTERROR) ret = menu;
3523 }
3524 else if (ht == HTSYSMENU)
3525 ret = get_win_sys_menu(menu->hWnd);
3526 else if (ht == HTMENU)
3527 ret = IntGetMenu( menu->hWnd );
3528 }
3529 return ret;
3530 }
3531
3532 /***********************************************************************
3533 * MenuMouseMove
3534 *
3535 * Return TRUE if we can go on with menu tracking.
3536 */
3537 static BOOL FASTCALL MENU_MouseMove(MTRACKER *pmt, PMENU PtMenu, UINT Flags)
3538 {
3539 UINT Index = NO_SELECTED_ITEM;
3540
3541 if ( PtMenu )
3542 {
3543 if (IS_SYSTEM_MENU(PtMenu))
3544 {
3545 Index = 0;
3546 //// ReactOS only HACK: CORE-2338
3547 // Windows tracks mouse moves to the system menu but does not open it.
3548 // Only keyboard tracking can do that.
3549 //
3550 TRACE("SystemMenu\n");
3551 return TRUE; // Stay inside the Loop!
3552 }
3553 else
3554 MENU_FindItemByCoords( PtMenu, pmt->Pt, &Index );
3555 }
3556
3557 if (Index == NO_SELECTED_ITEM)
3558 {
3559 MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NO_SELECTED_ITEM, TRUE, pmt->TopMenu);
3560 }
3561 else if (PtMenu->iItem != Index)
3562 {
3563 MENU_SwitchTracking(pmt, PtMenu, Index, Flags);
3564 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags);
3565 }
3566 return TRUE;
3567 }
3568
3569 /***********************************************************************
3570 * MenuGetSubPopup
3571 *
3572 * Return the handle of the selected sub-popup menu (if any).
3573 */
3574 static PMENU MENU_GetSubPopup( PMENU menu )
3575 {
3576 ITEM *item;
3577
3578 if ((!menu) || (menu->iItem == NO_SELECTED_ITEM)) return 0;
3579
3580 item = &menu->rgItems[menu->iItem];
3581 if (item && (item->spSubMenu) && (item->fState & MF_MOUSESELECT))
3582 {
3583 return item->spSubMenu;
3584 }
3585 return 0;
3586 }
3587
3588 /***********************************************************************
3589 * MenuDoNextMenu
3590 *
3591 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
3592 */
3593 static LRESULT FASTCALL MENU_DoNextMenu(MTRACKER* pmt, UINT Vk, UINT wFlags)
3594 {
3595 BOOL atEnd = FALSE;
3596
3597 /* When skipping left, we need to do something special after the
3598 first menu. */
3599 if (Vk == VK_LEFT && pmt->TopMenu->iItem == 0)
3600 {
3601 atEnd = TRUE;
3602 }
3603 /* When skipping right, for the non-system menu, we need to
3604 handle the last non-special menu item (ie skip any window
3605 icons such as MDI maximize, restore or close) */
3606 else if ((Vk == VK_RIGHT) && !IS_SYSTEM_MENU(pmt->TopMenu))
3607 {
3608 UINT i = pmt->TopMenu->iItem + 1;
3609 while (i < pmt->TopMenu->cItems) {
3610 if ((pmt->TopMenu->rgItems[i].wID >= SC_SIZE &&
3611 pmt->TopMenu->rgItems[i].wID <= SC_RESTORE)) {
3612 i++;
3613 } else break;
3614 }
3615 if (i == pmt->TopMenu->cItems) {
3616 atEnd = TRUE;
3617 }
3618 }
3619 /* When skipping right, we need to cater for the system menu */
3620 else if ((Vk == VK_RIGHT) && IS_SYSTEM_MENU(pmt->TopMenu))
3621 {
3622 if (pmt->TopMenu->iItem == (pmt->TopMenu->cItems - 1)) {
3623 atEnd = TRUE;
3624 }
3625 }
3626
3627 if ( atEnd )
3628 {
3629 MDINEXTMENU NextMenu;
3630 PMENU MenuTmp;
3631 PWND pwndTemp;
3632 HMENU hNewMenu;
3633 HWND hNewWnd;
3634 UINT Id = 0;
3635
3636 MenuTmp = (IS_SYSTEM_MENU(pmt->TopMenu)) ? co_IntGetSubMenu(pmt->TopMenu, 0) : pmt->TopMenu;
3637 NextMenu.hmenuIn = UserHMGetHandle(MenuTmp);
3638 NextMenu.hmenuNext = NULL;
3639 NextMenu.hwndNext = NULL;
3640 co_IntSendMessage(UserHMGetHandle(pmt->OwnerWnd), WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
3641
3642 TRACE("%p [%p] -> %p [%p]\n",
3643 pmt->CurrentMenu, pmt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
3644
3645 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
3646 {
3647 hNewWnd = UserHMGetHandle(pmt->OwnerWnd);
3648 if (IS_SYSTEM_MENU(pmt->TopMenu))
3649 {
3650 /* switch to the menu bar */
3651
3652 if (pmt->OwnerWnd->style & WS_CHILD || !(MenuTmp = IntGetMenu(hNewWnd))) return FALSE;
3653
3654 if (Vk == VK_LEFT)
3655 {
3656 Id = MenuTmp->cItems - 1;
3657
3658 /* Skip backwards over any system predefined icons,
3659 eg. MDI close, restore etc icons */
3660 while ((Id > 0) &&
3661 (MenuTmp->rgItems[Id].wID >= SC_SIZE &&
3662 MenuTmp->rgItems[Id].wID <= SC_RESTORE)) Id--;
3663
3664 }
3665 hNewMenu = UserHMGetHandle(MenuTmp);
3666 }
3667 else if (pmt->OwnerWnd->style & WS_SYSMENU)
3668 {
3669 /* switch to the system menu */
3670 MenuTmp = get_win_sys_menu(hNewWnd);
3671 if (MenuTmp) hNewMenu = UserHMGetHandle(MenuTmp);
3672 }
3673 else
3674 return FALSE;
3675 }
3676 else /* application returned a new menu to switch to */
3677 {
3678 hNewMenu = NextMenu.hmenuNext;
3679 hNewWnd = NextMenu.hwndNext;
3680
3681 if ((MenuTmp = UserGetMenuObject(hNewMenu)) && (pwndTemp = ValidateHwndNoErr(hNewWnd)))
3682 {
3683 if ( pwndTemp->style & WS_SYSMENU && (get_win_sys_menu(hNewWnd) == MenuTmp) )
3684 {
3685 /* get the real system menu */
3686 MenuTmp = get_win_sys_menu(hNewWnd);
3687 hNewMenu = UserHMGetHandle(MenuTmp);
3688 }
3689 else if (pwndTemp->style & WS_CHILD || IntGetMenu(hNewWnd) != MenuTmp)
3690 {
3691 /* FIXME: Not sure what to do here;
3692 * perhaps try to track NewMenu as a popup? */
3693
3694 WARN(" -- got confused.\n");
3695 return FALSE;
3696 }
3697 }
3698 else return FALSE;
3699 }
3700
3701 if (hNewMenu != UserHMGetHandle(pmt->TopMenu))
3702 {
3703 MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3704
3705 if (pmt->CurrentMenu != pmt->TopMenu)
3706 MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags);
3707 }
3708
3709 if (hNewWnd != UserHMGetHandle(pmt->OwnerWnd))
3710 {
3711 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
3712 pmt->OwnerWnd = ValidateHwndNoErr(hNewWnd);
3713 ///// Use thread pms!!!!
3714 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, hNewWnd);
3715 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
3716 co_UserSetCapture(UserHMGetHandle(pmt->OwnerWnd));
3717 pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED;
3718 }
3719
3720 pmt->TopMenu = pmt->CurrentMenu = UserGetMenuObject(hNewMenu); /* all subpopups are hidden */
3721 MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, Id, TRUE, 0);
3722
3723 return TRUE;
3724 }
3725 return FALSE;
3726 }
3727
3728 /***********************************************************************
3729 * MenuSuspendPopup
3730 *
3731 * The idea is not to show the popup if the next input message is
3732 * going to hide it anyway.
3733 */
3734 static BOOL FASTCALL MENU_SuspendPopup(MTRACKER* pmt, UINT uMsg)
3735 {
3736 MSG msg;
3737
3738 msg.hwnd = UserHMGetHandle(pmt->OwnerWnd); ////// ? silly wine'isms?
3739
3740 co_IntGetPeekMessage( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE, FALSE);
3741 pmt->TrackFlags |= TF_SKIPREMOVE;
3742
3743 switch( uMsg )
3744 {
3745 case WM_KEYDOWN:
3746 co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE, FALSE);
3747 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
3748 {
3749 co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE, FALSE);
3750 co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE, FALSE);
3751 if( msg.message == WM_KEYDOWN &&
3752 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
3753 {
3754 pmt->TrackFlags |= TF_SUSPENDPOPUP;
3755 return TRUE;
3756 }
3757 }
3758 break;
3759 }
3760 /* failures go through this */
3761 pmt->TrackFlags &= ~TF_SUSPENDPOPUP;
3762 return FALSE;
3763 }
3764
3765 /***********************************************************************
3766 * MenuKeyEscape
3767 *
3768 * Handle a VK_ESCAPE key event in a menu.
3769 */
3770 static BOOL FASTCALL MENU_KeyEscape(MTRACKER *pmt, UINT Flags)
3771 {
3772 BOOL EndMenu = TRUE;
3773
3774 if (pmt->CurrentMenu != pmt->TopMenu)
3775 {
3776 if (pmt->CurrentMenu && (pmt->CurrentMenu->fFlags & MNF_POPUP))
3777 {
3778 PMENU MenuPrev, MenuTmp;
3779
3780 MenuPrev = MenuTmp = pmt->TopMenu;
3781
3782 /* close topmost popup */
3783 while (MenuTmp != pmt->CurrentMenu)
3784 {
3785 MenuPrev = MenuTmp;
3786 MenuTmp = MENU_GetSubPopup(MenuPrev);
3787 }
3788
3789 MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags);
3790 pmt->CurrentMenu = MenuPrev;
3791 EndMenu = FALSE;
3792 }
3793 }
3794
3795 return EndMenu;
3796 }
3797
3798 /***********************************************************************
3799 * MenuKeyLeft
3800 *
3801 * Handle a VK_LEFT key event in a menu.
3802 */
3803 static void FASTCALL MENU_KeyLeft(MTRACKER* pmt, UINT Flags, UINT msg)
3804 {
3805 PMENU MenuTmp, MenuPrev;
3806 UINT PrevCol;
3807
3808 MenuPrev = MenuTmp = pmt->TopMenu;
3809
3810 /* Try to move 1 column left (if possible) */
3811 if ( (PrevCol = MENU_GetStartOfPrevColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM)
3812 {
3813 MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, PrevCol, TRUE, 0);
3814 return;
3815 }
3816
3817 /* close topmost popup */
3818 while (MenuTmp != pmt->CurrentMenu)
3819 {
3820 MenuPrev = MenuTmp;
3821 MenuTmp = MENU_GetSubPopup(MenuPrev);
3822 }
3823
3824 MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags);
3825 pmt->CurrentMenu = MenuPrev;
3826
3827 if ((MenuPrev == pmt->TopMenu) && !(pmt->TopMenu->fFlags & MNF_POPUP))
3828 {
3829 /* move menu bar selection if no more popups are left */
3830
3831 if (!MENU_DoNextMenu(pmt, VK_LEFT, Flags))
3832 MENU_MoveSelection(pmt->OwnerWnd, pmt->TopMenu, ITEM_PREV);
3833
3834 if (MenuPrev != MenuTmp || pmt->TrackFlags & TF_SUSPENDPOPUP)
3835 {
3836 /* A sublevel menu was displayed - display the next one
3837 * unless there is another displacement coming up */
3838
3839 if (!MENU_SuspendPopup(pmt, msg))
3840 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu,
3841 TRUE, Flags);
3842 }
3843 }
3844 }
3845
3846 /***********************************************************************
3847 * MenuKeyRight
3848 *
3849 * Handle a VK_RIGHT key event in a menu.
3850 */
3851 static void FASTCALL MENU_KeyRight(MTRACKER *pmt, UINT Flags, UINT msg)
3852 {
3853 PMENU menutmp;
3854 UINT NextCol;
3855
3856 TRACE("MenuKeyRight called, cur %p, top %p.\n",
3857 pmt->CurrentMenu, pmt->TopMenu);
3858
3859 if ((pmt->TopMenu->fFlags & MNF_POPUP) || (pmt->CurrentMenu != pmt->TopMenu))
3860 {
3861 /* If already displaying a popup, try to display sub-popup */
3862
3863 menutmp = pmt->CurrentMenu;
3864 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, menutmp, TRUE, Flags);
3865
3866 /* if subpopup was displayed then we are done */
3867 if (menutmp != pmt->CurrentMenu) return;
3868 }
3869
3870 /* Check to see if there's another column */
3871 if ( (NextCol = MENU_GetStartOfNextColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM)
3872 {
3873 TRACE("Going to %d.\n", NextCol);
3874 MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NextCol, TRUE, 0);
3875 return;
3876 }
3877
3878 if (!(pmt->TopMenu->fFlags & MNF_POPUP)) /* menu bar tracking */
3879 {
3880 if (pmt->CurrentMenu != pmt->TopMenu)
3881 {
3882 MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, Flags);
3883 menutmp = pmt->CurrentMenu = pmt->TopMenu;
3884 }
3885 else
3886 {
3887 menutmp = NULL;
3888 }
3889
3890 /* try to move to the next item */
3891 if ( !MENU_DoNextMenu(pmt, VK_RIGHT, Flags))
3892 MENU_MoveSelection(pmt->OwnerWnd, pmt->TopMenu, ITEM_NEXT);
3893
3894 if ( menutmp || pmt->TrackFlags & TF_SUSPENDPOPUP )
3895 {
3896 if ( !MENU_SuspendPopup(pmt, msg) )
3897 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu, TRUE, Flags);
3898 }
3899 }
3900 }
3901
3902 /***********************************************************************
3903 * MenuTrackMenu
3904 *
3905 * Menu tracking code.
3906 */
3907 static INT FASTCALL MENU_TrackMenu(PMENU pmenu, UINT wFlags, INT x, INT y,
3908 PWND pwnd, const RECT *lprect )
3909 {
3910 MSG msg;
3911 BOOL fRemove;
3912 INT executedMenuId = -1;
3913 MTRACKER mt;
3914 HWND capture_win;
3915 PMENU pmMouse;
3916 BOOL enterIdleSent = FALSE;
3917 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
3918
3919 if (pti != pwnd->head.pti)
3920 {
3921 ERR("Not the same PTI!!!!\n");
3922 }
3923
3924 mt.TrackFlags = 0;
3925 mt.CurrentMenu = pmenu;
3926 mt.TopMenu = pmenu;
3927 mt.OwnerWnd = pwnd;
3928 mt.Pt.x = x;
3929 mt.Pt.y = y;
3930
3931 TRACE("MTM : hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n",
3932 UserHMGetHandle(pmenu), wFlags, x, y, UserHMGetHandle(pwnd), lprect ? lprect->left : 0, lprect ? lprect->top : 0,
3933 lprect ? lprect->right : 0, lprect ? lprect->bottom : 0);
3934
3935 pti->MessageQueue->QF_flags &= ~QF_ACTIVATIONCHANGE;
3936
3937 if (wFlags & TPM_BUTTONDOWN)
3938 {
3939 /* Get the result in order to start the tracking or not */
3940 fRemove = MENU_ButtonDown( &mt, pmenu, wFlags );
3941 fInsideMenuLoop = fRemove;
3942 }
3943
3944 if (wFlags & TF_ENDMENU) fInsideMenuLoop = FALSE;
3945
3946 if (wFlags & TPM_POPUPMENU && pmenu->cItems == 0) // Tracking empty popup menu...
3947 {
3948 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL);
3949 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
3950 co_UserSetCapture(NULL); /* release the capture */
3951 return 0;
3952 }
3953
3954 capture_win = IntGetCapture();
3955
3956 while (fInsideMenuLoop)
3957 {
3958 BOOL ErrorExit = FALSE;
3959 if (!VerifyMenu( mt.CurrentMenu )) /* sometimes happens if I do a window manager close */
3960 break;
3961
3962 /* we have to keep the message in the queue until it's
3963 * clear that menu loop is not over yet. */
3964
3965 for (;;)
3966 {
3967 if (co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOREMOVE, FALSE ))
3968 {
3969 if (!IntCallMsgFilter( &msg, MSGF_MENU )) break;
3970 /* remove the message from the queue */
3971 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
3972 }
3973 else
3974 {
3975 /* ReactOS Checks */
3976 if (!VerifyWnd(mt.OwnerWnd) ||
3977 !ValidateHwndNoErr(mt.CurrentMenu->hWnd) ||
3978 pti->MessageQueue->QF_flags & QF_ACTIVATIONCHANGE ||
3979 capture_win != IntGetCapture() ) // Should not happen, but this is ReactOS...
3980 {
3981 ErrorExit = TRUE; // Do not wait on dead windows, now win test_capture_4 works.
3982 break;
3983 }
3984
3985 if (!enterIdleSent)
3986 {
3987 HWND win = mt.CurrentMenu->fFlags & MNF_POPUP ? mt.CurrentMenu->hWnd : NULL;
3988 enterIdleSent = TRUE;
3989 co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_ENTERIDLE, MSGF_MENU, (LPARAM) win);
3990 }
3991 co_IntWaitMessage(NULL, 0, 0);
3992 }
3993 }
3994
3995 if (ErrorExit) break; // Gracefully dropout.
3996
3997 /* check if EndMenu() tried to cancel us, by posting this message */
3998 if (msg.message == WM_CANCELMODE)
3999 {
4000 /* we are now out of the loop */
4001 fInsideMenuLoop = FALSE;
4002
4003 /* remove the message from the queue */
4004 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4005
4006 /* break out of internal loop, ala ESCAPE */
4007 break;
4008 }
4009
4010 mt.Pt = msg.pt;
4011
4012 if ( (msg.hwnd == mt.CurrentMenu->hWnd) || ((msg.message!=WM_TIMER) && (msg.message!=WM_SYSTIMER)) )
4013 enterIdleSent=FALSE;
4014
4015 fRemove = FALSE;
4016 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
4017 {
4018 /*
4019 * Use the mouse coordinates in lParam instead of those in the MSG
4020 * struct to properly handle synthetic messages. They are already
4021 * in screen coordinates.
4022 */
4023 mt.Pt.x = (short)LOWORD(msg.lParam);
4024 mt.Pt.y = (short)HIWORD(msg.lParam);
4025
4026 /* Find a menu for this mouse event */
4027 pmMouse = MENU_PtMenu( mt.TopMenu, mt.Pt );
4028
4029 switch(msg.message)
4030 {
4031 /* no WM_NC... messages in captured state */
4032
4033 case WM_RBUTTONDBLCLK:
4034 case WM_RBUTTONDOWN:
4035 if (!(wFlags & TPM_RIGHTBUTTON))
4036 {
4037 if ( msg.message == WM_RBUTTONDBLCLK ) fInsideMenuLoop = FALSE; // Must exit or loop forever!
4038 break;
4039 }
4040 /* fall through */
4041 case WM_LBUTTONDBLCLK:
4042 case WM_LBUTTONDOWN:
4043 /* If the message belongs to the menu, removes it from the queue */
4044 /* Else, end menu tracking */
4045 fRemove = MENU_ButtonDown(&mt, pmMouse, wFlags);
4046 fInsideMenuLoop = fRemove;
4047 if ( msg.message == WM_LBUTTONDBLCLK ||
4048 msg.message == WM_RBUTTONDBLCLK ) fInsideMenuLoop = FALSE; // Must exit or loop forever!
4049 break;
4050
4051 case WM_RBUTTONUP:
4052 if (!(wFlags & TPM_RIGHTBUTTON)) break;
4053 /* fall through */
4054 case WM_LBUTTONUP:
4055 /* Check if a menu was selected by the mouse */
4056 if (pmMouse)
4057 {
4058 executedMenuId = MENU_ButtonUp( &mt, pmMouse, wFlags);
4059
4060 /* End the loop if executedMenuId is an item ID */
4061 /* or if the job was done (executedMenuId = 0). */
4062 fRemove = (executedMenuId != -1);
4063 fInsideMenuLoop = !fRemove;
4064 }
4065 /* No menu was selected by the mouse */
4066 /* if the function was called by TrackPopupMenu, continue
4067 with the menu tracking. If not, stop it */
4068 else
4069 fInsideMenuLoop = ((wFlags & TPM_POPUPMENU) ? TRUE : FALSE);
4070
4071 break;
4072
4073 case WM_MOUSEMOVE:
4074 /* the selected menu item must be changed every time */
4075 /* the mouse moves. */
4076
4077 if (pmMouse)
4078 fInsideMenuLoop |= MENU_MouseMove( &mt, pmMouse, wFlags );
4079
4080 } /* switch(msg.message) - mouse */
4081 }
4082 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
4083 {
4084 fRemove = TRUE; /* Keyboard messages are always removed */
4085 switch(msg.message)
4086 {
4087 case WM_KEYDOWN:
4088 case WM_SYSKEYDOWN:
4089 switch(msg.wParam)
4090 {
4091 case VK_MENU:
4092 case VK_F10:
4093 fInsideMenuLoop = FALSE;
4094 break;
4095
4096 case VK_HOME:
4097 case VK_END:
4098 MENU_SelectItem(mt.OwnerWnd, mt.CurrentMenu, NO_SELECTED_ITEM, FALSE, 0 );
4099 MENU_MoveSelection(mt.OwnerWnd, mt.CurrentMenu, VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV);
4100 break;
4101
4102 case VK_UP:
4103 case VK_DOWN: /* If on menu bar, pull-down the menu */
4104 if (!(mt.CurrentMenu->fFlags & MNF_POPUP))
4105 mt.CurrentMenu = MENU_ShowSubPopup(mt.OwnerWnd, mt.TopMenu, TRUE, wFlags);
4106 else /* otherwise try to move selection */
4107 MENU_MoveSelection(mt.OwnerWnd, mt.CurrentMenu, (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
4108 break;
4109
4110 case VK_LEFT:
4111 MENU_KeyLeft( &mt, wFlags, msg.message );
4112 break;
4113
4114 case VK_RIGHT:
4115 MENU_KeyRight( &mt, wFlags, msg.message );
4116 break;
4117
4118 case VK_ESCAPE:
4119 fInsideMenuLoop = !MENU_KeyEscape(&mt, wFlags);
4120 break;
4121
4122 case VK_F1:
4123 {
4124 HELPINFO hi;
4125 hi.cbSize = sizeof(HELPINFO);
4126 hi.iContextType = HELPINFO_MENUITEM;
4127 if (mt.CurrentMenu->iItem == NO_SELECTED_ITEM)
4128 hi.iCtrlId = 0;
4129 else
4130 hi.iCtrlId = pmenu->rgItems[mt.CurrentMenu->iItem].wID;
4131 hi.hItemHandle = UserHMGetHandle(mt.CurrentMenu);
4132 hi.dwContextId = pmenu->dwContextHelpId;
4133 hi.MousePos = msg.pt;
4134 co_IntSendMessage( UserHMGetHandle(pwnd), WM_HELP, 0, (LPARAM)&hi);
4135 break;
4136 }
4137
4138 default:
4139 IntTranslateKbdMessage(&msg, 0);
4140 break;
4141 }
4142 break; /* WM_KEYDOWN */
4143
4144 case WM_CHAR:
4145 case WM_SYSCHAR:
4146 {
4147 UINT pos;
4148 BOOL fEndMenu;
4149
4150 if (msg.wParam == L'\r' || msg.wParam == L' ')
4151 {
4152 executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags);
4153 fEndMenu = (executedMenuId != -2);
4154 fInsideMenuLoop = !fEndMenu;
4155 break;
4156 }
4157
4158 /* Hack to avoid control chars. */
4159 /* We will find a better way real soon... */
4160 if (msg.wParam < 32) break;
4161
4162 pos = MENU_FindItemByKey(mt.OwnerWnd, mt.CurrentMenu, LOWORD(msg.wParam), FALSE);
4163
4164 if (pos == (UINT)-2) fInsideMenuLoop = FALSE;
4165 else if (pos == (UINT)-1) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0);
4166 else
4167 {
4168 MENU_SelectItem(mt.OwnerWnd, mt.CurrentMenu, pos, TRUE, 0);
4169 executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags);
4170 fEndMenu = (executedMenuId != -2);
4171 fInsideMenuLoop = !fEndMenu;
4172 }
4173 }
4174 break;
4175 } /* switch(msg.message) - kbd */
4176 }
4177 else
4178 {
4179 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4180 IntDispatchMessage( &msg );
4181 continue;
4182 }
4183
4184 if (fInsideMenuLoop) fRemove = TRUE;
4185
4186 /* finally remove message from the queue */
4187
4188 if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) )
4189 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4190 else mt.TrackFlags &= ~TF_SKIPREMOVE;
4191 }
4192
4193 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL);
4194 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
4195 co_UserSetCapture(NULL); /* release the capture */
4196
4197 /* If dropdown is still painted and the close box is clicked on
4198 then the menu will be destroyed as part of the DispatchMessage above.
4199 This will then invalidate the menu handle in mt.hTopMenu. We should
4200 check for this first. */
4201 if ( VerifyMenu( mt.TopMenu ) )
4202 {
4203 if (VerifyWnd(mt.OwnerWnd))
4204 {
4205 MENU_HideSubPopups(mt.OwnerWnd, mt.TopMenu, FALSE, wFlags);
4206
4207 if (mt.TopMenu->fFlags & MNF_POPUP)
4208 {
4209 PWND pwndTM = ValidateHwndNoErr(mt.TopMenu->hWnd);
4210 if (pwndTM)
4211 {
4212 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, pwndTM, OBJID_CLIENT, CHILDID_SELF, 0);
4213
4214 co_UserDestroyWindow(pwndTM);
4215 }
4216 mt.TopMenu->hWnd = NULL;
4217
4218 if (!(wFlags & TPM_NONOTIFY))
4219 {
4220 co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(mt.TopMenu),
4221 MAKELPARAM(0, IS_SYSTEM_MENU(mt.TopMenu)) );
4222 }
4223 }
4224 MENU_SelectItem( mt.OwnerWnd, mt.TopMenu, NO_SELECTED_ITEM, FALSE, 0 );
4225 co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 );
4226 }
4227
4228 /* Reset the variable for hiding menu */
4229 mt.TopMenu->TimeToHide = FALSE;
4230 }
4231
4232 EngSetLastError( ERROR_SUCCESS );
4233 /* The return value is only used by TrackPopupMenu */
4234 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
4235 if (executedMenuId == -1) executedMenuId = 0;
4236 return executedMenuId;
4237 }
4238
4239 /***********************************************************************
4240 * MenuInitTracking
4241 */
4242 static BOOL FASTCALL MENU_InitTracking(PWND pWnd, PMENU Menu, BOOL bPopup, UINT wFlags)
4243 {
4244 HWND capture_win;
4245 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
4246
4247 TRACE("hwnd=%p hmenu=%p\n", UserHMGetHandle(pWnd), UserHMGetHandle(Menu));
4248
4249 co_UserHideCaret(0);
4250
4251 /* This makes the menus of applications built with Delphi work.
4252 * It also enables menus to be displayed in more than one window,
4253 * but there are some bugs left that need to be fixed in this case.
4254 */
4255 if (!bPopup)
4256 {
4257 Menu->hWnd = UserHMGetHandle(pWnd);
4258 }
4259
4260 if (!top_popup) {
4261 top_popup = Menu->hWnd;
4262 top_popup_hmenu = UserHMGetHandle(Menu);
4263 }
4264
4265 fInsideMenuLoop = TRUE;
4266 fInEndMenu = FALSE;
4267
4268 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
4269 if (!(wFlags & TPM_NONOTIFY))
4270 {
4271 co_IntSendMessage( UserHMGetHandle(pWnd), WM_ENTERMENULOOP, bPopup, 0 );
4272 }
4273
4274 //
4275 // Capture is set before calling WM_INITMENU and after WM_ENTERMENULOOP, see msg_menu.
4276 //
4277 capture_win = (wFlags & TPM_POPUPMENU) ? Menu->hWnd : UserHMGetHandle(pWnd);
4278 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, capture_win); // 1
4279 co_UserSetCapture(capture_win); // 2
4280 pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED; // Set the Q bits so noone can change this!
4281
4282 co_IntSendMessage( UserHMGetHandle(pWnd), WM_SETCURSOR, (WPARAM)UserHMGetHandle(pWnd), HTCAPTION );
4283
4284 if (!(wFlags & TPM_NONOTIFY))
4285 {
4286 co_IntSendMessage( UserHMGetHandle(pWnd), WM_INITMENU, (WPARAM)UserHMGetHandle(Menu), 0 );
4287 /* If an app changed/recreated menu bar entries in WM_INITMENU
4288 * menu sizes will be recalculated once the menu created/shown.
4289 */
4290 }
4291
4292 IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART,
4293 pWnd,
4294 Menu->fFlags & MNF_SYSMENU ? OBJID_SYSMENU : OBJID_MENU,
4295 CHILDID_SELF, 0);
4296 return TRUE;
4297 }
4298
4299 /***********************************************************************
4300 * MenuExitTracking
4301 */
4302 static BOOL FASTCALL MENU_ExitTracking(PWND pWnd, BOOL bPopup, UINT wFlags)
4303 {
4304 TRACE("Exit Track hwnd=%p bPopup %d\n", UserHMGetHandle(pWnd), bPopup);
4305
4306 IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, pWnd, OBJID_WINDOW, CHILDID_SELF, 0);
4307
4308 if (!(wFlags & TPM_NONOTIFY))
4309 co_IntSendMessage( UserHMGetHandle(pWnd), WM_EXITMENULOOP, bPopup, 0 );
4310
4311 co_UserShowCaret(0);
4312
4313 top_popup = 0;
4314 top_popup_hmenu = NULL;
4315
4316 return TRUE;
4317 }
4318
4319 /***********************************************************************
4320 * MenuTrackMouseMenuBar
4321 *
4322 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
4323 */
4324 VOID MENU_TrackMouseMenuBar( PWND pWnd, ULONG ht, POINT pt)
4325 {
4326 PMENU pMenu = (ht == HTSYSMENU) ? IntGetSystemMenu(pWnd, FALSE) : IntGetMenu( UserHMGetHandle(pWnd) ); // See 74276 and CORE-12801
4327 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
4328
4329 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", pWnd, ht, pt.x, pt.y);
4330
4331 if (pWnd->ExStyle & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
4332 if (VerifyMenu(pMenu))
4333 {
4334 /* map point to parent client coordinates */
4335 PWND Parent = UserGetAncestor(pWnd, GA_PARENT );
4336 if (Parent != UserGetDesktopWindow())
4337 {
4338 IntScreenToClient(Parent, &pt);
4339 }
4340
4341 MENU_InitTracking(pWnd, pMenu, FALSE, wFlags);
4342 /* fetch the window menu again, it may have changed */
4343 pMenu = (ht == HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pWnd) ) : IntGetMenu( UserHMGetHandle(pWnd) );
4344 MENU_TrackMenu(pMenu, wFlags, pt.x, pt.y, pWnd, NULL);
4345 MENU_ExitTracking(pWnd, FALSE, wFlags);
4346 }
4347 }
4348
4349 /***********************************************************************
4350 * MenuTrackKbdMenuBar
4351 *
4352 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
4353 */
4354 VOID MENU_TrackKbdMenuBar(PWND pwnd, UINT wParam, WCHAR wChar)
4355 {
4356 UINT uItem = NO_SELECTED_ITEM;
4357 PMENU TrackMenu;
4358 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
4359
4360 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", UserHMGetHandle(pwnd), wParam, wChar);
4361
4362 /* find window that has a menu */
4363
4364 while (!( (pwnd->style & (WS_CHILD | WS_POPUP)) != WS_CHILD ) )
4365 if (!(pwnd = UserGetAncestor( pwnd, GA_PARENT ))) return;
4366
4367 /* check if we have to track a system menu */
4368
4369 TrackMenu = IntGetMenu( UserHMGetHandle(pwnd) );
4370 if (!TrackMenu || (pwnd->style & WS_MINIMIZE) != 0 || wChar == ' ' )
4371 {
4372 if (!(pwnd->style & WS_SYSMENU)) return;
4373 TrackMenu = get_win_sys_menu( UserHMGetHandle(pwnd) );
4374 uItem = 0;
4375 wParam |= HTSYSMENU; /* prevent item lookup */
4376 }
4377
4378 if (!VerifyMenu( TrackMenu )) return;
4379
4380 MENU_InitTracking( pwnd, TrackMenu, FALSE, wFlags );
4381
4382 /* fetch the window menu again, it may have changed */
4383 TrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pwnd) ) : IntGetMenu( UserHMGetHandle(pwnd) );
4384
4385 if( wChar && wChar != ' ' )
4386 {
4387 uItem = MENU_FindItemByKey( pwnd, TrackMenu, wChar, (wParam & HTSYSMENU) );
4388 if ( uItem >= (UINT)(-2) )
4389 {
4390 if( uItem == (UINT)(-1) ) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0);
4391 /* schedule end of menu tracking */
4392 wFlags |= TF_ENDMENU;
4393 goto track_menu;
4394 }
4395 }
4396
4397 MENU_SelectItem( pwnd, TrackMenu, uItem, TRUE, 0 );
4398
4399 if (!(wParam & HTSYSMENU) || wChar == ' ')
4400 {
4401 if( uItem == NO_SELECTED_ITEM )
4402 MENU_MoveSelection( pwnd, TrackMenu, ITEM_NEXT );
4403 else
4404 UserPostMessage( UserHMGetHandle(pwnd), WM_KEYDOWN, VK_RETURN, 0 );
4405 }
4406
4407 track_menu:
4408 MENU_TrackMenu( TrackMenu, wFlags, 0, 0, pwnd, NULL );
4409 MENU_ExitTracking( pwnd, FALSE, wFlags);
4410 }
4411
4412 /**********************************************************************
4413 * TrackPopupMenuEx (USER32.@)
4414 */
4415 BOOL WINAPI IntTrackPopupMenuEx( PMENU menu, UINT wFlags, int x, int y,
4416 PWND pWnd, LPTPMPARAMS lpTpm)
4417 {
4418 BOOL ret = FALSE;
4419 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
4420
4421 if (pti != pWnd->head.pti)
4422 {
4423 ERR("Must be the same pti!\n");
4424 return ret;
4425 }
4426
4427 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p \n", //rect %s\n",
4428 UserHMGetHandle(menu), wFlags, x, y, UserHMGetHandle(pWnd), lpTpm); //,
4429 //lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
4430
4431 if (menu->hWnd && IntIsWindow(menu->hWnd))
4432 {
4433 EngSetLastError( ERROR_POPUP_ALREADY_ACTIVE );
4434 return FALSE;
4435 }
4436
4437 if (MENU_InitPopup( pWnd, menu, wFlags ))
4438 {
4439 MENU_InitTracking(pWnd, menu, TRUE, wFlags);
4440
4441 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
4442 if (!(wFlags & TPM_NONOTIFY))
4443 {
4444 co_IntSendMessage( UserHMGetHandle(pWnd), WM_INITMENUPOPUP, (WPARAM) UserHMGetHandle(menu), 0);
4445 }
4446
4447 if (menu->fFlags & MNF_SYSMENU)
4448 MENU_InitSysMenuPopup( menu, pWnd->style, pWnd->pcls->style, HTSYSMENU);
4449
4450 if (MENU_ShowPopup(pWnd, menu, 0, wFlags | TPM_POPUPMENU, x, y))
4451 ret = MENU_TrackMenu( menu, wFlags | TPM_POPUPMENU, 0, 0, pWnd,
4452 lpTpm ? &lpTpm->rcExclude : NULL);
4453 else
4454 {
4455 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL);
4456 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
4457 co_UserSetCapture(NULL); /* release the capture */
4458 }
4459
4460 MENU_ExitTracking(pWnd, TRUE, wFlags);
4461
4462 if (menu->hWnd)
4463 {
4464 PWND pwndM = ValidateHwndNoErr( menu->hWnd );
4465 if (pwndM) // wine hack around this with their destroy function.
4466 {
4467 if (!(pWnd->state & WNDS_DESTROYED))
4468 co_UserDestroyWindow( pwndM ); // Fix wrong error return.
4469 }
4470 menu->hWnd = 0;
4471
4472 if (!(wFlags & TPM_NONOTIFY))
4473 {
4474 co_IntSendMessage( UserHMGetHandle(pWnd), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(menu),
4475 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
4476 }
4477 }
4478 }
4479 return ret;
4480 }
4481
4482 //
4483 // Menu Class Proc.
4484 //
4485 BOOL WINAPI
4486 PopupMenuWndProc(
4487 PWND Wnd,
4488 UINT Message,
4489 WPARAM wParam,
4490 LPARAM lParam,
4491 LRESULT *lResult)
4492 {
4493 PPOPUPMENU pPopupMenu;
4494
4495 *lResult = 0;
4496
4497 TRACE("PMWP : pwnd=%x msg=%d wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
4498
4499 if (Wnd)
4500 {
4501 if (!Wnd->fnid)
4502 {
4503 if (Message != WM_NCCREATE)
4504 {
4505 *lResult = IntDefWindowProc(Wnd, Message, wParam, lParam, FALSE);
4506 return TRUE;
4507 }
4508 Wnd->fnid = FNID_MENU;
4509 pPopupMenu = DesktopHeapAlloc( Wnd->head.rpdesk, sizeof(POPUPMENU) );
4510 if (pPopupMenu == NULL)
4511 {
4512 return TRUE;
4513 }
4514 pPopupMenu->posSelectedItem = NO_SELECTED_ITEM;
4515 pPopupMenu->spwndPopupMenu = Wnd;
4516 ((PMENUWND)Wnd)->ppopupmenu = pPopupMenu;
4517 TRACE("Pop Up Menu is Setup! Msg %d\n",Message);
4518 *lResult = 1;
4519 return TRUE;
4520 }
4521 else
4522 {
4523 if (Wnd->fnid != FNID_MENU)
4524 {
4525 ERR("Wrong window class for Menu! fnid %x\n",Wnd->fnid);
4526 return TRUE;
4527 }
4528 pPopupMenu = ((PMENUWND)Wnd)->ppopupmenu;
4529 }
4530 }
4531
4532 switch(Message)
4533 {
4534 case WM_CREATE:
4535 {
4536 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
4537 pPopupMenu->spmenu = UserGetMenuObject(cs->lpCreateParams);
4538 if (pPopupMenu->spmenu)
4539 {
4540 UserReferenceObject(pPopupMenu->spmenu);
4541 }
4542 break;
4543 }
4544
4545 case WM_MOUSEACTIVATE: /* We don't want to be activated */
4546 *lResult = MA_NOACTIVATE;
4547 break;
4548
4549 case WM_PAINT:
4550 {
4551 PAINTSTRUCT ps;
4552 IntBeginPaint(Wnd, &ps);
4553 MENU_DrawPopupMenu(Wnd, ps.hdc, pPopupMenu->spmenu);
4554 IntEndPaint(Wnd, &ps);
4555 break;
4556 }
4557
4558 case WM_PRINTCLIENT:
4559 {
4560 MENU_DrawPopupMenu( Wnd, (HDC)wParam, pPopupMenu->spmenu);
4561 break;
4562 }
4563
4564 case WM_ERASEBKGND:
4565 *lResult = 1;
4566 break;
4567
4568 case WM_DESTROY:
4569 /* zero out global pointer in case resident popup window was destroyed. */
4570 if (pPopupMenu)
4571 {
4572 if (UserHMGetHandle(Wnd) == top_popup)
4573 {
4574 top_popup = NULL;
4575 top_popup_hmenu = NULL;
4576 }
4577 }
4578 else
4579 {
4580 ERR("No Window Pop Up!\n");
4581 }
4582 break;
4583
4584 case WM_NCDESTROY:
4585 {
4586 if (pPopupMenu->spmenu)
4587 {
4588 IntReleaseMenuObject(pPopupMenu->spmenu);
4589 }
4590 DesktopHeapFree(Wnd->head.rpdesk, pPopupMenu );
4591 ((PMENUWND)Wnd)->ppopupmenu = 0;
4592 Wnd->fnid = FNID_DESTROY;
4593 break;
4594 }
4595
4596 case MM_SETMENUHANDLE: // wine'isms
4597 case MN_SETHMENU:
4598 {
4599 PMENU pmenu = UserGetMenuObject((HMENU)wParam);
4600 if (!pmenu)
4601 {
4602 ERR("Bad Menu Handle\n");
4603 break;
4604 }
4605 UserReferenceObject(pmenu);
4606 if (pPopupMenu->spmenu)
4607 {
4608 IntReleaseMenuObject(pPopupMenu->spmenu);
4609 }
4610 pPopupMenu->spmenu = pmenu;
4611 break;
4612 }
4613
4614 case MM_GETMENUHANDLE: // wine'isms
4615 case MN_GETHMENU:
4616 *lResult = (LRESULT)(pPopupMenu ? (pPopupMenu->spmenu ? UserHMGetHandle(pPopupMenu->spmenu) : NULL) : NULL);
4617 break;
4618
4619 default:
4620 if (Message > MN_GETHMENU && Message < MN_GETHMENU+19)
4621 {
4622 ERR("Someone is passing unknown menu messages %d\n",Message);
4623 }
4624 TRACE("PMWP to IDWP %d\n",Message);
4625 *lResult = IntDefWindowProc(Wnd, Message, wParam, lParam, FALSE);
4626 break;
4627 }
4628
4629 return TRUE;
4630 }
4631
4632 BOOL FASTCALL
4633 IntHiliteMenuItem(PWND WindowObject,
4634 PMENU MenuObject,
4635 UINT uItemHilite,
4636 UINT uHilite)
4637 {
4638 PITEM MenuItem;
4639 UINT uItem = uItemHilite;
4640
4641 if (!(MenuItem = MENU_FindItem( &MenuObject, &uItem, uHilite ))) return TRUE;
4642
4643 if (uHilite & MF_HILITE)
4644 {
4645 MenuItem->fState |= MF_HILITE;
4646 }
4647 else
4648 {
4649 MenuItem->fState &= ~MF_HILITE;
4650 }
4651 if (MenuObject->iItem == uItemHilite) return TRUE;
4652 MENU_HideSubPopups( WindowObject, MenuObject, FALSE, 0 );
4653 MENU_SelectItem( WindowObject, MenuObject, uItemHilite, TRUE, 0 );
4654
4655 return TRUE; // Always returns true!!!!
4656 }
4657
4658 BOOLEAN APIENTRY
4659 intGetTitleBarInfo(PWND pWindowObject, PTITLEBARINFO bti)
4660 {
4661
4662 DWORD dwStyle = 0;
4663 DWORD dwExStyle = 0;
4664 BOOLEAN retValue = TRUE;
4665
4666 if (bti->cbSize == sizeof(TITLEBARINFO))
4667 {
4668 RtlZeroMemory(&bti->rgstate[0],sizeof(DWORD)*(CCHILDREN_TITLEBAR+1));
4669
4670 bti->rgstate[0] = STATE_SYSTEM_FOCUSABLE;
4671
4672 dwStyle = pWindowObject->style;
4673 dwExStyle = pWindowObject->ExStyle;
4674
4675 bti->rcTitleBar.top = 0;
4676 bti->rcTitleBar.left = 0;
4677 bti->rcTitleBar.right = pWindowObject->rcWindow.right - pWindowObject->rcWindow.left;
4678 bti->rcTitleBar.bottom = pWindowObject->rcWindow.bottom - pWindowObject->rcWindow.top;
4679
4680 /* Is it iconiced ? */
4681 if ((dwStyle & WS_ICONIC)!=WS_ICONIC)
4682 {
4683 /* Remove frame from rectangle */
4684 if (HAS_THICKFRAME( dwStyle, dwExStyle ))
4685 {
4686 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXFRAME) and UserGetSystemMetrics(SM_CYFRAME) */
4687 RECTL_vInflateRect( &bti->rcTitleBar, -UserGetSystemMetrics(SM_CXFRAME), -UserGetSystemMetrics(SM_CYFRAME) );
4688 }
4689 else if (HAS_DLGFRAME( dwStyle, dwExStyle ))
4690 {
4691 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXDLGFRAME) and UserGetSystemMetrics(SM_CYDLGFRAME) */
4692 RECTL_vInflateRect( &bti->rcTitleBar, -UserGetSystemMetrics(SM_CXDLGFRAME), -UserGetSystemMetrics(SM_CYDLGFRAME));
4693 }
4694 else if (HAS_THINFRAME( dwStyle, dwExStyle))
4695 {
4696 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXBORDER) and UserGetSystemMetrics(SM_CYBORDER) */
4697 RECTL_vInflateRect( &bti->rcTitleBar, -UserGetSystemMetrics(SM_CXBORDER), -UserGetSystemMetrics(SM_CYBORDER) );
4698 }
4699
4700 /* We have additional border information if the window
4701 * is a child (but not an MDI child) */
4702 if ( (dwStyle & WS_CHILD) &&
4703 ((dwExStyle & WS_EX_MDICHILD) == 0 ) )
4704 {
4705 if (dwExStyle & WS_EX_CLIENTEDGE)
4706 {
4707 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXEDGE) and UserGetSystemMetrics(SM_CYEDGE) */
4708 RECTL_vInflateRect (&bti->rcTitleBar, -UserGetSystemMetrics(SM_CXEDGE), -UserGetSystemMetrics(SM_CYEDGE));
4709 }
4710
4711 if (dwExStyle & WS_EX_STATICEDGE)
4712 {
4713 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXBORDER) and UserGetSystemMetrics(SM_CYBORDER) */
4714 RECTL_vInflateRect (&bti->rcTitleBar, -UserGetSystemMetrics(SM_CXBORDER), -UserGetSystemMetrics(SM_CYBORDER));
4715 }
4716 }
4717 }
4718
4719 bti->rcTitleBar.top += pWindowObject->rcWindow.top;
4720 bti->rcTitleBar.left += pWindowObject->rcWindow.left;
4721 bti->rcTitleBar.right += pWindowObject->rcWindow.left;
4722
4723 bti->rcTitleBar.bottom = bti->rcTitleBar.top;
4724 if (dwExStyle & WS_EX_TOOLWINDOW)
4725 {
4726 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CYSMCAPTION) */
4727 bti->rcTitleBar.bottom += UserGetSystemMetrics(SM_CYSMCAPTION);
4728 }
4729 else
4730 {
4731 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CYCAPTION) and UserGetSystemMetrics(SM_CXSIZE) */
4732 bti->rcTitleBar.bottom += UserGetSystemMetrics(SM_CYCAPTION);
4733 bti->rcTitleBar.left += UserGetSystemMetrics(SM_CXSIZE);
4734 }
4735
4736 if (dwStyle & WS_CAPTION)
4737 {
4738 bti->rgstate[1] = STATE_SYSTEM_INVISIBLE;
4739 if (dwStyle & WS_SYSMENU)
4740 {
4741 if (!(dwStyle & (WS_MINIMIZEBOX|WS_MAXIMIZEBOX)))
4742 {
4743 bti->rgstate[2] = STATE_SYSTEM_INVISIBLE;
4744 bti->rgstate[3] = STATE_SYSTEM_INVISIBLE;
4745 }
4746 else
4747 {
4748 if (!(dwStyle & WS_MINIMIZEBOX))
4749 {
4750 bti->rgstate[2] = STATE_SYSTEM_UNAVAILABLE;
4751 }
4752 if (!(dwStyle & WS_MAXIMIZEBOX))
4753 {
4754 bti->rgstate[3] = STATE_SYSTEM_UNAVAILABLE;
4755 }
4756 }
4757
4758 if (!(dwExStyle & WS_EX_CONTEXTHELP))
4759 {
4760 bti->rgstate[4] = STATE_SYSTEM_INVISIBLE;
4761 }
4762 if (pWindowObject->pcls->style & CS_NOCLOSE)
4763 {
4764 bti->rgstate[5] = STATE_SYSTEM_UNAVAILABLE;
4765 }
4766 }
4767 else
4768 {
4769 bti->rgstate[2] = STATE_SYSTEM_INVISIBLE;
4770 bti->rgstate[3] = STATE_SYSTEM_INVISIBLE;
4771 bti->rgstate[4] = STATE_SYSTEM_INVISIBLE;
4772 bti->rgstate[5] = STATE_SYSTEM_INVISIBLE;
4773 }
4774 }
4775 else
4776 {
4777 bti->rgstate[0] |= STATE_SYSTEM_INVISIBLE;
4778 }
4779 }
4780 else
4781 {
4782 EngSetLastError(ERROR_INVALID_PARAMETER);
4783 retValue = FALSE;
4784 }
4785
4786 return retValue;
4787 }
4788
4789 DWORD FASTCALL
4790 UserInsertMenuItem(
4791 PMENU Menu,
4792 UINT uItem,
4793 BOOL fByPosition,
4794 LPCMENUITEMINFOW UnsafeItemInfo,
4795 PUNICODE_STRING lpstr)
4796 {
4797 NTSTATUS Status;
4798 ROSMENUITEMINFO ItemInfo;
4799
4800 /* Try to copy the whole MENUITEMINFOW structure */
4801 Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, sizeof(MENUITEMINFOW));
4802 if (NT_SUCCESS(Status))
4803 {
4804 if (sizeof(MENUITEMINFOW) != ItemInfo.cbSize
4805 && FIELD_OFFSET(MENUITEMINFOW, hbmpItem) != ItemInfo.cbSize)
4806 {
4807 EngSetLastError(ERROR_INVALID_PARAMETER);
4808 return FALSE;
4809 }
4810 return IntInsertMenuItem(Menu, uItem, fByPosition, &ItemInfo, lpstr);
4811 }
4812
4813 /* Try to copy without last field (not present in older versions) */
4814 Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, FIELD_OFFSET(MENUITEMINFOW, hbmpItem));
4815 if (NT_SUCCESS(Status))
4816 {
4817 if (FIELD_OFFSET(MENUITEMINFOW, hbmpItem) != ItemInfo.cbSize)
4818 {
4819 EngSetLastError(ERROR_INVALID_PARAMETER);
4820 return FALSE;
4821 }
4822 ItemInfo.hbmpItem = (HBITMAP)0;
4823 return IntInsertMenuItem(Menu, uItem, fByPosition, &ItemInfo, lpstr);
4824 }
4825
4826 SetLastNtError(Status);
4827 return FALSE;
4828 }
4829
4830 UINT FASTCALL IntGetMenuState( HMENU hMenu, UINT uId, UINT uFlags)
4831 {
4832 PMENU MenuObject;
4833 PITEM pItem;
4834
4835 if (!(MenuObject = UserGetMenuObject(hMenu)))
4836 {
4837 return (UINT)-1;
4838 }
4839
4840 if (!(pItem = MENU_FindItem( &MenuObject, &uId, uFlags ))) return -1;
4841
4842 if (pItem->spSubMenu)
4843 {
4844 return (pItem->spSubMenu->cItems << 8) | ((pItem->fState|pItem->fType|MF_POPUP) & 0xff);
4845 }
4846 else
4847 return (pItem->fType | pItem->fState);
4848 }
4849
4850 HMENU FASTCALL IntGetSubMenu( HMENU hMenu, int nPos)
4851 {
4852 PMENU MenuObject;
4853 PITEM pItem;
4854
4855 if (!(MenuObject = UserGetMenuObject(hMenu)))
4856 {
4857 return NULL;
4858 }
4859
4860 if (!(pItem = MENU_FindItem( &MenuObject, (UINT*)&nPos, MF_BYPOSITION ))) return NULL;
4861
4862 if (pItem->spSubMenu)
4863 {
4864 HMENU hsubmenu = UserHMGetHandle(pItem->spSubMenu);
4865 return hsubmenu;
4866 }
4867 return NULL;
4868 }
4869
4870 UINT FASTCALL IntFindSubMenu(HMENU *hMenu, HMENU hSubTarget )
4871 {
4872 PMENU menu, pSubTarget;
4873 UINT Pos;
4874 if (((*hMenu)==(HMENU)0xffff) ||(!(menu = UserGetMenuObject(*hMenu))))
4875 return NO_SELECTED_ITEM;
4876
4877 pSubTarget = UserGetMenuObject(hSubTarget);
4878
4879 Pos = MENU_FindSubMenu(&menu, pSubTarget );
4880
4881 *hMenu = (menu ? UserHMGetHandle(menu) : NULL);
4882
4883 return Pos;
4884 }
4885
4886
4887 HMENU FASTCALL UserCreateMenu(PDESKTOP Desktop, BOOL PopupMenu)
4888 {
4889 PWINSTATION_OBJECT WinStaObject;
4890 HANDLE Handle;
4891 PMENU Menu;
4892 NTSTATUS Status;
4893 PEPROCESS CurrentProcess = PsGetCurrentProcess();
4894
4895 if (gpepCSRSS != CurrentProcess)
4896 {
4897 /*
4898 * gpepCSRSS does not have a Win32WindowStation
4899 */
4900
4901 Status = IntValidateWindowStationHandle(CurrentProcess->Win32WindowStation,
4902 UserMode,
4903 0,
4904 &WinStaObject,
4905 0);
4906
4907 if (!NT_SUCCESS(Status))
4908 {
4909 ERR("Validation of window station handle (%p) failed\n",
4910 CurrentProcess->Win32WindowStation);
4911 SetLastNtError(Status);
4912 return (HMENU)0;
4913 }
4914 Menu = IntCreateMenu(&Handle, !PopupMenu, Desktop, GetW32ProcessInfo());
4915 if (Menu && Menu->head.rpdesk->rpwinstaParent != WinStaObject)
4916 {
4917 ERR("Desktop Window Station does not match Process one!\n");
4918 }
4919 ObDereferenceObject(WinStaObject);
4920 }
4921 else
4922 {
4923 Menu = IntCreateMenu(&Handle, !PopupMenu, GetW32ThreadInfo()->rpdesk, GetW32ProcessInfo());
4924 }
4925
4926 if (Menu) UserDereferenceObject(Menu);
4927 return (HMENU)Handle;
4928 }
4929
4930 BOOL FASTCALL
4931 IntMenuItemInfo(
4932 PMENU Menu,
4933 UINT Item,
4934 BOOL ByPosition,
4935 PROSMENUITEMINFO ItemInfo,
4936 BOOL SetOrGet,
4937 PUNICODE_STRING lpstr)
4938 {
4939 PITEM MenuItem;
4940 BOOL Ret;
4941
4942 if (!(MenuItem = MENU_FindItem( &Menu, &Item, (ByPosition ? MF_BYPOSITION : MF_BYCOMMAND) )))
4943 {
4944 EngSetLastError(ERROR_MENU_ITEM_NOT_FOUND);
4945 return( FALSE);
4946 }
4947 if (SetOrGet)
4948 {
4949 Ret = IntSetMenuItemInfo(Menu, MenuItem, ItemInfo, lpstr);
4950 }
4951 else
4952 {
4953 Ret = IntGetMenuItemInfo(Menu, MenuItem, ItemInfo);
4954 }
4955 return( Ret);
4956 }
4957
4958 BOOL FASTCALL
4959 UserMenuItemInfo(
4960 PMENU Menu,
4961 UINT Item,
4962 BOOL ByPosition,
4963 PROSMENUITEMINFO UnsafeItemInfo,
4964 BOOL SetOrGet,
4965 PUNICODE_STRING lpstr)
4966 {
4967 PITEM MenuItem;
4968 ROSMENUITEMINFO ItemInfo;
4969 NTSTATUS Status;
4970 UINT Size;
4971 BOOL Ret;
4972
4973 Status = MmCopyFromCaller(&Size, &UnsafeItemInfo->cbSize, sizeof(UINT));
4974 if (! NT_SUCCESS(Status))
4975 {
4976 SetLastNtError(Status);
4977 return( FALSE);
4978 }
4979 if ( Size != sizeof(MENUITEMINFOW) &&
4980 Size != FIELD_OFFSET(MENUITEMINFOW, hbmpItem) &&
4981 Size != sizeof(ROSMENUITEMINFO) )
4982 {
4983 EngSetLastError(ERROR_INVALID_PARAMETER);
4984 return( FALSE);
4985 }
4986 Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, Size);
4987 if (! NT_SUCCESS(Status))
4988 {
4989 SetLastNtError(Status);
4990 return( FALSE);
4991 }
4992 /* If this is a pre-0x0500 _WIN32_WINNT MENUITEMINFOW, you can't
4993 set/get hbmpItem */
4994 if (FIELD_OFFSET(MENUITEMINFOW, hbmpItem) == Size
4995 && 0 != (ItemInfo.fMask & MIIM_BITMAP))
4996 {
4997 EngSetLastError(ERROR_INVALID_PARAMETER);
4998 return( FALSE);
4999 }
5000
5001 if (!(MenuItem = MENU_FindItem( &Menu, &Item, (ByPosition ? MF_BYPOSITION : MF_BYCOMMAND) )))
5002 {
5003 /* workaround for Word 95: pretend that SC_TASKLIST item exists. */
5004 if ( SetOrGet && Item == SC_TASKLIST && !ByPosition )
5005 return TRUE;
5006
5007 EngSetLastError(ERROR_MENU_ITEM_NOT_FOUND);
5008 return( FALSE);
5009 }
5010
5011 if (SetOrGet)
5012 {
5013 Ret = IntSetMenuItemInfo(Menu, MenuItem, &ItemInfo, lpstr);
5014 }
5015 else
5016 {
5017 Ret = IntGetMenuItemInfo(Menu, MenuItem, &ItemInfo);
5018 if (Ret)
5019 {
5020 Status = MmCopyToCaller(UnsafeItemInfo, &ItemInfo, Size);
5021 if (! NT_SUCCESS(Status))
5022 {
5023 SetLastNtError(Status);
5024 return( FALSE);
5025 }
5026 }
5027 }
5028
5029 return( Ret);
5030 }
5031
5032 BOOL FASTCALL
5033 UserMenuInfo(
5034 PMENU Menu,
5035 PROSMENUINFO UnsafeMenuInfo,
5036 BOOL SetOrGet)
5037 {
5038 BOOL Res;
5039 DWORD Size;
5040 NTSTATUS Status;
5041 ROSMENUINFO MenuInfo;
5042
5043 Status = MmCopyFromCaller(&Size, &UnsafeMenuInfo->cbSize, sizeof(DWORD));
5044 if (! NT_SUCCESS(Status))
5045 {
5046 SetLastNtError(Status);
5047 return( FALSE);
5048 }
5049 if ( Size < sizeof(MENUINFO) || Size > sizeof(ROSMENUINFO) )
5050 {
5051 EngSetLastError(ERROR_INVALID_PARAMETER);
5052 return( FALSE);
5053 }
5054 Status = MmCopyFromCaller(&MenuInfo, UnsafeMenuInfo, Size);
5055 if (! NT_SUCCESS(Status))
5056 {
5057 SetLastNtError(Status);
5058 return( FALSE);
5059 }
5060
5061 if(SetOrGet)
5062 {
5063 /* Set MenuInfo */
5064 Res = IntSetMenuInfo(Menu, &MenuInfo);
5065 }
5066 else
5067 {
5068 /* Get MenuInfo */
5069 Res = IntGetMenuInfo(Menu, &MenuInfo);
5070 if (Res)
5071 {
5072 Status = MmCopyToCaller(UnsafeMenuInfo, &MenuInfo, Size);
5073 if (! NT_SUCCESS(Status))
5074 {
5075 SetLastNtError(Status);
5076 return( FALSE);
5077 }
5078 }
5079 }
5080
5081 return( Res);
5082 }
5083
5084 BOOL FASTCALL
5085 IntGetMenuItemRect(
5086 PWND pWnd,
5087 PMENU Menu,
5088 UINT uItem,
5089 PRECTL Rect)
5090 {
5091 LONG XMove, YMove;
5092 PITEM MenuItem;
5093 UINT I = uItem;
5094
5095 if ((MenuItem = MENU_FindItem (&Menu, &I, MF_BYPOSITION)))
5096 {
5097 Rect->left = MenuItem->xItem;
5098 Rect->top = MenuItem->yItem;
5099 Rect->right = MenuItem->cxItem; // Do this for now......
5100 Rect->bottom = MenuItem->cyItem;
5101 }
5102 else
5103 {
5104 ERR("Failed Item Lookup! %u\n", uItem);
5105 return FALSE;
5106 }
5107
5108 if (!pWnd)
5109 {
5110 HWND hWnd = Menu->hWnd;
5111 if (!(pWnd = UserGetWindowObject(hWnd))) return FALSE;
5112 }
5113
5114 if (Menu->fFlags & MNF_POPUP)
5115 {
5116 XMove = pWnd->rcClient.left;
5117 YMove = pWnd->rcClient.top;
5118 }
5119 else
5120 {
5121 XMove = pWnd->rcWindow.left;
5122 YMove = pWnd->rcWindow.top;
5123 }
5124
5125 Rect->left += XMove;
5126 Rect->top += YMove;
5127 Rect->right += XMove;
5128 Rect->bottom += YMove;
5129
5130 return TRUE;
5131 }
5132
5133 PMENU FASTCALL MENU_GetSystemMenu(PWND Window, PMENU Popup)
5134 {
5135 PMENU Menu, NewMenu = NULL, SysMenu = NULL;
5136 HMENU hSysMenu, hNewMenu = NULL;
5137 ROSMENUITEMINFO ItemInfoSet = {0};
5138 ROSMENUITEMINFO ItemInfo = {0};
5139 UNICODE_STRING MenuName;
5140
5141 hSysMenu = UserCreateMenu(Window->head.rpdesk, FALSE);
5142 if (NULL == hSysMenu)
5143 {
5144 return NULL;
5145 }
5146 SysMenu = UserGetMenuObject(hSysMenu);
5147 if (NULL == SysMenu)
5148 {
5149 UserDestroyMenu(hSysMenu);
5150 return NULL;
5151 }
5152
5153 SysMenu->fFlags |= MNF_SYSMENU;
5154 SysMenu->hWnd = UserHMGetHandle(Window);
5155
5156 if (!Popup)
5157 {
5158 //hNewMenu = co_IntLoadSysMenuTemplate();
5159 if ( Window->ExStyle & WS_EX_MDICHILD )
5160 {
5161 RtlInitUnicodeString( &MenuName, L"SYSMENUMDI");
5162 hNewMenu = co_IntCallLoadMenu( hModClient, &MenuName);
5163 }
5164 else
5165 {
5166 RtlInitUnicodeString( &MenuName, L"SYSMENU");
5167 hNewMenu = co_IntCallLoadMenu( hModClient, &MenuName);
5168 //ERR("%wZ\n",&MenuName);
5169 }
5170 if (!hNewMenu)
5171 {
5172 ERR("No Menu!!\n");
5173 IntDestroyMenuObject(SysMenu, FALSE);
5174 return NULL;
5175 }
5176 Menu = UserGetMenuObject(hNewMenu);
5177 if (!Menu)
5178 {
5179 IntDestroyMenuObject(SysMenu, FALSE);
5180 return NULL;
5181 }
5182
5183 // Do the rest in here.
5184
5185 Menu->fFlags |= MNS_CHECKORBMP | MNF_SYSMENU | MNF_POPUP;
5186
5187 ItemInfoSet.cbSize = sizeof( MENUITEMINFOW);
5188 ItemInfoSet.fMask = MIIM_BITMAP;
5189 ItemInfoSet.hbmpItem = HBMMENU_POPUP_CLOSE;
5190 IntMenuItemInfo(Menu, SC_CLOSE, FALSE, &ItemInfoSet, TRUE, NULL);
5191 ItemInfoSet.hbmpItem = HBMMENU_POPUP_RESTORE;
5192 IntMenuItemInfo(Menu, SC_RESTORE, FALSE, &ItemInfoSet, TRUE, NULL);
5193 ItemInfoSet.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
5194 IntMenuItemInfo(Menu, SC_MAXIMIZE, FALSE, &ItemInfoSet, TRUE, NULL);
5195 ItemInfoSet.hbmpItem = HBMMENU_POPUP_MINIMIZE;
5196 IntMenuItemInfo(Menu, SC_MINIMIZE, FALSE, &ItemInfoSet, TRUE, NULL);
5197
5198 NewMenu = IntCloneMenu(Menu);
5199 if (NewMenu == NULL)
5200 {
5201 IntDestroyMenuObject(Menu, FALSE);
5202 IntDestroyMenuObject(SysMenu, FALSE);
5203 return NULL;
5204 }
5205
5206 IntReleaseMenuObject(NewMenu);
5207 UserSetMenuDefaultItem(NewMenu, SC_CLOSE, FALSE);
5208
5209 IntDestroyMenuObject(Menu, FALSE);
5210 }
5211 else
5212 {
5213 NewMenu = Popup;
5214 }
5215 if (NewMenu)
5216 {
5217 NewMenu->fFlags |= MNF_SYSMENU | MNF_POPUP;
5218
5219 if (Window->pcls->style & CS_NOCLOSE)
5220 IntRemoveMenuItem(NewMenu, SC_CLOSE, MF_BYCOMMAND, TRUE);
5221
5222 ItemInfo.cbSize = sizeof(MENUITEMINFOW);
5223 ItemInfo.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_SUBMENU;
5224 ItemInfo.fType = MF_POPUP;
5225 ItemInfo.fState = MFS_ENABLED;
5226 ItemInfo.dwTypeData = NULL;
5227 ItemInfo.cch = 0;
5228 ItemInfo.hSubMenu = UserHMGetHandle(NewMenu);
5229 IntInsertMenuItem(SysMenu, (UINT) -1, TRUE, &ItemInfo, NULL);
5230
5231 return SysMenu;
5232 }
5233 ERR("failed to load system menu!\n");
5234 return NULL;
5235 }
5236
5237 PMENU FASTCALL
5238 IntGetSystemMenu(PWND Window, BOOL bRevert)
5239 {
5240 PMENU Menu;
5241
5242 if (bRevert)
5243 {
5244 if (Window->SystemMenu)
5245 {
5246 Menu = UserGetMenuObject(Window->SystemMenu);
5247 if (Menu && !(Menu->fFlags & MNF_SYSDESKMN))
5248 {
5249 IntDestroyMenuObject(Menu, TRUE);
5250 Window->SystemMenu = NULL;
5251 }
5252 }
5253 }
5254 else
5255 {
5256 Menu = Window->SystemMenu ? UserGetMenuObject(Window->SystemMenu) : NULL;
5257 if ((!Menu || Menu->fFlags & MNF_SYSDESKMN) && Window->style & WS_SYSMENU)
5258 {
5259 Menu = MENU_GetSystemMenu(Window, NULL);
5260 Window->SystemMenu = Menu ? UserHMGetHandle(Menu) : NULL;
5261 }
5262 }
5263
5264 if (Window->SystemMenu)
5265 {
5266 HMENU hMenu = IntGetSubMenu( Window->SystemMenu, 0);
5267 /* Store the dummy sysmenu handle to facilitate the refresh */
5268 /* of the close button if the SC_CLOSE item change */
5269 Menu = UserGetMenuObject(hMenu);
5270 if (Menu)
5271 {
5272 Menu->spwndNotify = Window;
5273 Menu->fFlags |= MNF_SYSSUBMENU;
5274 }
5275 return Menu;
5276 }
5277 return NULL;
5278 }
5279
5280 BOOL FASTCALL
5281 IntSetSystemMenu(PWND Window, PMENU Menu)
5282 {
5283 PMENU OldMenu;
5284
5285 if (!(Window->style & WS_SYSMENU)) return FALSE;
5286
5287 if (Window->SystemMenu)
5288 {
5289 OldMenu = UserGetMenuObject(Window->SystemMenu);
5290 if (OldMenu)
5291 {
5292 OldMenu->fFlags &= ~MNF_SYSMENU;
5293 IntDestroyMenuObject(OldMenu, TRUE);
5294 }
5295 }
5296
5297 OldMenu = MENU_GetSystemMenu(Window, Menu);
5298 if (OldMenu)
5299 { // Use spmenuSys too!
5300 Window->SystemMenu = UserHMGetHandle(OldMenu);
5301 }
5302 else
5303 Window->SystemMenu = NULL;
5304
5305 if (Menu && Window != Menu->spwndNotify)
5306 {
5307 Menu->spwndNotify = Window;
5308 }
5309
5310 return TRUE;
5311 }
5312
5313 BOOL FASTCALL
5314 IntSetMenu(
5315 PWND Wnd,
5316 HMENU Menu,
5317 BOOL *Changed)
5318 {
5319 PMENU OldMenu, NewMenu = NULL;
5320
5321 if ((Wnd->style & (WS_CHILD | WS_POPUP)) == WS_CHILD)
5322 {
5323 ERR("SetMenu: Window is a Child 0x%p!\n",UserHMGetHandle(Wnd));
5324 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5325 return FALSE;
5326 }
5327
5328 *Changed = (UlongToHandle(Wnd->IDMenu) != Menu);
5329 if (! *Changed)
5330 {
5331 return TRUE;
5332 }
5333
5334 if (Wnd->IDMenu)
5335 {
5336 OldMenu = IntGetMenuObject(UlongToHandle(Wnd->IDMenu));
5337 ASSERT(NULL == OldMenu || OldMenu->hWnd == UserHMGetHandle(Wnd));
5338 }
5339 else
5340 {
5341 OldMenu = NULL;
5342 }
5343
5344 if (NULL != Menu)
5345 {
5346 NewMenu = IntGetMenuObject(Menu);
5347 if (NULL == NewMenu)
5348 {
5349 if (NULL != OldMenu)
5350 {
5351 IntReleaseMenuObject(OldMenu);
5352 }
5353 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5354 return FALSE;
5355 }
5356 if (NULL != NewMenu->hWnd)
5357 {
5358 /* Can't use the same menu for two windows */
5359 if (NULL != OldMenu)
5360 {
5361 IntReleaseMenuObject(OldMenu);
5362 }
5363 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5364 return FALSE;
5365 }
5366
5367 }
5368
5369 Wnd->IDMenu = (UINT_PTR) Menu;
5370 if (NULL != NewMenu)
5371 {
5372 NewMenu->hWnd = UserHMGetHandle(Wnd);
5373 IntReleaseMenuObject(NewMenu);
5374 }
5375 if (NULL != OldMenu)
5376 {
5377 OldMenu->hWnd = NULL;
5378 IntReleaseMenuObject(OldMenu);
5379 }
5380
5381 return TRUE;
5382 }
5383
5384
5385 /* FUNCTIONS *****************************************************************/
5386
5387 /*
5388 * @implemented
5389 */
5390 /* http://www.cyber-ta.org/releases/malware-analysis/public/SOURCES/b47155634ccb2c30630da7e3666d3d07/b47155634ccb2c30630da7e3666d3d07.trace.html#NtUserGetIconSize */
5391 DWORD
5392 APIENTRY
5393 NtUserCalcMenuBar(
5394 HWND hwnd,
5395 DWORD leftBorder,
5396 DWORD rightBorder,
5397 DWORD top,
5398 LPRECT prc )
5399 {
5400 HDC hdc;
5401 PWND Window;
5402 RECT Rect;
5403 DWORD ret;
5404
5405 UserEnterExclusive();
5406
5407 if(!(Window = UserGetWindowObject(hwnd)))
5408 {
5409 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5410 UserLeave();
5411 return 0;
5412 }
5413
5414 hdc = UserGetDCEx(NULL, NULL, DCX_CACHE);
5415 if (!hdc)
5416 {
5417 UserLeave();
5418 return 0;
5419 }
5420
5421 Rect.left = leftBorder;
5422 Rect.right = Window->rcWindow.right - Window->rcWindow.left - rightBorder;
5423 Rect.top = top;
5424 Rect.bottom = 0;
5425
5426 ret = MENU_DrawMenuBar(hdc, &Rect, Window, TRUE);
5427
5428 UserReleaseDC( 0, hdc, FALSE );
5429
5430 UserLeave();
5431
5432 return ret;
5433 }
5434
5435 /*
5436 * @implemented
5437 */
5438 DWORD APIENTRY
5439 NtUserCheckMenuItem(
5440 HMENU hMenu,
5441 UINT uIDCheckItem,
5442 UINT uCheck)
5443 {
5444 PMENU Menu;
5445 DECLARE_RETURN(DWORD);
5446
5447 TRACE("Enter NtUserCheckMenuItem\n");
5448 UserEnterExclusive();
5449
5450 if(!(Menu = UserGetMenuObject(hMenu)))
5451 {
5452 RETURN( (DWORD)-1);
5453 }
5454
5455 RETURN( IntCheckMenuItem(Menu, uIDCheckItem, uCheck));
5456
5457 CLEANUP:
5458 TRACE("Leave NtUserCheckMenuItem, ret=%lu\n",_ret_);
5459 UserLeave();
5460 END_CLEANUP;
5461 }
5462
5463 /*
5464 * @implemented
5465 */
5466 BOOL APIENTRY
5467 NtUserDeleteMenu(
5468 HMENU hMenu,
5469 UINT uPosition,
5470 UINT uFlags)
5471 {
5472 PMENU Menu;
5473 DECLARE_RETURN(BOOL);
5474
5475 TRACE("Enter NtUserDeleteMenu\n");
5476 UserEnterExclusive();
5477
5478 if(!(Menu = UserGetMenuObject(hMenu)))
5479 {
5480 RETURN( FALSE);
5481 }
5482
5483 RETURN( IntRemoveMenuItem(Menu, uPosition, uFlags, TRUE));
5484
5485 CLEANUP:
5486 TRACE("Leave NtUserDeleteMenu, ret=%i\n",_ret_);
5487 UserLeave();
5488 END_CLEANUP;
5489 }
5490
5491 /*
5492 * NtUserGetSystemMenu
5493 *
5494 * The NtUserGetSystemMenu function allows the application to access the
5495 * window menu (also known as the system menu or the control menu) for
5496 * copying and modifying.
5497 *
5498 * Parameters
5499 * hWnd
5500 * Handle to the window that will own a copy of the window menu.
5501 * bRevert
5502 * Specifies the action to be taken. If this parameter is FALSE,
5503 * NtUserGetSystemMenu returns a handle to the copy of the window menu
5504 * currently in use. The copy is initially identical to the window menu
5505 * but it can be modified.
5506 * If this parameter is TRUE, GetSystemMenu resets the window menu back
5507 * to the default state. The previous window menu, if any, is destroyed.
5508 *
5509 * Return Value
5510 * If the bRevert parameter is FALSE, the return value is a handle to a
5511 * copy of the window menu. If the bRevert parameter is TRUE, the return
5512 * value is NULL.
5513 *
5514 * Status
5515 * @implemented
5516 */
5517
5518 HMENU APIENTRY
5519 NtUserGetSystemMenu(HWND hWnd, BOOL bRevert)
5520 {
5521 PWND Window;
5522 PMENU Menu;
5523 DECLARE_RETURN(HMENU);
5524
5525 TRACE("Enter NtUserGetSystemMenu\n");
5526 UserEnterExclusive();
5527
5528 if (!(Window = UserGetWindowObject(hWnd)))
5529 {
5530 RETURN(NULL);
5531 }
5532
5533 if (!(Menu = IntGetSystemMenu(Window, bRevert)))
5534 {
5535 RETURN(NULL);
5536 }
5537
5538 RETURN(Menu->head.h);
5539
5540 CLEANUP:
5541 TRACE("Leave NtUserGetSystemMenu, ret=%p\n", _ret_);
5542 UserLeave();
5543 END_CLEANUP;
5544 }
5545
5546 /*
5547 * NtUserSetSystemMenu
5548 *
5549 * Status
5550 * @implemented
5551 */
5552
5553 BOOL APIENTRY
5554 NtUserSetSystemMenu(HWND hWnd, HMENU hMenu)
5555 {
5556 BOOL Result = FALSE;
5557 PWND Window;
5558 PMENU Menu;
5559 DECLARE_RETURN(BOOL);
5560
5561 TRACE("Enter NtUserSetSystemMenu\n");
5562 UserEnterExclusive();
5563
5564 if (!(Window = UserGetWindowObject(hWnd)))
5565 {
5566 RETURN( FALSE);
5567 }
5568
5569 if (hMenu)
5570 {
5571 /*
5572 * Assign new menu handle and Up the Lock Count.
5573 */
5574 if (!(Menu = IntGetMenuObject(hMenu)))
5575 {
5576 RETURN( FALSE);
5577 }
5578
5579 Result = IntSetSystemMenu(Window, Menu);
5580 }
5581 else
5582 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5583
5584 RETURN( Result);
5585
5586 CLEANUP:
5587 TRACE("Leave NtUserSetSystemMenu, ret=%i\n",_ret_);
5588 UserLeave();
5589 END_CLEANUP;
5590 }
5591
5592 /*
5593 * @implemented
5594 */
5595 BOOLEAN APIENTRY
5596 NtUserGetTitleBarInfo(
5597 HWND hwnd,
5598 PTITLEBARINFO bti)
5599 {
5600 PWND WindowObject;
5601 TITLEBARINFO bartitleinfo;
5602 DECLARE_RETURN(BOOLEAN);
5603 BOOLEAN retValue = TRUE;
5604
5605 TRACE("Enter NtUserGetTitleBarInfo\n");
5606 UserEnterExclusive();
5607
5608 /* Vaildate the windows handle */
5609 if (!(WindowObject = UserGetWindowObject(hwnd)))
5610 {
5611 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5612 retValue = FALSE;
5613 }
5614
5615 _SEH2_TRY
5616 {
5617 /* Copy our usermode buffer bti to local buffer bartitleinfo */
5618 ProbeForRead(bti, sizeof(TITLEBARINFO), 1);
5619 RtlCopyMemory(&bartitleinfo, bti, sizeof(TITLEBARINFO));
5620 }
5621 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5622 {
5623 /* Fail copy the data */
5624 EngSetLastError(ERROR_INVALID_PARAMETER);
5625 retValue = FALSE;
5626 }
5627 _SEH2_END
5628
5629 /* Get the tile bar info */
5630 if (retValue)
5631 {
5632 retValue = intGetTitleBarInfo(WindowObject, &bartitleinfo);
5633 if (retValue)
5634 {
5635 _SEH2_TRY
5636 {
5637 /* Copy our buffer to user mode buffer bti */
5638 ProbeForWrite(bti, sizeof(TITLEBARINFO), 1);
5639 RtlCopyMemory(bti, &bartitleinfo, sizeof(TITLEBARINFO));
5640 }
5641 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5642 {
5643 /* Fail copy the data */
5644 EngSetLastError(ERROR_INVALID_PARAMETER);
5645 retValue = FALSE;
5646 }
5647 _SEH2_END
5648 }
5649 }
5650
5651 RETURN( retValue );
5652
5653 CLEANUP:
5654 TRACE("Leave NtUserGetTitleBarInfo, ret=%u\n",_ret_);
5655 UserLeave();
5656 END_CLEANUP;
5657 }
5658
5659 /*
5660 * @implemented
5661 */
5662 BOOL FASTCALL UserDestroyMenu(HMENU hMenu)
5663 {
5664 PMENU Menu;
5665 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
5666
5667 if(!(Menu = UserGetMenuObject(hMenu)))
5668 {
5669 return FALSE;
5670 }
5671
5672 if (Menu->head.rpdesk != pti->rpdesk)
5673 {
5674 EngSetLastError(ERROR_ACCESS_DENIED);
5675 return FALSE;
5676 }
5677 return IntDestroyMenuObject(Menu, FALSE);
5678 }
5679
5680 /*
5681 * @implemented
5682 */
5683 BOOL APIENTRY
5684 NtUserDestroyMenu(
5685 HMENU hMenu)
5686 {
5687 PMENU Menu;
5688 DECLARE_RETURN(BOOL);
5689
5690 TRACE("Enter NtUserDestroyMenu\n");
5691 UserEnterExclusive();
5692
5693 if(!(Menu = UserGetMenuObject(hMenu)))
5694 {
5695 RETURN( FALSE);
5696 }
5697 if (Menu->head.rpdesk != gptiCurrent->rpdesk)
5698 {
5699 EngSetLastError(ERROR_ACCESS_DENIED);
5700 RETURN( FALSE);
5701 }
5702 RETURN( IntDestroyMenuObject(Menu, TRUE));
5703
5704 CLEANUP:
5705 TRACE("Leave NtUserDestroyMenu, ret=%i\n",_ret_);
5706 UserLeave();
5707 END_CLEANUP;
5708 }
5709
5710 /*
5711 * @implemented
5712 */
5713 UINT APIENTRY
5714 NtUserEnableMenuItem(
5715 HMENU hMenu,
5716 UINT uIDEnableItem,
5717 UINT uEnable)
5718 {
5719 PMENU Menu;
5720 DECLARE_RETURN(UINT);
5721
5722 TRACE("Enter NtUserEnableMenuItem\n");
5723 UserEnterExclusive();
5724
5725 if(!(Menu = UserGetMenuObject(hMenu)))
5726 {
5727 RETURN(-1);
5728 }
5729
5730 RETURN( IntEnableMenuItem(Menu, uIDEnableItem, uEnable));
5731
5732 CLEANUP:
5733 TRACE("Leave NtUserEnableMenuItem, ret=%u\n",_ret_);
5734 UserLeave();
5735 END_CLEANUP;
5736 }
5737
5738 /*
5739 * @implemented
5740 */
5741 BOOL APIENTRY
5742 NtUserEndMenu(VOID)
5743 {
5744 //PWND pWnd;
5745 TRACE("Enter NtUserEndMenu\n");
5746 UserEnterExclusive();
5747 /* if ( gptiCurrent->pMenuState &&
5748 gptiCurrent->pMenuState->pGlobalPopupMenu )
5749 {
5750 pWnd = IntGetMSWND(gptiCurrent->pMenuState);
5751 if (pWnd)
5752 {
5753 UserPostMessage( UserHMGetHandle(pWnd), WM_CANCELMODE, 0, 0);
5754 }
5755 else
5756 gptiCurrent->pMenuState->fInsideMenuLoop = FALSE;
5757 }*/
5758 if (fInsideMenuLoop && top_popup)
5759 {
5760 fInsideMenuLoop = FALSE;
5761 UserPostMessage( top_popup, WM_CANCELMODE, 0, 0);
5762 }
5763 UserLeave();
5764 TRACE("Leave NtUserEndMenu\n");
5765 return TRUE;
5766 }
5767
5768 /*
5769 * @implemented
5770 */
5771 BOOL APIENTRY
5772 NtUserGetMenuBarInfo(
5773 HWND hwnd,
5774 LONG idObject,
5775 LONG idItem,
5776 PMENUBARINFO pmbi)
5777 {
5778 PWND pWnd;
5779 HMENU hMenu;
5780 MENUBARINFO kmbi;
5781 BOOL Ret;
5782 PPOPUPMENU pPopupMenu;
5783 USER_REFERENCE_ENTRY Ref;
5784 NTSTATUS Status = STATUS_SUCCESS;
5785 PMENU Menu = NULL;
5786 DECLARE_RETURN(BOOL);
5787
5788 TRACE("Enter NtUserGetMenuBarInfo\n");
5789 UserEnterShared();
5790
5791 if (!(pWnd = UserGetWindowObject(hwnd)))
5792 {
5793 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
5794 RETURN(FALSE);
5795 }
5796
5797 UserRefObjectCo(pWnd, &Ref);
5798
5799 RECTL_vSetEmptyRect(&kmbi.rcBar);
5800 kmbi.hMenu = NULL;
5801 kmbi.hwndMenu = NULL;
5802 kmbi.fBarFocused = FALSE;
5803 kmbi.fFocused = FALSE;
5804
5805 switch (idObject)
5806 {
5807 case OBJID_CLIENT:
5808 if (!pWnd->pcls->fnid)
5809 RETURN(FALSE);
5810 if (pWnd->pcls->fnid != FNID_MENU)
5811 {
5812 WARN("called on invalid window: %u\n", pWnd->pcls->fnid);
5813 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
5814 RETURN(FALSE);
5815 }
5816 // Windows does this! Wine checks for Atom and uses GetWindowLongPtrW.
5817 hMenu = (HMENU)co_IntSendMessage(hwnd, MN_GETHMENU, 0, 0);
5818 pPopupMenu = ((PMENUWND)pWnd)->ppopupmenu;
5819 if (pPopupMenu && pPopupMenu->spmenu)
5820 {
5821 if (UserHMGetHandle(pPopupMenu->spmenu) != hMenu)
5822 {
5823 ERR("Window Pop Up hMenu %p not the same as Get hMenu %p!\n",pPopupMenu->spmenu->head.h,hMenu);
5824 }
5825 }
5826 break;
5827 case OBJID_MENU:
5828 if (pWnd->style & WS_CHILD) RETURN(FALSE);
5829 hMenu = UlongToHandle(pWnd->IDMenu);
5830 TRACE("GMBI: OBJID_MENU hMenu %p\n",hMenu);
5831 break;
5832 case OBJID_SYSMENU:
5833 if (!(pWnd->style & WS_SYSMENU)) RETURN(FALSE);
5834 Menu = IntGetSystemMenu(pWnd, FALSE);
5835 hMenu = UserHMGetHandle(Menu);
5836 break;
5837 default:
5838 RETURN(FALSE);
5839 }
5840
5841 if (!hMenu)
5842 RETURN(FALSE);
5843
5844 _SEH2_TRY
5845 {
5846 ProbeForRead(pmbi, sizeof(MENUBARINFO), 1);
5847 kmbi.cbSize = pmbi->cbSize;
5848 }
5849 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5850 {
5851 kmbi.cbSize = 0;
5852 }
5853 _SEH2_END
5854
5855 if (kmbi.cbSize != sizeof(MENUBARINFO))
5856 {
5857 EngSetLastError(ERROR_INVALID_PARAMETER);
5858 RETURN(FALSE);
5859 }
5860
5861 if (!Menu) Menu = UserGetMenuObject(hMenu);
5862 if (!Menu)
5863 RETURN(FALSE);
5864
5865 if ((idItem < 0) || ((ULONG)idItem > Menu->cItems))
5866 RETURN(FALSE);
5867
5868 if (idItem == 0)
5869 {
5870 Ret = IntGetMenuItemRect(pWnd, Menu, 0, &kmbi.rcBar);
5871 kmbi.rcBar.right = kmbi.rcBar.left + Menu->cxMenu;
5872 kmbi.rcBar.bottom = kmbi.rcBar.top + Menu->cyMenu;
5873 TRACE("idItem a 0 %d\n",Ret);
5874 }
5875 else
5876 {
5877 Ret = IntGetMenuItemRect(pWnd, Menu, idItem-1, &kmbi.rcBar);
5878 TRACE("idItem b %d %d\n", idItem-1, Ret);
5879 }
5880
5881 kmbi.hMenu = hMenu;
5882 kmbi.fBarFocused = top_popup_hmenu == hMenu;
5883 TRACE("GMBI: top p hm %p hMenu %p\n",top_popup_hmenu, hMenu);
5884 if (idItem)
5885 {
5886 kmbi.fFocused = Menu->iItem == idItem-1;
5887 if (kmbi.fFocused && (Menu->rgItems[idItem - 1].spSubMenu))
5888 {
5889 kmbi.hwndMenu = Menu->rgItems[idItem - 1].spSubMenu->hWnd;
5890 }
5891 }
5892 else
5893 {
5894 kmbi.fFocused = kmbi.fBarFocused;
5895 }
5896
5897 _SEH2_TRY
5898 {
5899 ProbeForWrite(pmbi, sizeof(MENUBARINFO), 1);
5900 RtlCopyMemory(pmbi, &kmbi, sizeof(MENUBARINFO));
5901 }
5902 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5903 {
5904 Status = _SEH2_GetExceptionCode();
5905 }
5906 _SEH2_END
5907
5908 if (!NT_SUCCESS(Status))
5909 {
5910 SetLastNtError(Status);
5911 RETURN(FALSE);
5912 }
5913
5914 RETURN(TRUE);
5915
5916 CLEANUP:
5917 if (pWnd) UserDerefObjectCo(pWnd);
5918 TRACE("Leave NtUserGetMenuBarInfo, ret=%i\n",_ret_);
5919 UserLeave();
5920 END_CLEANUP;
5921 }
5922
5923 /*
5924 * @implemented
5925 */
5926 UINT APIENTRY
5927 NtUserGetMenuIndex(
5928 HMENU hMenu,
5929 HMENU hSubMenu)
5930 {
5931 PMENU Menu, SubMenu;
5932 PITEM MenuItem;
5933 UINT i;
5934 DECLARE_RETURN(UINT);
5935
5936 TRACE("Enter NtUserGetMenuIndex\n");
5937 UserEnterShared();
5938
5939 if ( !(Menu = UserGetMenuObject(hMenu)) ||
5940 !(SubMenu = UserGetMenuObject(hSubMenu)) )
5941 RETURN(0xFFFFFFFF);
5942
5943 MenuItem = Menu->rgItems;
5944 for (i = 0; i < Menu->cItems; i++, MenuItem++)
5945 {
5946 if (MenuItem->spSubMenu == SubMenu)
5947 RETURN(MenuItem->wID);
5948 }
5949 RETURN(0xFFFFFFFF);
5950
5951 CLEANUP:
5952 TRACE("Leave NtUserGetMenuIndex, ret=%u\n",_ret_);
5953 UserLeave();
5954 END_CLEANUP;
5955 }
5956
5957 /*
5958 * @implemented
5959 */
5960 BOOL APIENTRY
5961 NtUserGetMenuItemRect(
5962 HWND hWnd,
5963 HMENU hMenu,
5964 UINT uItem,
5965 PRECTL lprcItem)
5966 {
5967 PWND ReferenceWnd;
5968 LONG XMove, YMove;
5969 RECTL Rect;
5970 PMENU Menu;
5971 PITEM MenuItem;
5972 NTSTATUS Status = STATUS_SUCCESS;
5973 DECLARE_RETURN(BOOL);
5974
5975 TRACE("Enter NtUserGetMenuItemRect\n");
5976 UserEnterShared();
5977
5978 if (!(Menu = UserGetMenuObject(hMenu)))
5979 {
5980 RETURN(FALSE);
5981 }
5982
5983 if ((MenuItem = MENU_FindItem (&Menu, &uItem, MF_BYPOSITION)))
5984 {
5985 Rect.left = MenuItem->xItem;
5986 Rect.top = MenuItem->yItem;
5987 Rect.right = MenuItem->cxItem; // Do this for now......
5988 Rect.bottom = MenuItem->cyItem;
5989 }
5990 else
5991 RETURN(FALSE);
5992
5993 if(!hWnd)
5994 {
5995 hWnd = Menu->hWnd;
5996 }
5997
5998 if (lprcItem == NULL) RETURN( FALSE);
5999
6000 if (!(ReferenceWnd = UserGetWindowObject(hWnd))) RETURN( FALSE);
6001
6002 if (Menu->fFlags & MNF_POPUP)
6003 {
6004 XMove = ReferenceWnd->rcClient.left;
6005 YMove = ReferenceWnd->rcClient.top;
6006 }
6007 else
6008 {
6009 XMove = ReferenceWnd->rcWindow.left;
6010 YMove = ReferenceWnd->rcWindow.top;
6011 }
6012
6013 Rect.left += XMove;
6014 Rect.top += YMove;
6015 Rect.right += XMove;
6016 Rect.bottom += YMove;
6017
6018 _SEH2_TRY
6019 {
6020 RtlCopyMemory(lprcItem, &Rect, sizeof(RECTL));
6021 }
6022 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6023 {
6024 Status = _SEH2_GetExceptionCode();
6025 }
6026 _SEH2_END
6027
6028 if (!NT_SUCCESS(Status))
6029 {
6030 SetLastNtError(Status);
6031 RETURN(FALSE);
6032 }
6033 RETURN(TRUE);
6034
6035 CLEANUP:
6036 TRACE("Leave NtUserGetMenuItemRect, ret=%i\n",_ret_);
6037 UserLeave();
6038 END_CLEANUP;
6039 }
6040
6041 /*
6042 * @implemented
6043 */
6044 BOOL APIENTRY
6045 NtUserHiliteMenuItem(
6046 HWND hWnd,
6047 HMENU hMenu,
6048 UINT uItemHilite,
6049 UINT uHilite)
6050 {
6051 PMENU Menu;
6052 PWND Window;
6053 DECLARE_RETURN(BOOLEAN);
6054
6055 TRACE("Enter NtUserHiliteMenuItem\n");
6056 UserEnterExclusive();
6057
6058 if(!(Window = UserGetWindowObject(hWnd)))
6059 {
6060 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
6061 RETURN(FALSE);
6062 }
6063
6064 if(!(Menu = UserGetMenuObject(hMenu)))
6065 {
6066 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
6067 RETURN(FALSE);
6068 }
6069
6070 RETURN( IntHiliteMenuItem(Window, Menu, uItemHilite, uHilite));
6071
6072 CLEANUP:
6073 TRACE("Leave NtUserHiliteMenuItem, ret=%u\n",_ret_);
6074 UserLeave();
6075 END_CLEANUP;
6076 }
6077
6078 /*
6079 * @implemented
6080 */
6081 DWORD
6082 APIENTRY
6083 NtUserDrawMenuBarTemp(
6084 HWND hWnd,
6085 HDC hDC,
6086 PRECT pRect,
6087 HMENU hMenu,
6088 HFONT hFont)
6089 {
6090 PMENU Menu;
6091 PWND Window;
6092 RECT Rect;
6093 NTSTATUS Status = STATUS_SUCCESS;
6094 DECLARE_RETURN(DWORD);
6095
6096 ERR("Enter NtUserDrawMenuBarTemp\n");
6097 UserEnterExclusive();
6098
6099 if(!(Window = UserGetWindowObject(hWnd)))
6100 {
6101 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
6102 RETURN(0);
6103 }
6104
6105 if(!(Menu = UserGetMenuObject(hMenu)))
6106 {
6107 EngSetLastError(ERROR_INVALID_MENU_HANDLE);
6108 RETURN(0);
6109 }
6110
6111 _SEH2_TRY
6112 {
6113 ProbeForRead(pRect, sizeof(RECT), sizeof(ULONG));
6114 RtlCopyMemory(&Rect, pRect, sizeof(RECT));
6115 }
6116 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6117 {
6118 Status = _SEH2_GetExceptionCode();
6119 }
6120 _SEH2_END;
6121
6122 if (Status != STATUS_SUCCESS)
6123 {
6124 SetLastNtError(Status);
6125 RETURN(0);
6126 }
6127
6128 RETURN( IntDrawMenuBarTemp(Window, hDC, &Rect, Menu, hFont));
6129
6130 CLEANUP:
6131 ERR("Leave NtUserDrawMenuBarTemp, ret=%u\n",_ret_);
6132 UserLeave();
6133 END_CLEANUP;
6134 }
6135
6136 /*
6137 * @implemented
6138 */
6139 int APIENTRY
6140 NtUserMenuItemFromPoint(
6141 HWND hWnd,
6142 HMENU hMenu,
6143 DWORD X,
6144 DWORD Y)
6145 {
6146 PMENU Menu;
6147 PWND Window = NULL;
6148 PITEM mi;
6149 ULONG i;
6150 DECLARE_RETURN(int);
6151
6152 TRACE("Enter NtUserMenuItemFromPoint\n");
6153 UserEnterExclusive();
6154
6155 if (!(Menu = UserGetMenuObject(hMenu)))
6156 {
6157 RETURN( -1);
6158 }
6159
6160 if (!(Window = UserGetWindowObject(Menu->hWnd)))
6161 {
6162 RETURN( -1);
6163 }
6164
6165 X -= Window->rcWindow.left;
6166 Y -= Window->rcWindow.top;
6167
6168 mi = Menu->rgItems;
6169 for (i = 0; i < Menu->cItems; i++, mi++)
6170 {
6171 RECTL Rect;
6172
6173 Rect.left = mi->xItem;
6174 Rect.top = mi->yItem;
6175 Rect.right = mi->cxItem;
6176 Rect.bottom = mi->cyItem;
6177
6178 MENU_AdjustMenuItemRect(Menu, &Rect);
6179
6180 if (RECTL_bPointInRect(&Rect, X, Y))
6181 {
6182 break;
6183 }
6184 }
6185
6186 RETURN( (mi ? i : NO_SELECTED_ITEM));
6187
6188 CLEANUP:
6189 TRACE("Leave NtUserMenuItemFromPoint, ret=%i\n",_ret_);
6190 UserLeave();
6191 END_CLEANUP;
6192 }
6193
6194
6195 DWORD
6196 APIENTRY
6197 NtUserPaintMenuBar(
6198 HWND hWnd,
6199 HDC hDC,
6200 ULONG leftBorder,
6201 ULONG rightBorder,
6202 ULONG top,
6203 BOOL bActive)
6204 {
6205 PWND Window;
6206 RECT Rect;
6207 DWORD ret;
6208
6209 UserEnterExclusive();
6210
6211 if(!(Window = UserGetWindowObject(hWnd)))
6212 {
6213 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
6214 UserLeave();
6215 return 0;
6216 }
6217
6218 Rect.left = leftBorder;
6219 Rect.right = Window->rcWindow.right - Window->rcWindow.left - rightBorder;
6220 Rect.top = top;
6221 Rect.bottom = 0;
6222
6223 ret = MENU_DrawMenuBar(hDC, &Rect, Window, FALSE);
6224
6225 UserLeave();
6226
6227 return ret;
6228 }
6229
6230 /*
6231 * @implemented
6232 */
6233 BOOL APIENTRY
6234 NtUserRemoveMenu(
6235 HMENU hMenu,
6236 UINT uPosition,
6237 UINT uFlags)
6238 {
6239 PMENU Menu;
6240 DECLARE_RETURN(BOOL);
6241
6242 TRACE("Enter NtUserRemoveMenu\n");
6243 UserEnterExclusive();
6244
6245 if(!(Menu = UserGetMenuObject(hMenu)))
6246 {
6247 RETURN( FALSE);
6248 }
6249
6250 RETURN(IntRemoveMenuItem(Menu, uPosition, uFlags, FALSE));
6251
6252 CLEANUP:
6253 TRACE("Leave NtUserRemoveMenu, ret=%i\n",_ret_);
6254 UserLeave();
6255 END_CLEANUP;
6256
6257 }
6258
6259 /*
6260 * @implemented
6261 */
6262 BOOL APIENTRY
6263 NtUserSetMenu(
6264 HWND hWnd,
6265 HMENU Menu,
6266 BOOL Repaint)
6267 {
6268 PWND Window;
6269 BOOL Changed;
6270 DECLARE_RETURN(BOOL);
6271
6272 TRACE("Enter NtUserSetMenu\n");
6273 UserEnterExclusive();
6274
6275 if (!(Window = UserGetWindowObject(hWnd)))
6276 {
6277 RETURN( FALSE);
6278 }
6279
6280 if (!IntSetMenu(Window, Menu, &Changed))
6281 {
6282 RETURN( FALSE);
6283 }
6284
6285 // Not minimized and please repaint!!!
6286 if (!(Window->style & WS_MINIMIZE) && (Repaint || Changed))
6287 {
6288 USER_REFERENCE_ENTRY Ref;
6289 UserRefObjectCo(Window, &Ref);
6290 co_WinPosSetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
6291 UserDerefObjectCo(Window);
6292 }
6293
6294 RETURN( TRUE);
6295
6296 CLEANUP:
6297 TRACE("Leave NtUserSetMenu, ret=%i\n",_ret_);
6298 UserLeave();
6299 END_CLEANUP;
6300 }
6301
6302 /*
6303 * @implemented
6304 */
6305 BOOL APIENTRY
6306 NtUserSetMenuContextHelpId(
6307 HMENU hMenu,
6308 DWORD dwContextHelpId)
6309 {
6310 PMENU Menu;
6311 DECLARE_RETURN(BOOL);
6312
6313 TRACE("Enter NtUserSetMenuContextHelpId\n");
6314 UserEnterExclusive();
6315
6316 if(!(Menu = UserGetMenuObject(hMenu)))
6317 {
6318 RETURN( FALSE);
6319 }
6320
6321 RETURN(IntSetMenuContextHelpId(Menu, dwContextHelpId));
6322
6323 CLEANUP:
6324 TRACE("Leave NtUserSetMenuContextHelpId, ret=%i\n",_ret_);
6325 UserLeave();
6326 END_CLEANUP;
6327 }
6328
6329 /*
6330 * @implemented
6331 */
6332 BOOL APIENTRY
6333 NtUserSetMenuDefaultItem(
6334 HMENU hMenu,
6335 UINT uItem,
6336 UINT fByPos)
6337 {
6338 PMENU Menu;
6339 DECLARE_RETURN(BOOL);
6340
6341 TRACE("Enter NtUserSetMenuDefaultItem\n");
6342 UserEnterExclusive();
6343
6344 if(!(Menu = UserGetMenuObject(hMenu)))
6345 {
6346 RETURN( FALSE);
6347 }
6348
6349 RETURN( UserSetMenuDefaultItem(Menu, uItem, fByPos));
6350
6351 CLEANUP:
6352 TRACE("Leave NtUserSetMenuDefaultItem, ret=%i\n",_ret_);
6353 UserLeave();
6354 END_CLEANUP;
6355 }
6356
6357 /*
6358 * @implemented
6359 */
6360 BOOL APIENTRY
6361 NtUserSetMenuFlagRtoL(
6362 HMENU hMenu)
6363 {
6364 PMENU Menu;
6365 DECLARE_RETURN(BOOL);
6366
6367 TRACE("Enter NtUserSetMenuFlagRtoL\n");
6368 UserEnterExclusive();
6369
6370 if(!(Menu = UserGetMenuObject(hMenu)))
6371 {
6372 RETURN( FALSE);
6373 }
6374
6375 RETURN(IntSetMenuFlagRtoL(Menu));
6376
6377 CLEANUP:
6378 TRACE("Leave NtUserSetMenuFlagRtoL, ret=%i\n",_ret_);
6379 UserLeave();
6380 END_CLEANUP;
6381 }
6382
6383 /*
6384 * @implemented
6385 */
6386 BOOL APIENTRY
6387 NtUserThunkedMenuInfo(
6388 HMENU hMenu,
6389 LPCMENUINFO lpcmi)
6390 {
6391 PMENU Menu;
6392 DECLARE_RETURN(BOOL);
6393
6394 TRACE("Enter NtUserThunkedMenuInfo\n");
6395 UserEnterExclusive();
6396
6397 if (!(Menu = UserGetMenuObject(hMenu)))
6398 {
6399 RETURN(FALSE);
6400 }
6401
6402 RETURN(UserMenuInfo(Menu, (PROSMENUINFO)lpcmi, TRUE));
6403
6404 CLEANUP:
6405 TRACE("Leave NtUserThunkedMenuInfo, ret=%i\n",_ret_);
6406 UserLeave();
6407 END_CLEANUP;
6408 }
6409
6410 /*
6411 * @implemented
6412 */
6413 BOOL APIENTRY
6414 NtUserThunkedMenuItemInfo(
6415 HMENU hMenu,
6416 UINT uItem,
6417 BOOL fByPosition,
6418 BOOL bInsert,
6419 LPMENUITEMINFOW lpmii,
6420 PUNICODE_STRING lpszCaption)
6421 {
6422 PMENU Menu;
6423 NTSTATUS Status;
6424 UNICODE_STRING lstrCaption;
6425 DECLARE_RETURN(BOOL);
6426
6427 TRACE("Enter NtUserThunkedMenuItemInfo\n");
6428 UserEnterExclusive();
6429
6430 /* lpszCaption may be NULL, check for it and call RtlInitUnicodeString()
6431 if bInsert == TRUE call UserInsertMenuItem() else UserSetMenuItemInfo() */
6432
6433 RtlInitEmptyUnicodeString(&lstrCaption, NULL, 0);
6434
6435 if (!(Menu = UserGetMenuObject(hMenu)))
6436 {
6437 RETURN(FALSE);
6438 }
6439
6440 /* Check if we got a Caption */
6441 if (lpszCaption && lpszCaption->Buffer)
6442 {
6443 /* Copy the string to kernel mode */
6444 Status = ProbeAndCaptureUnicodeString( &lstrCaption,
6445 UserMode,
6446 lpszCaption);
6447 if (!NT_SUCCESS(Status))
6448 {
6449 ERR("Failed to capture MenuItem Caption (status 0x%08x)\n",Status);
6450 SetLastNtError(Status);
6451 RETURN(FALSE);
6452 }
6453 }
6454
6455 if (bInsert) RETURN( UserInsertMenuItem(Menu, uItem, fByPosition, lpmii, &lstrCaption));
6456
6457 RETURN( UserMenuItemInfo(Menu, uItem, fByPosition, (PROSMENUITEMINFO)lpmii, TRUE, &lstrCaption));
6458
6459 CLEANUP:
6460 if (lstrCaption.Buffer)
6461 {
6462 ReleaseCapturedUnicodeString(&lstrCaption, UserMode);
6463 }
6464
6465 TRACE("Leave NtUserThunkedMenuItemInfo, ret=%i\n",_ret_);
6466 UserLeave();
6467 END_CLEANUP;
6468 }
6469
6470 /*
6471 * @implemented
6472 */
6473 BOOL APIENTRY
6474 NtUserTrackPopupMenuEx(
6475 HMENU hMenu,
6476 UINT fuFlags,
6477 int x,
6478 int y,
6479 HWND hWnd,
6480 LPTPMPARAMS lptpm)
6481 {
6482 PMENU menu;
6483 PWND pWnd;
6484 TPMPARAMS tpm;
6485 BOOL Ret = FALSE;
6486 USER_REFERENCE_ENTRY Ref;
6487
6488 TRACE("Enter NtUserTrackPopupMenuEx\n");
6489 UserEnterExclusive();
6490 /* Parameter check */
6491 if (!(menu = UserGetMenuObject( hMenu )))
6492 {
6493 ERR("TPME : Invalid Menu handle.\n");
6494 EngSetLastError( ERROR_INVALID_MENU_HANDLE );
6495 goto Exit;
6496 }
6497
6498 if (!(pWnd = UserGetWindowObject(hWnd)))
6499 {
6500 ERR("TPME : Invalid Window handle.\n");
6501 goto Exit;
6502 }
6503
6504 if (lptpm)
6505 {
6506 _SEH2_TRY
6507 {
6508 ProbeForRead(lptpm, sizeof(TPMPARAMS), sizeof(ULONG));
6509 tpm = *lptpm;
6510 }
6511 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6512 {
6513 _SEH2_YIELD(goto Exit);
6514 }
6515 _SEH2_END
6516 }
6517 UserRefObjectCo(pWnd, &Ref);
6518 Ret = IntTrackPopupMenuEx(menu, fuFlags, x, y, pWnd, lptpm ? &tpm : NULL);
6519 UserDerefObjectCo(pWnd);
6520
6521 Exit:
6522 TRACE("Leave NtUserTrackPopupMenuEx, ret=%i\n",Ret);
6523 UserLeave();
6524 return Ret;
6525 }
6526
6527 /* EOF */