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