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