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