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