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