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(Menu->head.h, 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 = Menu->head.h; 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 ? MenuItem->spSubMenu->head.h : 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) != 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 /* Re-orient the menu around the x-axis */ 3005 x += width; 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 y -= height; 3027 } 3028 3029 if (pExclude) 3030 { 3031 RECT Cleaned; 3032 3033 if (RECTL_bIntersectRect(&Cleaned, pExclude, &monitor->rcMonitor) && 3034 RECTL_Intersect(&Cleaned, x, y, width, height)) 3035 { 3036 UINT flag_mods[] = { 3037 0, /* First try the 'normal' way */ 3038 TPM_BOTTOMALIGN | TPM_RIGHTALIGN, /* Then try the opposite side */ 3039 TPM_VERTICAL, /* Then swap horizontal / vertical */ 3040 TPM_BOTTOMALIGN | TPM_RIGHTALIGN | TPM_VERTICAL, /* Then the other side again (still swapped hor/ver) */ 3041 }; 3042 3043 UINT n; 3044 for (n = 0; n < RTL_NUMBER_OF(flag_mods); ++n) 3045 { 3046 INT tx = x; 3047 INT ty = y; 3048 3049 /* Try to move a bit around */ 3050 if (MENU_MoveRect(flags ^ flag_mods[n], &tx, &ty, width, height, &Cleaned, monitor) && 3051 !RECTL_Intersect(&Cleaned, tx, ty, width, height)) 3052 { 3053 x = tx; 3054 y = ty; 3055 break; 3056 } 3057 } 3058 /* If none worked, we go with the original x/y */ 3059 } 3060 } 3061 3062 #if SHOW_DEBUGRECT 3063 { 3064 RECT rr = {x, y, x + width, y + height}; 3065 DebugRect(&rr, RGB(0, 255, 0)); 3066 } 3067 #endif 3068 3069 pWnd = ValidateHwndNoErr( menu->hWnd ); 3070 3071 if (!pWnd) 3072 { 3073 ERR("menu->hWnd bad hwnd %p\n",menu->hWnd); 3074 return FALSE; 3075 } 3076 3077 if (!top_popup) { 3078 top_popup = menu->hWnd; 3079 top_popup_hmenu = UserHMGetHandle(menu); 3080 } 3081 3082 /* Display the window */ 3083 UserRefObjectCo(pWnd, &Ref); 3084 co_WinPosSetWindowPos( pWnd, HWND_TOPMOST, x, y, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE); 3085 3086 co_IntUpdateWindows(pWnd, RDW_ALLCHILDREN, FALSE); 3087 3088 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, pWnd, OBJID_CLIENT, CHILDID_SELF, 0); 3089 UserDerefObjectCo(pWnd); 3090 3091 return TRUE; 3092 } 3093 3094 /*********************************************************************** 3095 * MENU_EnsureMenuItemVisible 3096 */ 3097 void MENU_EnsureMenuItemVisible(PMENU lppop, UINT wIndex, HDC hdc) 3098 { 3099 USER_REFERENCE_ENTRY Ref; 3100 if (lppop->dwArrowsOn) 3101 { 3102 ITEM *item = &lppop->rgItems[wIndex]; 3103 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop); 3104 UINT nOldPos = lppop->iTop; 3105 RECT rc; 3106 UINT arrow_bitmap_height; 3107 PWND pWnd = ValidateHwndNoErr(lppop->hWnd); 3108 3109 IntGetClientRect(pWnd, &rc); 3110 3111 arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy; 3112 3113 rc.top += arrow_bitmap_height; 3114 rc.bottom -= arrow_bitmap_height; 3115 3116 nMaxHeight -= UserGetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height; 3117 UserRefObjectCo(pWnd, &Ref); 3118 if (item->cyItem > lppop->iTop + nMaxHeight) 3119 { 3120 lppop->iTop = item->cyItem - nMaxHeight; 3121 IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc); 3122 MENU_DrawScrollArrows(lppop, hdc); 3123 //ERR("Scroll Down iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight); 3124 } 3125 else if (item->yItem < lppop->iTop) 3126 { 3127 lppop->iTop = item->yItem; 3128 IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc); 3129 MENU_DrawScrollArrows(lppop, hdc); 3130 //ERR("Scroll Up iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight); 3131 } 3132 UserDerefObjectCo(pWnd); 3133 } 3134 } 3135 3136 /*********************************************************************** 3137 * MenuSelectItem 3138 */ 3139 static void FASTCALL MENU_SelectItem(PWND pwndOwner, PMENU menu, UINT wIndex, 3140 BOOL sendMenuSelect, PMENU topmenu) 3141 { 3142 HDC hdc; 3143 PWND pWnd; 3144 3145 TRACE("M_SI: owner=%p menu=%p index=0x%04x select=0x%04x\n", pwndOwner, menu, wIndex, sendMenuSelect); 3146 3147 if (!menu || !menu->cItems) return; 3148 3149 pWnd = ValidateHwndNoErr(menu->hWnd); 3150 3151 if (!pWnd) return; 3152 3153 if (menu->iItem == wIndex) return; 3154 3155 if (menu->fFlags & MNF_POPUP) 3156 hdc = UserGetDCEx(pWnd, 0, DCX_USESTYLE); 3157 else 3158 hdc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW); 3159 3160 if (!top_popup) { 3161 top_popup = menu->hWnd; //pPopupMenu->spwndActivePopup or 3162 //pPopupMenu->fIsTrackPopup set pPopupMenu->spwndPopupMenu; 3163 top_popup_hmenu = UserHMGetHandle(menu); //pPopupMenu->spmenu 3164 } 3165 3166 NtGdiSelectFont( hdc, ghMenuFont ); 3167 3168 /* Clear previous highlighted item */ 3169 if (menu->iItem != NO_SELECTED_ITEM) 3170 { 3171 menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT); 3172 MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc, &menu->rgItems[menu->iItem], 3173 menu->cyMenu, !(menu->fFlags & MNF_POPUP), 3174 ODA_SELECT); 3175 } 3176 3177 /* Highlight new item (if any) */ 3178 menu->iItem = wIndex; 3179 if (menu->iItem != NO_SELECTED_ITEM) 3180 { 3181 if (!(menu->rgItems[wIndex].fType & MF_SEPARATOR)) 3182 { 3183 menu->rgItems[wIndex].fState |= MF_HILITE; 3184 MENU_EnsureMenuItemVisible(menu, wIndex, hdc); 3185 MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc, 3186 &menu->rgItems[wIndex], menu->cyMenu, !(menu->fFlags & MNF_POPUP), ODA_SELECT); 3187 } 3188 if (sendMenuSelect) 3189 { 3190 ITEM *ip = &menu->rgItems[menu->iItem]; 3191 WPARAM wParam = MAKEWPARAM( ip->spSubMenu ? wIndex : ip->wID, 3192 ip->fType | ip->fState | 3193 (ip->spSubMenu ? MF_POPUP : 0) | 3194 (menu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) ); 3195 3196 co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(menu)); 3197 } 3198 } 3199 else if (sendMenuSelect) 3200 { 3201 if (topmenu) 3202 { 3203 int pos; 3204 pos = MENU_FindSubMenu(&topmenu, menu); 3205 if (pos != NO_SELECTED_ITEM) 3206 { 3207 ITEM *ip = &topmenu->rgItems[pos]; 3208 WPARAM wParam = MAKEWPARAM( Pos, ip->fType | ip->fState | 3209 (ip->spSubMenu ? MF_POPUP : 0) | 3210 (topmenu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) ); 3211 3212 co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(topmenu)); 3213 } 3214 } 3215 } 3216 UserReleaseDC(pWnd, hdc, FALSE); 3217 } 3218 3219 /*********************************************************************** 3220 * MenuMoveSelection 3221 * 3222 * Moves currently selected item according to the Offset parameter. 3223 * If there is no selection then it should select the last item if 3224 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT. 3225 */ 3226 static void FASTCALL MENU_MoveSelection(PWND pwndOwner, PMENU menu, INT offset) 3227 { 3228 INT i; 3229 3230 TRACE("pwnd=%x menu=%x off=0x%04x\n", pwndOwner, menu, offset); 3231 3232 if ((!menu) || (!menu->rgItems)) return; 3233 3234 if ( menu->iItem != NO_SELECTED_ITEM ) 3235 { 3236 if ( menu->cItems == 1 ) 3237 return; 3238 else 3239 for (i = menu->iItem + offset ; i >= 0 && i < menu->cItems 3240 ; i += offset) 3241 if (!(menu->rgItems[i].fType & MF_SEPARATOR)) 3242 { 3243 MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 ); 3244 return; 3245 } 3246 } 3247 3248 for ( i = (offset > 0) ? 0 : menu->cItems - 1; 3249 i >= 0 && i < menu->cItems ; i += offset) 3250 if (!(menu->rgItems[i].fType & MF_SEPARATOR)) 3251 { 3252 MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 ); 3253 return; 3254 } 3255 } 3256 3257 /*********************************************************************** 3258 * MenuHideSubPopups 3259 * 3260 * Hide the sub-popup menus of this menu. 3261 */ 3262 static void FASTCALL MENU_HideSubPopups(PWND pWndOwner, PMENU Menu, 3263 BOOL SendMenuSelect, UINT wFlags) 3264 { 3265 TRACE("owner=%x menu=%x 0x%04x\n", pWndOwner, Menu, SendMenuSelect); 3266 3267 if ( Menu && top_popup ) 3268 { 3269 PITEM Item; 3270 3271 if (Menu->iItem != NO_SELECTED_ITEM) 3272 { 3273 Item = &Menu->rgItems[Menu->iItem]; 3274 if (!(Item->spSubMenu) || 3275 !(Item->fState & MF_MOUSESELECT)) return; 3276 Item->fState &= ~MF_MOUSESELECT; 3277 } 3278 else 3279 return; 3280 3281 if (Item->spSubMenu) 3282 { 3283 PWND pWnd; 3284 if (!VerifyMenu(Item->spSubMenu)) return; 3285 pWnd = ValidateHwndNoErr(Item->spSubMenu->hWnd); 3286 MENU_HideSubPopups(pWndOwner, Item->spSubMenu, FALSE, wFlags); 3287 MENU_SelectItem(pWndOwner, Item->spSubMenu, NO_SELECTED_ITEM, SendMenuSelect, NULL); 3288 TRACE("M_HSP top p hm %p pWndOwner IDMenu %p\n",top_popup_hmenu,pWndOwner->IDMenu); 3289 co_UserDestroyWindow(pWnd); 3290 3291 /* Native returns handle to destroyed window */ 3292 if (!(wFlags & TPM_NONOTIFY)) 3293 { 3294 co_IntSendMessage( UserHMGetHandle(pWndOwner), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(Item->spSubMenu), 3295 MAKELPARAM(0, IS_SYSTEM_MENU(Item->spSubMenu) ? MF_SYSMENU : 0)); 3296 } 3297 //// 3298 // Call WM_UNINITMENUPOPUP FIRST before destroy!! 3299 // Fixes todo_wine User32 test menu.c line 2239 GetMenuBarInfo callback.... 3300 // 3301 Item->spSubMenu->hWnd = NULL; 3302 //// 3303 } 3304 } 3305 } 3306 3307 /*********************************************************************** 3308 * MenuShowSubPopup 3309 * 3310 * Display the sub-menu of the selected item of this menu. 3311 * Return the handle of the submenu, or menu if no submenu to display. 3312 */ 3313 static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFirst, UINT Flags) 3314 { 3315 RECT Rect, ParentRect; 3316 ITEM *Item; 3317 HDC Dc; 3318 PWND pWnd; 3319 3320 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, Menu, SelectFirst); 3321 3322 if (!Menu) return Menu; 3323 3324 if (Menu->iItem == NO_SELECTED_ITEM) return Menu; 3325 3326 Item = &Menu->rgItems[Menu->iItem]; 3327 if (!(Item->spSubMenu) || (Item->fState & (MF_GRAYED | MF_DISABLED))) 3328 return Menu; 3329 3330 /* message must be sent before using item, 3331 because nearly everything may be changed by the application ! */ 3332 3333 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */ 3334 if (!(Flags & TPM_NONOTIFY)) 3335 { 3336 co_IntSendMessage(UserHMGetHandle(WndOwner), WM_INITMENUPOPUP, 3337 (WPARAM) UserHMGetHandle(Item->spSubMenu), 3338 MAKELPARAM(Menu->iItem, IS_SYSTEM_MENU(Menu))); 3339 } 3340 3341 Item = &Menu->rgItems[Menu->iItem]; 3342 //Rect = ItemInfo.Rect; 3343 Rect.left = Item->xItem; 3344 Rect.top = Item->yItem; 3345 Rect.right = Item->cxItem; // Do this for now...... 3346 Rect.bottom = Item->cyItem; 3347 3348 pWnd = ValidateHwndNoErr(Menu->hWnd); 3349 3350 /* Grab the rect of our (entire) parent menu, so we can try to not overlap it */ 3351 if (Menu->fFlags & MNF_POPUP) 3352 { 3353 if (!IntGetWindowRect(pWnd, &ParentRect)) 3354 { 3355 ERR("No pWnd\n"); 3356 ParentRect = Rect; 3357 } 3358 3359 /* Ensure we can slightly overlap our parent */ 3360 RECTL_vInflateRect(&ParentRect, -UserGetSystemMetrics(SM_CXEDGE) * 2, 0); 3361 } 3362 else 3363 { 3364 /* Inside the menu bar, we do not want to grab the entire window... */ 3365 ParentRect = Rect; 3366 if (pWnd) 3367 RECTL_vOffsetRect(&ParentRect, pWnd->rcWindow.left, pWnd->rcWindow.top); 3368 } 3369 3370 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */ 3371 if (!(Item->fState & MF_HILITE)) 3372 { 3373 if (Menu->fFlags & MNF_POPUP) Dc = UserGetDCEx(pWnd, NULL, DCX_USESTYLE); 3374 else Dc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW); 3375 3376 NtGdiSelectFont(Dc, ghMenuFont); 3377 3378 Item->fState |= MF_HILITE; 3379 MENU_DrawMenuItem(pWnd, Menu, WndOwner, Dc, Item, Menu->cyMenu, 3380 !(Menu->fFlags & MNF_POPUP), ODA_DRAWENTIRE); 3381 3382 UserReleaseDC(pWnd, Dc, FALSE); 3383 } 3384 3385 if (!Item->yItem && !Item->xItem && !Item->cyItem && !Item->cxItem) 3386 { 3387 Item->xItem = Rect.left; 3388 Item->yItem = Rect.top; 3389 Item->cxItem = Rect.right; // Do this for now...... 3390 Item->cyItem = Rect.bottom; 3391 } 3392 Item->fState |= MF_MOUSESELECT; 3393 3394 if (IS_SYSTEM_MENU(Menu)) 3395 { 3396 MENU_InitSysMenuPopup(Item->spSubMenu, pWnd->style, pWnd->pcls->style, HTSYSMENU); 3397 3398 NC_GetSysPopupPos(pWnd, &Rect); 3399 /* Ensure we do not overlap this */ 3400 ParentRect = Rect; 3401 if (Flags & TPM_LAYOUTRTL) Rect.left = Rect.right; 3402 Rect.top = Rect.bottom; 3403 Rect.right = UserGetSystemMetrics(SM_CXSIZE); 3404 Rect.bottom = UserGetSystemMetrics(SM_CYSIZE); 3405 } 3406 else 3407 { 3408 IntGetWindowRect(pWnd, &Rect); 3409 if (Menu->fFlags & MNF_POPUP) 3410 { 3411 RECT rc; 3412 rc.left = Item->xItem; 3413 rc.top = Item->yItem; 3414 rc.right = Item->cxItem; 3415 rc.bottom = Item->cyItem; 3416 3417 MENU_AdjustMenuItemRect(Menu, &rc); 3418 3419 /* The first item in the popup menu has to be at the 3420 same y position as the focused menu item */ 3421 if(Flags & TPM_LAYOUTRTL) 3422 Rect.left += UserGetSystemMetrics(SM_CXDLGFRAME); 3423 else 3424 Rect.left += rc.right - UserGetSystemMetrics(SM_CXDLGFRAME); 3425 3426 Rect.top += rc.top; 3427 } 3428 else 3429 { 3430 if(Flags & TPM_LAYOUTRTL) 3431 Rect.left += Rect.right - Item->xItem; //ItemInfo.Rect.left; 3432 else 3433 Rect.left += Item->xItem; //ItemInfo.Rect.left; 3434 Rect.top += Item->cyItem; //ItemInfo.Rect.bottom; 3435 Rect.right = Item->cxItem - Item->xItem; //ItemInfo.Rect.right - ItemInfo.Rect.left; 3436 Rect.bottom = Item->cyItem - Item->yItem; //ItemInfo.Rect.bottom - ItemInfo.Rect.top; 3437 } 3438 } 3439 3440 /* Next menu does not need to be shown vertical anymore */ 3441 if (Menu->fFlags & MNF_POPUP) 3442 Flags &= (~TPM_VERTICAL); 3443 3444 3445 3446 /* use default alignment for submenus */ 3447 Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN); 3448 3449 MENU_InitPopup( WndOwner, Item->spSubMenu, Flags ); 3450 3451 MENU_ShowPopup( WndOwner, Item->spSubMenu, Menu->iItem, Flags, 3452 Rect.left, Rect.top, &ParentRect); 3453 if (SelectFirst) 3454 { 3455 MENU_MoveSelection(WndOwner, Item->spSubMenu, ITEM_NEXT); 3456 } 3457 return Item->spSubMenu; 3458 } 3459 3460 /*********************************************************************** 3461 * MenuExecFocusedItem 3462 * 3463 * Execute a menu item (for instance when user pressed Enter). 3464 * Return the wID of the executed item. Otherwise, -1 indicating 3465 * that no menu item was executed, -2 if a popup is shown; 3466 * Have to receive the flags for the TrackPopupMenu options to avoid 3467 * sending unwanted message. 3468 * 3469 */ 3470 static INT FASTCALL MENU_ExecFocusedItem(MTRACKER *pmt, PMENU Menu, UINT Flags) 3471 { 3472 PITEM Item; 3473 3474 TRACE("%p menu=%p\n", pmt, Menu); 3475 3476 if (!Menu || !Menu->cItems || Menu->iItem == NO_SELECTED_ITEM) 3477 { 3478 return -1; 3479 } 3480 3481 Item = &Menu->rgItems[Menu->iItem]; 3482 3483 TRACE("%p %08x %p\n", Menu, Item->wID, Item->spSubMenu); 3484 3485 if (!(Item->spSubMenu)) 3486 { 3487 if (!(Item->fState & (MF_GRAYED | MF_DISABLED)) && !(Item->fType & MF_SEPARATOR)) 3488 { 3489 /* If TPM_RETURNCMD is set you return the id, but 3490 do not send a message to the owner */ 3491 if (!(Flags & TPM_RETURNCMD)) 3492 { 3493 if (Menu->fFlags & MNF_SYSMENU) 3494 { 3495 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_SYSCOMMAND, Item->wID, 3496 MAKELPARAM((SHORT) pmt->Pt.x, (SHORT) pmt->Pt.y)); 3497 } 3498 else 3499 { 3500 DWORD dwStyle = ((Menu->fFlags & MNS_STYLE_MASK) | ( pmt->TopMenu ? (pmt->TopMenu->fFlags & MNS_STYLE_MASK) : 0) ); 3501 3502 if (dwStyle & MNS_NOTIFYBYPOS) 3503 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_MENUCOMMAND, Menu->iItem, (LPARAM)UserHMGetHandle(Menu)); 3504 else 3505 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_COMMAND, Item->wID, 0); 3506 } 3507 } 3508 return Item->wID; 3509 } 3510 } 3511 else 3512 { 3513 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, Menu, TRUE, Flags); 3514 return -2; 3515 } 3516 3517 return -1; 3518 } 3519 3520 /*********************************************************************** 3521 * MenuSwitchTracking 3522 * 3523 * Helper function for menu navigation routines. 3524 */ 3525 static void FASTCALL MENU_SwitchTracking(MTRACKER* pmt, PMENU PtMenu, UINT Index, UINT wFlags) 3526 { 3527 TRACE("%x menu=%x 0x%04x\n", pmt, PtMenu, Index); 3528 3529 if ( pmt->TopMenu != PtMenu && 3530 !((PtMenu->fFlags | pmt->TopMenu->fFlags) & MNF_POPUP) ) 3531 { 3532 /* both are top level menus (system and menu-bar) */ 3533 MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags); 3534 MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, NO_SELECTED_ITEM, FALSE, NULL); 3535 pmt->TopMenu = PtMenu; 3536 } 3537 else 3538 { 3539 MENU_HideSubPopups(pmt->OwnerWnd, PtMenu, FALSE, wFlags); 3540 } 3541 3542 MENU_SelectItem(pmt->OwnerWnd, PtMenu, Index, TRUE, NULL); 3543 } 3544 3545 /*********************************************************************** 3546 * MenuButtonDown 3547 * 3548 * Return TRUE if we can go on with menu tracking. 3549 */ 3550 static BOOL FASTCALL MENU_ButtonDown(MTRACKER* pmt, PMENU PtMenu, UINT Flags) 3551 { 3552 TRACE("%x PtMenu=%p\n", pmt, PtMenu); 3553 3554 if (PtMenu) 3555 { 3556 UINT id = 0; 3557 PITEM item; 3558 if (IS_SYSTEM_MENU(PtMenu)) 3559 { 3560 item = PtMenu->rgItems; 3561 } 3562 else 3563 { 3564 item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &id ); 3565 } 3566 3567 if (item) 3568 { 3569 if (PtMenu->iItem != id) 3570 MENU_SwitchTracking(pmt, PtMenu, id, Flags); 3571 3572 /* If the popup menu is not already "popped" */ 3573 if (!(item->fState & MF_MOUSESELECT)) 3574 { 3575 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags); 3576 } 3577 3578 return TRUE; 3579 } 3580 /* Else the click was on the menu bar, finish the tracking */ 3581 } 3582 return FALSE; 3583 } 3584 3585 /*********************************************************************** 3586 * MenuButtonUp 3587 * 3588 * Return the value of MenuExecFocusedItem if 3589 * the selected item was not a popup. Else open the popup. 3590 * A -1 return value indicates that we go on with menu tracking. 3591 * 3592 */ 3593 static INT FASTCALL MENU_ButtonUp(MTRACKER *pmt, PMENU PtMenu, UINT Flags) 3594 { 3595 TRACE("%p pmenu=%x\n", pmt, PtMenu); 3596 3597 if (PtMenu) 3598 { 3599 UINT Id = 0; 3600 ITEM *item; 3601 3602 if ( IS_SYSTEM_MENU(PtMenu) ) 3603 { 3604 item = PtMenu->rgItems; 3605 } 3606 else 3607 { 3608 item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &Id ); 3609 } 3610 3611 if (item && ( PtMenu->iItem == Id)) 3612 { 3613 if (!(item->spSubMenu)) 3614 { 3615 INT ExecutedMenuId = MENU_ExecFocusedItem( pmt, PtMenu, Flags); 3616 if (ExecutedMenuId == -1 || ExecutedMenuId == -2) return -1; 3617 return ExecutedMenuId; 3618 } 3619 3620 /* If we are dealing with the menu bar */ 3621 /* and this is a click on an already "popped" item: */ 3622 /* Stop the menu tracking and close the opened submenus */ 3623 if (pmt->TopMenu == PtMenu && PtMenu->TimeToHide) 3624 { 3625 return 0; 3626 } 3627 } 3628 if ( IntGetMenu(PtMenu->hWnd) == PtMenu ) 3629 { 3630 PtMenu->TimeToHide = TRUE; 3631 } 3632 } 3633 return -1; 3634 } 3635 3636 /*********************************************************************** 3637 * MenuPtMenu 3638 * 3639 * Walks menu chain trying to find a menu pt maps to. 3640 */ 3641 static PMENU FASTCALL MENU_PtMenu(PMENU menu, POINT pt) 3642 { 3643 PITEM pItem; 3644 PMENU ret = NULL; 3645 3646 if (!menu) return NULL; 3647 3648 /* try subpopup first (if any) */ 3649 if (menu->iItem != NO_SELECTED_ITEM) 3650 { 3651 pItem = menu->rgItems; 3652 if ( pItem ) pItem = &pItem[menu->iItem]; 3653 if ( pItem && pItem->spSubMenu && pItem->fState & MF_MOUSESELECT) 3654 { 3655 ret = MENU_PtMenu( pItem->spSubMenu, pt); 3656 } 3657 } 3658 3659 /* check the current window (avoiding WM_HITTEST) */ 3660 if (!ret) 3661 { 3662 PWND pWnd = ValidateHwndNoErr(menu->hWnd); 3663 INT ht = GetNCHitEx(pWnd, pt); 3664 if ( menu->fFlags & MNF_POPUP ) 3665 { 3666 if (ht != HTNOWHERE && ht != HTERROR) ret = menu; 3667 } 3668 else if (ht == HTSYSMENU) 3669 ret = get_win_sys_menu(menu->hWnd); 3670 else if (ht == HTMENU) 3671 ret = IntGetMenu( menu->hWnd ); 3672 } 3673 return ret; 3674 } 3675 3676 /*********************************************************************** 3677 * MenuMouseMove 3678 * 3679 * Return TRUE if we can go on with menu tracking. 3680 */ 3681 static BOOL FASTCALL MENU_MouseMove(MTRACKER *pmt, PMENU PtMenu, UINT Flags) 3682 { 3683 UINT Index = NO_SELECTED_ITEM; 3684 3685 if ( PtMenu ) 3686 MENU_FindItemByCoords( PtMenu, pmt->Pt, &Index ); 3687 3688 if (Index == NO_SELECTED_ITEM) 3689 { 3690 MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NO_SELECTED_ITEM, TRUE, pmt->TopMenu); 3691 } 3692 else if (PtMenu->iItem != Index) 3693 { 3694 MENU_SwitchTracking(pmt, PtMenu, Index, Flags); 3695 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags); 3696 } 3697 return TRUE; 3698 } 3699 3700 /*********************************************************************** 3701 * MenuGetSubPopup 3702 * 3703 * Return the handle of the selected sub-popup menu (if any). 3704 */ 3705 static PMENU MENU_GetSubPopup( PMENU menu ) 3706 { 3707 ITEM *item; 3708 3709 if ((!menu) || (menu->iItem == NO_SELECTED_ITEM)) return 0; 3710 3711 item = &menu->rgItems[menu->iItem]; 3712 if (item && (item->spSubMenu) && (item->fState & MF_MOUSESELECT)) 3713 { 3714 return item->spSubMenu; 3715 } 3716 return 0; 3717 } 3718 3719 /*********************************************************************** 3720 * MenuDoNextMenu 3721 * 3722 * NOTE: WM_NEXTMENU documented in Win32 is a bit different. 3723 */ 3724 static LRESULT FASTCALL MENU_DoNextMenu(MTRACKER* pmt, UINT Vk, UINT wFlags) 3725 { 3726 BOOL atEnd = FALSE; 3727 3728 /* When skipping left, we need to do something special after the 3729 first menu. */ 3730 if (Vk == VK_LEFT && pmt->TopMenu->iItem == 0) 3731 { 3732 atEnd = TRUE; 3733 } 3734 /* When skipping right, for the non-system menu, we need to 3735 handle the last non-special menu item (ie skip any window 3736 icons such as MDI maximize, restore or close) */ 3737 else if ((Vk == VK_RIGHT) && !IS_SYSTEM_MENU(pmt->TopMenu)) 3738 { 3739 UINT i = pmt->TopMenu->iItem + 1; 3740 while (i < pmt->TopMenu->cItems) { 3741 if ((pmt->TopMenu->rgItems[i].wID >= SC_SIZE && 3742 pmt->TopMenu->rgItems[i].wID <= SC_RESTORE)) { 3743 i++; 3744 } else break; 3745 } 3746 if (i == pmt->TopMenu->cItems) { 3747 atEnd = TRUE; 3748 } 3749 } 3750 /* When skipping right, we need to cater for the system menu */ 3751 else if ((Vk == VK_RIGHT) && IS_SYSTEM_MENU(pmt->TopMenu)) 3752 { 3753 if (pmt->TopMenu->iItem == (pmt->TopMenu->cItems - 1)) { 3754 atEnd = TRUE; 3755 } 3756 } 3757 3758 if ( atEnd ) 3759 { 3760 MDINEXTMENU NextMenu; 3761 PMENU MenuTmp; 3762 PWND pwndTemp; 3763 HMENU hNewMenu; 3764 HWND hNewWnd; 3765 UINT Id = 0; 3766 3767 MenuTmp = (IS_SYSTEM_MENU(pmt->TopMenu)) ? co_IntGetSubMenu(pmt->TopMenu, 0) : pmt->TopMenu; 3768 NextMenu.hmenuIn = UserHMGetHandle(MenuTmp); 3769 NextMenu.hmenuNext = NULL; 3770 NextMenu.hwndNext = NULL; 3771 co_IntSendMessage(UserHMGetHandle(pmt->OwnerWnd), WM_NEXTMENU, Vk, (LPARAM) &NextMenu); 3772 3773 TRACE("%p [%p] -> %p [%p]\n", 3774 pmt->CurrentMenu, pmt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext ); 3775 3776 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext) 3777 { 3778 hNewWnd = UserHMGetHandle(pmt->OwnerWnd); 3779 if (IS_SYSTEM_MENU(pmt->TopMenu)) 3780 { 3781 /* switch to the menu bar */ 3782 3783 if (pmt->OwnerWnd->style & WS_CHILD || !(MenuTmp = IntGetMenu(hNewWnd))) return FALSE; 3784 3785 if (Vk == VK_LEFT) 3786 { 3787 Id = MenuTmp->cItems - 1; 3788 3789 /* Skip backwards over any system predefined icons, 3790 eg. MDI close, restore etc icons */ 3791 while ((Id > 0) && 3792 (MenuTmp->rgItems[Id].wID >= SC_SIZE && 3793 MenuTmp->rgItems[Id].wID <= SC_RESTORE)) Id--; 3794 3795 } 3796 hNewMenu = UserHMGetHandle(MenuTmp); 3797 } 3798 else if (pmt->OwnerWnd->style & WS_SYSMENU) 3799 { 3800 /* switch to the system menu */ 3801 MenuTmp = get_win_sys_menu(hNewWnd); 3802 if (MenuTmp) hNewMenu = UserHMGetHandle(MenuTmp); 3803 else hNewMenu = NULL; 3804 } 3805 else 3806 return FALSE; 3807 } 3808 else /* application returned a new menu to switch to */ 3809 { 3810 hNewMenu = NextMenu.hmenuNext; 3811 hNewWnd = NextMenu.hwndNext; 3812 3813 if ((MenuTmp = UserGetMenuObject(hNewMenu)) && (pwndTemp = ValidateHwndNoErr(hNewWnd))) 3814 { 3815 if ( pwndTemp->style & WS_SYSMENU && (get_win_sys_menu(hNewWnd) == MenuTmp) ) 3816 { 3817 /* get the real system menu */ 3818 MenuTmp = get_win_sys_menu(hNewWnd); 3819 hNewMenu = UserHMGetHandle(MenuTmp); 3820 } 3821 else if (pwndTemp->style & WS_CHILD || IntGetMenu(hNewWnd) != MenuTmp) 3822 { 3823 /* FIXME: Not sure what to do here; 3824 * perhaps try to track NewMenu as a popup? */ 3825 3826 WARN(" -- got confused.\n"); 3827 return FALSE; 3828 } 3829 } 3830 else return FALSE; 3831 } 3832 3833 if (hNewMenu != UserHMGetHandle(pmt->TopMenu)) 3834 { 3835 MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, NO_SELECTED_ITEM, FALSE, 0 ); 3836 3837 if (pmt->CurrentMenu != pmt->TopMenu) 3838 MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags); 3839 } 3840 3841 if (hNewWnd != UserHMGetHandle(pmt->OwnerWnd)) 3842 { 3843 PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); 3844 pmt->OwnerWnd = ValidateHwndNoErr(hNewWnd); 3845 ///// Use thread pms!!!! 3846 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, hNewWnd); 3847 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED; 3848 co_UserSetCapture(UserHMGetHandle(pmt->OwnerWnd)); 3849 pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED; 3850 } 3851 3852 pmt->TopMenu = pmt->CurrentMenu = UserGetMenuObject(hNewMenu); /* all subpopups are hidden */ 3853 MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, Id, TRUE, 0); 3854 3855 return TRUE; 3856 } 3857 return FALSE; 3858 } 3859 3860 /*********************************************************************** 3861 * MenuSuspendPopup 3862 * 3863 * The idea is not to show the popup if the next input message is 3864 * going to hide it anyway. 3865 */ 3866 static BOOL FASTCALL MENU_SuspendPopup(MTRACKER* pmt, UINT uMsg) 3867 { 3868 MSG msg; 3869 3870 msg.hwnd = UserHMGetHandle(pmt->OwnerWnd); ////// ? silly wine'isms? 3871 3872 co_IntGetPeekMessage( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE, FALSE); 3873 pmt->TrackFlags |= TF_SKIPREMOVE; 3874 3875 switch( uMsg ) 3876 { 3877 case WM_KEYDOWN: 3878 co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE, FALSE); 3879 if( msg.message == WM_KEYUP || msg.message == WM_PAINT ) 3880 { 3881 co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE, FALSE); 3882 co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE, FALSE); 3883 if( msg.message == WM_KEYDOWN && 3884 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT)) 3885 { 3886 pmt->TrackFlags |= TF_SUSPENDPOPUP; 3887 return TRUE; 3888 } 3889 } 3890 break; 3891 } 3892 /* failures go through this */ 3893 pmt->TrackFlags &= ~TF_SUSPENDPOPUP; 3894 return FALSE; 3895 } 3896 3897 /*********************************************************************** 3898 * MenuKeyEscape 3899 * 3900 * Handle a VK_ESCAPE key event in a menu. 3901 */ 3902 static BOOL FASTCALL MENU_KeyEscape(MTRACKER *pmt, UINT Flags) 3903 { 3904 BOOL EndMenu = TRUE; 3905 3906 if (pmt->CurrentMenu != pmt->TopMenu) 3907 { 3908 if (pmt->CurrentMenu && (pmt->CurrentMenu->fFlags & MNF_POPUP)) 3909 { 3910 PMENU MenuPrev, MenuTmp; 3911 3912 MenuPrev = MenuTmp = pmt->TopMenu; 3913 3914 /* close topmost popup */ 3915 while (MenuTmp != pmt->CurrentMenu) 3916 { 3917 MenuPrev = MenuTmp; 3918 MenuTmp = MENU_GetSubPopup(MenuPrev); 3919 } 3920 3921 MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags); 3922 pmt->CurrentMenu = MenuPrev; 3923 EndMenu = FALSE; 3924 } 3925 } 3926 3927 return EndMenu; 3928 } 3929 3930 /*********************************************************************** 3931 * MenuKeyLeft 3932 * 3933 * Handle a VK_LEFT key event in a menu. 3934 */ 3935 static void FASTCALL MENU_KeyLeft(MTRACKER* pmt, UINT Flags, UINT msg) 3936 { 3937 PMENU MenuTmp, MenuPrev; 3938 UINT PrevCol; 3939 3940 MenuPrev = MenuTmp = pmt->TopMenu; 3941 3942 /* Try to move 1 column left (if possible) */ 3943 if ( (PrevCol = MENU_GetStartOfPrevColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM) 3944 { 3945 MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, PrevCol, TRUE, 0); 3946 return; 3947 } 3948 3949 /* close topmost popup */ 3950 while (MenuTmp != pmt->CurrentMenu) 3951 { 3952 MenuPrev = MenuTmp; 3953 MenuTmp = MENU_GetSubPopup(MenuPrev); 3954 } 3955 3956 MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags); 3957 pmt->CurrentMenu = MenuPrev; 3958 3959 if ((MenuPrev == pmt->TopMenu) && !(pmt->TopMenu->fFlags & MNF_POPUP)) 3960 { 3961 /* move menu bar selection if no more popups are left */ 3962 3963 if (!MENU_DoNextMenu(pmt, VK_LEFT, Flags)) 3964 MENU_MoveSelection(pmt->OwnerWnd, pmt->TopMenu, ITEM_PREV); 3965 3966 if (MenuPrev != MenuTmp || pmt->TrackFlags & TF_SUSPENDPOPUP) 3967 { 3968 /* A sublevel menu was displayed - display the next one 3969 * unless there is another displacement coming up */ 3970 3971 if (!MENU_SuspendPopup(pmt, msg)) 3972 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu, 3973 TRUE, Flags); 3974 } 3975 } 3976 } 3977 3978 /*********************************************************************** 3979 * MenuKeyRight 3980 * 3981 * Handle a VK_RIGHT key event in a menu. 3982 */ 3983 static void FASTCALL MENU_KeyRight(MTRACKER *pmt, UINT Flags, UINT msg) 3984 { 3985 PMENU menutmp; 3986 UINT NextCol; 3987 3988 TRACE("MenuKeyRight called, cur %p, top %p.\n", 3989 pmt->CurrentMenu, pmt->TopMenu); 3990 3991 if ((pmt->TopMenu->fFlags & MNF_POPUP) || (pmt->CurrentMenu != pmt->TopMenu)) 3992 { 3993 /* If already displaying a popup, try to display sub-popup */ 3994 3995 menutmp = pmt->CurrentMenu; 3996 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, menutmp, TRUE, Flags); 3997 3998 /* if subpopup was displayed then we are done */ 3999 if (menutmp != pmt->CurrentMenu) return; 4000 } 4001 4002 /* Check to see if there's another column */ 4003 if ( (NextCol = MENU_GetStartOfNextColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM) 4004 { 4005 TRACE("Going to %d.\n", NextCol); 4006 MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NextCol, TRUE, 0); 4007 return; 4008 } 4009 4010 if (!(pmt->TopMenu->fFlags & MNF_POPUP)) /* menu bar tracking */ 4011 { 4012 if (pmt->CurrentMenu != pmt->TopMenu) 4013 { 4014 MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, Flags); 4015 menutmp = pmt->CurrentMenu = pmt->TopMenu; 4016 } 4017 else 4018 { 4019 menutmp = NULL; 4020 } 4021 4022 /* try to move to the next item */ 4023 if ( !MENU_DoNextMenu(pmt, VK_RIGHT, Flags)) 4024 MENU_MoveSelection(pmt->OwnerWnd, pmt->TopMenu, ITEM_NEXT); 4025 4026 if ( menutmp || pmt->TrackFlags & TF_SUSPENDPOPUP ) 4027 { 4028 if ( !MENU_SuspendPopup(pmt, msg) ) 4029 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu, TRUE, Flags); 4030 } 4031 } 4032 } 4033 4034 /*********************************************************************** 4035 * MenuTrackMenu 4036 * 4037 * Menu tracking code. 4038 */ 4039 static INT FASTCALL MENU_TrackMenu(PMENU pmenu, UINT wFlags, INT x, INT y, 4040 PWND pwnd) 4041 { 4042 MSG msg; 4043 BOOL fRemove; 4044 INT executedMenuId = -1; 4045 MTRACKER mt; 4046 HWND capture_win; 4047 PMENU pmMouse; 4048 BOOL enterIdleSent = FALSE; 4049 PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); 4050 4051 if (pti != pwnd->head.pti) 4052 { 4053 ERR("Not the same PTI!!!!\n"); 4054 } 4055 4056 mt.TrackFlags = 0; 4057 mt.CurrentMenu = pmenu; 4058 mt.TopMenu = pmenu; 4059 mt.OwnerWnd = pwnd; 4060 mt.Pt.x = x; 4061 mt.Pt.y = y; 4062 4063 TRACE("MTM : hmenu=%p flags=0x%08x (%d,%d) hwnd=%x\n", 4064 UserHMGetHandle(pmenu), wFlags, x, y, UserHMGetHandle(pwnd)); 4065 4066 pti->MessageQueue->QF_flags &= ~QF_ACTIVATIONCHANGE; 4067 4068 if (wFlags & TPM_BUTTONDOWN) 4069 { 4070 /* Get the result in order to start the tracking or not */ 4071 fRemove = MENU_ButtonDown( &mt, pmenu, wFlags ); 4072 fInsideMenuLoop = fRemove; 4073 } 4074 4075 if (wFlags & TF_ENDMENU) fInsideMenuLoop = FALSE; 4076 4077 if (wFlags & TPM_POPUPMENU && pmenu->cItems == 0) // Tracking empty popup menu... 4078 { 4079 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL); 4080 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED; 4081 co_UserSetCapture(NULL); /* release the capture */ 4082 return 0; 4083 } 4084 4085 capture_win = IntGetCapture(); 4086 4087 while (fInsideMenuLoop) 4088 { 4089 BOOL ErrorExit = FALSE; 4090 if (!VerifyMenu( mt.CurrentMenu )) /* sometimes happens if I do a window manager close */ 4091 break; 4092 4093 /* we have to keep the message in the queue until it's 4094 * clear that menu loop is not over yet. */ 4095 4096 for (;;) 4097 { 4098 if (co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOREMOVE, FALSE )) 4099 { 4100 if (!IntCallMsgFilter( &msg, MSGF_MENU )) break; 4101 /* remove the message from the queue */ 4102 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE ); 4103 } 4104 else 4105 { 4106 /* ReactOS Checks */ 4107 if (!VerifyWnd(mt.OwnerWnd) || 4108 !ValidateHwndNoErr(mt.CurrentMenu->hWnd) || 4109 //pti->MessageQueue->QF_flags & QF_ACTIVATIONCHANGE || // See CORE-17338 4110 capture_win != IntGetCapture() ) // Should not happen, but this is ReactOS... 4111 { 4112 ErrorExit = TRUE; // Do not wait on dead windows, now win test_capture_4 works. 4113 break; 4114 } 4115 4116 if (!enterIdleSent) 4117 { 4118 HWND win = mt.CurrentMenu->fFlags & MNF_POPUP ? mt.CurrentMenu->hWnd : NULL; 4119 enterIdleSent = TRUE; 4120 co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_ENTERIDLE, MSGF_MENU, (LPARAM) win); 4121 } 4122 co_IntWaitMessage(NULL, 0, 0); 4123 } 4124 } 4125 4126 if (ErrorExit) break; // Gracefully dropout. 4127 4128 /* check if EndMenu() tried to cancel us, by posting this message */ 4129 if (msg.message == WM_CANCELMODE) 4130 { 4131 /* we are now out of the loop */ 4132 fInsideMenuLoop = FALSE; 4133 4134 /* remove the message from the queue */ 4135 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE ); 4136 4137 /* break out of internal loop, ala ESCAPE */ 4138 break; 4139 } 4140 4141 mt.Pt = msg.pt; 4142 4143 if ( (msg.hwnd == mt.CurrentMenu->hWnd) || ((msg.message!=WM_TIMER) && (msg.message!=WM_SYSTIMER)) ) 4144 enterIdleSent=FALSE; 4145 4146 fRemove = FALSE; 4147 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST)) 4148 { 4149 /* 4150 * Use the mouse coordinates in lParam instead of those in the MSG 4151 * struct to properly handle synthetic messages. They are already 4152 * in screen coordinates. 4153 */ 4154 mt.Pt.x = (short)LOWORD(msg.lParam); 4155 mt.Pt.y = (short)HIWORD(msg.lParam); 4156 4157 /* Find a menu for this mouse event */ 4158 pmMouse = MENU_PtMenu( mt.TopMenu, mt.Pt ); 4159 4160 switch(msg.message) 4161 { 4162 /* no WM_NC... messages in captured state */ 4163 4164 case WM_RBUTTONDBLCLK: 4165 case WM_RBUTTONDOWN: 4166 if (!(wFlags & TPM_RIGHTBUTTON)) 4167 { 4168 if ( msg.message == WM_RBUTTONDBLCLK ) fInsideMenuLoop = FALSE; // Must exit or loop forever! 4169 break; 4170 } 4171 /* fall through */ 4172 case WM_LBUTTONDBLCLK: 4173 case WM_LBUTTONDOWN: 4174 /* If the message belongs to the menu, removes it from the queue */ 4175 /* Else, end menu tracking */ 4176 fRemove = MENU_ButtonDown(&mt, pmMouse, wFlags); 4177 fInsideMenuLoop = fRemove; 4178 if ( msg.message == WM_LBUTTONDBLCLK || 4179 msg.message == WM_RBUTTONDBLCLK ) fInsideMenuLoop = FALSE; // Must exit or loop forever! 4180 break; 4181 4182 case WM_RBUTTONUP: 4183 if (!(wFlags & TPM_RIGHTBUTTON)) break; 4184 /* fall through */ 4185 case WM_LBUTTONUP: 4186 /* Check if a menu was selected by the mouse */ 4187 if (pmMouse) 4188 { 4189 executedMenuId = MENU_ButtonUp( &mt, pmMouse, wFlags); 4190 4191 /* End the loop if executedMenuId is an item ID */ 4192 /* or if the job was done (executedMenuId = 0). */ 4193 fRemove = (executedMenuId != -1); 4194 fInsideMenuLoop = !fRemove; 4195 } 4196 /* No menu was selected by the mouse */ 4197 /* if the function was called by TrackPopupMenu, continue 4198 with the menu tracking. If not, stop it */ 4199 else 4200 fInsideMenuLoop = ((wFlags & TPM_POPUPMENU) ? TRUE : FALSE); 4201 4202 break; 4203 4204 case WM_MOUSEMOVE: 4205 /* the selected menu item must be changed every time */ 4206 /* the mouse moves. */ 4207 4208 if (pmMouse) 4209 fInsideMenuLoop |= MENU_MouseMove( &mt, pmMouse, wFlags ); 4210 4211 } /* switch(msg.message) - mouse */ 4212 } 4213 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST)) 4214 { 4215 fRemove = TRUE; /* Keyboard messages are always removed */ 4216 switch(msg.message) 4217 { 4218 case WM_KEYDOWN: 4219 case WM_SYSKEYDOWN: 4220 switch(msg.wParam) 4221 { 4222 case VK_MENU: 4223 case VK_F10: 4224 fInsideMenuLoop = FALSE; 4225 break; 4226 4227 case VK_HOME: 4228 case VK_END: 4229 MENU_SelectItem(mt.OwnerWnd, mt.CurrentMenu, NO_SELECTED_ITEM, FALSE, 0 ); 4230 MENU_MoveSelection(mt.OwnerWnd, mt.CurrentMenu, VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV); 4231 break; 4232 4233 case VK_UP: 4234 case VK_DOWN: /* If on menu bar, pull-down the menu */ 4235 if (!(mt.CurrentMenu->fFlags & MNF_POPUP)) 4236 mt.CurrentMenu = MENU_ShowSubPopup(mt.OwnerWnd, mt.TopMenu, TRUE, wFlags); 4237 else /* otherwise try to move selection */ 4238 MENU_MoveSelection(mt.OwnerWnd, mt.CurrentMenu, (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT ); 4239 break; 4240 4241 case VK_LEFT: 4242 MENU_KeyLeft( &mt, wFlags, msg.message ); 4243 break; 4244 4245 case VK_RIGHT: 4246 MENU_KeyRight( &mt, wFlags, msg.message ); 4247 break; 4248 4249 case VK_ESCAPE: 4250 fInsideMenuLoop = !MENU_KeyEscape(&mt, wFlags); 4251 break; 4252 4253 case VK_F1: 4254 { 4255 HELPINFO hi; 4256 hi.cbSize = sizeof(HELPINFO); 4257 hi.iContextType = HELPINFO_MENUITEM; 4258 if (mt.CurrentMenu->iItem == NO_SELECTED_ITEM) 4259 hi.iCtrlId = 0; 4260 else 4261 hi.iCtrlId = pmenu->rgItems[mt.CurrentMenu->iItem].wID; 4262 hi.hItemHandle = UserHMGetHandle(mt.CurrentMenu); 4263 hi.dwContextId = pmenu->dwContextHelpId; 4264 hi.MousePos = msg.pt; 4265 co_IntSendMessage( UserHMGetHandle(pwnd), WM_HELP, 0, (LPARAM)&hi); 4266 break; 4267 } 4268 4269 default: 4270 IntTranslateKbdMessage(&msg, 0); 4271 break; 4272 } 4273 break; /* WM_KEYDOWN */ 4274 4275 case WM_CHAR: 4276 case WM_SYSCHAR: 4277 { 4278 UINT pos; 4279 BOOL fEndMenu; 4280 4281 if (msg.wParam == L'\r' || msg.wParam == L' ') 4282 { 4283 executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags); 4284 fEndMenu = (executedMenuId != -2); 4285 fInsideMenuLoop = !fEndMenu; 4286 break; 4287 } 4288 4289 /* Hack to avoid control chars. */ 4290 /* We will find a better way real soon... */ 4291 if (msg.wParam < 32) break; 4292 4293 pos = MENU_FindItemByKey(mt.OwnerWnd, mt.CurrentMenu, LOWORD(msg.wParam), FALSE); 4294 4295 if (pos == (UINT)-2) fInsideMenuLoop = FALSE; 4296 else if (pos == (UINT)-1) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0); 4297 else 4298 { 4299 MENU_SelectItem(mt.OwnerWnd, mt.CurrentMenu, pos, TRUE, 0); 4300 executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags); 4301 fEndMenu = (executedMenuId != -2); 4302 fInsideMenuLoop = !fEndMenu; 4303 } 4304 } 4305 break; 4306 } /* switch(msg.message) - kbd */ 4307 } 4308 else 4309 { 4310 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE ); 4311 IntDispatchMessage( &msg ); 4312 continue; 4313 } 4314 4315 if (fInsideMenuLoop) fRemove = TRUE; 4316 4317 /* finally remove message from the queue */ 4318 4319 if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) ) 4320 co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE ); 4321 else mt.TrackFlags &= ~TF_SKIPREMOVE; 4322 } 4323 4324 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL); 4325 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED; 4326 co_UserSetCapture(NULL); /* release the capture */ 4327 4328 /* If dropdown is still painted and the close box is clicked on 4329 then the menu will be destroyed as part of the DispatchMessage above. 4330 This will then invalidate the menu handle in mt.hTopMenu. We should 4331 check for this first. */ 4332 if ( VerifyMenu( mt.TopMenu ) ) 4333 { 4334 if (VerifyWnd(mt.OwnerWnd)) 4335 { 4336 MENU_HideSubPopups(mt.OwnerWnd, mt.TopMenu, FALSE, wFlags); 4337 4338 if (mt.TopMenu->fFlags & MNF_POPUP) 4339 { 4340 PWND pwndTM = ValidateHwndNoErr(mt.TopMenu->hWnd); 4341 if (pwndTM) 4342 { 4343 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, pwndTM, OBJID_CLIENT, CHILDID_SELF, 0); 4344 4345 co_UserDestroyWindow(pwndTM); 4346 } 4347 mt.TopMenu->hWnd = NULL; 4348 4349 if (!(wFlags & TPM_NONOTIFY)) 4350 { 4351 co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(mt.TopMenu), 4352 MAKELPARAM(0, IS_SYSTEM_MENU(mt.TopMenu) ? MF_SYSMENU : 0)); 4353 } 4354 } 4355 MENU_SelectItem( mt.OwnerWnd, mt.TopMenu, NO_SELECTED_ITEM, FALSE, 0 ); 4356 co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 ); 4357 } 4358 4359 /* Reset the variable for hiding menu */ 4360 mt.TopMenu->TimeToHide = FALSE; 4361 } 4362 4363 EngSetLastError( ERROR_SUCCESS ); 4364 /* The return value is only used by TrackPopupMenu */ 4365 if (!(wFlags & TPM_RETURNCMD)) return TRUE; 4366 if (executedMenuId == -1) executedMenuId = 0; 4367 return executedMenuId; 4368 } 4369 4370 /*********************************************************************** 4371 * MenuInitTracking 4372 */ 4373 static BOOL FASTCALL MENU_InitTracking(PWND pWnd, PMENU Menu, BOOL bPopup, UINT wFlags) 4374 { 4375 HWND capture_win; 4376 PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); 4377 4378 TRACE("hwnd=%p hmenu=%p\n", UserHMGetHandle(pWnd), UserHMGetHandle(Menu)); 4379 4380 co_UserHideCaret(0); 4381 4382 /* This makes the menus of applications built with Delphi work. 4383 * It also enables menus to be displayed in more than one window, 4384 * but there are some bugs left that need to be fixed in this case. 4385 */ 4386 if (!bPopup) 4387 { 4388 Menu->hWnd = UserHMGetHandle(pWnd); 4389 } 4390 4391 if (!top_popup) { 4392 top_popup = Menu->hWnd; 4393 top_popup_hmenu = UserHMGetHandle(Menu); 4394 } 4395 4396 fInsideMenuLoop = TRUE; 4397 fInEndMenu = FALSE; 4398 4399 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */ 4400 if (!(wFlags & TPM_NONOTIFY)) 4401 { 4402 co_IntSendMessage( UserHMGetHandle(pWnd), WM_ENTERMENULOOP, bPopup, 0 ); 4403 } 4404 4405 // 4406 // Capture is set before calling WM_INITMENU and after WM_ENTERMENULOOP, see msg_menu. 4407 // 4408 capture_win = (wFlags & TPM_POPUPMENU) ? Menu->hWnd : UserHMGetHandle(pWnd); 4409 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, capture_win); // 1 4410 co_UserSetCapture(capture_win); // 2 4411 pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED; // Set the Q bits so noone can change this! 4412 4413 co_IntSendMessage( UserHMGetHandle(pWnd), WM_SETCURSOR, (WPARAM)UserHMGetHandle(pWnd), HTCAPTION ); 4414 4415 if (!(wFlags & TPM_NONOTIFY)) 4416 { 4417 co_IntSendMessage( UserHMGetHandle(pWnd), WM_INITMENU, (WPARAM)UserHMGetHandle(Menu), 0 ); 4418 /* If an app changed/recreated menu bar entries in WM_INITMENU 4419 * menu sizes will be recalculated once the menu created/shown. 4420 */ 4421 } 4422 4423 IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART, 4424 pWnd, 4425 Menu->fFlags & MNF_SYSMENU ? OBJID_SYSMENU : OBJID_MENU, 4426 CHILDID_SELF, 0); 4427 return TRUE; 4428 } 4429 4430 /*********************************************************************** 4431 * MenuExitTracking 4432 */ 4433 static BOOL FASTCALL MENU_ExitTracking(PWND pWnd, BOOL bPopup, UINT wFlags) 4434 { 4435 TRACE("Exit Track hwnd=%p bPopup %d\n", UserHMGetHandle(pWnd), bPopup); 4436 4437 IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, pWnd, OBJID_WINDOW, CHILDID_SELF, 0); 4438 4439 if (!(wFlags & TPM_NONOTIFY)) 4440 co_IntSendMessage( UserHMGetHandle(pWnd), WM_EXITMENULOOP, bPopup, 0 ); 4441 4442 co_UserShowCaret(0); 4443 4444 top_popup = 0; 4445 top_popup_hmenu = NULL; 4446 4447 return TRUE; 4448 } 4449 4450 /*********************************************************************** 4451 * MenuTrackMouseMenuBar 4452 * 4453 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand(). 4454 */ 4455 VOID MENU_TrackMouseMenuBar( PWND pWnd, ULONG ht, POINT pt) 4456 { 4457 PMENU pMenu = (ht == HTSYSMENU) ? IntGetSystemMenu(pWnd, FALSE) : IntGetMenu( UserHMGetHandle(pWnd) ); // See 74276 and CORE-12801 4458 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL; 4459 4460 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", pWnd, ht, pt.x, pt.y); 4461 4462 if (pWnd->ExStyle & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL; 4463 if (VerifyMenu(pMenu)) 4464 { 4465 /* map point to parent client coordinates */ 4466 PWND Parent = UserGetAncestor(pWnd, GA_PARENT ); 4467 if (Parent != UserGetDesktopWindow()) 4468 { 4469 IntScreenToClient(Parent, &pt); 4470 } 4471 4472 MENU_InitTracking(pWnd, pMenu, FALSE, wFlags); 4473 /* fetch the window menu again, it may have changed */ 4474 pMenu = (ht == HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pWnd) ) : IntGetMenu( UserHMGetHandle(pWnd) ); 4475 MENU_TrackMenu(pMenu, wFlags, pt.x, pt.y, pWnd); 4476 MENU_ExitTracking(pWnd, FALSE, wFlags); 4477 } 4478 } 4479 4480 /*********************************************************************** 4481 * MenuTrackKbdMenuBar 4482 * 4483 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand(). 4484 */ 4485 VOID MENU_TrackKbdMenuBar(PWND pwnd, UINT wParam, WCHAR wChar) 4486 { 4487 UINT uItem = NO_SELECTED_ITEM; 4488 PMENU TrackMenu; 4489 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON; 4490 4491 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", UserHMGetHandle(pwnd), wParam, wChar); 4492 4493 /* find window that has a menu */ 4494 4495 while (!( (pwnd->style & (WS_CHILD | WS_POPUP)) != WS_CHILD ) ) 4496 if (!(pwnd = UserGetAncestor( pwnd, GA_PARENT ))) return; 4497 4498 /* check if we have to track a system menu */ 4499 4500 TrackMenu = IntGetMenu( UserHMGetHandle(pwnd) ); 4501 if (!TrackMenu || (pwnd->style & WS_MINIMIZE) != 0 || wChar == ' ' ) 4502 { 4503 if (!(pwnd->style & WS_SYSMENU)) return; 4504 TrackMenu = get_win_sys_menu( UserHMGetHandle(pwnd) ); 4505 uItem = 0; 4506 wParam |= HTSYSMENU; /* prevent item lookup */ 4507 } 4508 4509 if (!VerifyMenu( TrackMenu )) return; 4510 4511 MENU_InitTracking( pwnd, TrackMenu, FALSE, wFlags ); 4512 4513 /* fetch the window menu again, it may have changed */ 4514 TrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pwnd) ) : IntGetMenu( UserHMGetHandle(pwnd) ); 4515 4516 if( wChar && wChar != ' ' ) 4517 { 4518 uItem = MENU_FindItemByKey( pwnd, TrackMenu, wChar, (wParam & HTSYSMENU) ); 4519 if ( uItem >= (UINT)(-2) ) 4520 { 4521 if( uItem == (UINT)(-1) ) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0); 4522 /* schedule end of menu tracking */ 4523 wFlags |= TF_ENDMENU; 4524 goto track_menu; 4525 } 4526 } 4527 4528 MENU_SelectItem( pwnd, TrackMenu, uItem, TRUE, 0 ); 4529 4530 if (!(wParam & HTSYSMENU) || wChar == ' ') 4531 { 4532 if( uItem == NO_SELECTED_ITEM ) 4533 MENU_MoveSelection( pwnd, TrackMenu, ITEM_NEXT ); 4534 else 4535 UserPostMessage( UserHMGetHandle(pwnd), WM_KEYDOWN, VK_RETURN, 0 ); 4536 } 4537 4538 track_menu: 4539 MENU_TrackMenu( TrackMenu, wFlags, 0, 0, pwnd ); 4540 MENU_ExitTracking( pwnd, FALSE, wFlags); 4541 } 4542 4543 /********************************************************************** 4544 * TrackPopupMenuEx (USER32.@) 4545 */ 4546 BOOL WINAPI IntTrackPopupMenuEx( PMENU menu, UINT wFlags, int x, int y, 4547 PWND pWnd, LPTPMPARAMS lpTpm) 4548 { 4549 BOOL ret = FALSE; 4550 PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); 4551 4552 if (pti != pWnd->head.pti) 4553 { 4554 ERR("Must be the same pti!\n"); 4555 return ret; 4556 } 4557 4558 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p \n", //rect %s\n", 4559 UserHMGetHandle(menu), wFlags, x, y, UserHMGetHandle(pWnd), lpTpm); //, 4560 //lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" ); 4561 4562 if (menu->hWnd && IntIsWindow(menu->hWnd)) 4563 { 4564 EngSetLastError( ERROR_POPUP_ALREADY_ACTIVE ); 4565 return FALSE; 4566 } 4567 4568 if (MENU_InitPopup( pWnd, menu, wFlags )) 4569 { 4570 MENU_InitTracking(pWnd, menu, TRUE, wFlags); 4571 4572 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */ 4573 if (!(wFlags & TPM_NONOTIFY)) 4574 { 4575 co_IntSendMessage( UserHMGetHandle(pWnd), WM_INITMENUPOPUP, (WPARAM) UserHMGetHandle(menu), MAKELPARAM(0, IS_SYSTEM_MENU(menu))); 4576 } 4577 4578 if (menu->fFlags & MNF_SYSMENU) 4579 MENU_InitSysMenuPopup( menu, pWnd->style, pWnd->pcls->style, HTSYSMENU); 4580 4581 if (MENU_ShowPopup(pWnd, menu, 0, wFlags | TPM_POPUPMENU, x, y, lpTpm ? &lpTpm->rcExclude : NULL)) 4582 ret = MENU_TrackMenu( menu, wFlags | TPM_POPUPMENU, 0, 0, pWnd); 4583 else 4584 { 4585 MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL); 4586 pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED; 4587 co_UserSetCapture(NULL); /* release the capture */ 4588 } 4589 4590 MENU_ExitTracking(pWnd, TRUE, wFlags); 4591 4592 if (menu->hWnd) 4593 { 4594 PWND pwndM = ValidateHwndNoErr( menu->hWnd ); 4595 if (pwndM) // wine hack around this with their destroy function. 4596 { 4597 if (!(pWnd->state & WNDS_DESTROYED)) 4598 co_UserDestroyWindow( pwndM ); // Fix wrong error return. 4599 } 4600 menu->hWnd = 0; 4601 4602 if (!(wFlags & TPM_NONOTIFY)) 4603 { 4604 co_IntSendMessage( UserHMGetHandle(pWnd), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(menu), 4605 MAKELPARAM(0, IS_SYSTEM_MENU(menu) ? MF_SYSMENU : 0)); 4606 } 4607 } 4608 } 4609 return ret; 4610 } 4611 4612 // 4613 // Menu Class Proc. 4614 // 4615 BOOL WINAPI 4616 PopupMenuWndProc( 4617 PWND Wnd, 4618 UINT Message, 4619 WPARAM wParam, 4620 LPARAM lParam, 4621 LRESULT *lResult) 4622 { 4623 PPOPUPMENU pPopupMenu; 4624 4625 *lResult = 0; 4626 4627 TRACE("PMWP : pwnd=%x msg=%d wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam); 4628 4629 if (Wnd) 4630 { 4631 if (!Wnd->fnid) 4632 { 4633 if (Message != WM_NCCREATE) 4634 { 4635 *lResult = IntDefWindowProc(Wnd, Message, wParam, lParam, FALSE); 4636 return TRUE; 4637 } 4638 Wnd->fnid = FNID_MENU; 4639 pPopupMenu = DesktopHeapAlloc( Wnd->head.rpdesk, sizeof(POPUPMENU) ); 4640 if (pPopupMenu == NULL) 4641 { 4642 return TRUE; 4643 } 4644 pPopupMenu->posSelectedItem = NO_SELECTED_ITEM; 4645 pPopupMenu->spwndPopupMenu = Wnd; 4646 ((PMENUWND)Wnd)->ppopupmenu = pPopupMenu; 4647 TRACE("Pop Up Menu is Setup! Msg %d\n",Message); 4648 *lResult = 1; 4649 return TRUE; 4650 } 4651 else 4652 { 4653 if (Wnd->fnid != FNID_MENU) 4654 { 4655 ERR("Wrong window class for Menu! fnid %x\n",Wnd->fnid); 4656 return TRUE; 4657 } 4658 pPopupMenu = ((PMENUWND)Wnd)->ppopupmenu; 4659 } 4660 } 4661 4662 switch(Message) 4663 { 4664 case WM_CREATE: 4665 { 4666 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam; 4667 pPopupMenu->spmenu = UserGetMenuObject(cs->lpCreateParams); 4668 if (pPopupMenu->spmenu) 4669 { 4670 UserReferenceObject(pPopupMenu->spmenu); 4671 } 4672 break; 4673 } 4674 4675 case WM_MOUSEACTIVATE: /* We don't want to be activated */ 4676 *lResult = MA_NOACTIVATE; 4677 break; 4678 4679 case WM_PAINT: 4680 { 4681 PAINTSTRUCT ps; 4682 IntBeginPaint(Wnd, &ps); 4683 MENU_DrawPopupMenu(Wnd, ps.hdc, pPopupMenu->spmenu); 4684 IntEndPaint(Wnd, &ps); 4685 break; 4686 } 4687 4688 case WM_PRINTCLIENT: 4689 { 4690 MENU_DrawPopupMenu( Wnd, (HDC)wParam, pPopupMenu->spmenu); 4691 break; 4692 } 4693 4694 case WM_ERASEBKGND: 4695 *lResult = 1; 4696 break; 4697 4698 case WM_DESTROY: 4699 /* zero out global pointer in case resident popup window was destroyed. */ 4700 if (pPopupMenu) 4701 { 4702 if (UserHMGetHandle(Wnd) == top_popup) 4703 { 4704 top_popup = NULL; 4705 top_popup_hmenu = NULL; 4706 } 4707 } 4708 else 4709 { 4710 ERR("No Window Pop Up!\n"); 4711 } 4712 break; 4713 4714 case WM_NCDESTROY: 4715 { 4716 if (pPopupMenu->spmenu) 4717 { 4718 IntReleaseMenuObject(pPopupMenu->spmenu); 4719 } 4720 DesktopHeapFree(Wnd->head.rpdesk, pPopupMenu ); 4721 ((PMENUWND)Wnd)->ppopupmenu = 0; 4722 Wnd->fnid = FNID_DESTROY; 4723 break; 4724 } 4725 4726 case MM_SETMENUHANDLE: // wine'isms 4727 case MN_SETHMENU: 4728 { 4729 PMENU pmenu = UserGetMenuObject((HMENU)wParam); 4730 if (!pmenu) 4731 { 4732 ERR("Bad Menu Handle\n"); 4733 break; 4734 } 4735 UserReferenceObject(pmenu); 4736 if (pPopupMenu->spmenu) 4737 { 4738 IntReleaseMenuObject(pPopupMenu->spmenu); 4739 } 4740 pPopupMenu->spmenu = pmenu; 4741 break; 4742 } 4743 4744 case MM_GETMENUHANDLE: // wine'isms 4745 case MN_GETHMENU: 4746 *lResult = (LRESULT)(pPopupMenu ? (pPopupMenu->spmenu ? UserHMGetHandle(pPopupMenu->spmenu) : NULL) : NULL); 4747 break; 4748 4749 default: 4750 if (Message > MN_GETHMENU && Message < MN_GETHMENU+19) 4751 { 4752 ERR("Someone is passing unknown menu messages %d\n",Message); 4753 } 4754 TRACE("PMWP to IDWP %d\n",Message); 4755 *lResult = IntDefWindowProc(Wnd, Message, wParam, lParam, FALSE); 4756 break; 4757 } 4758 4759 return TRUE; 4760 } 4761 4762 BOOL FASTCALL 4763 IntHiliteMenuItem(PWND WindowObject, 4764 PMENU MenuObject, 4765 UINT uItemHilite, 4766 UINT uHilite) 4767 { 4768 PITEM MenuItem; 4769 UINT uItem = uItemHilite; 4770 4771 if (!(MenuItem = MENU_FindItem( &MenuObject, &uItem, uHilite ))) return TRUE; 4772 4773 if (uHilite & MF_HILITE) 4774 { 4775 MenuItem->fState |= MF_HILITE; 4776 } 4777 else 4778 { 4779 MenuItem->fState &= ~MF_HILITE; 4780 } 4781 if (MenuObject->iItem == uItemHilite) return TRUE; 4782 MENU_HideSubPopups( WindowObject, MenuObject, FALSE, 0 ); 4783 MENU_SelectItem( WindowObject, MenuObject, uItemHilite, TRUE, 0 ); 4784 4785 return TRUE; // Always returns true!!!! 4786 } 4787 4788 BOOLEAN APIENTRY 4789 intGetTitleBarInfo(PWND pWindowObject, PTITLEBARINFO bti) 4790 { 4791 4792 DWORD dwStyle = 0; 4793 DWORD dwExStyle = 0; 4794 BOOLEAN retValue = TRUE; 4795 4796 if (bti->cbSize == sizeof(TITLEBARINFO)) 4797 { 4798 RtlZeroMemory(&bti->rgstate[0],sizeof(DWORD)*(CCHILDREN_TITLEBAR+1)); 4799 4800 bti->rgstate[0] = STATE_SYSTEM_FOCUSABLE; 4801 4802 dwStyle = pWindowObject->style; 4803 dwExStyle = pWindowObject->ExStyle; 4804 4805 bti->rcTitleBar.top = 0; 4806 bti->rcTitleBar.left = 0; 4807 bti->rcTitleBar.right = pWindowObject->rcWindow.right - pWindowObject->rcWindow.left; 4808 bti->rcTitleBar.bottom = pWindowObject->rcWindow.bottom - pWindowObject->rcWindow.top; 4809 4810 /* Is it iconiced ? */ 4811 if ((dwStyle & WS_ICONIC)!=WS_ICONIC) 4812 { 4813 /* Remove frame from rectangle */ 4814 if (HAS_THICKFRAME( dwStyle, dwExStyle )) 4815 { 4816 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXFRAME) and UserGetSystemMetrics(SM_CYFRAME) */ 4817 RECTL_vInflateRect( &bti->rcTitleBar, -UserGetSystemMetrics(SM_CXFRAME), -UserGetSystemMetrics(SM_CYFRAME) ); 4818 } 4819 else if (HAS_DLGFRAME( dwStyle, dwExStyle )) 4820 { 4821 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXDLGFRAME) and UserGetSystemMetrics(SM_CYDLGFRAME) */ 4822 RECTL_vInflateRect( &bti->rcTitleBar, -UserGetSystemMetrics(SM_CXDLGFRAME), -UserGetSystemMetrics(SM_CYDLGFRAME)); 4823 } 4824 else if (HAS_THINFRAME( dwStyle, dwExStyle)) 4825 { 4826 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXBORDER) and UserGetSystemMetrics(SM_CYBORDER) */ 4827 RECTL_vInflateRect( &bti->rcTitleBar, -UserGetSystemMetrics(SM_CXBORDER), -UserGetSystemMetrics(SM_CYBORDER) ); 4828 } 4829 4830 /* We have additional border information if the window 4831 * is a child (but not an MDI child) */ 4832 if ( (dwStyle & WS_CHILD) && 4833 ((dwExStyle & WS_EX_MDICHILD) == 0 ) ) 4834 { 4835 if (dwExStyle & WS_EX_CLIENTEDGE) 4836 { 4837 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXEDGE) and UserGetSystemMetrics(SM_CYEDGE) */ 4838 RECTL_vInflateRect (&bti->rcTitleBar, -UserGetSystemMetrics(SM_CXEDGE), -UserGetSystemMetrics(SM_CYEDGE)); 4839 } 4840 4841 if (dwExStyle & WS_EX_STATICEDGE) 4842 { 4843 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXBORDER) and UserGetSystemMetrics(SM_CYBORDER) */ 4844 RECTL_vInflateRect (&bti->rcTitleBar, -UserGetSystemMetrics(SM_CXBORDER), -UserGetSystemMetrics(SM_CYBORDER)); 4845 } 4846 } 4847 } 4848 4849 bti->rcTitleBar.top += pWindowObject->rcWindow.top; 4850 bti->rcTitleBar.left += pWindowObject->rcWindow.left; 4851 bti->rcTitleBar.right += pWindowObject->rcWindow.left; 4852 4853 bti->rcTitleBar.bottom = bti->rcTitleBar.top; 4854 if (dwExStyle & WS_EX_TOOLWINDOW) 4855 { 4856 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CYSMCAPTION) */ 4857 bti->rcTitleBar.bottom += UserGetSystemMetrics(SM_CYSMCAPTION); 4858 } 4859 else 4860 { 4861 /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CYCAPTION) and UserGetSystemMetrics(SM_CXSIZE) */ 4862 bti->rcTitleBar.bottom += UserGetSystemMetrics(SM_CYCAPTION); 4863 bti->rcTitleBar.left += UserGetSystemMetrics(SM_CXSIZE); 4864 } 4865 4866 if (dwStyle & WS_CAPTION) 4867 { 4868 bti->rgstate[1] = STATE_SYSTEM_INVISIBLE; 4869 if (dwStyle & WS_SYSMENU) 4870 { 4871 if (!(dwStyle & (WS_MINIMIZEBOX|WS_MAXIMIZEBOX))) 4872 { 4873 bti->rgstate[2] = STATE_SYSTEM_INVISIBLE; 4874 bti->rgstate[3] = STATE_SYSTEM_INVISIBLE; 4875 } 4876 else 4877 { 4878 if (!(dwStyle & WS_MINIMIZEBOX)) 4879 { 4880 bti->rgstate[2] = STATE_SYSTEM_UNAVAILABLE; 4881 } 4882 if (!(dwStyle & WS_MAXIMIZEBOX)) 4883 { 4884 bti->rgstate[3] = STATE_SYSTEM_UNAVAILABLE; 4885 } 4886 } 4887 4888 if (!(dwExStyle & WS_EX_CONTEXTHELP)) 4889 { 4890 bti->rgstate[4] = STATE_SYSTEM_INVISIBLE; 4891 } 4892 if (pWindowObject->pcls->style & CS_NOCLOSE) 4893 { 4894 bti->rgstate[5] = STATE_SYSTEM_UNAVAILABLE; 4895 } 4896 } 4897 else 4898 { 4899 bti->rgstate[2] = STATE_SYSTEM_INVISIBLE; 4900 bti->rgstate[3] = STATE_SYSTEM_INVISIBLE; 4901 bti->rgstate[4] = STATE_SYSTEM_INVISIBLE; 4902 bti->rgstate[5] = STATE_SYSTEM_INVISIBLE; 4903 } 4904 } 4905 else 4906 { 4907 bti->rgstate[0] |= STATE_SYSTEM_INVISIBLE; 4908 } 4909 } 4910 else 4911 { 4912 EngSetLastError(ERROR_INVALID_PARAMETER); 4913 retValue = FALSE; 4914 } 4915 4916 return retValue; 4917 } 4918 4919 DWORD FASTCALL 4920 UserInsertMenuItem( 4921 PMENU Menu, 4922 UINT uItem, 4923 BOOL fByPosition, 4924 LPCMENUITEMINFOW UnsafeItemInfo, 4925 PUNICODE_STRING lpstr) 4926 { 4927 NTSTATUS Status; 4928 ROSMENUITEMINFO ItemInfo; 4929 4930 /* Try to copy the whole MENUITEMINFOW structure */ 4931 Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, sizeof(MENUITEMINFOW)); 4932 if (NT_SUCCESS(Status)) 4933 { 4934 if (sizeof(MENUITEMINFOW) != ItemInfo.cbSize 4935 && FIELD_OFFSET(MENUITEMINFOW, hbmpItem) != ItemInfo.cbSize) 4936 { 4937 EngSetLastError(ERROR_INVALID_PARAMETER); 4938 return FALSE; 4939 } 4940 return IntInsertMenuItem(Menu, uItem, fByPosition, &ItemInfo, lpstr); 4941 } 4942 4943 /* Try to copy without last field (not present in older versions) */ 4944 Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, FIELD_OFFSET(MENUITEMINFOW, hbmpItem)); 4945 if (NT_SUCCESS(Status)) 4946 { 4947 if (FIELD_OFFSET(MENUITEMINFOW, hbmpItem) != ItemInfo.cbSize) 4948 { 4949 EngSetLastError(ERROR_INVALID_PARAMETER); 4950 return FALSE; 4951 } 4952 ItemInfo.hbmpItem = (HBITMAP)0; 4953 return IntInsertMenuItem(Menu, uItem, fByPosition, &ItemInfo, lpstr); 4954 } 4955 4956 SetLastNtError(Status); 4957 return FALSE; 4958 } 4959 4960 UINT FASTCALL IntGetMenuState( HMENU hMenu, UINT uId, UINT uFlags) 4961 { 4962 PMENU MenuObject; 4963 PITEM pItem; 4964 4965 if (!(MenuObject = UserGetMenuObject(hMenu))) 4966 { 4967 return (UINT)-1; 4968 } 4969 4970 if (!(pItem = MENU_FindItem( &MenuObject, &uId, uFlags ))) return -1; 4971 4972 if (pItem->spSubMenu) 4973 { 4974 return (pItem->spSubMenu->cItems << 8) | ((pItem->fState|pItem->fType|MF_POPUP) & 0xff); 4975 } 4976 else 4977 return (pItem->fType | pItem->fState); 4978 } 4979 4980 HMENU FASTCALL IntGetSubMenu( HMENU hMenu, int nPos) 4981 { 4982 PMENU MenuObject; 4983 PITEM pItem; 4984 4985 if (!(MenuObject = UserGetMenuObject(hMenu))) 4986 { 4987 return NULL; 4988 } 4989 4990 if (!(pItem = MENU_FindItem( &MenuObject, (UINT*)&nPos, MF_BYPOSITION ))) return NULL; 4991 4992 if (pItem->spSubMenu) 4993 { 4994 HMENU hsubmenu = UserHMGetHandle(pItem->spSubMenu); 4995 return hsubmenu; 4996 } 4997 return NULL; 4998 } 4999 5000 UINT FASTCALL IntFindSubMenu(HMENU *hMenu, HMENU hSubTarget ) 5001 { 5002 PMENU menu, pSubTarget; 5003 UINT Pos; 5004 if (((*hMenu)==(HMENU)0xffff) ||(!(menu = UserGetMenuObject(*hMenu)))) 5005 return NO_SELECTED_ITEM; 5006 5007 pSubTarget = UserGetMenuObject(hSubTarget); 5008 5009 Pos = MENU_FindSubMenu(&menu, pSubTarget ); 5010 5011 *hMenu = (menu ? UserHMGetHandle(menu) : NULL); 5012 5013 return Pos; 5014 } 5015 5016 5017 HMENU FASTCALL UserCreateMenu(PDESKTOP Desktop, BOOL PopupMenu) 5018 { 5019 PWINSTATION_OBJECT WinStaObject; 5020 HANDLE Handle; 5021 PMENU Menu; 5022 NTSTATUS Status; 5023 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 5024 5025 if (gpepCSRSS != CurrentProcess) 5026 { 5027 /* 5028 * gpepCSRSS does not have a Win32WindowStation 5029 */ 5030 5031 Status = IntValidateWindowStationHandle(CurrentProcess->Win32WindowStation, 5032 UserMode, 5033 0, 5034 &WinStaObject, 5035 0); 5036 5037 if (!NT_SUCCESS(Status)) 5038 { 5039 ERR("Validation of window station handle (%p) failed\n", 5040 CurrentProcess->Win32WindowStation); 5041 SetLastNtError(Status); 5042 return (HMENU)0; 5043 } 5044 Menu = IntCreateMenu(&Handle, !PopupMenu, Desktop, GetW32ProcessInfo()); 5045 if (Menu && Menu->head.rpdesk->rpwinstaParent != WinStaObject) 5046 { 5047 ERR("Desktop Window Station does not match Process one!\n"); 5048 } 5049 ObDereferenceObject(WinStaObject); 5050 } 5051 else 5052 { 5053 Menu = IntCreateMenu(&Handle, !PopupMenu, GetW32ThreadInfo()->rpdesk, GetW32ProcessInfo()); 5054 } 5055 5056 if (Menu) UserDereferenceObject(Menu); 5057 return (HMENU)Handle; 5058 } 5059 5060 BOOL FASTCALL 5061 IntMenuItemInfo( 5062 PMENU Menu, 5063 UINT Item, 5064 BOOL ByPosition, 5065 PROSMENUITEMINFO ItemInfo, 5066 BOOL SetOrGet, 5067 PUNICODE_STRING lpstr) 5068 { 5069 PITEM MenuItem; 5070 BOOL Ret; 5071 5072 if (!(MenuItem = MENU_FindItem( &Menu, &Item, (ByPosition ? MF_BYPOSITION : MF_BYCOMMAND) ))) 5073 { 5074 EngSetLastError(ERROR_MENU_ITEM_NOT_FOUND); 5075 return( FALSE); 5076 } 5077 if (SetOrGet) 5078 { 5079 Ret = IntSetMenuItemInfo(Menu, MenuItem, ItemInfo, lpstr); 5080 } 5081 else 5082 { 5083 Ret = IntGetMenuItemInfo(Menu, MenuItem, ItemInfo); 5084 } 5085 return( Ret); 5086 } 5087 5088 BOOL FASTCALL 5089 UserMenuItemInfo( 5090 PMENU Menu, 5091 UINT Item, 5092 BOOL ByPosition, 5093 PROSMENUITEMINFO UnsafeItemInfo, 5094 BOOL SetOrGet, 5095 PUNICODE_STRING lpstr) 5096 { 5097 PITEM MenuItem; 5098 ROSMENUITEMINFO ItemInfo; 5099 NTSTATUS Status; 5100 UINT Size; 5101 BOOL Ret; 5102 5103 Status = MmCopyFromCaller(&Size, &UnsafeItemInfo->cbSize, sizeof(UINT)); 5104 if (! NT_SUCCESS(Status)) 5105 { 5106 SetLastNtError(Status); 5107 return( FALSE); 5108 } 5109 if ( Size != sizeof(MENUITEMINFOW) && 5110 Size != FIELD_OFFSET(MENUITEMINFOW, hbmpItem) && 5111 Size != sizeof(ROSMENUITEMINFO) ) 5112 { 5113 EngSetLastError(ERROR_INVALID_PARAMETER); 5114 return( FALSE); 5115 } 5116 Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, Size); 5117 if (! NT_SUCCESS(Status)) 5118 { 5119 SetLastNtError(Status); 5120 return( FALSE); 5121 } 5122 /* If this is a pre-0x0500 _WIN32_WINNT MENUITEMINFOW, you can't 5123 set/get hbmpItem */ 5124 if (FIELD_OFFSET(MENUITEMINFOW, hbmpItem) == Size 5125 && 0 != (ItemInfo.fMask & MIIM_BITMAP)) 5126 { 5127 EngSetLastError(ERROR_INVALID_PARAMETER); 5128 return( FALSE); 5129 } 5130 5131 if (!(MenuItem = MENU_FindItem( &Menu, &Item, (ByPosition ? MF_BYPOSITION : MF_BYCOMMAND) ))) 5132 { 5133 /* workaround for Word 95: pretend that SC_TASKLIST item exists. */ 5134 if ( SetOrGet && Item == SC_TASKLIST && !ByPosition ) 5135 return TRUE; 5136 5137 EngSetLastError(ERROR_MENU_ITEM_NOT_FOUND); 5138 return( FALSE); 5139 } 5140 5141 if (SetOrGet) 5142 { 5143 Ret = IntSetMenuItemInfo(Menu, MenuItem, &ItemInfo, lpstr); 5144 } 5145 else 5146 { 5147 Ret = IntGetMenuItemInfo(Menu, MenuItem, &ItemInfo); 5148 if (Ret) 5149 { 5150 Status = MmCopyToCaller(UnsafeItemInfo, &ItemInfo, Size); 5151 if (! NT_SUCCESS(Status)) 5152 { 5153 SetLastNtError(Status); 5154 return( FALSE); 5155 } 5156 } 5157 } 5158 5159 return( Ret); 5160 } 5161 5162 BOOL FASTCALL 5163 UserMenuInfo( 5164 PMENU Menu, 5165 PROSMENUINFO UnsafeMenuInfo, 5166 BOOL SetOrGet) 5167 { 5168 BOOL Res; 5169 DWORD Size; 5170 NTSTATUS Status; 5171 ROSMENUINFO MenuInfo; 5172 5173 Status = MmCopyFromCaller(&Size, &UnsafeMenuInfo->cbSize, sizeof(DWORD)); 5174 if (! NT_SUCCESS(Status)) 5175 { 5176 SetLastNtError(Status); 5177 return( FALSE); 5178 } 5179 if ( Size < sizeof(MENUINFO) || Size > sizeof(ROSMENUINFO) ) 5180 { 5181 EngSetLastError(ERROR_INVALID_PARAMETER); 5182 return( FALSE); 5183 } 5184 Status = MmCopyFromCaller(&MenuInfo, UnsafeMenuInfo, Size); 5185 if (! NT_SUCCESS(Status)) 5186 { 5187 SetLastNtError(Status); 5188 return( FALSE); 5189 } 5190 5191 if(SetOrGet) 5192 { 5193 /* Set MenuInfo */ 5194 Res = IntSetMenuInfo(Menu, &MenuInfo); 5195 } 5196 else 5197 { 5198 /* Get MenuInfo */ 5199 Res = IntGetMenuInfo(Menu, &MenuInfo); 5200 if (Res) 5201 { 5202 Status = MmCopyToCaller(UnsafeMenuInfo, &MenuInfo, Size); 5203 if (! NT_SUCCESS(Status)) 5204 { 5205 SetLastNtError(Status); 5206 return( FALSE); 5207 } 5208 } 5209 } 5210 5211 return( Res); 5212 } 5213 5214 BOOL FASTCALL 5215 IntGetMenuItemRect( 5216 PWND pWnd, 5217 PMENU Menu, 5218 UINT uItem, 5219 PRECTL Rect) 5220 { 5221 LONG XMove, YMove; 5222 PITEM MenuItem; 5223 UINT I = uItem; 5224 5225 if ((MenuItem = MENU_FindItem (&Menu, &I, MF_BYPOSITION))) 5226 { 5227 Rect->left = MenuItem->xItem; 5228 Rect->top = MenuItem->yItem; 5229 Rect->right = MenuItem->cxItem; // Do this for now...... 5230 Rect->bottom = MenuItem->cyItem; 5231 } 5232 else 5233 { 5234 ERR("Failed Item Lookup! %u\n", uItem); 5235 return FALSE; 5236 } 5237 5238 if (!pWnd) 5239 { 5240 HWND hWnd = Menu->hWnd; 5241 if (!(pWnd = UserGetWindowObject(hWnd))) return FALSE; 5242 } 5243 5244 if (Menu->fFlags & MNF_POPUP) 5245 { 5246 XMove = pWnd->rcClient.left; 5247 YMove = pWnd->rcClient.top; 5248 } 5249 else 5250 { 5251 XMove = pWnd->rcWindow.left; 5252 YMove = pWnd->rcWindow.top; 5253 } 5254 5255 Rect->left += XMove; 5256 Rect->top += YMove; 5257 Rect->right += XMove; 5258 Rect->bottom += YMove; 5259 5260 return TRUE; 5261 } 5262 5263 PMENU FASTCALL MENU_GetSystemMenu(PWND Window, PMENU Popup) 5264 { 5265 PMENU Menu, NewMenu = NULL, SysMenu = NULL; 5266 HMENU hSysMenu, hNewMenu = NULL; 5267 ROSMENUITEMINFO ItemInfoSet = {0}; 5268 ROSMENUITEMINFO ItemInfo = {0}; 5269 UNICODE_STRING MenuName; 5270 5271 hSysMenu = UserCreateMenu(Window->head.rpdesk, FALSE); 5272 if (NULL == hSysMenu) 5273 { 5274 return NULL; 5275 } 5276 SysMenu = UserGetMenuObject(hSysMenu); 5277 if (NULL == SysMenu) 5278 { 5279 UserDestroyMenu(hSysMenu); 5280 return NULL; 5281 } 5282 5283 SysMenu->fFlags |= MNF_SYSMENU; 5284 SysMenu->hWnd = UserHMGetHandle(Window); 5285 5286 if (!Popup) 5287 { 5288 //hNewMenu = co_IntLoadSysMenuTemplate(); 5289 if ( Window->ExStyle & WS_EX_MDICHILD ) 5290 { 5291 RtlInitUnicodeString( &MenuName, L"SYSMENUMDI"); 5292 hNewMenu = co_IntCallLoadMenu( hModClient, &MenuName); 5293 } 5294 else 5295 { 5296 RtlInitUnicodeString( &MenuName, L"SYSMENU"); 5297 hNewMenu = co_IntCallLoadMenu( hModClient, &MenuName); 5298 //ERR("%wZ\n",&MenuName); 5299 } 5300 if (!hNewMenu) 5301 { 5302 ERR("No Menu!!\n"); 5303 IntDestroyMenuObject(SysMenu, FALSE); 5304 return NULL; 5305 } 5306 Menu = UserGetMenuObject(hNewMenu); 5307 if (!Menu) 5308 { 5309 IntDestroyMenuObject(SysMenu, FALSE); 5310 return NULL; 5311 } 5312 5313 // Do the rest in here. 5314 5315 Menu->fFlags |= MNS_CHECKORBMP | MNF_SYSMENU | MNF_POPUP; 5316 5317 ItemInfoSet.cbSize = sizeof( MENUITEMINFOW); 5318 ItemInfoSet.fMask = MIIM_BITMAP; 5319 ItemInfoSet.hbmpItem = HBMMENU_POPUP_CLOSE; 5320 IntMenuItemInfo(Menu, SC_CLOSE, FALSE, &ItemInfoSet, TRUE, NULL); 5321 ItemInfoSet.hbmpItem = HBMMENU_POPUP_RESTORE; 5322 IntMenuItemInfo(Menu, SC_RESTORE, FALSE, &ItemInfoSet, TRUE, NULL); 5323 ItemInfoSet.hbmpItem = HBMMENU_POPUP_MAXIMIZE; 5324 IntMenuItemInfo(Menu, SC_MAXIMIZE, FALSE, &ItemInfoSet, TRUE, NULL); 5325 ItemInfoSet.hbmpItem = HBMMENU_POPUP_MINIMIZE; 5326 IntMenuItemInfo(Menu, SC_MINIMIZE, FALSE, &ItemInfoSet, TRUE, NULL); 5327 5328 NewMenu = IntCloneMenu(Menu); 5329 if (NewMenu == NULL) 5330 { 5331 IntDestroyMenuObject(Menu, FALSE); 5332 IntDestroyMenuObject(SysMenu, FALSE); 5333 return NULL; 5334 } 5335 5336 IntReleaseMenuObject(NewMenu); 5337 UserSetMenuDefaultItem(NewMenu, SC_CLOSE, FALSE); 5338 5339 IntDestroyMenuObject(Menu, FALSE); 5340 } 5341 else 5342 { 5343 NewMenu = Popup; 5344 } 5345 if (NewMenu) 5346 { 5347 NewMenu->fFlags |= MNF_SYSMENU | MNF_POPUP; 5348 5349 if (Window->pcls->style & CS_NOCLOSE) 5350 IntRemoveMenuItem(NewMenu, SC_CLOSE, MF_BYCOMMAND, TRUE); 5351 5352 ItemInfo.cbSize = sizeof(MENUITEMINFOW); 5353 ItemInfo.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_SUBMENU; 5354 ItemInfo.fType = MF_POPUP; 5355 ItemInfo.fState = MFS_ENABLED; 5356 ItemInfo.dwTypeData = NULL; 5357 ItemInfo.cch = 0; 5358 ItemInfo.hSubMenu = UserHMGetHandle(NewMenu); 5359 IntInsertMenuItem(SysMenu, (UINT) -1, TRUE, &ItemInfo, NULL); 5360 5361 return SysMenu; 5362 } 5363 ERR("failed to load system menu!\n"); 5364 return NULL; 5365 } 5366 5367 PMENU FASTCALL 5368 IntGetSystemMenu(PWND Window, BOOL bRevert) 5369 { 5370 PMENU Menu; 5371 5372 if (bRevert) 5373 { 5374 if (Window->SystemMenu) 5375 { 5376 Menu = UserGetMenuObject(Window->SystemMenu); 5377 if (Menu && !(Menu->fFlags & MNF_SYSDESKMN)) 5378 { 5379 IntDestroyMenuObject(Menu, TRUE); 5380 Window->SystemMenu = NULL; 5381 } 5382 } 5383 } 5384 else 5385 { 5386 Menu = Window->SystemMenu ? UserGetMenuObject(Window->SystemMenu) : NULL; 5387 if ((!Menu || Menu->fFlags & MNF_SYSDESKMN) && Window->style & WS_SYSMENU) 5388 { 5389 Menu = MENU_GetSystemMenu(Window, NULL); 5390 Window->SystemMenu = Menu ? UserHMGetHandle(Menu) : NULL; 5391 } 5392 } 5393 5394 if (Window->SystemMenu) 5395 { 5396 HMENU hMenu = IntGetSubMenu( Window->SystemMenu, 0); 5397 /* Store the dummy sysmenu handle to facilitate the refresh */ 5398 /* of the close button if the SC_CLOSE item change */ 5399 Menu = UserGetMenuObject(hMenu); 5400 if (Menu) 5401 { 5402 Menu->spwndNotify = Window; 5403 Menu->fFlags |= MNF_SYSSUBMENU; 5404 } 5405 return Menu; 5406 } 5407 return NULL; 5408 } 5409 5410 BOOL FASTCALL 5411 IntSetSystemMenu(PWND Window, PMENU Menu) 5412 { 5413 PMENU OldMenu; 5414 5415 if (!(Window->style & WS_SYSMENU)) return FALSE; 5416 5417 if (Window->SystemMenu) 5418 { 5419 OldMenu = UserGetMenuObject(Window->SystemMenu); 5420 if (OldMenu) 5421 { 5422 OldMenu->fFlags &= ~MNF_SYSMENU; 5423 IntDestroyMenuObject(OldMenu, TRUE); 5424 } 5425 } 5426 5427 OldMenu = MENU_GetSystemMenu(Window, Menu); 5428 if (OldMenu) 5429 { // Use spmenuSys too! 5430 Window->SystemMenu = UserHMGetHandle(OldMenu); 5431 } 5432 else 5433 Window->SystemMenu = NULL; 5434 5435 if (Menu && Window != Menu->spwndNotify) 5436 { 5437 Menu->spwndNotify = Window; 5438 } 5439 5440 return TRUE; 5441 } 5442 5443 BOOL FASTCALL 5444 IntSetMenu( 5445 PWND Wnd, 5446 HMENU Menu, 5447 BOOL *Changed) 5448 { 5449 PMENU OldMenu, NewMenu = NULL; 5450 5451 if ((Wnd->style & (WS_CHILD | WS_POPUP)) == WS_CHILD) 5452 { 5453 ERR("SetMenu: Window is a Child 0x%p!\n",UserHMGetHandle(Wnd)); 5454 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE); 5455 return FALSE; 5456 } 5457 5458 *Changed = (UlongToHandle(Wnd->IDMenu) != Menu); 5459 if (! *Changed) 5460 { 5461 return TRUE; 5462 } 5463 5464 if (Wnd->IDMenu) 5465 { 5466 OldMenu = IntGetMenuObject(UlongToHandle(Wnd->IDMenu)); 5467 ASSERT(NULL == OldMenu || OldMenu->hWnd == UserHMGetHandle(Wnd)); 5468 } 5469 else 5470 { 5471 OldMenu = NULL; 5472 } 5473 5474 if (NULL != Menu) 5475 { 5476 NewMenu = IntGetMenuObject(Menu); 5477 if (NULL == NewMenu) 5478 { 5479 if (NULL != OldMenu) 5480 { 5481 IntReleaseMenuObject(OldMenu); 5482 } 5483 EngSetLastError(ERROR_INVALID_MENU_HANDLE); 5484 return FALSE; 5485 } 5486 if (NULL != NewMenu->hWnd) 5487 { 5488 /* Can't use the same menu for two windows */ 5489 if (NULL != OldMenu) 5490 { 5491 IntReleaseMenuObject(OldMenu); 5492 } 5493 EngSetLastError(ERROR_INVALID_MENU_HANDLE); 5494 return FALSE; 5495 } 5496 5497 } 5498 5499 Wnd->IDMenu = (UINT_PTR) Menu; 5500 if (NULL != NewMenu) 5501 { 5502 NewMenu->hWnd = UserHMGetHandle(Wnd); 5503 IntReleaseMenuObject(NewMenu); 5504 } 5505 if (NULL != OldMenu) 5506 { 5507 OldMenu->hWnd = NULL; 5508 IntReleaseMenuObject(OldMenu); 5509 } 5510 5511 return TRUE; 5512 } 5513 5514 5515 /* FUNCTIONS *****************************************************************/ 5516 5517 /* 5518 * @implemented 5519 */ 5520 /* http://www.cyber-ta.org/releases/malware-analysis/public/SOURCES/b47155634ccb2c30630da7e3666d3d07/b47155634ccb2c30630da7e3666d3d07.trace.html#NtUserGetIconSize */ 5521 DWORD 5522 APIENTRY 5523 NtUserCalcMenuBar( 5524 HWND hwnd, 5525 DWORD leftBorder, 5526 DWORD rightBorder, 5527 DWORD top, 5528 LPRECT prc ) 5529 { 5530 HDC hdc; 5531 PWND Window; 5532 RECT Rect; 5533 DWORD ret; 5534 5535 UserEnterExclusive(); 5536 5537 if(!(Window = UserGetWindowObject(hwnd))) 5538 { 5539 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE); 5540 UserLeave(); 5541 return 0; 5542 } 5543 5544 hdc = UserGetDCEx(NULL, NULL, DCX_CACHE); 5545 if (!hdc) 5546 { 5547 UserLeave(); 5548 return 0; 5549 } 5550 5551 Rect.left = leftBorder; 5552 Rect.right = Window->rcWindow.right - Window->rcWindow.left - rightBorder; 5553 Rect.top = top; 5554 Rect.bottom = 0; 5555 5556 ret = MENU_DrawMenuBar(hdc, &Rect, Window, TRUE); 5557 5558 UserReleaseDC( 0, hdc, FALSE ); 5559 5560 UserLeave(); 5561 5562 return ret; 5563 } 5564 5565 /* 5566 * @implemented 5567 */ 5568 DWORD APIENTRY 5569 NtUserCheckMenuItem( 5570 HMENU hMenu, 5571 UINT uIDCheckItem, 5572 UINT uCheck) 5573 { 5574 PMENU Menu; 5575 DECLARE_RETURN(DWORD); 5576 5577 TRACE("Enter NtUserCheckMenuItem\n"); 5578 UserEnterExclusive(); 5579 5580 if(!(Menu = UserGetMenuObject(hMenu))) 5581 { 5582 RETURN( (DWORD)-1); 5583 } 5584 5585 RETURN( IntCheckMenuItem(Menu, uIDCheckItem, uCheck)); 5586 5587 CLEANUP: 5588 TRACE("Leave NtUserCheckMenuItem, ret=%lu\n",_ret_); 5589 UserLeave(); 5590 END_CLEANUP; 5591 } 5592 5593 /* 5594 * @implemented 5595 */ 5596 BOOL APIENTRY 5597 NtUserDeleteMenu( 5598 HMENU hMenu, 5599 UINT uPosition, 5600 UINT uFlags) 5601 { 5602 PMENU Menu; 5603 DECLARE_RETURN(BOOL); 5604 5605 TRACE("Enter NtUserDeleteMenu\n"); 5606 UserEnterExclusive(); 5607 5608 if(!(Menu = UserGetMenuObject(hMenu))) 5609 { 5610 RETURN( FALSE); 5611 } 5612 5613 RETURN( IntRemoveMenuItem(Menu, uPosition, uFlags, TRUE)); 5614 5615 CLEANUP: 5616 TRACE("Leave NtUserDeleteMenu, ret=%i\n",_ret_); 5617 UserLeave(); 5618 END_CLEANUP; 5619 } 5620 5621 /* 5622 * NtUserGetSystemMenu 5623 * 5624 * The NtUserGetSystemMenu function allows the application to access the 5625 * window menu (also known as the system menu or the control menu) for 5626 * copying and modifying. 5627 * 5628 * Parameters 5629 * hWnd 5630 * Handle to the window that will own a copy of the window menu. 5631 * bRevert 5632 * Specifies the action to be taken. If this parameter is FALSE, 5633 * NtUserGetSystemMenu returns a handle to the copy of the window menu 5634 * currently in use. The copy is initially identical to the window menu 5635 * but it can be modified. 5636 * If this parameter is TRUE, GetSystemMenu resets the window menu back 5637 * to the default state. The previous window menu, if any, is destroyed. 5638 * 5639 * Return Value 5640 * If the bRevert parameter is FALSE, the return value is a handle to a 5641 * copy of the window menu. If the bRevert parameter is TRUE, the return 5642 * value is NULL. 5643 * 5644 * Status 5645 * @implemented 5646 */ 5647 5648 HMENU APIENTRY 5649 NtUserGetSystemMenu(HWND hWnd, BOOL bRevert) 5650 { 5651 PWND Window; 5652 PMENU Menu; 5653 DECLARE_RETURN(HMENU); 5654 5655 TRACE("Enter NtUserGetSystemMenu\n"); 5656 UserEnterExclusive(); 5657 5658 if (!(Window = UserGetWindowObject(hWnd))) 5659 { 5660 RETURN(NULL); 5661 } 5662 5663 if (!(Menu = IntGetSystemMenu(Window, bRevert))) 5664 { 5665 RETURN(NULL); 5666 } 5667 5668 RETURN(Menu->head.h); 5669 5670 CLEANUP: 5671 TRACE("Leave NtUserGetSystemMenu, ret=%p\n", _ret_); 5672 UserLeave(); 5673 END_CLEANUP; 5674 } 5675 5676 /* 5677 * NtUserSetSystemMenu 5678 * 5679 * Status 5680 * @implemented 5681 */ 5682 5683 BOOL APIENTRY 5684 NtUserSetSystemMenu(HWND hWnd, HMENU hMenu) 5685 { 5686 BOOL Result = FALSE; 5687 PWND Window; 5688 PMENU Menu; 5689 DECLARE_RETURN(BOOL); 5690 5691 TRACE("Enter NtUserSetSystemMenu\n"); 5692 UserEnterExclusive(); 5693 5694 if (!(Window = UserGetWindowObject(hWnd))) 5695 { 5696 RETURN( FALSE); 5697 } 5698 5699 if (hMenu) 5700 { 5701 /* 5702 * Assign new menu handle and Up the Lock Count. 5703 */ 5704 if (!(Menu = IntGetMenuObject(hMenu))) 5705 { 5706 RETURN( FALSE); 5707 } 5708 5709 Result = IntSetSystemMenu(Window, Menu); 5710 } 5711 else 5712 EngSetLastError(ERROR_INVALID_MENU_HANDLE); 5713 5714 RETURN( Result); 5715 5716 CLEANUP: 5717 TRACE("Leave NtUserSetSystemMenu, ret=%i\n",_ret_); 5718 UserLeave(); 5719 END_CLEANUP; 5720 } 5721 5722 /* 5723 * @implemented 5724 */ 5725 BOOLEAN APIENTRY 5726 NtUserGetTitleBarInfo( 5727 HWND hwnd, 5728 PTITLEBARINFO bti) 5729 { 5730 PWND WindowObject; 5731 TITLEBARINFO bartitleinfo; 5732 DECLARE_RETURN(BOOLEAN); 5733 BOOLEAN retValue = TRUE; 5734 5735 TRACE("Enter NtUserGetTitleBarInfo\n"); 5736 UserEnterExclusive(); 5737 5738 /* Vaildate the windows handle */ 5739 if (!(WindowObject = UserGetWindowObject(hwnd))) 5740 { 5741 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE); 5742 retValue = FALSE; 5743 } 5744 5745 _SEH2_TRY 5746 { 5747 /* Copy our usermode buffer bti to local buffer bartitleinfo */ 5748 ProbeForRead(bti, sizeof(TITLEBARINFO), 1); 5749 RtlCopyMemory(&bartitleinfo, bti, sizeof(TITLEBARINFO)); 5750 } 5751 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5752 { 5753 /* Fail copy the data */ 5754 EngSetLastError(ERROR_INVALID_PARAMETER); 5755 retValue = FALSE; 5756 } 5757 _SEH2_END 5758 5759 /* Get the tile bar info */ 5760 if (retValue) 5761 { 5762 retValue = intGetTitleBarInfo(WindowObject, &bartitleinfo); 5763 if (retValue) 5764 { 5765 _SEH2_TRY 5766 { 5767 /* Copy our buffer to user mode buffer bti */ 5768 ProbeForWrite(bti, sizeof(TITLEBARINFO), 1); 5769 RtlCopyMemory(bti, &bartitleinfo, sizeof(TITLEBARINFO)); 5770 } 5771 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5772 { 5773 /* Fail copy the data */ 5774 EngSetLastError(ERROR_INVALID_PARAMETER); 5775 retValue = FALSE; 5776 } 5777 _SEH2_END 5778 } 5779 } 5780 5781 RETURN( retValue ); 5782 5783 CLEANUP: 5784 TRACE("Leave NtUserGetTitleBarInfo, ret=%u\n",_ret_); 5785 UserLeave(); 5786 END_CLEANUP; 5787 } 5788 5789 /* 5790 * @implemented 5791 */ 5792 BOOL FASTCALL UserDestroyMenu(HMENU hMenu) 5793 { 5794 PMENU Menu; 5795 PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); 5796 5797 if(!(Menu = UserGetMenuObject(hMenu))) 5798 { 5799 return FALSE; 5800 } 5801 5802 if (Menu->head.rpdesk != pti->rpdesk) 5803 { 5804 EngSetLastError(ERROR_ACCESS_DENIED); 5805 return FALSE; 5806 } 5807 return IntDestroyMenuObject(Menu, FALSE); 5808 } 5809 5810 /* 5811 * @implemented 5812 */ 5813 BOOL APIENTRY 5814 NtUserDestroyMenu( 5815 HMENU hMenu) 5816 { 5817 PMENU Menu; 5818 DECLARE_RETURN(BOOL); 5819 5820 TRACE("Enter NtUserDestroyMenu\n"); 5821 UserEnterExclusive(); 5822 5823 if(!(Menu = UserGetMenuObject(hMenu))) 5824 { 5825 RETURN( FALSE); 5826 } 5827 if (Menu->head.rpdesk != gptiCurrent->rpdesk) 5828 { 5829 EngSetLastError(ERROR_ACCESS_DENIED); 5830 RETURN( FALSE); 5831 } 5832 RETURN( IntDestroyMenuObject(Menu, TRUE)); 5833 5834 CLEANUP: 5835 TRACE("Leave NtUserDestroyMenu, ret=%i\n",_ret_); 5836 UserLeave(); 5837 END_CLEANUP; 5838 } 5839 5840 /* 5841 * @implemented 5842 */ 5843 UINT APIENTRY 5844 NtUserEnableMenuItem( 5845 HMENU hMenu, 5846 UINT uIDEnableItem, 5847 UINT uEnable) 5848 { 5849 PMENU Menu; 5850 DECLARE_RETURN(UINT); 5851 5852 TRACE("Enter NtUserEnableMenuItem\n"); 5853 UserEnterExclusive(); 5854 5855 if(!(Menu = UserGetMenuObject(hMenu))) 5856 { 5857 RETURN(-1); 5858 } 5859 5860 RETURN( IntEnableMenuItem(Menu, uIDEnableItem, uEnable)); 5861 5862 CLEANUP: 5863 TRACE("Leave NtUserEnableMenuItem, ret=%u\n",_ret_); 5864 UserLeave(); 5865 END_CLEANUP; 5866 } 5867 5868 /* 5869 * @implemented 5870 */ 5871 BOOL APIENTRY 5872 NtUserEndMenu(VOID) 5873 { 5874 //PWND pWnd; 5875 TRACE("Enter NtUserEndMenu\n"); 5876 UserEnterExclusive(); 5877 /* if ( gptiCurrent->pMenuState && 5878 gptiCurrent->pMenuState->pGlobalPopupMenu ) 5879 { 5880 pWnd = IntGetMSWND(gptiCurrent->pMenuState); 5881 if (pWnd) 5882 { 5883 UserPostMessage( UserHMGetHandle(pWnd), WM_CANCELMODE, 0, 0); 5884 } 5885 else 5886 gptiCurrent->pMenuState->fInsideMenuLoop = FALSE; 5887 }*/ 5888 if (fInsideMenuLoop && top_popup) 5889 { 5890 fInsideMenuLoop = FALSE; 5891 UserPostMessage( top_popup, WM_CANCELMODE, 0, 0); 5892 } 5893 UserLeave(); 5894 TRACE("Leave NtUserEndMenu\n"); 5895 return TRUE; 5896 } 5897 5898 /* 5899 * @implemented 5900 */ 5901 BOOL APIENTRY 5902 NtUserGetMenuBarInfo( 5903 HWND hwnd, 5904 LONG idObject, 5905 LONG idItem, 5906 PMENUBARINFO pmbi) 5907 { 5908 PWND pWnd; 5909 HMENU hMenu; 5910 MENUBARINFO kmbi; 5911 BOOL Ret; 5912 PPOPUPMENU pPopupMenu; 5913 USER_REFERENCE_ENTRY Ref; 5914 NTSTATUS Status = STATUS_SUCCESS; 5915 PMENU Menu = NULL; 5916 DECLARE_RETURN(BOOL); 5917 5918 TRACE("Enter NtUserGetMenuBarInfo\n"); 5919 UserEnterShared(); 5920 5921 if (!(pWnd = UserGetWindowObject(hwnd))) 5922 { 5923 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE); 5924 RETURN(FALSE); 5925 } 5926 5927 UserRefObjectCo(pWnd, &Ref); 5928 5929 RECTL_vSetEmptyRect(&kmbi.rcBar); 5930 kmbi.hMenu = NULL; 5931 kmbi.hwndMenu = NULL; 5932 kmbi.fBarFocused = FALSE; 5933 kmbi.fFocused = FALSE; 5934 5935 switch (idObject) 5936 { 5937 case OBJID_CLIENT: 5938 if (!pWnd->pcls->fnid) 5939 RETURN(FALSE); 5940 if (pWnd->pcls->fnid != FNID_MENU) 5941 { 5942 WARN("called on invalid window: %u\n", pWnd->pcls->fnid); 5943 EngSetLastError(ERROR_INVALID_MENU_HANDLE); 5944 RETURN(FALSE); 5945 } 5946 // Windows does this! Wine checks for Atom and uses GetWindowLongPtrW. 5947 hMenu = (HMENU)co_IntSendMessage(hwnd, MN_GETHMENU, 0, 0); 5948 pPopupMenu = ((PMENUWND)pWnd)->ppopupmenu; 5949 if (pPopupMenu && pPopupMenu->spmenu) 5950 { 5951 if (UserHMGetHandle(pPopupMenu->spmenu) != hMenu) 5952 { 5953 ERR("Window Pop Up hMenu %p not the same as Get hMenu %p!\n",pPopupMenu->spmenu->head.h,hMenu); 5954 } 5955 } 5956 break; 5957 case OBJID_MENU: 5958 if (pWnd->style & WS_CHILD) RETURN(FALSE); 5959 hMenu = UlongToHandle(pWnd->IDMenu); 5960 TRACE("GMBI: OBJID_MENU hMenu %p\n",hMenu); 5961 break; 5962 case OBJID_SYSMENU: 5963 if (!(pWnd->style & WS_SYSMENU)) RETURN(FALSE); 5964 Menu = IntGetSystemMenu(pWnd, FALSE); 5965 hMenu = UserHMGetHandle(Menu); 5966 break; 5967 default: 5968 RETURN(FALSE); 5969 } 5970 5971 if (!hMenu) 5972 RETURN(FALSE); 5973 5974 _SEH2_TRY 5975 { 5976 ProbeForRead(pmbi, sizeof(MENUBARINFO), 1); 5977 kmbi.cbSize = pmbi->cbSize; 5978 } 5979 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5980 { 5981 kmbi.cbSize = 0; 5982 } 5983 _SEH2_END 5984 5985 if (kmbi.cbSize != sizeof(MENUBARINFO)) 5986 { 5987 EngSetLastError(ERROR_INVALID_PARAMETER); 5988 RETURN(FALSE); 5989 } 5990 5991 if (!Menu) Menu = UserGetMenuObject(hMenu); 5992 if (!Menu) 5993 RETURN(FALSE); 5994 5995 if ((idItem < 0) || ((ULONG)idItem > Menu->cItems)) 5996 RETURN(FALSE); 5997 5998 if (idItem == 0) 5999 { 6000 Ret = IntGetMenuItemRect(pWnd, Menu, 0, &kmbi.rcBar); 6001 kmbi.rcBar.right = kmbi.rcBar.left + Menu->cxMenu; 6002 kmbi.rcBar.bottom = kmbi.rcBar.top + Menu->cyMenu; 6003 TRACE("idItem a 0 %d\n",Ret); 6004 } 6005 else 6006 { 6007 Ret = IntGetMenuItemRect(pWnd, Menu, idItem-1, &kmbi.rcBar); 6008 TRACE("idItem b %d %d\n", idItem-1, Ret); 6009 } 6010 6011 kmbi.hMenu = hMenu; 6012 kmbi.fBarFocused = top_popup_hmenu == hMenu; 6013 TRACE("GMBI: top p hm %p hMenu %p\n",top_popup_hmenu, hMenu); 6014 if (idItem) 6015 { 6016 kmbi.fFocused = Menu->iItem == idItem-1; 6017 if (kmbi.fFocused && (Menu->rgItems[idItem - 1].spSubMenu)) 6018 { 6019 kmbi.hwndMenu = Menu->rgItems[idItem - 1].spSubMenu->hWnd; 6020 } 6021 } 6022 else 6023 { 6024 kmbi.fFocused = kmbi.fBarFocused; 6025 } 6026 6027 _SEH2_TRY 6028 { 6029 ProbeForWrite(pmbi, sizeof(MENUBARINFO), 1); 6030 RtlCopyMemory(pmbi, &kmbi, sizeof(MENUBARINFO)); 6031 } 6032 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 6033 { 6034 Status = _SEH2_GetExceptionCode(); 6035 } 6036 _SEH2_END 6037 6038 if (!NT_SUCCESS(Status)) 6039 { 6040 SetLastNtError(Status); 6041 RETURN(FALSE); 6042 } 6043 6044 RETURN(TRUE); 6045 6046 CLEANUP: 6047 if (pWnd) UserDerefObjectCo(pWnd); 6048 TRACE("Leave NtUserGetMenuBarInfo, ret=%i\n",_ret_); 6049 UserLeave(); 6050 END_CLEANUP; 6051 } 6052 6053 /* 6054 * @implemented 6055 */ 6056 UINT APIENTRY 6057 NtUserGetMenuIndex( 6058 HMENU hMenu, 6059 HMENU hSubMenu) 6060 { 6061 PMENU Menu, SubMenu; 6062 PITEM MenuItem; 6063 UINT i; 6064 DECLARE_RETURN(UINT); 6065 6066 TRACE("Enter NtUserGetMenuIndex\n"); 6067 UserEnterShared(); 6068 6069 if ( !(Menu = UserGetMenuObject(hMenu)) || 6070 !(SubMenu = UserGetMenuObject(hSubMenu)) ) 6071 RETURN(0xFFFFFFFF); 6072 6073 MenuItem = Menu->rgItems; 6074 for (i = 0; i < Menu->cItems; i++, MenuItem++) 6075 { 6076 if (MenuItem->spSubMenu == SubMenu) 6077 RETURN(MenuItem->wID); 6078 } 6079 RETURN(0xFFFFFFFF); 6080 6081 CLEANUP: 6082 TRACE("Leave NtUserGetMenuIndex, ret=%u\n",_ret_); 6083 UserLeave(); 6084 END_CLEANUP; 6085 } 6086 6087 /* 6088 * @implemented 6089 */ 6090 BOOL APIENTRY 6091 NtUserGetMenuItemRect( 6092 HWND hWnd, 6093 HMENU hMenu, 6094 UINT uItem, 6095 PRECTL lprcItem) 6096 { 6097 PWND ReferenceWnd; 6098 LONG XMove, YMove; 6099 RECTL Rect; 6100 PMENU Menu; 6101 PITEM MenuItem; 6102 NTSTATUS Status = STATUS_SUCCESS; 6103 DECLARE_RETURN(BOOL); 6104 6105 TRACE("Enter NtUserGetMenuItemRect\n"); 6106 UserEnterShared(); 6107 6108 if (!(Menu = UserGetMenuObject(hMenu))) 6109 { 6110 RETURN(FALSE); 6111 } 6112 6113 if ((MenuItem = MENU_FindItem (&Menu, &uItem, MF_BYPOSITION))) 6114 { 6115 Rect.left = MenuItem->xItem; 6116 Rect.top = MenuItem->yItem; 6117 Rect.right = MenuItem->cxItem; // Do this for now...... 6118 Rect.bottom = MenuItem->cyItem; 6119 } 6120 else 6121 RETURN(FALSE); 6122 6123 if(!hWnd) 6124 { 6125 hWnd = Menu->hWnd; 6126 } 6127 6128 if (lprcItem == NULL) RETURN( FALSE); 6129 6130 if (!(ReferenceWnd = UserGetWindowObject(hWnd))) RETURN( FALSE); 6131 6132 if (Menu->fFlags & MNF_POPUP) 6133 { 6134 XMove = ReferenceWnd->rcClient.left; 6135 YMove = ReferenceWnd->rcClient.top; 6136 } 6137 else 6138 { 6139 XMove = ReferenceWnd->rcWindow.left; 6140 YMove = ReferenceWnd->rcWindow.top; 6141 } 6142 6143 Rect.left += XMove; 6144 Rect.top += YMove; 6145 Rect.right += XMove; 6146 Rect.bottom += YMove; 6147 6148 _SEH2_TRY 6149 { 6150 RtlCopyMemory(lprcItem, &Rect, sizeof(RECTL)); 6151 } 6152 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 6153 { 6154 Status = _SEH2_GetExceptionCode(); 6155 } 6156 _SEH2_END 6157 6158 if (!NT_SUCCESS(Status)) 6159 { 6160 SetLastNtError(Status); 6161 RETURN(FALSE); 6162 } 6163 RETURN(TRUE); 6164 6165 CLEANUP: 6166 TRACE("Leave NtUserGetMenuItemRect, ret=%i\n",_ret_); 6167 UserLeave(); 6168 END_CLEANUP; 6169 } 6170 6171 /* 6172 * @implemented 6173 */ 6174 BOOL APIENTRY 6175 NtUserHiliteMenuItem( 6176 HWND hWnd, 6177 HMENU hMenu, 6178 UINT uItemHilite, 6179 UINT uHilite) 6180 { 6181 PMENU Menu; 6182 PWND Window; 6183 DECLARE_RETURN(BOOLEAN); 6184 6185 TRACE("Enter NtUserHiliteMenuItem\n"); 6186 UserEnterExclusive(); 6187 6188 if(!(Window = UserGetWindowObject(hWnd))) 6189 { 6190 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE); 6191 RETURN(FALSE); 6192 } 6193 6194 if(!(Menu = UserGetMenuObject(hMenu))) 6195 { 6196 EngSetLastError(ERROR_INVALID_MENU_HANDLE); 6197 RETURN(FALSE); 6198 } 6199 6200 RETURN( IntHiliteMenuItem(Window, Menu, uItemHilite, uHilite)); 6201 6202 CLEANUP: 6203 TRACE("Leave NtUserHiliteMenuItem, ret=%u\n",_ret_); 6204 UserLeave(); 6205 END_CLEANUP; 6206 } 6207 6208 /* 6209 * @implemented 6210 */ 6211 DWORD 6212 APIENTRY 6213 NtUserDrawMenuBarTemp( 6214 HWND hWnd, 6215 HDC hDC, 6216 PRECT pRect, 6217 HMENU hMenu, 6218 HFONT hFont) 6219 { 6220 PMENU Menu; 6221 PWND Window; 6222 RECT Rect; 6223 NTSTATUS Status = STATUS_SUCCESS; 6224 DECLARE_RETURN(DWORD); 6225 6226 ERR("Enter NtUserDrawMenuBarTemp\n"); 6227 UserEnterExclusive(); 6228 6229 if(!(Window = UserGetWindowObject(hWnd))) 6230 { 6231 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE); 6232 RETURN(0); 6233 } 6234 6235 if(!(Menu = UserGetMenuObject(hMenu))) 6236 { 6237 EngSetLastError(ERROR_INVALID_MENU_HANDLE); 6238 RETURN(0); 6239 } 6240 6241 _SEH2_TRY 6242 { 6243 ProbeForRead(pRect, sizeof(RECT), sizeof(ULONG)); 6244 RtlCopyMemory(&Rect, pRect, sizeof(RECT)); 6245 } 6246 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 6247 { 6248 Status = _SEH2_GetExceptionCode(); 6249 } 6250 _SEH2_END; 6251 6252 if (Status != STATUS_SUCCESS) 6253 { 6254 SetLastNtError(Status); 6255 RETURN(0); 6256 } 6257 6258 RETURN( IntDrawMenuBarTemp(Window, hDC, &Rect, Menu, hFont)); 6259 6260 CLEANUP: 6261 ERR("Leave NtUserDrawMenuBarTemp, ret=%u\n",_ret_); 6262 UserLeave(); 6263 END_CLEANUP; 6264 } 6265 6266 /* 6267 * @implemented 6268 */ 6269 int APIENTRY 6270 NtUserMenuItemFromPoint( 6271 HWND hWnd, 6272 HMENU hMenu, 6273 DWORD X, 6274 DWORD Y) 6275 { 6276 PMENU Menu; 6277 PWND Window = NULL; 6278 PITEM mi; 6279 ULONG i; 6280 DECLARE_RETURN(int); 6281 6282 TRACE("Enter NtUserMenuItemFromPoint\n"); 6283 UserEnterExclusive(); 6284 6285 if (!(Menu = UserGetMenuObject(hMenu))) 6286 { 6287 RETURN( -1); 6288 } 6289 6290 if (!(Window = UserGetWindowObject(Menu->hWnd))) 6291 { 6292 RETURN( -1); 6293 } 6294 6295 X -= Window->rcWindow.left; 6296 Y -= Window->rcWindow.top; 6297 6298 mi = Menu->rgItems; 6299 for (i = 0; i < Menu->cItems; i++, mi++) 6300 { 6301 RECTL Rect; 6302 6303 Rect.left = mi->xItem; 6304 Rect.top = mi->yItem; 6305 Rect.right = mi->cxItem; 6306 Rect.bottom = mi->cyItem; 6307 6308 MENU_AdjustMenuItemRect(Menu, &Rect); 6309 6310 if (RECTL_bPointInRect(&Rect, X, Y)) 6311 { 6312 break; 6313 } 6314 } 6315 6316 RETURN( (mi ? i : NO_SELECTED_ITEM)); 6317 6318 CLEANUP: 6319 TRACE("Leave NtUserMenuItemFromPoint, ret=%i\n",_ret_); 6320 UserLeave(); 6321 END_CLEANUP; 6322 } 6323 6324 6325 DWORD 6326 APIENTRY 6327 NtUserPaintMenuBar( 6328 HWND hWnd, 6329 HDC hDC, 6330 ULONG leftBorder, 6331 ULONG rightBorder, 6332 ULONG top, 6333 BOOL bActive) 6334 { 6335 PWND Window; 6336 RECT Rect; 6337 DWORD ret; 6338 6339 UserEnterExclusive(); 6340 6341 if(!(Window = UserGetWindowObject(hWnd))) 6342 { 6343 EngSetLastError(ERROR_INVALID_WINDOW_HANDLE); 6344 UserLeave(); 6345 return 0; 6346 } 6347 6348 Rect.left = leftBorder; 6349 Rect.right = Window->rcWindow.right - Window->rcWindow.left - rightBorder; 6350 Rect.top = top; 6351 Rect.bottom = 0; 6352 6353 ret = MENU_DrawMenuBar(hDC, &Rect, Window, FALSE); 6354 6355 UserLeave(); 6356 6357 return ret; 6358 } 6359 6360 /* 6361 * @implemented 6362 */ 6363 BOOL APIENTRY 6364 NtUserRemoveMenu( 6365 HMENU hMenu, 6366 UINT uPosition, 6367 UINT uFlags) 6368 { 6369 PMENU Menu; 6370 DECLARE_RETURN(BOOL); 6371 6372 TRACE("Enter NtUserRemoveMenu\n"); 6373 UserEnterExclusive(); 6374 6375 if(!(Menu = UserGetMenuObject(hMenu))) 6376 { 6377 RETURN( FALSE); 6378 } 6379 6380 RETURN(IntRemoveMenuItem(Menu, uPosition, uFlags, FALSE)); 6381 6382 CLEANUP: 6383 TRACE("Leave NtUserRemoveMenu, ret=%i\n",_ret_); 6384 UserLeave(); 6385 END_CLEANUP; 6386 6387 } 6388 6389 /* 6390 * @implemented 6391 */ 6392 BOOL APIENTRY 6393 NtUserSetMenu( 6394 HWND hWnd, 6395 HMENU Menu, 6396 BOOL Repaint) 6397 { 6398 PWND Window; 6399 BOOL Changed; 6400 DECLARE_RETURN(BOOL); 6401 6402 TRACE("Enter NtUserSetMenu\n"); 6403 UserEnterExclusive(); 6404 6405 if (!(Window = UserGetWindowObject(hWnd))) 6406 { 6407 RETURN( FALSE); 6408 } 6409 6410 if (!IntSetMenu(Window, Menu, &Changed)) 6411 { 6412 RETURN( FALSE); 6413 } 6414 6415 // Not minimized and please repaint!!! 6416 if (!(Window->style & WS_MINIMIZE) && (Repaint || Changed)) 6417 { 6418 USER_REFERENCE_ENTRY Ref; 6419 UserRefObjectCo(Window, &Ref); 6420 co_WinPosSetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED); 6421 UserDerefObjectCo(Window); 6422 } 6423 6424 RETURN( TRUE); 6425 6426 CLEANUP: 6427 TRACE("Leave NtUserSetMenu, ret=%i\n",_ret_); 6428 UserLeave(); 6429 END_CLEANUP; 6430 } 6431 6432 /* 6433 * @implemented 6434 */ 6435 BOOL APIENTRY 6436 NtUserSetMenuContextHelpId( 6437 HMENU hMenu, 6438 DWORD dwContextHelpId) 6439 { 6440 PMENU Menu; 6441 DECLARE_RETURN(BOOL); 6442 6443 TRACE("Enter NtUserSetMenuContextHelpId\n"); 6444 UserEnterExclusive(); 6445 6446 if(!(Menu = UserGetMenuObject(hMenu))) 6447 { 6448 RETURN( FALSE); 6449 } 6450 6451 RETURN(IntSetMenuContextHelpId(Menu, dwContextHelpId)); 6452 6453 CLEANUP: 6454 TRACE("Leave NtUserSetMenuContextHelpId, ret=%i\n",_ret_); 6455 UserLeave(); 6456 END_CLEANUP; 6457 } 6458 6459 /* 6460 * @implemented 6461 */ 6462 BOOL APIENTRY 6463 NtUserSetMenuDefaultItem( 6464 HMENU hMenu, 6465 UINT uItem, 6466 UINT fByPos) 6467 { 6468 PMENU Menu; 6469 DECLARE_RETURN(BOOL); 6470 6471 TRACE("Enter NtUserSetMenuDefaultItem\n"); 6472 UserEnterExclusive(); 6473 6474 if(!(Menu = UserGetMenuObject(hMenu))) 6475 { 6476 RETURN( FALSE); 6477 } 6478 6479 RETURN( UserSetMenuDefaultItem(Menu, uItem, fByPos)); 6480 6481 CLEANUP: 6482 TRACE("Leave NtUserSetMenuDefaultItem, ret=%i\n",_ret_); 6483 UserLeave(); 6484 END_CLEANUP; 6485 } 6486 6487 /* 6488 * @implemented 6489 */ 6490 BOOL APIENTRY 6491 NtUserSetMenuFlagRtoL( 6492 HMENU hMenu) 6493 { 6494 PMENU Menu; 6495 DECLARE_RETURN(BOOL); 6496 6497 TRACE("Enter NtUserSetMenuFlagRtoL\n"); 6498 UserEnterExclusive(); 6499 6500 if(!(Menu = UserGetMenuObject(hMenu))) 6501 { 6502 RETURN( FALSE); 6503 } 6504 6505 RETURN(IntSetMenuFlagRtoL(Menu)); 6506 6507 CLEANUP: 6508 TRACE("Leave NtUserSetMenuFlagRtoL, ret=%i\n",_ret_); 6509 UserLeave(); 6510 END_CLEANUP; 6511 } 6512 6513 /* 6514 * @implemented 6515 */ 6516 BOOL APIENTRY 6517 NtUserThunkedMenuInfo( 6518 HMENU hMenu, 6519 LPCMENUINFO lpcmi) 6520 { 6521 PMENU Menu; 6522 DECLARE_RETURN(BOOL); 6523 6524 TRACE("Enter NtUserThunkedMenuInfo\n"); 6525 UserEnterExclusive(); 6526 6527 if (!(Menu = UserGetMenuObject(hMenu))) 6528 { 6529 RETURN(FALSE); 6530 } 6531 6532 RETURN(UserMenuInfo(Menu, (PROSMENUINFO)lpcmi, TRUE)); 6533 6534 CLEANUP: 6535 TRACE("Leave NtUserThunkedMenuInfo, ret=%i\n",_ret_); 6536 UserLeave(); 6537 END_CLEANUP; 6538 } 6539 6540 /* 6541 * @implemented 6542 */ 6543 BOOL APIENTRY 6544 NtUserThunkedMenuItemInfo( 6545 HMENU hMenu, 6546 UINT uItem, 6547 BOOL fByPosition, 6548 BOOL bInsert, 6549 LPMENUITEMINFOW lpmii, 6550 PUNICODE_STRING lpszCaption) 6551 { 6552 PMENU Menu; 6553 NTSTATUS Status; 6554 UNICODE_STRING lstrCaption; 6555 DECLARE_RETURN(BOOL); 6556 6557 TRACE("Enter NtUserThunkedMenuItemInfo\n"); 6558 UserEnterExclusive(); 6559 6560 /* lpszCaption may be NULL, check for it and call RtlInitUnicodeString() 6561 if bInsert == TRUE call UserInsertMenuItem() else UserSetMenuItemInfo() */ 6562 6563 RtlInitEmptyUnicodeString(&lstrCaption, NULL, 0); 6564 6565 if (!(Menu = UserGetMenuObject(hMenu))) 6566 { 6567 RETURN(FALSE); 6568 } 6569 6570 /* Check if we got a Caption */ 6571 if (lpszCaption && lpszCaption->Buffer) 6572 { 6573 /* Copy the string to kernel mode */ 6574 Status = ProbeAndCaptureUnicodeString( &lstrCaption, 6575 UserMode, 6576 lpszCaption); 6577 if (!NT_SUCCESS(Status)) 6578 { 6579 ERR("Failed to capture MenuItem Caption (status 0x%08x)\n",Status); 6580 SetLastNtError(Status); 6581 RETURN(FALSE); 6582 } 6583 } 6584 6585 if (bInsert) RETURN( UserInsertMenuItem(Menu, uItem, fByPosition, lpmii, &lstrCaption)); 6586 6587 RETURN( UserMenuItemInfo(Menu, uItem, fByPosition, (PROSMENUITEMINFO)lpmii, TRUE, &lstrCaption)); 6588 6589 CLEANUP: 6590 if (lstrCaption.Buffer) 6591 { 6592 ReleaseCapturedUnicodeString(&lstrCaption, UserMode); 6593 } 6594 6595 TRACE("Leave NtUserThunkedMenuItemInfo, ret=%i\n",_ret_); 6596 UserLeave(); 6597 END_CLEANUP; 6598 } 6599 6600 /* 6601 * @implemented 6602 */ 6603 BOOL APIENTRY 6604 NtUserTrackPopupMenuEx( 6605 HMENU hMenu, 6606 UINT fuFlags, 6607 int x, 6608 int y, 6609 HWND hWnd, 6610 LPTPMPARAMS lptpm) 6611 { 6612 PMENU menu; 6613 PWND pWnd; 6614 TPMPARAMS tpm; 6615 BOOL Ret = FALSE; 6616 USER_REFERENCE_ENTRY Ref; 6617 6618 TRACE("Enter NtUserTrackPopupMenuEx\n"); 6619 UserEnterExclusive(); 6620 /* Parameter check */ 6621 if (!(menu = UserGetMenuObject( hMenu ))) 6622 { 6623 ERR("TPME : Invalid Menu handle.\n"); 6624 EngSetLastError( ERROR_INVALID_MENU_HANDLE ); 6625 goto Exit; 6626 } 6627 6628 if (!(pWnd = UserGetWindowObject(hWnd))) 6629 { 6630 ERR("TPME : Invalid Window handle.\n"); 6631 goto Exit; 6632 } 6633 6634 if (lptpm) 6635 { 6636 _SEH2_TRY 6637 { 6638 ProbeForRead(lptpm, sizeof(TPMPARAMS), sizeof(ULONG)); 6639 tpm = *lptpm; 6640 } 6641 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 6642 { 6643 _SEH2_YIELD(goto Exit); 6644 } 6645 _SEH2_END 6646 } 6647 UserRefObjectCo(pWnd, &Ref); 6648 Ret = IntTrackPopupMenuEx(menu, fuFlags, x, y, pWnd, lptpm ? &tpm : NULL); 6649 UserDerefObjectCo(pWnd); 6650 6651 Exit: 6652 TRACE("Leave NtUserTrackPopupMenuEx, ret=%i\n",Ret); 6653 UserLeave(); 6654 return Ret; 6655 } 6656 6657 /* EOF */ 6658