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