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