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