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 <commoncontrols.h> 22 #include <shlwapi_undoc.h> 23 #include <uxtheme.h> 24 #include <vssym32.h> 25 26 #include "CMenuBand.h" 27 #include "CMenuToolbars.h" 28 29 #define IDS_MENU_EMPTY 34561 30 31 WINE_DEFAULT_DEBUG_CHANNEL(CMenuToolbars); 32 33 // FIXME: Enable if/when wine comctl supports this flag properly 34 #define USE_TBSTYLE_EX_VERTICAL 0 35 36 // User-defined timer ID used while hot-tracking around the menu 37 #define TIMERID_HOTTRACK 1 38 39 LRESULT CMenuToolbarBase::OnWinEventWrap(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 40 { 41 LRESULT lr; 42 bHandled = OnWinEvent(m_hWnd, uMsg, wParam, lParam, &lr) != S_FALSE; 43 return lr; 44 } 45 46 HRESULT CMenuToolbarBase::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult) 47 { 48 NMHDR * hdr; 49 HRESULT hr; 50 LRESULT result; 51 52 if (theResult) 53 *theResult = 0; 54 switch (uMsg) 55 { 56 case WM_COMMAND: 57 //return OnCommand(wParam, lParam, theResult); 58 return S_OK; 59 60 case WM_NOTIFY: 61 hdr = reinterpret_cast<LPNMHDR>(lParam); 62 switch (hdr->code) 63 { 64 case TBN_DELETINGBUTTON: 65 return OnDeletingButton(reinterpret_cast<LPNMTOOLBAR>(hdr)); 66 67 case PGN_CALCSIZE: 68 return OnPagerCalcSize(reinterpret_cast<LPNMPGCALCSIZE>(hdr)); 69 70 case TBN_DROPDOWN: 71 return ProcessClick(reinterpret_cast<LPNMTOOLBAR>(hdr)->iItem); 72 73 case TBN_HOTITEMCHANGE: 74 //return OnHotItemChange(reinterpret_cast<LPNMTBHOTITEM>(hdr), theResult); 75 return S_OK; 76 77 case NM_CUSTOMDRAW: 78 hr = OnCustomDraw(reinterpret_cast<LPNMTBCUSTOMDRAW>(hdr), &result); 79 if (theResult) 80 *theResult = result; 81 return hr; 82 83 case TBN_GETINFOTIP: 84 return OnGetInfoTip(reinterpret_cast<LPNMTBGETINFOTIP>(hdr)); 85 86 // Silence unhandled items so that they don't print as unknown 87 case RBN_CHILDSIZE: 88 return S_OK; 89 90 case TTN_GETDISPINFO: 91 return S_OK; 92 93 case NM_RELEASEDCAPTURE: 94 break; 95 96 case NM_CLICK: 97 case NM_RDOWN: 98 case NM_LDOWN: 99 break; 100 101 case TBN_GETDISPINFO: 102 break; 103 104 case TBN_BEGINDRAG: 105 case TBN_ENDDRAG: 106 break; 107 108 case NM_TOOLTIPSCREATED: 109 break; 110 111 case TBN_DRAGOUT: return S_FALSE; 112 113 default: 114 TRACE("WM_NOTIFY unknown code %d, %d\n", hdr->code, hdr->idFrom); 115 return S_OK; 116 } 117 return S_FALSE; 118 case WM_WININICHANGE: 119 if (wParam == SPI_SETFLATMENU) 120 { 121 SystemParametersInfo(SPI_GETFLATMENU, 0, &m_useFlatMenus, 0); 122 } 123 } 124 125 return S_FALSE; 126 } 127 128 HRESULT CMenuToolbarBase::DisableMouseTrack(BOOL bDisable) 129 { 130 if (m_disableMouseTrack != bDisable) 131 { 132 m_disableMouseTrack = bDisable; 133 TRACE("DisableMouseTrack %d\n", bDisable); 134 } 135 return S_OK; 136 } 137 138 HRESULT CMenuToolbarBase::OnPagerCalcSize(LPNMPGCALCSIZE csize) 139 { 140 SIZE tbs; 141 GetSizes(NULL, &tbs, NULL); 142 if (csize->dwFlag == PGF_CALCHEIGHT) 143 { 144 csize->iHeight = tbs.cy; 145 } 146 else if (csize->dwFlag == PGF_CALCWIDTH) 147 { 148 csize->iWidth = tbs.cx; 149 } 150 return S_OK; 151 } 152 153 HRESULT CMenuToolbarBase::OnCustomDraw(LPNMTBCUSTOMDRAW cdraw, LRESULT * theResult) 154 { 155 bool isHot, isPopup, isActive; 156 TBBUTTONINFO btni; 157 158 switch (cdraw->nmcd.dwDrawStage) 159 { 160 case CDDS_PREPAINT: 161 *theResult = CDRF_NOTIFYITEMDRAW; 162 return S_OK; 163 164 case CDDS_ITEMPREPAINT: 165 166 HWND tlw; 167 m_menuBand->_GetTopLevelWindow(&tlw); 168 169 // The item with an active submenu gets the CHECKED flag. 170 isHot = m_hotBar == this && (int) cdraw->nmcd.dwItemSpec == m_hotItem; 171 isPopup = m_popupBar == this && (int) cdraw->nmcd.dwItemSpec == m_popupItem; 172 isActive = (GetForegroundWindow() == tlw) || (m_popupBar == this); 173 174 if (m_hotItem < 0 && isPopup) 175 isHot = TRUE; 176 177 if ((m_useFlatMenus && isHot) || (m_initFlags & SMINIT_VERTICAL)) 178 { 179 COLORREF clrText; 180 HBRUSH bgBrush; 181 RECT rc = cdraw->nmcd.rc; 182 HDC hdc = cdraw->nmcd.hdc; 183 184 // Remove HOT and CHECKED flags (will restore HOT if necessary) 185 cdraw->nmcd.uItemState &= ~(CDIS_HOT | CDIS_CHECKED); 186 187 // Decide on the colors 188 if (isHot) 189 { 190 cdraw->nmcd.uItemState |= CDIS_HOT; 191 192 clrText = GetSysColor(COLOR_HIGHLIGHTTEXT); 193 bgBrush = GetSysColorBrush(m_useFlatMenus ? COLOR_MENUHILIGHT : COLOR_HIGHLIGHT); 194 } 195 else 196 { 197 clrText = GetSysColor(COLOR_MENUTEXT); 198 bgBrush = GetSysColorBrush(COLOR_MENU); 199 } 200 201 // Paint the background color with the selected color 202 FillRect(hdc, &rc, bgBrush); 203 204 // Set the text color in advance, this color will be assigned when the ITEMPOSTPAINT triggers 205 SetTextColor(hdc, clrText); 206 207 // Set the text color, will be used by the internal drawing code 208 cdraw->clrText = clrText; 209 cdraw->iListGap += 4; 210 211 // Tell the default drawing code we don't want any fanciness, not even a background. 212 *theResult = CDRF_NOTIFYPOSTPAINT | TBCDRF_NOBACKGROUND | TBCDRF_NOEDGES | TBCDRF_NOOFFSET | TBCDRF_NOMARK | 0x00800000; // FIXME: the last bit is Vista+, useful for debugging only 213 } 214 else 215 { 216 // Set the text color, will be used by the internal drawing code 217 cdraw->clrText = GetSysColor(isActive ? COLOR_MENUTEXT : COLOR_GRAYTEXT); 218 219 // Remove HOT and CHECKED flags (will restore HOT if necessary) 220 cdraw->nmcd.uItemState &= ~CDIS_HOT; 221 222 // Decide on the colors 223 if (isHot) 224 { 225 cdraw->nmcd.uItemState |= CDIS_HOT; 226 } 227 228 *theResult = 0; 229 } 230 231 return S_OK; 232 233 case CDDS_ITEMPOSTPAINT: 234 235 // Fetch the button style 236 btni.cbSize = sizeof(btni); 237 btni.dwMask = TBIF_STYLE; 238 GetButtonInfo(cdraw->nmcd.dwItemSpec, &btni); 239 240 // Check if we need to draw a submenu arrow 241 if (btni.fsStyle & BTNS_DROPDOWN) 242 { 243 // TODO: Support RTL text modes by drawing a leftwards arrow aligned to the left of the control 244 245 // "8" is the rightwards dropdown arrow in the Marlett font 246 WCHAR text [] = L"8"; 247 248 // Configure the font to draw with Marlett, keeping the current background color as-is 249 SelectObject(cdraw->nmcd.hdc, m_marlett); 250 SetBkMode(cdraw->nmcd.hdc, TRANSPARENT); 251 252 // Tweak the alignment by 1 pixel so the menu draws like the Windows start menu. 253 RECT rc = cdraw->nmcd.rc; 254 rc.right += 1; 255 256 // The arrow is drawn at the right of the item's rect, aligned vertically. 257 DrawTextEx(cdraw->nmcd.hdc, text, 1, &rc, DT_NOCLIP | DT_VCENTER | DT_RIGHT | DT_SINGLELINE, NULL); 258 } 259 *theResult = TRUE; 260 return S_OK; 261 262 default: 263 *theResult = 0L; 264 break; 265 } 266 return S_OK; 267 } 268 269 CMenuToolbarBase::CMenuToolbarBase(CMenuBand *menuBand, BOOL usePager) : 270 m_pager(WC_PAGESCROLLER, this), 271 m_useFlatMenus(FALSE), 272 m_disableMouseTrack(FALSE), 273 m_timerEnabled(FALSE), 274 m_menuBand(menuBand), 275 m_dwMenuFlags(0), 276 m_hasSizes(FALSE), 277 m_usePager(usePager), 278 m_hotBar(NULL), 279 m_hotItem(-1), 280 m_popupBar(NULL), 281 m_popupItem(-1), 282 m_isTrackingPopup(FALSE), 283 m_cancelingPopup(FALSE) 284 { 285 m_idealSize.cx = 0; 286 m_idealSize.cy = 0; 287 m_itemSize.cx = 0; 288 m_itemSize.cy = 0; 289 m_marlett = CreateFont( 290 0, 0, 0, 0, 0, 0, 0, 0, DEFAULT_CHARSET, 291 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 292 DEFAULT_QUALITY, FF_DONTCARE, L"Marlett"); 293 } 294 295 CMenuToolbarBase::~CMenuToolbarBase() 296 { 297 ClearToolbar(); 298 299 if (m_hWnd) 300 DestroyWindow(); 301 302 if (m_pager.m_hWnd) 303 m_pager.DestroyWindow(); 304 305 DeleteObject(m_marlett); 306 } 307 308 void CMenuToolbarBase::InvalidateDraw() 309 { 310 InvalidateRect(NULL, FALSE); 311 } 312 313 HRESULT CMenuToolbarBase::ShowDW(BOOL fShow) 314 { 315 ShowWindow(fShow ? SW_SHOW : SW_HIDE); 316 317 // Ensure that the right image list is assigned to the toolbar 318 UpdateImageLists(); 319 320 // For custom-drawing 321 SystemParametersInfo(SPI_GETFLATMENU, 0, &m_useFlatMenus, 0); 322 323 return S_OK; 324 } 325 326 HRESULT CMenuToolbarBase::UpdateImageLists() 327 { 328 if ((m_initFlags & (SMINIT_TOPLEVEL | SMINIT_VERTICAL)) == SMINIT_TOPLEVEL) // not vertical. 329 { 330 // No image list, prevents the buttons from having a margin at the left side 331 SetImageList(NULL); 332 return S_OK; 333 } 334 335 // Assign the correct imagelist and padding based on the current icon size 336 337 int shiml; 338 if (m_menuBand->UseBigIcons()) 339 { 340 shiml = SHIL_LARGE; 341 SetPadding(4, 0); 342 } 343 else 344 { 345 shiml = SHIL_SMALL; 346 SetPadding(4, 4); 347 } 348 349 IImageList * piml; 350 HRESULT hr = SHGetImageList(shiml, IID_PPV_ARG(IImageList, &piml)); 351 if (FAILED_UNEXPECTEDLY(hr)) 352 { 353 SetImageList(NULL); 354 } 355 else 356 { 357 SetImageList((HIMAGELIST)piml); 358 } 359 return S_OK; 360 } 361 362 HRESULT CMenuToolbarBase::Close() 363 { 364 if (m_hWnd) 365 DestroyWindow(); 366 367 if (m_pager.m_hWnd) 368 m_pager.DestroyWindow(); 369 370 return S_OK; 371 } 372 373 HRESULT CMenuToolbarBase::CreateToolbar(HWND hwndParent, DWORD dwFlags) 374 { 375 LONG tbStyles = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 376 TBSTYLE_TOOLTIPS | TBSTYLE_TRANSPARENT | TBSTYLE_REGISTERDROP | TBSTYLE_LIST | TBSTYLE_FLAT | TBSTYLE_CUSTOMERASE | 377 CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | CCS_TOP; 378 LONG tbExStyles = TBSTYLE_EX_DOUBLEBUFFER | WS_EX_TOOLWINDOW; 379 380 if (dwFlags & SMINIT_VERTICAL) 381 { 382 // Activate vertical semantics 383 tbStyles |= CCS_VERT; 384 385 #if USE_TBSTYLE_EX_VERTICAL 386 tbExStyles |= TBSTYLE_EX_VERTICAL; 387 #endif 388 } 389 390 m_initFlags = dwFlags; 391 392 // Get a temporary rect to use while creating the toolbar window. 393 // Ensure that it is not a null rect. 394 RECT rc; 395 if (!::GetClientRect(hwndParent, &rc) || 396 (rc.left == rc.right) || 397 (rc.top == rc.bottom)) 398 { 399 rc.left = 0; 400 rc.top = 0; 401 rc.right = 1; 402 rc.bottom = 1; 403 } 404 405 // HACK & FIXME: CORE-17505 406 SubclassWindow(CToolbar::Create(hwndParent, tbStyles, tbExStyles)); 407 408 SetWindowTheme(m_hWnd, L"", L""); 409 410 SystemParametersInfo(SPI_GETFLATMENU, 0, &m_useFlatMenus, 0); 411 412 m_menuBand->AdjustForTheme(m_useFlatMenus); 413 414 // If needed, create the pager. 415 if (m_usePager) 416 { 417 LONG pgStyles = PGS_VERT | WS_CHILD | WS_VISIBLE; 418 LONG pgExStyles = 0; 419 420 HWND hwndPager = CreateWindowEx( 421 pgExStyles, WC_PAGESCROLLER, NULL, 422 pgStyles, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, 423 hwndParent, NULL, _AtlBaseModule.GetModuleInstance(), 0); 424 425 m_pager.SubclassWindow(hwndPager); 426 427 ::SetParent(m_hWnd, hwndPager); 428 429 m_pager.SendMessageW(PGM_SETCHILD, 0, reinterpret_cast<LPARAM>(m_hWnd)); 430 } 431 432 // Configure the image lists 433 UpdateImageLists(); 434 435 return S_OK; 436 } 437 438 HRESULT CMenuToolbarBase::GetSizes(SIZE* pMinSize, SIZE* pMaxSize, SIZE* pIntegralSize) 439 { 440 if (pMinSize) 441 *pMinSize = m_idealSize; 442 if (pMaxSize) 443 *pMaxSize = m_idealSize; 444 if (pIntegralSize) 445 *pIntegralSize = m_itemSize; 446 447 if (m_hasSizes) 448 return S_OK; 449 450 TRACE("Sizes out of date, recalculating.\n"); 451 452 if (!m_hWnd) 453 { 454 return S_OK; 455 } 456 457 // Obtain the ideal size, to be used as min and max 458 GetMaxSize(&m_idealSize); 459 GetIdealSize((m_initFlags & SMINIT_VERTICAL) != 0, &m_idealSize); 460 461 TRACE("Ideal Size: (%d, %d) for %d buttons\n", m_idealSize, GetButtonCount()); 462 463 // Obtain the button size, to be used as the integral size 464 DWORD size = GetButtonSize(); 465 m_itemSize.cx = GET_X_LPARAM(size); 466 m_itemSize.cy = GET_Y_LPARAM(size); 467 m_hasSizes = TRUE; 468 469 if (pMinSize) 470 *pMinSize = m_idealSize; 471 if (pMaxSize) 472 *pMaxSize = m_idealSize; 473 if (pIntegralSize) 474 *pIntegralSize = m_itemSize; 475 476 return S_OK; 477 } 478 479 HRESULT CMenuToolbarBase::SetPosSize(int x, int y, int cx, int cy) 480 { 481 // Update the toolbar or pager to fit the requested rect 482 // If we have a pager, set the toolbar height to the ideal height of the toolbar 483 if (m_pager.m_hWnd) 484 { 485 SetWindowPos(NULL, x, y, cx, m_idealSize.cy, 0); 486 m_pager.SetWindowPos(NULL, x, y, cx, cy, 0); 487 } 488 else 489 { 490 SetWindowPos(NULL, x, y, cx, cy, 0); 491 } 492 493 // In a vertical menu, resize the buttons to fit the width 494 if (m_initFlags & SMINIT_VERTICAL) 495 { 496 DWORD btnSize = GetButtonSize(); 497 SetButtonSize(cx, GET_Y_LPARAM(btnSize)); 498 } 499 500 return S_OK; 501 } 502 503 HRESULT CMenuToolbarBase::IsWindowOwner(HWND hwnd) 504 { 505 if (m_hWnd && m_hWnd == hwnd) return S_OK; 506 if (m_pager.m_hWnd && m_pager.m_hWnd == hwnd) return S_OK; 507 return S_FALSE; 508 } 509 510 HRESULT CMenuToolbarBase::GetWindow(HWND *phwnd) 511 { 512 if (!phwnd) 513 return E_FAIL; 514 515 if (m_pager.m_hWnd) 516 *phwnd = m_pager.m_hWnd; 517 else 518 *phwnd = m_hWnd; 519 520 return S_OK; 521 } 522 523 HRESULT CMenuToolbarBase::OnGetInfoTip(NMTBGETINFOTIP * tip) 524 { 525 INT index; 526 DWORD_PTR dwData; 527 528 INT iItem = tip->iItem; 529 530 GetDataFromId(iItem, &index, &dwData); 531 532 return InternalGetTooltip(iItem, index, dwData, tip->pszText, tip->cchTextMax); 533 } 534 535 HRESULT CMenuToolbarBase::OnPopupTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 536 { 537 if (wParam != TIMERID_HOTTRACK) 538 { 539 bHandled = FALSE; 540 return 0; 541 } 542 543 KillTimer(TIMERID_HOTTRACK); 544 545 if (!m_timerEnabled) 546 return 0; 547 548 m_timerEnabled = FALSE; 549 550 if (m_hotItem < 0) 551 return 0; 552 553 // Returns S_FALSE if the current item did not show a submenu 554 HRESULT hr = PopupItem(m_hotItem, FALSE); 555 if (hr != S_FALSE) 556 return 0; 557 558 // If we didn't switch submenus, cancel the current popup regardless 559 if (m_popupBar) 560 { 561 HRESULT hr = CancelCurrentPopup(); 562 if (FAILED_UNEXPECTEDLY(hr)) 563 return 0; 564 } 565 566 return 0; 567 } 568 569 HRESULT CMenuToolbarBase::KillPopupTimer() 570 { 571 if (m_timerEnabled) 572 { 573 m_timerEnabled = FALSE; 574 KillTimer(TIMERID_HOTTRACK); 575 return S_OK; 576 } 577 return S_FALSE; 578 } 579 580 HRESULT CMenuToolbarBase::ChangeHotItem(CMenuToolbarBase * toolbar, INT item, DWORD dwFlags) 581 { 582 // Ignore the change if it already matches the stored info 583 if (m_hotBar == toolbar && m_hotItem == item) 584 return S_FALSE; 585 586 // Prevent a change of hot item if the change was triggered by the mouse, 587 // and mouse tracking is disabled. 588 if (m_disableMouseTrack && dwFlags & HICF_MOUSE) 589 { 590 TRACE("Hot item change prevented by DisableMouseTrack\n"); 591 return S_OK; 592 } 593 594 // Notify the toolbar if the hot-tracking left this toolbar 595 if (m_hotBar == this && toolbar != this) 596 { 597 SetHotItem(-1); 598 } 599 600 TRACE("Hot item changed from %p %p, to %p %p\n", m_hotBar, m_hotItem, toolbar, item); 601 m_hotBar = toolbar; 602 m_hotItem = item; 603 604 if (m_hotBar == this) 605 { 606 if (m_isTrackingPopup && !(m_initFlags & SMINIT_VERTICAL)) 607 { 608 // If the menubar has an open submenu, switch to the new item's submenu immediately 609 PopupItem(m_hotItem, FALSE); 610 } 611 else if (dwFlags & HICF_MOUSE) 612 { 613 // Vertical menus show/hide the submenu after a delay, 614 // but only with the mouse. 615 if (m_initFlags & SMINIT_VERTICAL) 616 { 617 DWORD elapsed = 0; 618 SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &elapsed, 0); 619 SetTimer(TIMERID_HOTTRACK, elapsed); 620 m_timerEnabled = TRUE; 621 TRACE("SetTimer called with m_hotItem=%d\n", m_hotItem); 622 } 623 } 624 else 625 { 626 TBBUTTONINFO info; 627 info.cbSize = sizeof(info); 628 info.dwMask = 0; 629 630 int index = GetButtonInfo(item, &info); 631 632 SetHotItem(index); 633 } 634 } 635 636 InvalidateDraw(); 637 return S_OK; 638 } 639 640 HRESULT CMenuToolbarBase::ChangePopupItem(CMenuToolbarBase * toolbar, INT item) 641 { 642 // Ignore the change if it already matches the stored info 643 if (m_popupBar == toolbar && m_popupItem == item) 644 return S_FALSE; 645 646 // Notify the toolbar if the popup-tracking this toolbar 647 if (m_popupBar == this && toolbar != this) 648 { 649 CheckButton(m_popupItem, FALSE); 650 m_isTrackingPopup = FALSE; 651 } 652 653 m_popupBar = toolbar; 654 m_popupItem = item; 655 656 if (m_popupBar == this) 657 { 658 CheckButton(m_popupItem, TRUE); 659 } 660 661 InvalidateDraw(); 662 return S_OK; 663 } 664 665 LRESULT CMenuToolbarBase::IsTrackedItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 666 { 667 TBBUTTON btn; 668 INT idx = (INT)wParam; 669 670 if (m_hotBar != this) 671 return S_FALSE; 672 673 if (idx < 0) 674 return S_FALSE; 675 676 if (!GetButton(idx, &btn)) 677 return E_FAIL; 678 679 if (m_hotItem == btn.idCommand) 680 return S_OK; 681 682 if (m_popupItem == btn.idCommand) 683 return S_OK; 684 685 return S_FALSE; 686 } 687 688 LRESULT CMenuToolbarBase::ChangeTrackedItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 689 { 690 TBBUTTON btn; 691 BOOL wasTracking = LOWORD(lParam); 692 BOOL mouse = HIWORD(lParam); 693 INT idx = (INT)wParam; 694 695 if (idx < 0) 696 { 697 m_isTrackingPopup = FALSE; 698 return m_menuBand->_ChangeHotItem(NULL, -1, HICF_MOUSE); 699 } 700 701 if (!GetButton(idx, &btn)) 702 return E_FAIL; 703 704 TRACE("ChangeTrackedItem %d, %d\n", idx, wasTracking); 705 m_isTrackingPopup = wasTracking; 706 return m_menuBand->_ChangeHotItem(this, btn.idCommand, mouse ? HICF_MOUSE : 0); 707 } 708 709 HRESULT CMenuToolbarBase::PopupSubMenu(UINT iItem, UINT index, IShellMenu* childShellMenu, BOOL keyInitiated) 710 { 711 // Calculate the submenu position and exclude area 712 RECT rc = { 0 }; 713 714 if (!GetItemRect(index, &rc)) 715 return E_FAIL; 716 717 POINT a = { rc.left, rc.top }; 718 POINT b = { rc.right, rc.bottom }; 719 720 ClientToScreen(&a); 721 ClientToScreen(&b); 722 723 POINTL pt = { a.x, b.y }; 724 RECTL rcl = { a.x, a.y, b.x, b.y }; 725 726 if (m_initFlags & SMINIT_VERTICAL) 727 { 728 // FIXME: Hardcoding this here feels hacky. 729 if (IsAppThemed()) 730 { 731 pt.x = b.x - 1; 732 pt.y = a.y - 1; 733 } 734 else 735 { 736 pt.x = b.x - 3; 737 pt.y = a.y - 3; 738 } 739 } 740 741 // Display the submenu 742 m_isTrackingPopup = TRUE; 743 744 m_menuBand->_ChangePopupItem(this, iItem); 745 m_menuBand->_OnPopupSubMenu(childShellMenu, &pt, &rcl, keyInitiated); 746 747 return S_OK; 748 } 749 750 HRESULT CMenuToolbarBase::PopupSubMenu(UINT iItem, UINT index, HMENU menu) 751 { 752 // Calculate the submenu position and exclude area 753 RECT rc = { 0 }; 754 755 if (!GetItemRect(index, &rc)) 756 return E_FAIL; 757 758 POINT a = { rc.left, rc.top }; 759 POINT b = { rc.right, rc.bottom }; 760 761 ClientToScreen(&a); 762 ClientToScreen(&b); 763 764 POINT pt = { a.x, b.y }; 765 RECT rcl = { a.x, a.y, b.x, b.y }; 766 767 if (m_initFlags & SMINIT_VERTICAL) 768 { 769 pt.x = b.x; 770 pt.y = a.y; 771 } 772 773 HMENU popup = GetSubMenu(menu, index); 774 775 // Display the submenu 776 m_isTrackingPopup = TRUE; 777 m_menuBand->_ChangePopupItem(this, iItem); 778 m_menuBand->_TrackSubMenu(popup, pt.x, pt.y, rcl); 779 m_menuBand->_ChangePopupItem(NULL, -1); 780 m_isTrackingPopup = FALSE; 781 782 return S_OK; 783 } 784 785 HRESULT CMenuToolbarBase::TrackContextMenu(IContextMenu* contextMenu, POINT pt) 786 { 787 // Cancel submenus 788 m_menuBand->_KillPopupTimers(); 789 if (m_popupBar) 790 m_menuBand->_CancelCurrentPopup(); 791 792 // Display the context menu 793 return m_menuBand->_TrackContextMenu(contextMenu, pt.x, pt.y); 794 } 795 796 HRESULT CMenuToolbarBase::BeforeCancelPopup() 797 { 798 m_cancelingPopup = TRUE; 799 TRACE("BeforeCancelPopup\n"); 800 return S_OK; 801 } 802 803 HRESULT CMenuToolbarBase::ProcessClick(INT iItem) 804 { 805 if (m_disableMouseTrack) 806 { 807 TRACE("Item click prevented by DisableMouseTrack\n"); 808 return S_OK; 809 } 810 811 // If a button is clicked while a submenu was open, cancel the submenu. 812 if (!(m_initFlags & SMINIT_VERTICAL) && m_isTrackingPopup) 813 { 814 TRACE("OnCommand cancelled because it was tracking submenu.\n"); 815 return S_FALSE; 816 } 817 818 if (PopupItem(iItem, FALSE) == S_OK) 819 { 820 TRACE("PopupItem returned S_OK\n"); 821 return S_FALSE; 822 } 823 824 TRACE("Executing...\n"); 825 826 return m_menuBand->_MenuItemSelect(MPOS_EXECUTE); 827 } 828 829 HRESULT CMenuToolbarBase::ProcessContextMenu(INT iItem) 830 { 831 INT index; 832 DWORD_PTR data; 833 834 GetDataFromId(iItem, &index, &data); 835 836 DWORD pos = GetMessagePos(); 837 POINT pt = { GET_X_LPARAM(pos), GET_Y_LPARAM(pos) }; 838 839 return InternalContextMenu(iItem, index, data, pt); 840 } 841 842 HRESULT CMenuToolbarBase::MenuBarMouseDown(INT iIndex, BOOL isLButton) 843 { 844 TBBUTTON btn; 845 846 GetButton(iIndex, &btn); 847 848 if ((m_initFlags & SMINIT_VERTICAL) 849 || m_popupBar 850 || m_cancelingPopup) 851 { 852 m_cancelingPopup = FALSE; 853 return S_OK; 854 } 855 856 return ProcessClick(btn.idCommand); 857 } 858 859 HRESULT CMenuToolbarBase::MenuBarMouseUp(INT iIndex, BOOL isLButton) 860 { 861 TBBUTTON btn; 862 863 m_cancelingPopup = FALSE; 864 865 if (!(m_initFlags & SMINIT_VERTICAL)) 866 return S_OK; 867 868 GetButton(iIndex, &btn); 869 870 if (isLButton) 871 return ProcessClick(btn.idCommand); 872 else 873 return ProcessContextMenu(btn.idCommand); 874 } 875 876 HRESULT CMenuToolbarBase::PrepareExecuteItem(INT iItem) 877 { 878 this->m_menuBand->_KillPopupTimers(); 879 880 m_executeItem = iItem; 881 return GetDataFromId(iItem, &m_executeIndex, &m_executeData); 882 } 883 884 HRESULT CMenuToolbarBase::ExecuteItem() 885 { 886 return InternalExecuteItem(m_executeItem, m_executeItem, m_executeData); 887 } 888 889 HRESULT CMenuToolbarBase::KeyboardItemChange(DWORD dwSelectType) 890 { 891 int prev = m_hotItem; 892 int index = -1; 893 894 if (dwSelectType != 0xFFFFFFFF) 895 { 896 int count = GetButtonCount(); 897 898 if (dwSelectType == VK_HOME) 899 { 900 index = 0; 901 dwSelectType = VK_DOWN; 902 } 903 else if (dwSelectType == VK_END) 904 { 905 index = count - 1; 906 dwSelectType = VK_UP; 907 } 908 else 909 { 910 if (m_hotItem >= 0) 911 { 912 TBBUTTONINFO info = { 0 }; 913 info.cbSize = sizeof(TBBUTTONINFO); 914 info.dwMask = 0; 915 index = GetButtonInfo(m_hotItem, &info); 916 } 917 918 if (index < 0) 919 { 920 if (dwSelectType == VK_UP) 921 { 922 index = count - 1; 923 } 924 else if (dwSelectType == VK_DOWN) 925 { 926 index = 0; 927 } 928 } 929 else 930 { 931 if (dwSelectType == VK_UP) 932 { 933 index--; 934 } 935 else if (dwSelectType == VK_DOWN) 936 { 937 index++; 938 } 939 } 940 } 941 942 TBBUTTON btn = { 0 }; 943 while (index >= 0 && index < count) 944 { 945 DWORD res = GetButton(index, &btn); 946 if (!res) 947 return E_FAIL; 948 949 if (btn.dwData) 950 { 951 if (prev != btn.idCommand) 952 { 953 TRACE("Setting Hot item to %d\n", index); 954 if (!(m_initFlags & SMINIT_VERTICAL) && m_isTrackingPopup) 955 { 956 HWND tlw; 957 m_menuBand->_GetTopLevelWindow(&tlw); 958 ::SendMessageW(tlw, WM_CANCELMODE, 0, 0); 959 PostMessageW(WM_USER_CHANGETRACKEDITEM, index, MAKELPARAM(m_isTrackingPopup, FALSE)); 960 } 961 else 962 m_menuBand->_ChangeHotItem(this, btn.idCommand, 0); 963 } 964 return S_OK; 965 } 966 967 if (dwSelectType == VK_UP) 968 { 969 index--; 970 } 971 else if (dwSelectType == VK_DOWN) 972 { 973 index++; 974 } 975 } 976 977 return S_FALSE; 978 } 979 980 if (prev != -1) 981 { 982 TRACE("Setting Hot item to null\n"); 983 m_menuBand->_ChangeHotItem(NULL, -1, 0); 984 } 985 986 return S_FALSE; 987 } 988 989 HRESULT CMenuToolbarBase::AddButton(DWORD commandId, LPCWSTR caption, BOOL hasSubMenu, INT iconId, DWORD_PTR buttonData, BOOL last) 990 { 991 TBBUTTON tbb = { 0 }; 992 993 tbb.fsState = TBSTATE_ENABLED; 994 #if !USE_TBSTYLE_EX_VERTICAL 995 if (!last && (m_initFlags & SMINIT_VERTICAL)) 996 tbb.fsState |= TBSTATE_WRAP; 997 #endif 998 tbb.fsStyle = BTNS_CHECKGROUP; 999 1000 if (hasSubMenu && (m_initFlags & SMINIT_VERTICAL)) 1001 tbb.fsStyle |= BTNS_DROPDOWN; 1002 1003 if (!(m_initFlags & SMINIT_VERTICAL)) 1004 tbb.fsStyle |= BTNS_AUTOSIZE; 1005 1006 tbb.iString = (INT_PTR) caption; 1007 tbb.idCommand = commandId; 1008 1009 tbb.iBitmap = iconId; 1010 tbb.dwData = buttonData; 1011 1012 m_hasSizes = FALSE; 1013 1014 if (!AddButtons(1, &tbb)) 1015 return HRESULT_FROM_WIN32(GetLastError()); 1016 return S_OK; 1017 } 1018 1019 HRESULT CMenuToolbarBase::AddSeparator(BOOL last) 1020 { 1021 TBBUTTON tbb = { 0 }; 1022 1023 tbb.fsState = TBSTATE_ENABLED; 1024 #if !USE_TBSTYLE_EX_VERTICAL 1025 if (!last && (m_initFlags & SMINIT_VERTICAL)) 1026 tbb.fsState |= TBSTATE_WRAP; 1027 #endif 1028 tbb.fsStyle = BTNS_SEP; 1029 tbb.iBitmap = 0; 1030 1031 m_hasSizes = FALSE; 1032 1033 if (!AddButtons(1, &tbb)) 1034 return HRESULT_FROM_WIN32(GetLastError()); 1035 1036 return S_OK; 1037 } 1038 1039 HRESULT CMenuToolbarBase::AddPlaceholder() 1040 { 1041 TBBUTTON tbb = { 0 }; 1042 WCHAR MenuString[128]; 1043 1044 LoadStringW(GetModuleHandle(L"shell32.dll"), IDS_MENU_EMPTY, MenuString, _countof(MenuString)); 1045 1046 tbb.fsState = 0; 1047 tbb.fsStyle = 0; 1048 tbb.iString = (INT_PTR) MenuString; 1049 tbb.iBitmap = -1; 1050 1051 m_hasSizes = FALSE; 1052 1053 if (!AddButtons(1, &tbb)) 1054 return HRESULT_FROM_WIN32(GetLastError()); 1055 1056 return S_OK; 1057 } 1058 1059 HRESULT CMenuToolbarBase::ClearToolbar() 1060 { 1061 while (DeleteButton(0)) 1062 { 1063 // empty; 1064 } 1065 m_hasSizes = FALSE; 1066 return S_OK; 1067 } 1068 1069 HRESULT CMenuToolbarBase::GetDataFromId(INT iItem, INT* pIndex, DWORD_PTR* pData) 1070 { 1071 if (pData) 1072 *pData = NULL; 1073 1074 if (pIndex) 1075 *pIndex = -1; 1076 1077 if (iItem < 0) 1078 return S_OK; 1079 1080 TBBUTTONINFO info = { 0 }; 1081 1082 info.cbSize = sizeof(TBBUTTONINFO); 1083 info.dwMask = TBIF_COMMAND | TBIF_LPARAM; 1084 1085 int index = GetButtonInfo(iItem, &info); 1086 if (index < 0) 1087 return E_FAIL; 1088 1089 if (pIndex) 1090 *pIndex = index; 1091 1092 if (pData) 1093 *pData = info.lParam; 1094 1095 return S_OK; 1096 } 1097 1098 HRESULT CMenuToolbarBase::CancelCurrentPopup() 1099 { 1100 return m_menuBand->_CancelCurrentPopup(); 1101 } 1102 1103 HRESULT CMenuToolbarBase::PopupItem(INT iItem, BOOL keyInitiated) 1104 { 1105 INT index; 1106 DWORD_PTR dwData; 1107 1108 if (iItem < 0) 1109 return S_OK; 1110 1111 if (m_popupBar == this && m_popupItem == iItem) 1112 return S_OK; 1113 1114 GetDataFromId(iItem, &index, &dwData); 1115 1116 HRESULT hr = InternalHasSubMenu(iItem, index, dwData); 1117 if (hr != S_OK) 1118 return hr; 1119 1120 if (m_popupBar) 1121 { 1122 HRESULT hr = CancelCurrentPopup(); 1123 if (FAILED_UNEXPECTEDLY(hr)) 1124 return hr; 1125 } 1126 1127 if (!(m_initFlags & SMINIT_VERTICAL)) 1128 { 1129 TRACE("PopupItem non-vertical %d %d\n", index, iItem); 1130 m_menuBand->_ChangeHotItem(this, iItem, 0); 1131 } 1132 1133 return InternalPopupItem(iItem, index, dwData, keyInitiated); 1134 } 1135 1136 CMenuStaticToolbar::CMenuStaticToolbar(CMenuBand *menuBand) : 1137 CMenuToolbarBase(menuBand, FALSE), 1138 m_hmenu(NULL), 1139 m_hwndMenu(NULL) 1140 { 1141 } 1142 1143 CMenuStaticToolbar::~CMenuStaticToolbar() 1144 { 1145 } 1146 1147 HRESULT CMenuStaticToolbar::GetMenu( 1148 _Out_opt_ HMENU *phmenu, 1149 _Out_opt_ HWND *phwnd, 1150 _Out_opt_ DWORD *pdwFlags) 1151 { 1152 if (phmenu) 1153 *phmenu = m_hmenu; 1154 if (phwnd) 1155 *phwnd = m_hwndMenu; 1156 if (pdwFlags) 1157 *pdwFlags = m_dwMenuFlags; 1158 1159 return S_OK; 1160 } 1161 1162 HRESULT CMenuStaticToolbar::SetMenu( 1163 HMENU hmenu, 1164 HWND hwnd, 1165 DWORD dwFlags) 1166 { 1167 m_hmenu = hmenu; 1168 m_hwndMenu = hwnd; 1169 m_dwMenuFlags = dwFlags; 1170 1171 if (IsWindow()) 1172 ClearToolbar(); 1173 1174 return S_OK; 1175 } 1176 1177 HRESULT CMenuStaticToolbar::FillToolbar(BOOL clearFirst) 1178 { 1179 int i; 1180 int ic = GetMenuItemCount(m_hmenu); 1181 1182 if (clearFirst) 1183 { 1184 ClearToolbar(); 1185 } 1186 1187 int count = 0; 1188 for (i = 0; i < ic; i++) 1189 { 1190 BOOL last = i + 1 == ic; 1191 1192 MENUITEMINFOW info; 1193 1194 info.cbSize = sizeof(info); 1195 info.dwTypeData = NULL; 1196 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID; 1197 1198 if (!GetMenuItemInfoW(m_hmenu, i, TRUE, &info)) 1199 { 1200 TRACE("Error obtaining info for menu item at pos=%d\n", i); 1201 continue; 1202 } 1203 1204 count++; 1205 1206 if (info.fType & MFT_SEPARATOR) 1207 { 1208 AddSeparator(last); 1209 } 1210 else if (!(info.fType & MFT_BITMAP)) 1211 { 1212 info.cch++; 1213 info.dwTypeData = (PWSTR) HeapAlloc(GetProcessHeap(), 0, (info.cch + 1) * sizeof(WCHAR)); 1214 1215 info.fMask = MIIM_STRING | MIIM_SUBMENU | MIIM_ID; 1216 GetMenuItemInfoW(m_hmenu, i, TRUE, &info); 1217 1218 SMINFO * sminfo = new SMINFO(); 1219 sminfo->dwMask = SMIM_ICON | SMIM_FLAGS; 1220 1221 HRESULT hr = m_menuBand->_CallCBWithItemId(info.wID, SMC_GETINFO, 0, reinterpret_cast<LPARAM>(sminfo)); 1222 if (FAILED_UNEXPECTEDLY(hr)) 1223 { 1224 delete sminfo; 1225 return hr; 1226 } 1227 1228 AddButton(info.wID, info.dwTypeData, info.hSubMenu != NULL, sminfo->iIcon, reinterpret_cast<DWORD_PTR>(sminfo), last); 1229 1230 HeapFree(GetProcessHeap(), 0, info.dwTypeData); 1231 } 1232 } 1233 1234 return S_OK; 1235 } 1236 1237 HRESULT CMenuStaticToolbar::InternalGetTooltip(INT iItem, INT index, DWORD_PTR dwData, LPWSTR pszText, INT cchTextMax) 1238 { 1239 //SMINFO * info = reinterpret_cast<SMINFO*>(dwData); 1240 UNIMPLEMENTED; 1241 return E_NOTIMPL; 1242 } 1243 1244 HRESULT CMenuStaticToolbar::OnDeletingButton(const NMTOOLBAR * tb) 1245 { 1246 delete reinterpret_cast<SMINFO*>(tb->tbButton.dwData); 1247 return S_OK; 1248 } 1249 1250 HRESULT CMenuStaticToolbar::InternalContextMenu(INT iItem, INT index, DWORD_PTR dwData, POINT pt) 1251 { 1252 CComPtr<IContextMenu> contextMenu; 1253 HRESULT hr = m_menuBand->_CallCBWithItemId(iItem, SMC_GETOBJECT, 1254 reinterpret_cast<WPARAM>(&IID_IContextMenu), reinterpret_cast<LPARAM>(&contextMenu)); 1255 if (hr != S_OK) 1256 return hr; 1257 1258 return TrackContextMenu(contextMenu, pt); 1259 } 1260 1261 HRESULT CMenuStaticToolbar::InternalExecuteItem(INT iItem, INT index, DWORD_PTR data) 1262 { 1263 return m_menuBand->_CallCBWithItemId(iItem, SMC_EXEC, 0, 0); 1264 } 1265 1266 HRESULT CMenuStaticToolbar::InternalPopupItem(INT iItem, INT index, DWORD_PTR dwData, BOOL keyInitiated) 1267 { 1268 SMINFO * nfo = reinterpret_cast<SMINFO*>(dwData); 1269 if (!nfo) 1270 return E_FAIL; 1271 1272 if (nfo->dwFlags&SMIF_TRACKPOPUP) 1273 { 1274 return PopupSubMenu(iItem, index, m_hmenu); 1275 } 1276 else 1277 { 1278 CComPtr<IShellMenu> shellMenu; 1279 HRESULT hr = m_menuBand->_CallCBWithItemId(iItem, SMC_GETOBJECT, reinterpret_cast<WPARAM>(&IID_IShellMenu), reinterpret_cast<LPARAM>(&shellMenu)); 1280 if (FAILED_UNEXPECTEDLY(hr)) 1281 return hr; 1282 1283 return PopupSubMenu(iItem, index, shellMenu, keyInitiated); 1284 } 1285 } 1286 1287 HRESULT CMenuStaticToolbar::InternalHasSubMenu(INT iItem, INT index, DWORD_PTR dwData) 1288 { 1289 return ::GetSubMenu(m_hmenu, index) ? S_OK : S_FALSE; 1290 } 1291 1292 CMenuSFToolbar::CMenuSFToolbar(CMenuBand * menuBand) : 1293 CMenuToolbarBase(menuBand, TRUE), 1294 m_shellFolder(NULL), 1295 m_idList(NULL), 1296 m_hKey(NULL) 1297 { 1298 } 1299 1300 CMenuSFToolbar::~CMenuSFToolbar() 1301 { 1302 } 1303 1304 int CALLBACK PidlListSort(void* item1, void* item2, LPARAM lParam) 1305 { 1306 IShellFolder * psf = (IShellFolder*) lParam; 1307 PCUIDLIST_RELATIVE pidl1 = (PCUIDLIST_RELATIVE) item1; 1308 PCUIDLIST_RELATIVE pidl2 = (PCUIDLIST_RELATIVE) item2; 1309 HRESULT hr = psf->CompareIDs(0, pidl1, pidl2); 1310 if (FAILED(hr)) 1311 { 1312 // No way to cancel, so sort to equal. 1313 return 0; 1314 } 1315 return (int)(short)LOWORD(hr); 1316 } 1317 1318 HRESULT CMenuSFToolbar::FillToolbar(BOOL clearFirst) 1319 { 1320 HRESULT hr; 1321 1322 CComPtr<IEnumIDList> eidl; 1323 hr = m_shellFolder->EnumObjects(GetToolbar(), SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &eidl); 1324 if (FAILED_UNEXPECTEDLY(hr)) 1325 return hr; 1326 1327 HDPA dpaSort = DPA_Create(10); 1328 1329 LPITEMIDLIST item = NULL; 1330 hr = eidl->Next(1, &item, NULL); 1331 while (hr == S_OK) 1332 { 1333 if (m_menuBand->_CallCBWithItemPidl(item, 0x10000000, 0, 0) == S_FALSE) 1334 { 1335 DPA_AppendPtr(dpaSort, item); 1336 } 1337 else 1338 { 1339 CoTaskMemFree(item); 1340 } 1341 1342 hr = eidl->Next(1, &item, NULL); 1343 } 1344 1345 // If no items were added, show the "empty" placeholder 1346 if (DPA_GetPtrCount(dpaSort) == 0) 1347 { 1348 DPA_Destroy(dpaSort); 1349 return AddPlaceholder(); 1350 } 1351 1352 TRACE("FillToolbar added %d items to the DPA\n", DPA_GetPtrCount(dpaSort)); 1353 1354 DPA_Sort(dpaSort, PidlListSort, (LPARAM) m_shellFolder.p); 1355 1356 for (int i = 0; i<DPA_GetPtrCount(dpaSort);) 1357 { 1358 PWSTR MenuString; 1359 1360 INT index = 0; 1361 INT indexOpen = 0; 1362 1363 STRRET sr = { STRRET_CSTR, { 0 } }; 1364 1365 item = (LPITEMIDLIST)DPA_GetPtr(dpaSort, i); 1366 1367 hr = m_shellFolder->GetDisplayNameOf(item, SIGDN_NORMALDISPLAY, &sr); 1368 if (FAILED_UNEXPECTEDLY(hr)) 1369 { 1370 DPA_Destroy(dpaSort); 1371 return hr; 1372 } 1373 1374 StrRetToStr(&sr, NULL, &MenuString); 1375 1376 index = SHMapPIDLToSystemImageListIndex(m_shellFolder, item, &indexOpen); 1377 1378 LPCITEMIDLIST itemc = item; 1379 1380 SFGAOF attrs = SFGAO_FOLDER; 1381 hr = m_shellFolder->GetAttributesOf(1, &itemc, &attrs); 1382 1383 DWORD_PTR dwData = reinterpret_cast<DWORD_PTR>(item); 1384 1385 // Fetch next item already, so we know if the current one is the last 1386 i++; 1387 1388 AddButton(i, MenuString, attrs & SFGAO_FOLDER, index, dwData, i >= DPA_GetPtrCount(dpaSort)); 1389 1390 CoTaskMemFree(MenuString); 1391 } 1392 1393 DPA_Destroy(dpaSort); 1394 return hr; 1395 } 1396 1397 HRESULT CMenuSFToolbar::InternalGetTooltip(INT iItem, INT index, DWORD_PTR dwData, LPWSTR pszText, INT cchTextMax) 1398 { 1399 //ITEMIDLIST * pidl = reinterpret_cast<LPITEMIDLIST>(dwData); 1400 UNIMPLEMENTED; 1401 return E_NOTIMPL; 1402 } 1403 1404 HRESULT CMenuSFToolbar::OnDeletingButton(const NMTOOLBAR * tb) 1405 { 1406 ILFree(reinterpret_cast<LPITEMIDLIST>(tb->tbButton.dwData)); 1407 return S_OK; 1408 } 1409 1410 HRESULT CMenuSFToolbar::SetShellFolder(IShellFolder *psf, LPCITEMIDLIST pidlFolder, HKEY hKey, DWORD dwFlags) 1411 { 1412 m_shellFolder = psf; 1413 m_idList = ILClone(pidlFolder); 1414 m_hKey = hKey; 1415 m_dwMenuFlags = dwFlags; 1416 1417 if (IsWindow()) 1418 ClearToolbar(); 1419 1420 return S_OK; 1421 } 1422 1423 HRESULT CMenuSFToolbar::GetShellFolder(DWORD *pdwFlags, LPITEMIDLIST *ppidl, REFIID riid, void **ppv) 1424 { 1425 HRESULT hr; 1426 1427 hr = m_shellFolder->QueryInterface(riid, ppv); 1428 if (FAILED_UNEXPECTEDLY(hr)) 1429 return hr; 1430 1431 if (pdwFlags) 1432 *pdwFlags = m_dwMenuFlags; 1433 1434 if (ppidl) 1435 { 1436 LPITEMIDLIST pidl = NULL; 1437 1438 if (m_idList) 1439 { 1440 pidl = ILClone(m_idList); 1441 if (!pidl) 1442 { 1443 ERR("ILClone failed!\n"); 1444 (*reinterpret_cast<IUnknown**>(ppv))->Release(); 1445 return E_FAIL; 1446 } 1447 } 1448 1449 *ppidl = pidl; 1450 } 1451 1452 return hr; 1453 } 1454 1455 HRESULT CMenuSFToolbar::InternalContextMenu(INT iItem, INT index, DWORD_PTR dwData, POINT pt) 1456 { 1457 HRESULT hr; 1458 CComPtr<IContextMenu> contextMenu = NULL; 1459 LPCITEMIDLIST pidl = reinterpret_cast<LPCITEMIDLIST>(dwData); 1460 1461 hr = m_shellFolder->GetUIObjectOf(GetToolbar(), 1, &pidl, IID_NULL_PPV_ARG(IContextMenu, &contextMenu)); 1462 if (FAILED_UNEXPECTEDLY(hr)) 1463 { 1464 return hr; 1465 } 1466 1467 hr = TrackContextMenu(contextMenu, pt); 1468 1469 return hr; 1470 } 1471 1472 HRESULT CMenuSFToolbar::InternalExecuteItem(INT iItem, INT index, DWORD_PTR data) 1473 { 1474 return m_menuBand->_CallCBWithItemPidl(reinterpret_cast<LPITEMIDLIST>(data), SMC_SFEXEC, 0, 0); 1475 } 1476 1477 HRESULT CMenuSFToolbar::InternalPopupItem(INT iItem, INT index, DWORD_PTR dwData, BOOL keyInitiated) 1478 { 1479 HRESULT hr; 1480 UINT uId; 1481 UINT uIdAncestor; 1482 DWORD flags; 1483 CComPtr<IShellMenuCallback> psmc; 1484 CComPtr<IShellMenu> shellMenu; 1485 1486 LPITEMIDLIST pidl = reinterpret_cast<LPITEMIDLIST>(dwData); 1487 1488 if (!pidl) 1489 return E_FAIL; 1490 1491 hr = CMenuBand_CreateInstance(IID_PPV_ARG(IShellMenu, &shellMenu)); 1492 if (FAILED_UNEXPECTEDLY(hr)) 1493 return hr; 1494 1495 m_menuBand->GetMenuInfo(&psmc, &uId, &uIdAncestor, &flags); 1496 1497 // FIXME: not sure what to use as uId/uIdAncestor here 1498 hr = shellMenu->Initialize(psmc, 0, uId, SMINIT_VERTICAL); 1499 if (FAILED_UNEXPECTEDLY(hr)) 1500 return hr; 1501 1502 CComPtr<IShellFolder> childFolder; 1503 hr = m_shellFolder->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &childFolder)); 1504 if (FAILED_UNEXPECTEDLY(hr)) 1505 return hr; 1506 1507 hr = shellMenu->SetShellFolder(childFolder, NULL, NULL, SMSET_TOP); 1508 if (FAILED_UNEXPECTEDLY(hr)) 1509 return hr; 1510 1511 return PopupSubMenu(iItem, index, shellMenu, keyInitiated); 1512 } 1513 1514 HRESULT CMenuSFToolbar::InternalHasSubMenu(INT iItem, INT index, DWORD_PTR dwData) 1515 { 1516 HRESULT hr; 1517 LPCITEMIDLIST pidl = reinterpret_cast<LPITEMIDLIST>(dwData); 1518 1519 SFGAOF attrs = SFGAO_FOLDER; 1520 hr = m_shellFolder->GetAttributesOf(1, &pidl, &attrs); 1521 if (FAILED_UNEXPECTEDLY(hr)) 1522 return hr; 1523 1524 return (attrs & SFGAO_FOLDER) ? S_OK : S_FALSE; 1525 } 1526