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