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