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