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