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