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