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