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