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