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