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