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