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