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