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