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