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