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