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