1 /* 2 * Shell Menu Band 3 * 4 * Copyright 2014 David Quintana 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 #include "shellmenu.h" 21 #include <windowsx.h> 22 #include <commoncontrols.h> 23 #include <shlwapi_undoc.h> 24 25 #include "CMenuBand.h" 26 #include "CMenuToolbars.h" 27 #include "CMenuFocusManager.h" 28 29 WINE_DEFAULT_DEBUG_CHANNEL(CMenuBand); 30 31 #undef UNIMPLEMENTED 32 33 #define UNIMPLEMENTED TRACE("%s is UNIMPLEMENTED!\n", __FUNCTION__) 34 35 extern "C" 36 HRESULT WINAPI CMenuBand_Constructor(REFIID riid, LPVOID *ppv) 37 { 38 return ShellObjectCreator<CMenuBand>(riid, ppv); 39 } 40 41 CMenuBand::CMenuBand() : 42 m_staticToolbar(NULL), 43 m_SFToolbar(NULL), 44 m_site(NULL), 45 m_psmc(NULL), 46 m_subMenuChild(NULL), 47 m_subMenuParent(NULL), 48 m_childBand(NULL), 49 m_parentBand(NULL), 50 m_hmenu(NULL), 51 m_menuOwner(NULL), 52 m_useBigIcons(FALSE), 53 m_topLevelWindow(NULL), 54 m_hotBar(NULL), 55 m_hotItem(-1), 56 m_popupBar(NULL), 57 m_popupItem(-1), 58 m_Show(FALSE), 59 m_shellBottom(FALSE), 60 m_trackedPopup(NULL), 61 m_trackedHwnd(NULL) 62 { 63 m_focusManager = CMenuFocusManager::AcquireManager(); 64 } 65 66 CMenuBand::~CMenuBand() 67 { 68 CMenuFocusManager::ReleaseManager(m_focusManager); 69 70 if (m_staticToolbar) 71 delete m_staticToolbar; 72 73 if (m_SFToolbar) 74 delete m_SFToolbar; 75 76 if (m_hmenu) 77 DestroyMenu(m_hmenu); 78 } 79 80 HRESULT STDMETHODCALLTYPE CMenuBand::Initialize( 81 IShellMenuCallback *psmc, 82 UINT uId, 83 UINT uIdAncestor, 84 DWORD dwFlags) 85 { 86 if (m_psmc != psmc) 87 m_psmc = psmc; 88 m_uId = uId; 89 m_uIdAncestor = uIdAncestor; 90 m_dwFlags = dwFlags; 91 92 if (m_psmc) 93 { 94 _CallCB(SMC_CREATE, 0, reinterpret_cast<LPARAM>(&m_UserData)); 95 } 96 97 return S_OK; 98 } 99 100 HRESULT STDMETHODCALLTYPE CMenuBand::GetMenuInfo( 101 IShellMenuCallback **ppsmc, 102 UINT *puId, 103 UINT *puIdAncestor, 104 DWORD *pdwFlags) 105 { 106 if (!pdwFlags) // maybe? 107 return E_INVALIDARG; 108 109 if (ppsmc) 110 { 111 if (m_psmc) 112 m_psmc->AddRef(); 113 *ppsmc = m_psmc; 114 } 115 116 if (puId) 117 *puId = m_uId; 118 119 if (puIdAncestor) 120 *puIdAncestor = m_uIdAncestor; 121 122 *pdwFlags = m_dwFlags; 123 124 return S_OK; 125 } 126 127 HRESULT STDMETHODCALLTYPE CMenuBand::SetMenu( 128 HMENU hmenu, 129 HWND hwnd, 130 DWORD dwFlags) 131 { 132 HRESULT hr; 133 134 TRACE("CMenuBand::SetMenu called, hmenu=%p; hwnd=%p, flags=%x\n", hmenu, hwnd, dwFlags); 135 136 BOOL created = FALSE; 137 138 if (m_hmenu && m_hmenu != hmenu) 139 { 140 DestroyMenu(m_hmenu); 141 m_hmenu = NULL; 142 } 143 144 m_hmenu = hmenu; 145 m_menuOwner = hwnd; 146 147 if (m_hmenu && m_staticToolbar == NULL) 148 { 149 m_staticToolbar = new CMenuStaticToolbar(this); 150 created = true; 151 } 152 153 if (m_staticToolbar) 154 { 155 hr = m_staticToolbar->SetMenu(hmenu, hwnd, dwFlags); 156 if (FAILED_UNEXPECTEDLY(hr)) 157 return hr; 158 } 159 160 if (m_site) 161 { 162 HWND hwndParent; 163 164 hr = m_site->GetWindow(&hwndParent); 165 if (FAILED_UNEXPECTEDLY(hr)) 166 return hr; 167 168 if (created) 169 { 170 hr = m_staticToolbar->CreateToolbar(hwndParent, m_dwFlags); 171 if (FAILED_UNEXPECTEDLY(hr)) 172 return hr; 173 174 hr = m_staticToolbar->FillToolbar(); 175 } 176 else 177 { 178 hr = m_staticToolbar->FillToolbar(TRUE); 179 } 180 } 181 182 return hr; 183 } 184 185 HRESULT STDMETHODCALLTYPE CMenuBand::GetMenu( 186 HMENU *phmenu, 187 HWND *phwnd, 188 DWORD *pdwFlags) 189 { 190 if (m_staticToolbar == NULL) 191 return E_FAIL; 192 193 return m_staticToolbar->GetMenu(phmenu, phwnd, pdwFlags); 194 } 195 196 HRESULT STDMETHODCALLTYPE CMenuBand::SetSite(IUnknown *pUnkSite) 197 { 198 HWND hwndParent; 199 HRESULT hr; 200 201 m_site = NULL; 202 203 if (pUnkSite == NULL) 204 return S_OK; 205 206 hwndParent = NULL; 207 hr = pUnkSite->QueryInterface(IID_PPV_ARG(IOleWindow, &m_site)); 208 if (FAILED_UNEXPECTEDLY(hr)) 209 return hr; 210 211 hr = m_site->GetWindow(&hwndParent); 212 if (FAILED_UNEXPECTEDLY(hr)) 213 return hr; 214 215 if (!::IsWindow(hwndParent)) 216 return E_FAIL; 217 218 if (m_staticToolbar != NULL) 219 { 220 hr = m_staticToolbar->CreateToolbar(hwndParent, m_dwFlags); 221 if (FAILED_UNEXPECTEDLY(hr)) 222 return hr; 223 224 hr = m_staticToolbar->FillToolbar(); 225 if (FAILED_UNEXPECTEDLY(hr)) 226 return hr; 227 } 228 229 if (m_SFToolbar != NULL) 230 { 231 hr = m_SFToolbar->CreateToolbar(hwndParent, m_dwFlags); 232 if (FAILED_UNEXPECTEDLY(hr)) 233 return hr; 234 235 hr = m_SFToolbar->FillToolbar(); 236 if (FAILED_UNEXPECTEDLY(hr)) 237 return hr; 238 } 239 240 hr = IUnknown_QueryService(m_site, SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &m_subMenuParent)); 241 if (hr != E_NOINTERFACE && FAILED_UNEXPECTEDLY(hr)) 242 return hr; 243 244 CComPtr<IOleWindow> pTopLevelWindow; 245 hr = IUnknown_QueryService(m_site, SID_STopLevelBrowser, IID_PPV_ARG(IOleWindow, &pTopLevelWindow)); 246 if (FAILED_UNEXPECTEDLY(hr)) 247 return hr; 248 249 return pTopLevelWindow->GetWindow(&m_topLevelWindow); 250 } 251 252 HRESULT STDMETHODCALLTYPE CMenuBand::GetSite(REFIID riid, PVOID *ppvSite) 253 { 254 if (m_site == NULL) 255 return E_FAIL; 256 257 return m_site->QueryInterface(riid, ppvSite); 258 } 259 260 HRESULT STDMETHODCALLTYPE CMenuBand::GetWindow(HWND *phwnd) 261 { 262 if (m_SFToolbar != NULL) 263 return m_SFToolbar->GetWindow(phwnd); 264 265 if (m_staticToolbar != NULL) 266 return m_staticToolbar->GetWindow(phwnd); 267 268 if (phwnd) *phwnd = NULL; 269 270 return E_FAIL; 271 } 272 273 HRESULT STDMETHODCALLTYPE CMenuBand::OnPosRectChangeDB(RECT *prc) 274 { 275 SIZE maxStatic = { 0 }; 276 SIZE maxShlFld = { 0 }; 277 HRESULT hr = S_OK; 278 279 if (m_staticToolbar != NULL) 280 hr = m_staticToolbar->GetSizes(NULL, &maxStatic, NULL); 281 if (FAILED_UNEXPECTEDLY(hr)) 282 return hr; 283 284 if (m_SFToolbar != NULL) 285 hr = m_SFToolbar->GetSizes(NULL, &maxShlFld, NULL); 286 if (FAILED_UNEXPECTEDLY(hr)) 287 return hr; 288 289 if (m_staticToolbar == NULL && m_SFToolbar == NULL) 290 return E_FAIL; 291 292 int sy = min(prc->bottom - prc->top, maxStatic.cy + maxShlFld.cy); 293 294 int syStatic = maxStatic.cy; 295 int syShlFld = sy - syStatic; 296 297 // TODO: Windows has a more complex system to decide ordering. 298 // Because we only support two toolbars at once, this is enough for us. 299 if (m_shellBottom) 300 { 301 // Static menu on top 302 if (m_SFToolbar) 303 { 304 m_SFToolbar->SetPosSize( 305 prc->left, 306 prc->top + syStatic, 307 prc->right - prc->left, 308 syShlFld); 309 } 310 if (m_staticToolbar) 311 { 312 m_staticToolbar->SetPosSize( 313 prc->left, 314 prc->top, 315 prc->right - prc->left, 316 syStatic); 317 } 318 } 319 else 320 { 321 // Folder menu on top 322 if (m_SFToolbar) 323 { 324 m_SFToolbar->SetPosSize( 325 prc->left, 326 prc->top, 327 prc->right - prc->left, 328 syShlFld); 329 } 330 if (m_staticToolbar) 331 { 332 m_staticToolbar->SetPosSize( 333 prc->left, 334 prc->top + syShlFld, 335 prc->right - prc->left, 336 syStatic); 337 } 338 } 339 340 return S_OK; 341 } 342 343 HRESULT STDMETHODCALLTYPE CMenuBand::GetBandInfo( 344 DWORD dwBandID, 345 DWORD dwViewMode, 346 DESKBANDINFO *pdbi) 347 { 348 SIZE minStatic = { 0 }; 349 SIZE minShlFld = { 0 }; 350 SIZE maxStatic = { 0 }; 351 SIZE maxShlFld = { 0 }; 352 SIZE intStatic = { 0 }; 353 SIZE intShlFld = { 0 }; 354 355 HRESULT hr = S_OK; 356 357 if (m_staticToolbar != NULL) 358 hr = m_staticToolbar->GetSizes(&minStatic, &maxStatic, &intStatic); 359 if (FAILED_UNEXPECTEDLY(hr)) 360 return hr; 361 362 if (m_SFToolbar != NULL) 363 hr = m_SFToolbar->GetSizes(&minShlFld, &maxShlFld, &intShlFld); 364 if (FAILED_UNEXPECTEDLY(hr)) 365 return hr; 366 367 if (m_staticToolbar == NULL && m_SFToolbar == NULL) 368 return E_FAIL; 369 370 if (m_dwFlags & SMINIT_VERTICAL) 371 { 372 pdbi->ptMinSize.x = max(minStatic.cx, minShlFld.cx) + 20; 373 pdbi->ptMinSize.y = minStatic.cy + minShlFld.cy; 374 pdbi->ptMaxSize.x = max(maxStatic.cx, maxShlFld.cx) + 20; 375 pdbi->ptMaxSize.y = maxStatic.cy + maxShlFld.cy; 376 pdbi->dwModeFlags = DBIMF_VARIABLEHEIGHT; 377 } 378 else 379 { 380 pdbi->ptMinSize.x = minStatic.cx + minShlFld.cx; 381 pdbi->ptMinSize.y = max(minStatic.cy, minShlFld.cy); 382 pdbi->ptMaxSize.x = maxStatic.cx + maxShlFld.cx; 383 pdbi->ptMaxSize.y = max(maxStatic.cy, maxShlFld.cy); 384 } 385 pdbi->ptIntegral.x = max(intStatic.cx, intShlFld.cx); 386 pdbi->ptIntegral.y = max(intStatic.cy, intShlFld.cy); 387 pdbi->ptActual = pdbi->ptMinSize; 388 389 return S_OK; 390 } 391 392 HRESULT STDMETHODCALLTYPE CMenuBand::ShowDW(BOOL fShow) 393 { 394 HRESULT hr = S_OK; 395 396 if (m_Show == fShow) 397 return S_OK; 398 399 m_Show = fShow; 400 401 if (m_staticToolbar != NULL) 402 { 403 hr = m_staticToolbar->ShowDW(fShow); 404 if (FAILED_UNEXPECTEDLY(hr)) 405 return hr; 406 } 407 408 if (m_SFToolbar != NULL) 409 { 410 hr = m_SFToolbar->ShowDW(fShow); 411 if (FAILED_UNEXPECTEDLY(hr)) 412 return hr; 413 } 414 415 if (fShow) 416 { 417 hr = _CallCB(SMC_INITMENU, 0, 0); 418 if (FAILED_UNEXPECTEDLY(hr)) 419 return hr; 420 } 421 else if (m_parentBand) 422 { 423 m_parentBand->SetClient(NULL); 424 } 425 426 if (_IsPopup() == S_OK) 427 { 428 if (fShow) 429 hr = m_focusManager->PushMenuPopup(this); 430 else 431 hr = m_focusManager->PopMenuPopup(this); 432 } 433 else 434 { 435 if (fShow) 436 hr = m_focusManager->PushMenuBar(this); 437 else 438 hr = m_focusManager->PopMenuBar(this); 439 } 440 441 return S_OK; 442 } 443 444 HRESULT STDMETHODCALLTYPE CMenuBand::CloseDW(DWORD dwReserved) 445 { 446 if (m_subMenuChild) 447 { 448 m_subMenuChild->OnSelect(MPOS_CANCELLEVEL); 449 } 450 451 if (m_subMenuChild) 452 { 453 TRACE("Child object should have removed itself.\n"); 454 } 455 456 ShowDW(FALSE); 457 458 if (m_staticToolbar != NULL) 459 { 460 m_staticToolbar->Close(); 461 } 462 463 if (m_SFToolbar != NULL) 464 { 465 m_SFToolbar->Close(); 466 } 467 468 if (m_site) m_site.Release(); 469 if (m_subMenuChild) m_subMenuChild.Release(); 470 if (m_subMenuParent) m_subMenuParent.Release(); 471 if (m_childBand) m_childBand.Release(); 472 if (m_parentBand) m_parentBand.Release(); 473 474 return S_OK; 475 } 476 477 HRESULT STDMETHODCALLTYPE CMenuBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg) 478 { 479 HRESULT hr; 480 481 if (m_subMenuParent) 482 { 483 hr = m_subMenuParent->SetSubMenu(this, fActivate); 484 if (FAILED_UNEXPECTEDLY(hr)) 485 return hr; 486 } 487 488 if (fActivate) 489 { 490 CComPtr<IOleWindow> pTopLevelWindow; 491 hr = IUnknown_QueryService(m_site, SID_SMenuPopup, IID_PPV_ARG(IOleWindow, &pTopLevelWindow)); 492 if (FAILED_UNEXPECTEDLY(hr)) 493 return hr; 494 495 hr = pTopLevelWindow->GetWindow(&m_topLevelWindow); 496 if (FAILED_UNEXPECTEDLY(hr)) 497 return hr; 498 } 499 else 500 { 501 m_topLevelWindow = NULL; 502 } 503 504 return S_FALSE; 505 } 506 507 HRESULT STDMETHODCALLTYPE CMenuBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut) 508 { 509 if (!pguidCmdGroup) 510 return E_FAIL; 511 512 if (IsEqualGUID(*pguidCmdGroup, CLSID_MenuBand)) 513 { 514 if (nCmdID == 16) // set (big) icon size 515 { 516 this->m_useBigIcons = nCmdexecopt == 2; 517 return S_OK; 518 } 519 else if (nCmdID == 19) // popup-related 520 { 521 return S_FALSE; 522 } 523 else if (nCmdID == 5) // select an item 524 { 525 if (nCmdexecopt == 0) // first 526 { 527 _KeyboardItemChange(VK_HOME); 528 } 529 else // last 530 { 531 _KeyboardItemChange(VK_END); 532 } 533 return S_FALSE; 534 } 535 536 return S_FALSE; 537 } 538 539 UNIMPLEMENTED; 540 return S_OK; 541 } 542 543 HRESULT STDMETHODCALLTYPE CMenuBand::QueryService(REFGUID guidService, REFIID riid, void **ppvObject) 544 { 545 if (IsEqualIID(guidService, SID_SMenuBandChild) || 546 IsEqualIID(guidService, SID_SMenuBandBottom) || 547 IsEqualIID(guidService, SID_SMenuBandBottomSelected)) 548 return this->QueryInterface(riid, ppvObject); 549 WARN("Unknown service requested %s\n", wine_dbgstr_guid(&guidService)); 550 return E_NOINTERFACE; 551 } 552 553 HRESULT STDMETHODCALLTYPE CMenuBand::Popup(POINTL *ppt, RECTL *prcExclude, MP_POPUPFLAGS dwFlags) 554 { 555 UNIMPLEMENTED; 556 return S_OK; 557 } 558 559 HRESULT STDMETHODCALLTYPE CMenuBand::OnSelect(DWORD dwSelectType) 560 { 561 // When called from outside, this is straightforward: 562 // Things that a submenu needs to know, are spread down, and 563 // things that the parent needs to know, are spread up. No drama. 564 // The fun is in _MenuItemSelect (internal method). 565 switch (dwSelectType) 566 { 567 case MPOS_CHILDTRACKING: 568 if (!m_subMenuParent) 569 break; 570 // TODO: Cancel timers? 571 return m_subMenuParent->OnSelect(dwSelectType); 572 case MPOS_SELECTLEFT: 573 if (m_subMenuChild) 574 m_subMenuChild->OnSelect(MPOS_CANCELLEVEL); 575 if (!m_subMenuParent) 576 break; 577 return m_subMenuParent->OnSelect(dwSelectType); 578 case MPOS_SELECTRIGHT: 579 if (!m_subMenuParent) 580 break; 581 return m_subMenuParent->OnSelect(dwSelectType); 582 case MPOS_EXECUTE: 583 case MPOS_FULLCANCEL: 584 if (m_subMenuChild) 585 m_subMenuChild->OnSelect(dwSelectType); 586 if (!m_subMenuParent) 587 break; 588 return m_subMenuParent->OnSelect(dwSelectType); 589 case MPOS_CANCELLEVEL: 590 if (m_subMenuChild) 591 m_subMenuChild->OnSelect(dwSelectType); 592 break; 593 } 594 return S_FALSE; 595 } 596 597 HRESULT STDMETHODCALLTYPE CMenuBand::SetSubMenu(IMenuPopup *pmp, BOOL fSet) 598 { 599 UNIMPLEMENTED; 600 return S_OK; 601 } 602 603 // Used by the focus manager to update the child band pointer 604 HRESULT CMenuBand::_SetChildBand(CMenuBand * child) 605 { 606 m_childBand = child; 607 if (!child) 608 { 609 _ChangePopupItem(NULL, -1); 610 } 611 return S_OK; 612 } 613 614 // User by the focus manager to update the parent band pointer 615 HRESULT CMenuBand::_SetParentBand(CMenuBand * parent) 616 { 617 m_parentBand = parent; 618 return S_OK; 619 } 620 621 HRESULT CMenuBand::_IsPopup() 622 { 623 return !(m_dwFlags & SMINIT_VERTICAL); 624 } 625 626 HRESULT CMenuBand::_IsTracking() 627 { 628 return m_popupBar != NULL; 629 } 630 631 HRESULT STDMETHODCALLTYPE CMenuBand::SetClient(IUnknown *punkClient) 632 { 633 CComPtr<IMenuPopup> child = m_subMenuChild; 634 635 m_subMenuChild = NULL; 636 637 if (child) 638 { 639 IUnknown_SetSite(child, NULL); 640 child.Release(); 641 } 642 643 if (!punkClient) 644 { 645 return S_OK; 646 } 647 648 return punkClient->QueryInterface(IID_PPV_ARG(IMenuPopup, &m_subMenuChild)); 649 } 650 651 HRESULT STDMETHODCALLTYPE CMenuBand::GetClient(IUnknown **ppunkClient) 652 { 653 if (!ppunkClient) 654 return E_POINTER; 655 *ppunkClient = NULL; 656 657 if (m_subMenuChild) 658 { 659 m_subMenuChild->AddRef(); 660 *ppunkClient = m_subMenuChild; 661 } 662 663 return S_OK; 664 } 665 666 HRESULT STDMETHODCALLTYPE CMenuBand::IsMenuMessage(MSG *pmsg) 667 { 668 return S_FALSE; 669 } 670 671 HRESULT STDMETHODCALLTYPE CMenuBand::TranslateMenuMessage(MSG *pmsg, LRESULT *plRet) 672 { 673 return S_FALSE; 674 } 675 676 HRESULT STDMETHODCALLTYPE CMenuBand::SetShellFolder(IShellFolder *psf, LPCITEMIDLIST pidlFolder, HKEY hKey, DWORD dwFlags) 677 { 678 if (m_SFToolbar == NULL) 679 { 680 m_SFToolbar = new CMenuSFToolbar(this); 681 } 682 683 HRESULT hr = m_SFToolbar->SetShellFolder(psf, pidlFolder, hKey, dwFlags); 684 if (FAILED_UNEXPECTEDLY(hr)) 685 return hr; 686 687 m_shellBottom = (dwFlags & SMSET_BOTTOM) != 0; 688 689 if (m_site) 690 { 691 HWND hwndParent; 692 693 hr = m_site->GetWindow(&hwndParent); 694 if (FAILED_UNEXPECTEDLY(hr)) 695 return hr; 696 697 hr = m_SFToolbar->CreateToolbar(hwndParent, m_dwFlags); 698 if (FAILED_UNEXPECTEDLY(hr)) 699 return hr; 700 701 hr = m_SFToolbar->FillToolbar(); 702 } 703 704 return hr; 705 } 706 707 HRESULT STDMETHODCALLTYPE CMenuBand::GetShellFolder(DWORD *pdwFlags, LPITEMIDLIST *ppidl, REFIID riid, void **ppv) 708 { 709 if (m_SFToolbar) 710 return m_SFToolbar->GetShellFolder(pdwFlags, ppidl, riid, ppv); 711 return E_FAIL; 712 } 713 714 HRESULT STDMETHODCALLTYPE CMenuBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult) 715 { 716 *theResult = 0; 717 718 if (uMsg == WM_WININICHANGE && wParam == SPI_SETFLATMENU) 719 { 720 BOOL bFlatMenus; 721 SystemParametersInfo(SPI_GETFLATMENU, 0, &bFlatMenus, 0); 722 AdjustForTheme(bFlatMenus); 723 return S_OK; 724 } 725 726 if (m_staticToolbar && m_staticToolbar->IsWindowOwner(hWnd) == S_OK) 727 { 728 return m_staticToolbar->OnWinEvent(hWnd, uMsg, wParam, lParam, theResult); 729 } 730 731 if (m_SFToolbar && m_SFToolbar->IsWindowOwner(hWnd) == S_OK) 732 { 733 return m_SFToolbar->OnWinEvent(hWnd, uMsg, wParam, lParam, theResult); 734 } 735 736 return S_FALSE; 737 } 738 739 HRESULT STDMETHODCALLTYPE CMenuBand::IsWindowOwner(HWND hWnd) 740 { 741 if (m_staticToolbar && m_staticToolbar->IsWindowOwner(hWnd) == S_OK) 742 return S_OK; 743 744 if (m_SFToolbar && m_SFToolbar->IsWindowOwner(hWnd) == S_OK) 745 return S_OK; 746 747 return S_FALSE; 748 } 749 750 HRESULT CMenuBand::_CallCBWithItemId(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam) 751 { 752 return _CallCB(uMsg, wParam, lParam, id); 753 } 754 755 HRESULT CMenuBand::_CallCBWithItemPidl(LPITEMIDLIST pidl, UINT uMsg, WPARAM wParam, LPARAM lParam) 756 { 757 return _CallCB(uMsg, wParam, lParam, 0, pidl); 758 } 759 760 HRESULT CMenuBand::_CallCB(UINT uMsg, WPARAM wParam, LPARAM lParam, UINT id, LPITEMIDLIST pidl) 761 { 762 if (!m_psmc) 763 return S_FALSE; 764 765 SMDATA smData = { 0 }; 766 smData.punk = static_cast<IShellMenu2*>(this); 767 smData.uId = id; 768 smData.uIdParent = m_uId; 769 smData.uIdAncestor = m_uIdAncestor; 770 smData.pidlItem = pidl; 771 smData.hwnd = m_menuOwner ? m_menuOwner : m_topLevelWindow; 772 smData.hmenu = m_hmenu; 773 if (m_SFToolbar) 774 m_SFToolbar->GetShellFolder(NULL, &smData.pidlFolder, IID_PPV_ARG(IShellFolder, &smData.psf)); 775 HRESULT hr = m_psmc->CallbackSM(&smData, uMsg, wParam, lParam); 776 ILFree(smData.pidlFolder); 777 if (smData.psf) 778 smData.psf->Release(); 779 return hr; 780 } 781 782 HRESULT CMenuBand::_TrackSubMenu(HMENU popup, INT x, INT y, RECT& rcExclude) 783 { 784 TPMPARAMS params = { sizeof(TPMPARAMS), rcExclude }; 785 UINT flags = TPM_VERPOSANIMATION | TPM_VERTICAL | TPM_LEFTALIGN; 786 HWND hwnd = m_menuOwner ? m_menuOwner : m_topLevelWindow; 787 788 m_trackedPopup = popup; 789 m_trackedHwnd = hwnd; 790 791 m_focusManager->PushTrackedPopup(popup); 792 ::TrackPopupMenuEx(popup, flags, x, y, hwnd, ¶ms); 793 m_focusManager->PopTrackedPopup(popup); 794 795 m_trackedPopup = NULL; 796 m_trackedHwnd = NULL; 797 798 _DisableMouseTrack(FALSE); 799 800 return S_OK; 801 } 802 803 HRESULT CMenuBand::_TrackContextMenu(IContextMenu * contextMenu, INT x, INT y) 804 { 805 HRESULT hr; 806 UINT uCommand; 807 808 // Ensure that the menu doesn't disappear on us 809 CComPtr<IContextMenu> ctxMenu = contextMenu; 810 811 HMENU popup = CreatePopupMenu(); 812 813 if (popup == NULL) 814 return E_FAIL; 815 816 TRACE("Before Query\n"); 817 hr = contextMenu->QueryContextMenu(popup, 0, 0, UINT_MAX, CMF_NORMAL); 818 if (FAILED_UNEXPECTEDLY(hr)) 819 { 820 TRACE("Query failed\n"); 821 DestroyMenu(popup); 822 return hr; 823 } 824 825 HWND hwnd = m_menuOwner ? m_menuOwner : m_topLevelWindow; 826 827 m_focusManager->PushTrackedPopup(popup); 828 829 TRACE("Before Tracking\n"); 830 uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, x, y, hwnd, NULL); 831 832 m_focusManager->PopTrackedPopup(popup); 833 834 if (uCommand != 0) 835 { 836 _MenuItemSelect(MPOS_FULLCANCEL); 837 838 TRACE("Before InvokeCommand\n"); 839 CMINVOKECOMMANDINFO cmi = { 0 }; 840 cmi.cbSize = sizeof(cmi); 841 cmi.lpVerb = MAKEINTRESOURCEA(uCommand); 842 cmi.hwnd = hwnd; 843 hr = contextMenu->InvokeCommand(&cmi); 844 TRACE("InvokeCommand returned hr=%08x\n", hr); 845 } 846 else 847 { 848 TRACE("TrackPopupMenu failed. Code=%d, LastError=%d\n", uCommand, GetLastError()); 849 hr = S_FALSE; 850 } 851 852 DestroyMenu(popup); 853 return hr; 854 } 855 856 HRESULT CMenuBand::_GetTopLevelWindow(HWND*topLevel) 857 { 858 *topLevel = m_topLevelWindow; 859 return S_OK; 860 } 861 862 HRESULT CMenuBand::_ChangeHotItem(CMenuToolbarBase * tb, INT id, DWORD dwFlags) 863 { 864 if (m_hotBar == tb && m_hotItem == id) 865 return S_FALSE; 866 867 TRACE("Hot item changed from %p %p, to %p %p\n", m_hotBar, m_hotItem, tb, id); 868 869 _KillPopupTimers(); 870 871 m_hotBar = tb; 872 m_hotItem = id; 873 if (m_staticToolbar) m_staticToolbar->ChangeHotItem(tb, id, dwFlags); 874 if (m_SFToolbar) m_SFToolbar->ChangeHotItem(tb, id, dwFlags); 875 876 _MenuItemSelect(MPOS_CHILDTRACKING); 877 878 return S_OK; 879 } 880 881 HRESULT CMenuBand::_ChangePopupItem(CMenuToolbarBase * tb, INT id) 882 { 883 TRACE("Popup item changed from %p %p, to %p %p\n", m_popupBar, m_popupItem, tb, id); 884 885 m_popupBar = tb; 886 m_popupItem = id; 887 if (m_staticToolbar) m_staticToolbar->ChangePopupItem(tb, id); 888 if (m_SFToolbar) m_SFToolbar->ChangePopupItem(tb, id); 889 890 return S_OK; 891 } 892 893 HRESULT CMenuBand::_KeyboardItemChange(DWORD change) 894 { 895 HRESULT hr; 896 CMenuToolbarBase *tb = m_hotBar; 897 898 if (!tb) 899 { 900 // If no hot item was selected choose the appropriate toolbar 901 if (change == VK_UP || change == VK_END) 902 { 903 if (m_staticToolbar) 904 tb = m_staticToolbar; 905 else 906 tb = m_SFToolbar; 907 } 908 else if (change == VK_DOWN || change == VK_HOME) 909 { 910 if (m_SFToolbar) 911 tb = m_SFToolbar; 912 else 913 tb = m_staticToolbar; 914 } 915 } 916 917 // Ask the first toolbar to change 918 hr = tb->KeyboardItemChange(change); 919 920 if (hr != S_FALSE) 921 return hr; 922 923 // Select the second toolbar based on the first 924 if (tb == m_SFToolbar && m_staticToolbar) 925 tb = m_staticToolbar; 926 else if (m_SFToolbar) 927 tb = m_SFToolbar; 928 929 if (!tb) 930 return hr; 931 932 // Ask the second toolbar to change 933 return tb->KeyboardItemChange(change == VK_DOWN ? VK_HOME : VK_END); 934 } 935 936 HRESULT CMenuBand::_MenuItemSelect(DWORD changeType) 937 { 938 // Needed to prevent the this point from vanishing mid-function 939 CComPtr<CMenuBand> safeThis = this; 940 HRESULT hr; 941 942 if (m_dwFlags & SMINIT_VERTICAL) 943 { 944 switch (changeType) 945 { 946 case VK_UP: 947 case VK_DOWN: 948 return _KeyboardItemChange(changeType); 949 950 // TODO: Left/Right across multi-column menus, if they ever work. 951 case VK_LEFT: 952 changeType = MPOS_SELECTLEFT; 953 break; 954 case VK_RIGHT: 955 changeType = MPOS_SELECTRIGHT; 956 break; 957 } 958 } 959 else 960 { 961 // In horizontal menubars, left/right are equivalent to vertical's up/down 962 switch (changeType) 963 { 964 case VK_LEFT: 965 hr = _KeyboardItemChange(VK_UP); 966 if (hr != S_FALSE) 967 return hr; 968 case VK_RIGHT: 969 hr = _KeyboardItemChange(VK_DOWN); 970 if (hr != S_FALSE) 971 return hr; 972 } 973 } 974 975 // In this context, the parent is the CMenuDeskBar, so when it bubbles upward, 976 // it is notifying the deskbar, and not the the higher-level menu. 977 // Same for the child: since it points to a CMenuDeskBar, it's not just recursing. 978 switch (changeType) 979 { 980 case MPOS_EXECUTE: 981 { 982 CMenuToolbarBase * tb = m_hotBar; 983 int item = m_hotItem; 984 tb->PrepareExecuteItem(item); 985 if (m_subMenuParent) 986 { 987 m_subMenuParent->OnSelect(changeType); 988 } 989 TRACE("Menu closed, executing item...\n"); 990 tb->ExecuteItem(); 991 break; 992 } 993 case MPOS_SELECTLEFT: 994 if (m_parentBand && m_parentBand->_IsPopup()==S_FALSE) 995 return m_parentBand->_MenuItemSelect(VK_LEFT); 996 if (m_subMenuChild) 997 return m_subMenuChild->OnSelect(MPOS_CANCELLEVEL); 998 if (!m_subMenuParent) 999 return S_OK; 1000 return m_subMenuParent->OnSelect(MPOS_CANCELLEVEL); 1001 1002 case MPOS_SELECTRIGHT: 1003 if (m_hotBar && m_hotItem >= 0 && m_hotBar->PopupItem(m_hotItem, TRUE) == S_OK) 1004 return S_FALSE; 1005 if (m_parentBand) 1006 return m_parentBand->_MenuItemSelect(VK_RIGHT); 1007 if (!m_subMenuParent) 1008 return S_OK; 1009 return m_subMenuParent->OnSelect(MPOS_SELECTRIGHT); 1010 1011 default: 1012 if (!m_subMenuParent) 1013 return S_OK; 1014 return m_subMenuParent->OnSelect(changeType); 1015 } 1016 1017 return S_OK; 1018 } 1019 1020 HRESULT CMenuBand::_CancelCurrentPopup() 1021 { 1022 if (m_subMenuChild) 1023 { 1024 HRESULT hr = m_subMenuChild->OnSelect(MPOS_CANCELLEVEL); 1025 return hr; 1026 } 1027 1028 if (m_trackedPopup) 1029 { 1030 ::SendMessage(m_trackedHwnd, WM_CANCELMODE, 0, 0); 1031 return S_OK; 1032 } 1033 1034 return S_FALSE; 1035 } 1036 1037 HRESULT CMenuBand::_OnPopupSubMenu(IShellMenu * childShellMenu, POINTL * pAt, RECTL * pExclude, BOOL keyInitiated) 1038 { 1039 HRESULT hr = 0; 1040 CComPtr<IBandSite> pBandSite; 1041 CComPtr<IDeskBar> pDeskBar; 1042 1043 // Create the necessary objects 1044 #if USE_SYSTEM_MENUSITE 1045 hr = CoCreateInstance(CLSID_MenuBandSite, 1046 NULL, 1047 CLSCTX_INPROC_SERVER, 1048 IID_PPV_ARG(IBandSite, &pBandSite)); 1049 #else 1050 hr = CMenuSite_Constructor(IID_PPV_ARG(IBandSite, &pBandSite)); 1051 #endif 1052 if (FAILED_UNEXPECTEDLY(hr)) 1053 return hr; 1054 1055 #if USE_SYSTEM_MENUDESKBAR 1056 hr = CoCreateInstance(CLSID_MenuDeskBar, 1057 NULL, 1058 CLSCTX_INPROC_SERVER, 1059 IID_PPV_ARG(IDeskBar, &pDeskBar)); 1060 #else 1061 hr = CMenuDeskBar_Constructor(IID_PPV_ARG(IDeskBar, &pDeskBar)); 1062 #endif 1063 if (FAILED_UNEXPECTEDLY(hr)) 1064 return hr; 1065 1066 hr = pDeskBar->SetClient(pBandSite); 1067 if (FAILED_UNEXPECTEDLY(hr)) 1068 return hr; 1069 1070 hr = pBandSite->AddBand(childShellMenu); 1071 if (FAILED_UNEXPECTEDLY(hr)) 1072 return hr; 1073 1074 // 1075 CComPtr<IMenuPopup> popup; 1076 hr = pDeskBar->QueryInterface(IID_PPV_ARG(IMenuPopup, &popup)); 1077 if (FAILED_UNEXPECTEDLY(hr)) 1078 return hr; 1079 1080 m_subMenuChild = popup; 1081 1082 if (m_subMenuParent) 1083 IUnknown_SetSite(popup, m_subMenuParent); 1084 else 1085 IUnknown_SetSite(popup, m_site); 1086 1087 DWORD flags = MPPF_RIGHT; 1088 1089 if (keyInitiated && m_dwFlags & SMINIT_VERTICAL) 1090 flags |= MPPF_INITIALSELECT; 1091 1092 popup->Popup(pAt, pExclude, flags); 1093 1094 return S_OK; 1095 } 1096 1097 HRESULT CMenuBand::_BeforeCancelPopup() 1098 { 1099 if (m_staticToolbar) 1100 m_staticToolbar->BeforeCancelPopup(); 1101 if (m_SFToolbar) 1102 m_SFToolbar->BeforeCancelPopup(); 1103 return S_OK; 1104 } 1105 1106 HRESULT CMenuBand::_DisableMouseTrack(BOOL bDisable) 1107 { 1108 if (m_staticToolbar) 1109 m_staticToolbar->DisableMouseTrack(bDisable); 1110 if (m_SFToolbar) 1111 m_SFToolbar->DisableMouseTrack(bDisable); 1112 return S_OK; 1113 } 1114 1115 HRESULT CMenuBand::_KillPopupTimers() 1116 { 1117 HRESULT hr = S_OK; 1118 if (m_staticToolbar) 1119 hr = m_staticToolbar->KillPopupTimer(); 1120 if (FAILED(hr)) 1121 return hr; 1122 1123 if (m_SFToolbar) 1124 hr = m_SFToolbar->KillPopupTimer(); 1125 1126 return hr; 1127 } 1128 1129 HRESULT CMenuBand::_MenuBarMouseDown(HWND hwnd, INT item, BOOL isLButton) 1130 { 1131 if (m_staticToolbar && m_staticToolbar->IsWindowOwner(hwnd) == S_OK) 1132 m_staticToolbar->MenuBarMouseDown(item, isLButton); 1133 if (m_SFToolbar && m_SFToolbar->IsWindowOwner(hwnd) == S_OK) 1134 m_SFToolbar->MenuBarMouseDown(item, isLButton); 1135 return S_OK; 1136 } 1137 1138 HRESULT CMenuBand::_MenuBarMouseUp(HWND hwnd, INT item) 1139 { 1140 if (m_staticToolbar && m_staticToolbar->IsWindowOwner(hwnd) == S_OK) 1141 m_staticToolbar->MenuBarMouseUp(item); 1142 if (m_SFToolbar && m_SFToolbar->IsWindowOwner(hwnd) == S_OK) 1143 m_SFToolbar->MenuBarMouseUp(item); 1144 return S_OK; 1145 } 1146 1147 HRESULT CMenuBand::_HasSubMenu() 1148 { 1149 return m_popupBar ? S_OK : S_FALSE; 1150 } 1151 1152 HRESULT CMenuBand::AdjustForTheme(BOOL bFlatStyle) 1153 { 1154 return IUnknown_QueryServiceExec(m_site, SID_SMenuPopup, &CGID_MenuDeskBar, 4, bFlatStyle, NULL, NULL); 1155 } 1156 1157 HRESULT STDMETHODCALLTYPE CMenuBand::InvalidateItem(LPSMDATA psmd, DWORD dwFlags) 1158 { 1159 UNIMPLEMENTED; 1160 return S_OK; 1161 } 1162 1163 HRESULT STDMETHODCALLTYPE CMenuBand::GetState(LPSMDATA psmd) 1164 { 1165 UNIMPLEMENTED; 1166 return S_OK; 1167 } 1168 1169 HRESULT STDMETHODCALLTYPE CMenuBand::SetMenuToolbar(IUnknown *punk, DWORD dwFlags) 1170 { 1171 UNIMPLEMENTED; 1172 return S_OK; 1173 } 1174 1175 HRESULT STDMETHODCALLTYPE CMenuBand::ResizeBorderDW(LPCRECT prcBorder, IUnknown *punkToolbarSite, BOOL fReserved) 1176 { 1177 UNIMPLEMENTED; 1178 return S_OK; 1179 } 1180 1181 HRESULT STDMETHODCALLTYPE CMenuBand::ContextSensitiveHelp(BOOL fEnterMode) 1182 { 1183 UNIMPLEMENTED; 1184 return S_OK; 1185 } 1186 1187 HRESULT STDMETHODCALLTYPE CMenuBand::GetSubMenu(THIS) 1188 { 1189 UNIMPLEMENTED; 1190 return S_OK; 1191 } 1192 1193 HRESULT STDMETHODCALLTYPE CMenuBand::SetToolbar(THIS) 1194 { 1195 UNIMPLEMENTED; 1196 return S_OK; 1197 } 1198 1199 HRESULT STDMETHODCALLTYPE CMenuBand::SetMinWidth(THIS) 1200 { 1201 UNIMPLEMENTED; 1202 return S_OK; 1203 } 1204 1205 HRESULT STDMETHODCALLTYPE CMenuBand::SetNoBorder(THIS) 1206 { 1207 UNIMPLEMENTED; 1208 return S_OK; 1209 } 1210 1211 HRESULT STDMETHODCALLTYPE CMenuBand::SetTheme(THIS) 1212 { 1213 UNIMPLEMENTED; 1214 return S_OK; 1215 } 1216 1217 HRESULT STDMETHODCALLTYPE CMenuBand::GetTop(THIS) 1218 { 1219 UNIMPLEMENTED; 1220 return S_OK; 1221 } 1222 1223 HRESULT STDMETHODCALLTYPE CMenuBand::GetBottom(THIS) 1224 { 1225 UNIMPLEMENTED; 1226 return S_OK; 1227 } 1228 1229 HRESULT STDMETHODCALLTYPE CMenuBand::GetTracked(THIS) 1230 { 1231 UNIMPLEMENTED; 1232 return S_OK; 1233 } 1234 1235 HRESULT STDMETHODCALLTYPE CMenuBand::GetParentSite(THIS) 1236 { 1237 UNIMPLEMENTED; 1238 return S_OK; 1239 } 1240 1241 HRESULT STDMETHODCALLTYPE CMenuBand::GetState(THIS) 1242 { 1243 UNIMPLEMENTED; 1244 return S_OK; 1245 } 1246 1247 HRESULT STDMETHODCALLTYPE CMenuBand::DoDefaultAction(THIS) 1248 { 1249 UNIMPLEMENTED; 1250 return S_OK; 1251 } 1252 1253 HRESULT STDMETHODCALLTYPE CMenuBand::IsEmpty(THIS) 1254 { 1255 UNIMPLEMENTED; 1256 return S_OK; 1257 } 1258 1259 HRESULT STDMETHODCALLTYPE CMenuBand::HasFocusIO() 1260 { 1261 if (m_popupBar) 1262 return S_OK; 1263 return S_FALSE; 1264 } 1265 1266 HRESULT STDMETHODCALLTYPE CMenuBand::TranslateAcceleratorIO(LPMSG lpMsg) 1267 { 1268 // TODO: Alt down -> toggle menu focus 1269 return S_FALSE; 1270 } 1271 1272 HRESULT STDMETHODCALLTYPE CMenuBand::IsDirty() 1273 { 1274 UNIMPLEMENTED; 1275 return S_OK; 1276 } 1277 1278 HRESULT STDMETHODCALLTYPE CMenuBand::Load(IStream *pStm) 1279 { 1280 UNIMPLEMENTED; 1281 return S_OK; 1282 } 1283 1284 HRESULT STDMETHODCALLTYPE CMenuBand::Save(IStream *pStm, BOOL fClearDirty) 1285 { 1286 UNIMPLEMENTED; 1287 return S_OK; 1288 } 1289 1290 HRESULT STDMETHODCALLTYPE CMenuBand::GetSizeMax(ULARGE_INTEGER *pcbSize) 1291 { 1292 UNIMPLEMENTED; 1293 return S_OK; 1294 } 1295 1296 HRESULT STDMETHODCALLTYPE CMenuBand::GetClassID(CLSID *pClassID) 1297 { 1298 UNIMPLEMENTED; 1299 return S_OK; 1300 } 1301 1302 HRESULT STDMETHODCALLTYPE CMenuBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText) 1303 { 1304 UNIMPLEMENTED; 1305 return S_OK; 1306 } 1307