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 *ppsmc = m_psmc; 103 if (*ppsmc) 104 (*ppsmc)->AddRef(); 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 *ppunkClient = m_subMenuChild; 659 (*ppunkClient)->AddRef(); 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 if (theResult) 727 *theResult = 0; 728 729 if (uMsg == WM_WININICHANGE && wParam == SPI_SETFLATMENU) 730 { 731 BOOL bFlatMenus; 732 SystemParametersInfo(SPI_GETFLATMENU, 0, &bFlatMenus, 0); 733 AdjustForTheme(bFlatMenus); 734 735 if (m_staticToolbar) 736 m_staticToolbar->OnWinEvent(hWnd, uMsg, wParam, lParam, theResult); 737 738 if (m_SFToolbar) 739 m_SFToolbar->OnWinEvent(hWnd, uMsg, wParam, lParam, theResult); 740 741 return S_OK; 742 } 743 744 if (m_staticToolbar && m_staticToolbar->IsWindowOwner(hWnd) == S_OK) 745 { 746 return m_staticToolbar->OnWinEvent(hWnd, uMsg, wParam, lParam, theResult); 747 } 748 749 if (m_SFToolbar && m_SFToolbar->IsWindowOwner(hWnd) == S_OK) 750 { 751 return m_SFToolbar->OnWinEvent(hWnd, uMsg, wParam, lParam, theResult); 752 } 753 754 return S_FALSE; 755 } 756 757 HRESULT STDMETHODCALLTYPE CMenuBand::IsWindowOwner(HWND hWnd) 758 { 759 if (m_staticToolbar && m_staticToolbar->IsWindowOwner(hWnd) == S_OK) 760 return S_OK; 761 762 if (m_SFToolbar && m_SFToolbar->IsWindowOwner(hWnd) == S_OK) 763 return S_OK; 764 765 return S_FALSE; 766 } 767 768 HRESULT CMenuBand::_CallCBWithItemId(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam) 769 { 770 return _CallCB(uMsg, wParam, lParam, id); 771 } 772 773 HRESULT CMenuBand::_CallCBWithItemPidl(LPITEMIDLIST pidl, UINT uMsg, WPARAM wParam, LPARAM lParam) 774 { 775 return _CallCB(uMsg, wParam, lParam, 0, pidl); 776 } 777 778 HRESULT CMenuBand::_CallCB(UINT uMsg, WPARAM wParam, LPARAM lParam, UINT id, LPITEMIDLIST pidl) 779 { 780 if (!m_psmc) 781 return S_FALSE; 782 783 SMDATA smData = { 0 }; 784 smData.punk = static_cast<IShellMenu2*>(this); 785 smData.uId = id; 786 smData.uIdParent = m_uId; 787 smData.uIdAncestor = m_uIdAncestor; 788 smData.pidlItem = pidl; 789 smData.hwnd = m_menuOwner ? m_menuOwner : m_topLevelWindow; 790 smData.hmenu = m_hmenu; 791 if (m_SFToolbar) 792 m_SFToolbar->GetShellFolder(NULL, &smData.pidlFolder, IID_PPV_ARG(IShellFolder, &smData.psf)); 793 HRESULT hr = m_psmc->CallbackSM(&smData, uMsg, wParam, lParam); 794 ILFree(smData.pidlFolder); 795 if (smData.psf) 796 smData.psf->Release(); 797 return hr; 798 } 799 800 HRESULT CMenuBand::_TrackSubMenu(HMENU popup, INT x, INT y, RECT& rcExclude) 801 { 802 TPMPARAMS params = { sizeof(TPMPARAMS), rcExclude }; 803 UINT flags = TPM_VERPOSANIMATION | TPM_VERTICAL | TPM_LEFTALIGN; 804 HWND hwnd = m_menuOwner ? m_menuOwner : m_topLevelWindow; 805 806 m_trackedPopup = popup; 807 m_trackedHwnd = hwnd; 808 809 m_focusManager->PushTrackedPopup(popup); 810 ::TrackPopupMenuEx(popup, flags, x, y, hwnd, ¶ms); 811 m_focusManager->PopTrackedPopup(popup); 812 813 m_trackedPopup = NULL; 814 m_trackedHwnd = NULL; 815 816 _DisableMouseTrack(FALSE); 817 818 return S_OK; 819 } 820 821 HRESULT CMenuBand::_TrackContextMenu(IContextMenu * contextMenu, INT x, INT y) 822 { 823 HRESULT hr; 824 UINT uCommand; 825 826 // Ensure that the menu doesn't disappear on us 827 CComPtr<IContextMenu> ctxMenu = contextMenu; 828 829 HMENU popup = CreatePopupMenu(); 830 831 if (popup == NULL) 832 return E_FAIL; 833 834 TRACE("Before Query\n"); 835 hr = contextMenu->QueryContextMenu(popup, 0, 0, UINT_MAX, CMF_NORMAL); 836 if (FAILED_UNEXPECTEDLY(hr)) 837 { 838 TRACE("Query failed\n"); 839 DestroyMenu(popup); 840 return hr; 841 } 842 843 HWND hwnd = m_menuOwner ? m_menuOwner : m_topLevelWindow; 844 845 m_focusManager->PushTrackedPopup(popup); 846 847 TRACE("Before Tracking\n"); 848 uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, x, y, hwnd, NULL); 849 850 m_focusManager->PopTrackedPopup(popup); 851 852 if (uCommand != 0) 853 { 854 _MenuItemSelect(MPOS_FULLCANCEL); 855 856 TRACE("Before InvokeCommand\n"); 857 CMINVOKECOMMANDINFO cmi = { 0 }; 858 cmi.cbSize = sizeof(cmi); 859 cmi.lpVerb = MAKEINTRESOURCEA(uCommand); 860 cmi.hwnd = hwnd; 861 hr = contextMenu->InvokeCommand(&cmi); 862 TRACE("InvokeCommand returned hr=%08x\n", hr); 863 } 864 else 865 { 866 TRACE("TrackPopupMenu failed. Code=%d, LastError=%d\n", uCommand, GetLastError()); 867 hr = S_FALSE; 868 } 869 870 DestroyMenu(popup); 871 return hr; 872 } 873 874 HRESULT CMenuBand::_GetTopLevelWindow(HWND*topLevel) 875 { 876 *topLevel = m_topLevelWindow; 877 return S_OK; 878 } 879 880 HRESULT CMenuBand::_ChangeHotItem(CMenuToolbarBase * tb, INT id, DWORD dwFlags) 881 { 882 if (m_hotBar == tb && m_hotItem == id) 883 return S_FALSE; 884 885 TRACE("Hot item changed from %p %p, to %p %p\n", m_hotBar, m_hotItem, tb, id); 886 887 _KillPopupTimers(); 888 889 m_hotBar = tb; 890 m_hotItem = id; 891 if (m_staticToolbar) m_staticToolbar->ChangeHotItem(tb, id, dwFlags); 892 if (m_SFToolbar) m_SFToolbar->ChangeHotItem(tb, id, dwFlags); 893 894 _MenuItemSelect(MPOS_CHILDTRACKING); 895 896 return S_OK; 897 } 898 899 HRESULT CMenuBand::_ChangePopupItem(CMenuToolbarBase * tb, INT id) 900 { 901 TRACE("Popup item changed from %p %p, to %p %p\n", m_popupBar, m_popupItem, tb, id); 902 903 m_popupBar = tb; 904 m_popupItem = id; 905 if (m_staticToolbar) m_staticToolbar->ChangePopupItem(tb, id); 906 if (m_SFToolbar) m_SFToolbar->ChangePopupItem(tb, id); 907 908 return S_OK; 909 } 910 911 HRESULT CMenuBand::_KeyboardItemChange(DWORD change) 912 { 913 HRESULT hr; 914 CMenuToolbarBase *tb = m_hotBar; 915 916 if (!tb) 917 { 918 // If no hot item was selected choose the appropriate toolbar 919 if (change == VK_UP || change == VK_END) 920 { 921 if (m_staticToolbar) 922 tb = m_staticToolbar; 923 else 924 tb = m_SFToolbar; 925 } 926 else if (change == VK_DOWN || change == VK_HOME) 927 { 928 if (m_SFToolbar) 929 tb = m_SFToolbar; 930 else 931 tb = m_staticToolbar; 932 } 933 } 934 935 // Ask the first toolbar to change 936 hr = tb->KeyboardItemChange(change); 937 938 if (hr != S_FALSE) 939 return hr; 940 941 // Select the second toolbar based on the first 942 if (tb == m_SFToolbar && m_staticToolbar) 943 tb = m_staticToolbar; 944 else if (m_SFToolbar) 945 tb = m_SFToolbar; 946 947 if (!tb) 948 return hr; 949 950 // Ask the second toolbar to change 951 return tb->KeyboardItemChange(change == VK_DOWN ? VK_HOME : VK_END); 952 } 953 954 HRESULT CMenuBand::_MenuItemSelect(DWORD changeType) 955 { 956 // Needed to prevent the this point from vanishing mid-function 957 CComPtr<CMenuBand> safeThis = this; 958 HRESULT hr; 959 960 if (m_dwFlags & SMINIT_VERTICAL) 961 { 962 switch (changeType) 963 { 964 case VK_UP: 965 case VK_DOWN: 966 return _KeyboardItemChange(changeType); 967 968 // TODO: Left/Right across multi-column menus, if they ever work. 969 case VK_LEFT: 970 changeType = MPOS_SELECTLEFT; 971 break; 972 case VK_RIGHT: 973 changeType = MPOS_SELECTRIGHT; 974 break; 975 } 976 } 977 else 978 { 979 // In horizontal menubars, left/right are equivalent to vertical's up/down 980 switch (changeType) 981 { 982 case VK_LEFT: 983 hr = _KeyboardItemChange(VK_UP); 984 if (hr != S_FALSE) 985 return hr; 986 case VK_RIGHT: 987 hr = _KeyboardItemChange(VK_DOWN); 988 if (hr != S_FALSE) 989 return hr; 990 } 991 } 992 993 // In this context, the parent is the CMenuDeskBar, so when it bubbles upward, 994 // it is notifying the deskbar, and not the the higher-level menu. 995 // Same for the child: since it points to a CMenuDeskBar, it's not just recursing. 996 switch (changeType) 997 { 998 case MPOS_EXECUTE: 999 { 1000 CMenuToolbarBase * tb = m_hotBar; 1001 int item = m_hotItem; 1002 tb->PrepareExecuteItem(item); 1003 if (m_subMenuParent) 1004 { 1005 m_subMenuParent->OnSelect(changeType); 1006 } 1007 TRACE("Menu closed, executing item...\n"); 1008 tb->ExecuteItem(); 1009 break; 1010 } 1011 case MPOS_SELECTLEFT: 1012 if (m_parentBand && m_parentBand->_IsPopup()==S_FALSE) 1013 return m_parentBand->_MenuItemSelect(VK_LEFT); 1014 if (m_subMenuChild) 1015 return m_subMenuChild->OnSelect(MPOS_CANCELLEVEL); 1016 if (!m_subMenuParent) 1017 return S_OK; 1018 return m_subMenuParent->OnSelect(MPOS_CANCELLEVEL); 1019 1020 case MPOS_SELECTRIGHT: 1021 if (m_hotBar && m_hotItem >= 0 && m_hotBar->PopupItem(m_hotItem, TRUE) == S_OK) 1022 return S_FALSE; 1023 if (m_parentBand) 1024 return m_parentBand->_MenuItemSelect(VK_RIGHT); 1025 if (!m_subMenuParent) 1026 return S_OK; 1027 return m_subMenuParent->OnSelect(MPOS_SELECTRIGHT); 1028 1029 default: 1030 if (!m_subMenuParent) 1031 return S_OK; 1032 return m_subMenuParent->OnSelect(changeType); 1033 } 1034 1035 return S_OK; 1036 } 1037 1038 HRESULT CMenuBand::_CancelCurrentPopup() 1039 { 1040 if (m_subMenuChild) 1041 { 1042 HRESULT hr = m_subMenuChild->OnSelect(MPOS_CANCELLEVEL); 1043 return hr; 1044 } 1045 1046 if (m_trackedPopup) 1047 { 1048 ::SendMessage(m_trackedHwnd, WM_CANCELMODE, 0, 0); 1049 return S_OK; 1050 } 1051 1052 return S_FALSE; 1053 } 1054 1055 HRESULT CMenuBand::_OnPopupSubMenu(IShellMenu * childShellMenu, POINTL * pAt, RECTL * pExclude, BOOL keyInitiated) 1056 { 1057 HRESULT hr = 0; 1058 CComPtr<IBandSite> pBandSite; 1059 CComPtr<IDeskBar> pDeskBar; 1060 1061 // Create the necessary objects 1062 hr = CMenuSite_CreateInstance(IID_PPV_ARG(IBandSite, &pBandSite)); 1063 if (FAILED_UNEXPECTEDLY(hr)) 1064 return hr; 1065 1066 hr = CMenuDeskBar_CreateInstance(IID_PPV_ARG(IDeskBar, &pDeskBar)); 1067 if (FAILED_UNEXPECTEDLY(hr)) 1068 return hr; 1069 1070 hr = pDeskBar->SetClient(pBandSite); 1071 if (FAILED_UNEXPECTEDLY(hr)) 1072 return hr; 1073 1074 hr = pBandSite->AddBand(childShellMenu); 1075 if (FAILED_UNEXPECTEDLY(hr)) 1076 return hr; 1077 1078 // 1079 CComPtr<IMenuPopup> popup; 1080 hr = pDeskBar->QueryInterface(IID_PPV_ARG(IMenuPopup, &popup)); 1081 if (FAILED_UNEXPECTEDLY(hr)) 1082 return hr; 1083 1084 m_subMenuChild = popup; 1085 1086 if (m_subMenuParent) 1087 IUnknown_SetSite(popup, m_subMenuParent); 1088 else 1089 IUnknown_SetSite(popup, m_site); 1090 1091 DWORD flags = MPPF_RIGHT; 1092 1093 if (keyInitiated && m_dwFlags & SMINIT_VERTICAL) 1094 flags |= MPPF_INITIALSELECT; 1095 1096 popup->Popup(pAt, pExclude, flags); 1097 1098 return S_OK; 1099 } 1100 1101 HRESULT CMenuBand::_BeforeCancelPopup() 1102 { 1103 if (m_staticToolbar) 1104 m_staticToolbar->BeforeCancelPopup(); 1105 if (m_SFToolbar) 1106 m_SFToolbar->BeforeCancelPopup(); 1107 return S_OK; 1108 } 1109 1110 HRESULT CMenuBand::_DisableMouseTrack(BOOL bDisable) 1111 { 1112 if (m_staticToolbar) 1113 m_staticToolbar->DisableMouseTrack(bDisable); 1114 if (m_SFToolbar) 1115 m_SFToolbar->DisableMouseTrack(bDisable); 1116 return S_OK; 1117 } 1118 1119 HRESULT CMenuBand::_KillPopupTimers() 1120 { 1121 HRESULT hr = S_OK; 1122 if (m_staticToolbar) 1123 hr = m_staticToolbar->KillPopupTimer(); 1124 if (FAILED(hr)) 1125 return hr; 1126 1127 if (m_SFToolbar) 1128 hr = m_SFToolbar->KillPopupTimer(); 1129 1130 return hr; 1131 } 1132 1133 HRESULT CMenuBand::_MenuBarMouseDown(HWND hwnd, INT item, BOOL isLButton) 1134 { 1135 if (m_staticToolbar && m_staticToolbar->IsWindowOwner(hwnd) == S_OK) 1136 m_staticToolbar->MenuBarMouseDown(item, isLButton); 1137 if (m_SFToolbar && m_SFToolbar->IsWindowOwner(hwnd) == S_OK) 1138 m_SFToolbar->MenuBarMouseDown(item, isLButton); 1139 return S_OK; 1140 } 1141 1142 HRESULT CMenuBand::_MenuBarMouseUp(HWND hwnd, INT item, BOOL isLButton) 1143 { 1144 if (m_staticToolbar && m_staticToolbar->IsWindowOwner(hwnd) == S_OK) 1145 m_staticToolbar->MenuBarMouseUp(item, isLButton); 1146 if (m_SFToolbar && m_SFToolbar->IsWindowOwner(hwnd) == S_OK) 1147 m_SFToolbar->MenuBarMouseUp(item, isLButton); 1148 return S_OK; 1149 } 1150 1151 HRESULT CMenuBand::_HasSubMenu() 1152 { 1153 return m_popupBar ? S_OK : S_FALSE; 1154 } 1155 1156 HRESULT CMenuBand::AdjustForTheme(BOOL bFlatStyle) 1157 { 1158 return IUnknown_QueryServiceExec(m_site, SID_SMenuPopup, &CGID_MenuDeskBar, 4, bFlatStyle, NULL, NULL); 1159 } 1160 1161 HRESULT STDMETHODCALLTYPE CMenuBand::InvalidateItem(LPSMDATA psmd, DWORD dwFlags) 1162 { 1163 UNIMPLEMENTED; 1164 return S_OK; 1165 } 1166 1167 HRESULT STDMETHODCALLTYPE CMenuBand::GetState(LPSMDATA psmd) 1168 { 1169 UNIMPLEMENTED; 1170 return S_OK; 1171 } 1172 1173 HRESULT STDMETHODCALLTYPE CMenuBand::SetMenuToolbar(IUnknown *punk, DWORD dwFlags) 1174 { 1175 UNIMPLEMENTED; 1176 return S_OK; 1177 } 1178 1179 HRESULT STDMETHODCALLTYPE CMenuBand::ResizeBorderDW(LPCRECT prcBorder, IUnknown *punkToolbarSite, BOOL fReserved) 1180 { 1181 UNIMPLEMENTED; 1182 return S_OK; 1183 } 1184 1185 HRESULT STDMETHODCALLTYPE CMenuBand::ContextSensitiveHelp(BOOL fEnterMode) 1186 { 1187 UNIMPLEMENTED; 1188 return S_OK; 1189 } 1190 1191 HRESULT STDMETHODCALLTYPE CMenuBand::GetSubMenu(THIS) 1192 { 1193 UNIMPLEMENTED; 1194 return S_OK; 1195 } 1196 1197 HRESULT STDMETHODCALLTYPE CMenuBand::SetToolbar(THIS) 1198 { 1199 UNIMPLEMENTED; 1200 return S_OK; 1201 } 1202 1203 HRESULT STDMETHODCALLTYPE CMenuBand::SetMinWidth(THIS) 1204 { 1205 UNIMPLEMENTED; 1206 return S_OK; 1207 } 1208 1209 HRESULT STDMETHODCALLTYPE CMenuBand::SetNoBorder(THIS) 1210 { 1211 UNIMPLEMENTED; 1212 return S_OK; 1213 } 1214 1215 HRESULT STDMETHODCALLTYPE CMenuBand::SetTheme(THIS) 1216 { 1217 UNIMPLEMENTED; 1218 return S_OK; 1219 } 1220 1221 HRESULT STDMETHODCALLTYPE CMenuBand::GetTop(THIS) 1222 { 1223 UNIMPLEMENTED; 1224 return S_OK; 1225 } 1226 1227 HRESULT STDMETHODCALLTYPE CMenuBand::GetBottom(THIS) 1228 { 1229 UNIMPLEMENTED; 1230 return S_OK; 1231 } 1232 1233 HRESULT STDMETHODCALLTYPE CMenuBand::GetTracked(THIS) 1234 { 1235 UNIMPLEMENTED; 1236 return S_OK; 1237 } 1238 1239 HRESULT STDMETHODCALLTYPE CMenuBand::GetParentSite(THIS) 1240 { 1241 UNIMPLEMENTED; 1242 return S_OK; 1243 } 1244 1245 HRESULT STDMETHODCALLTYPE CMenuBand::GetState(THIS) 1246 { 1247 UNIMPLEMENTED; 1248 return S_OK; 1249 } 1250 1251 HRESULT STDMETHODCALLTYPE CMenuBand::DoDefaultAction(THIS) 1252 { 1253 UNIMPLEMENTED; 1254 return S_OK; 1255 } 1256 1257 HRESULT STDMETHODCALLTYPE CMenuBand::IsEmpty(THIS) 1258 { 1259 UNIMPLEMENTED; 1260 return S_OK; 1261 } 1262 1263 HRESULT STDMETHODCALLTYPE CMenuBand::HasFocusIO() 1264 { 1265 if (m_popupBar) 1266 return S_OK; 1267 return S_FALSE; 1268 } 1269 1270 HRESULT STDMETHODCALLTYPE CMenuBand::TranslateAcceleratorIO(LPMSG lpMsg) 1271 { 1272 // TODO: Alt down -> toggle menu focus 1273 return S_FALSE; 1274 } 1275 1276 HRESULT STDMETHODCALLTYPE CMenuBand::IsDirty() 1277 { 1278 UNIMPLEMENTED; 1279 return S_OK; 1280 } 1281 1282 HRESULT STDMETHODCALLTYPE CMenuBand::Load(IStream *pStm) 1283 { 1284 UNIMPLEMENTED; 1285 return S_OK; 1286 } 1287 1288 HRESULT STDMETHODCALLTYPE CMenuBand::Save(IStream *pStm, BOOL fClearDirty) 1289 { 1290 UNIMPLEMENTED; 1291 return S_OK; 1292 } 1293 1294 HRESULT STDMETHODCALLTYPE CMenuBand::GetSizeMax(ULARGE_INTEGER *pcbSize) 1295 { 1296 UNIMPLEMENTED; 1297 return S_OK; 1298 } 1299 1300 HRESULT STDMETHODCALLTYPE CMenuBand::GetClassID(CLSID *pClassID) 1301 { 1302 UNIMPLEMENTED; 1303 return S_OK; 1304 } 1305 1306 HRESULT STDMETHODCALLTYPE CMenuBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText) 1307 { 1308 UNIMPLEMENTED; 1309 return S_OK; 1310 } 1311 1312 extern "C" 1313 HRESULT WINAPI RSHELL_CMenuBand_CreateInstance(REFIID riid, LPVOID *ppv) 1314 { 1315 return ShellObjectCreator<CMenuBand>(riid, ppv); 1316 } 1317