[SHELL32] -CMenuBand: When the flat menus style is enabled, shell menu bars should...
[reactos.git] / reactos / 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 // Unknown
109 case -714: return S_FALSE;
110
111 default:
112 TRACE("WM_NOTIFY unknown code %d, %d\n", hdr->code, hdr->idFrom);
113 return S_OK;
114 }
115 return S_FALSE;
116 }
117
118 return S_FALSE;
119 }
120
121 HRESULT CMenuToolbarBase::DisableMouseTrack(BOOL bDisable)
122 {
123 if (m_disableMouseTrack != bDisable)
124 {
125 m_disableMouseTrack = bDisable;
126 TRACE("DisableMouseTrack %d\n", bDisable);
127 }
128 return S_OK;
129 }
130
131 HRESULT CMenuToolbarBase::OnPagerCalcSize(LPNMPGCALCSIZE csize)
132 {
133 SIZE tbs;
134 GetSizes(NULL, &tbs, NULL);
135 if (csize->dwFlag == PGF_CALCHEIGHT)
136 {
137 csize->iHeight = tbs.cy;
138 }
139 else if (csize->dwFlag == PGF_CALCWIDTH)
140 {
141 csize->iWidth = tbs.cx;
142 }
143 return S_OK;
144 }
145
146 HRESULT CMenuToolbarBase::OnCustomDraw(LPNMTBCUSTOMDRAW cdraw, LRESULT * theResult)
147 {
148 bool isHot, isPopup;
149 TBBUTTONINFO btni;
150
151 switch (cdraw->nmcd.dwDrawStage)
152 {
153 case CDDS_PREPAINT:
154 *theResult = CDRF_NOTIFYITEMDRAW;
155 return S_OK;
156
157 case CDDS_ITEMPREPAINT:
158
159 // The item with an active submenu gets the CHECKED flag.
160 isHot = m_hotBar == this && (int) cdraw->nmcd.dwItemSpec == m_hotItem;
161 isPopup = m_popupBar == this && (int) cdraw->nmcd.dwItemSpec == m_popupItem;
162
163 if (m_hotItem < 0 && isPopup)
164 isHot = TRUE;
165
166 if ((m_useFlatMenus && isHot) || (m_initFlags & SMINIT_VERTICAL))
167 {
168 COLORREF clrText;
169 HBRUSH bgBrush;
170 RECT rc = cdraw->nmcd.rc;
171 HDC hdc = cdraw->nmcd.hdc;
172
173 // Remove HOT and CHECKED flags (will restore HOT if necessary)
174 cdraw->nmcd.uItemState &= ~(CDIS_HOT | CDIS_CHECKED);
175
176 // Decide on the colors
177 if (isHot)
178 {
179 cdraw->nmcd.uItemState |= CDIS_HOT;
180
181 clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
182 bgBrush = GetSysColorBrush(m_useFlatMenus ? COLOR_MENUHILIGHT : COLOR_HIGHLIGHT);
183 }
184 else
185 {
186 clrText = GetSysColor(COLOR_MENUTEXT);
187 bgBrush = GetSysColorBrush(COLOR_MENU);
188 }
189
190 // Paint the background color with the selected color
191 FillRect(hdc, &rc, bgBrush);
192
193 // Set the text color in advance, this color will be assigned when the ITEMPOSTPAINT triggers
194 SetTextColor(hdc, clrText);
195
196 // Set the text color, will be used by the internal drawing code
197 cdraw->clrText = clrText;
198 cdraw->iListGap += 4;
199
200 // Tell the default drawing code we don't want any fanciness, not even a background.
201 *theResult = CDRF_NOTIFYPOSTPAINT | TBCDRF_NOBACKGROUND | TBCDRF_NOEDGES | TBCDRF_NOOFFSET | TBCDRF_NOMARK | 0x00800000; // FIXME: the last bit is Vista+, useful for debugging only
202 }
203 else
204 {
205 // Remove HOT and CHECKED flags (will restore HOT if necessary)
206 cdraw->nmcd.uItemState &= ~CDIS_HOT;
207
208 // Decide on the colors
209 if (isHot)
210 {
211 cdraw->nmcd.uItemState |= CDIS_HOT;
212 }
213
214 *theResult = 0;
215 }
216
217 return S_OK;
218
219 case CDDS_ITEMPOSTPAINT:
220
221 // Fetch the button style
222 btni.cbSize = sizeof(btni);
223 btni.dwMask = TBIF_STYLE;
224 GetButtonInfo(cdraw->nmcd.dwItemSpec, &btni);
225
226 // Check if we need to draw a submenu arrow
227 if (btni.fsStyle & BTNS_DROPDOWN)
228 {
229 // TODO: Support RTL text modes by drawing a leftwards arrow aligned to the left of the control
230
231 // "8" is the rightwards dropdown arrow in the Marlett font
232 WCHAR text [] = L"8";
233
234 // Configure the font to draw with Marlett, keeping the current background color as-is
235 SelectObject(cdraw->nmcd.hdc, m_marlett);
236 SetBkMode(cdraw->nmcd.hdc, TRANSPARENT);
237
238 // Tweak the alignment by 1 pixel so the menu draws like the Windows start menu.
239 RECT rc = cdraw->nmcd.rc;
240 rc.right += 1;
241
242 // The arrow is drawn at the right of the item's rect, aligned vertically.
243 DrawTextEx(cdraw->nmcd.hdc, text, 1, &rc, DT_NOCLIP | DT_VCENTER | DT_RIGHT | DT_SINGLELINE, NULL);
244 }
245 *theResult = TRUE;
246 return S_OK;
247 }
248 return S_OK;
249 }
250
251 CMenuToolbarBase::CMenuToolbarBase(CMenuBand *menuBand, BOOL usePager) :
252 m_pager(this, 1),
253 m_useFlatMenus(FALSE),
254 m_SubclassOld(NULL),
255 m_disableMouseTrack(FALSE),
256 m_timerEnabled(FALSE),
257 m_menuBand(menuBand),
258 m_dwMenuFlags(0),
259 m_hasSizes(FALSE),
260 m_usePager(usePager),
261 m_hotBar(NULL),
262 m_hotItem(-1),
263 m_popupBar(NULL),
264 m_popupItem(-1),
265 m_isTrackingPopup(FALSE),
266 m_cancelingPopup(FALSE)
267 {
268 m_idealSize.cx = 0;
269 m_idealSize.cy = 0;
270 m_itemSize.cx = 0;
271 m_itemSize.cy = 0;
272 m_marlett = CreateFont(
273 0, 0, 0, 0, 0, 0, 0, 0, DEFAULT_CHARSET,
274 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
275 DEFAULT_QUALITY, FF_DONTCARE, L"Marlett");
276 }
277
278 CMenuToolbarBase::~CMenuToolbarBase()
279 {
280 if (m_hWnd)
281 DestroyWindow();
282
283 if (m_pager.m_hWnd)
284 m_pager.DestroyWindow();
285
286 DeleteObject(m_marlett);
287 }
288
289 void CMenuToolbarBase::InvalidateDraw()
290 {
291 InvalidateRect(NULL, FALSE);
292 }
293
294 HRESULT CMenuToolbarBase::ShowDW(BOOL fShow)
295 {
296 ShowWindow(fShow ? SW_SHOW : SW_HIDE);
297
298 // Ensure that the right image list is assigned to the toolbar
299 UpdateImageLists();
300
301 // For custom-drawing
302 SystemParametersInfo(SPI_GETFLATMENU, 0, &m_useFlatMenus, 0);
303
304 return S_OK;
305 }
306
307 HRESULT CMenuToolbarBase::UpdateImageLists()
308 {
309 if ((m_initFlags & (SMINIT_TOPLEVEL | SMINIT_VERTICAL)) == SMINIT_TOPLEVEL) // not vertical.
310 {
311 // No image list, prevents the buttons from having a margin at the left side
312 SetImageList(NULL);
313 return S_OK;
314 }
315
316 // Assign the correct imagelist and padding based on the current icon size
317
318 int shiml;
319 if (m_menuBand->UseBigIcons())
320 {
321 shiml = SHIL_LARGE;
322 SetPadding(4, 0);
323 }
324 else
325 {
326 shiml = SHIL_SMALL;
327 SetPadding(4, 4);
328 }
329
330 IImageList * piml;
331 HRESULT hr = SHGetImageList(shiml, IID_PPV_ARG(IImageList, &piml));
332 if (FAILED_UNEXPECTEDLY(hr))
333 {
334 SetImageList(NULL);
335 }
336 else
337 {
338 SetImageList((HIMAGELIST)piml);
339 }
340 return S_OK;
341 }
342
343 HRESULT CMenuToolbarBase::Close()
344 {
345 if (m_hWnd)
346 DestroyWindow();
347
348 if (m_pager.m_hWnd)
349 m_pager.DestroyWindow();
350
351 return S_OK;
352 }
353
354 HRESULT CMenuToolbarBase::CreateToolbar(HWND hwndParent, DWORD dwFlags)
355 {
356 LONG tbStyles = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
357 TBSTYLE_TOOLTIPS | TBSTYLE_TRANSPARENT | TBSTYLE_REGISTERDROP | TBSTYLE_LIST | TBSTYLE_FLAT | TBSTYLE_CUSTOMERASE |
358 CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | CCS_TOP;
359 LONG tbExStyles = TBSTYLE_EX_DOUBLEBUFFER | WS_EX_TOOLWINDOW;
360
361 if (dwFlags & SMINIT_VERTICAL)
362 {
363 // Activate vertical semantics
364 tbStyles |= CCS_VERT;
365
366 #if USE_TBSTYLE_EX_VERTICAL
367 tbExStyles |= TBSTYLE_EX_VERTICAL;
368 #endif
369 }
370
371 m_initFlags = dwFlags;
372
373 // Get a temporary rect to use while creating the toolbar window.
374 // Ensure that it is not a null rect.
375 RECT rc;
376 if (!::GetClientRect(hwndParent, &rc) ||
377 (rc.left == rc.right) ||
378 (rc.top == rc.bottom))
379 {
380 rc.left = 0;
381 rc.top = 0;
382 rc.right = 1;
383 rc.bottom = 1;
384 }
385
386 SubclassWindow(CToolbar::Create(hwndParent, tbStyles, tbExStyles));
387
388 SetWindowTheme(m_hWnd, L"", L"");
389
390 SystemParametersInfo(SPI_GETFLATMENU, 0, &m_useFlatMenus, 0);
391
392 m_menuBand->AdjustForTheme(m_useFlatMenus);
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 INT idx = (INT)wParam;
649
650 if (m_hotBar != this)
651 return S_FALSE;
652
653 if (idx < 0)
654 return S_FALSE;
655
656 if (!GetButton(idx, &btn))
657 return E_FAIL;
658
659 if (m_hotItem == btn.idCommand)
660 return S_OK;
661
662 if (m_popupItem == btn.idCommand)
663 return S_OK;
664
665 return S_FALSE;
666 }
667
668 LRESULT CMenuToolbarBase::ChangeTrackedItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
669 {
670 TBBUTTON btn;
671 BOOL wasTracking = LOWORD(lParam);
672 BOOL mouse = HIWORD(lParam);
673 INT idx = (INT)wParam;
674
675 if (idx < 0)
676 {
677 m_isTrackingPopup = FALSE;
678 return m_menuBand->_ChangeHotItem(NULL, -1, HICF_MOUSE);
679 }
680
681 if (!GetButton(idx, &btn))
682 return E_FAIL;
683
684 TRACE("ChangeTrackedItem %d, %d\n", idx, wasTracking);
685 m_isTrackingPopup = wasTracking;
686 return m_menuBand->_ChangeHotItem(this, btn.idCommand, mouse ? HICF_MOUSE : 0);
687 }
688
689 HRESULT CMenuToolbarBase::PopupSubMenu(UINT iItem, UINT index, IShellMenu* childShellMenu, BOOL keyInitiated)
690 {
691 // Calculate the submenu position and exclude area
692 RECT rc = { 0 };
693
694 if (!GetItemRect(index, &rc))
695 return E_FAIL;
696
697 POINT a = { rc.left, rc.top };
698 POINT b = { rc.right, rc.bottom };
699
700 ClientToScreen(&a);
701 ClientToScreen(&b);
702
703 POINTL pt = { a.x, b.y };
704 RECTL rcl = { a.x, a.y, b.x, b.y };
705
706 if (m_initFlags & SMINIT_VERTICAL)
707 {
708 // FIXME: Hardcoding this here feels hacky.
709 if (IsAppThemed())
710 {
711 pt.x = b.x - 1;
712 pt.y = a.y - 1;
713 }
714 else
715 {
716 pt.x = b.x - 3;
717 pt.y = a.y - 3;
718 }
719 }
720
721 // Display the submenu
722 m_isTrackingPopup = TRUE;
723
724 m_menuBand->_ChangePopupItem(this, iItem);
725 m_menuBand->_OnPopupSubMenu(childShellMenu, &pt, &rcl, keyInitiated);
726
727 return S_OK;
728 }
729
730 HRESULT CMenuToolbarBase::PopupSubMenu(UINT iItem, UINT index, HMENU menu)
731 {
732 // Calculate the submenu position and exclude area
733 RECT rc = { 0 };
734
735 if (!GetItemRect(index, &rc))
736 return E_FAIL;
737
738 POINT a = { rc.left, rc.top };
739 POINT b = { rc.right, rc.bottom };
740
741 ClientToScreen(&a);
742 ClientToScreen(&b);
743
744 POINT pt = { a.x, b.y };
745 RECT rcl = { a.x, a.y, b.x, b.y };
746
747 if (m_initFlags & SMINIT_VERTICAL)
748 {
749 pt.x = b.x;
750 pt.y = a.y;
751 }
752
753 HMENU popup = GetSubMenu(menu, index);
754
755 // Display the submenu
756 m_isTrackingPopup = TRUE;
757 m_menuBand->_ChangePopupItem(this, iItem);
758 m_menuBand->_TrackSubMenu(popup, pt.x, pt.y, rcl);
759 m_menuBand->_ChangePopupItem(NULL, -1);
760 m_isTrackingPopup = FALSE;
761
762 return S_OK;
763 }
764
765 HRESULT CMenuToolbarBase::TrackContextMenu(IContextMenu* contextMenu, POINT pt)
766 {
767 // Cancel submenus
768 m_menuBand->_KillPopupTimers();
769 if (m_popupBar)
770 m_menuBand->_CancelCurrentPopup();
771
772 // Display the context menu
773 return m_menuBand->_TrackContextMenu(contextMenu, pt.x, pt.y);
774 }
775
776 HRESULT CMenuToolbarBase::BeforeCancelPopup()
777 {
778 m_cancelingPopup = TRUE;
779 TRACE("BeforeCancelPopup\n");
780 return S_OK;
781 }
782
783 HRESULT CMenuToolbarBase::ProcessClick(INT iItem)
784 {
785 if (m_disableMouseTrack)
786 {
787 TRACE("Item click prevented by DisableMouseTrack\n");
788 return S_OK;
789 }
790
791 // If a button is clicked while a submenu was open, cancel the submenu.
792 if (!(m_initFlags & SMINIT_VERTICAL) && m_isTrackingPopup)
793 {
794 TRACE("OnCommand cancelled because it was tracking submenu.\n");
795 return S_FALSE;
796 }
797
798 if (PopupItem(iItem, FALSE) == S_OK)
799 {
800 TRACE("PopupItem returned S_OK\n");
801 return S_FALSE;
802 }
803
804 TRACE("Executing...\n");
805
806 return m_menuBand->_MenuItemSelect(MPOS_EXECUTE);
807 }
808
809 HRESULT CMenuToolbarBase::ProcessContextMenu(INT iItem)
810 {
811 INT index;
812 DWORD_PTR data;
813
814 GetDataFromId(iItem, &index, &data);
815
816 DWORD pos = GetMessagePos();
817 POINT pt = { GET_X_LPARAM(pos), GET_Y_LPARAM(pos) };
818
819 return InternalContextMenu(iItem, index, data, pt);
820 }
821
822 HRESULT CMenuToolbarBase::MenuBarMouseDown(INT iIndex, BOOL isLButton)
823 {
824 TBBUTTON btn;
825
826 GetButton(iIndex, &btn);
827
828 if ((m_initFlags & SMINIT_VERTICAL)
829 || m_popupBar
830 || m_cancelingPopup)
831 {
832 m_cancelingPopup = FALSE;
833 return S_OK;
834 }
835
836 return ProcessClick(btn.idCommand);
837 }
838
839 HRESULT CMenuToolbarBase::MenuBarMouseUp(INT iIndex, BOOL isLButton)
840 {
841 TBBUTTON btn;
842
843 m_cancelingPopup = FALSE;
844
845 if (!(m_initFlags & SMINIT_VERTICAL))
846 return S_OK;
847
848 GetButton(iIndex, &btn);
849
850 if (isLButton)
851 return ProcessClick(btn.idCommand);
852 else
853 return ProcessContextMenu(btn.idCommand);
854 }
855
856 HRESULT CMenuToolbarBase::PrepareExecuteItem(INT iItem)
857 {
858 this->m_menuBand->_KillPopupTimers();
859
860 m_executeItem = iItem;
861 return GetDataFromId(iItem, &m_executeIndex, &m_executeData);
862 }
863
864 HRESULT CMenuToolbarBase::ExecuteItem()
865 {
866 return InternalExecuteItem(m_executeItem, m_executeItem, m_executeData);
867 }
868
869 HRESULT CMenuToolbarBase::KeyboardItemChange(DWORD dwSelectType)
870 {
871 int prev = m_hotItem;
872 int index = -1;
873
874 if (dwSelectType != 0xFFFFFFFF)
875 {
876 int count = GetButtonCount();
877
878 if (dwSelectType == VK_HOME)
879 {
880 index = 0;
881 dwSelectType = VK_DOWN;
882 }
883 else if (dwSelectType == VK_END)
884 {
885 index = count - 1;
886 dwSelectType = VK_UP;
887 }
888 else
889 {
890 if (m_hotItem >= 0)
891 {
892 TBBUTTONINFO info = { 0 };
893 info.cbSize = sizeof(TBBUTTONINFO);
894 info.dwMask = 0;
895 index = GetButtonInfo(m_hotItem, &info);
896 }
897
898 if (index < 0)
899 {
900 if (dwSelectType == VK_UP)
901 {
902 index = count - 1;
903 }
904 else if (dwSelectType == VK_DOWN)
905 {
906 index = 0;
907 }
908 }
909 else
910 {
911 if (dwSelectType == VK_UP)
912 {
913 index--;
914 }
915 else if (dwSelectType == VK_DOWN)
916 {
917 index++;
918 }
919 }
920 }
921
922 TBBUTTON btn = { 0 };
923 while (index >= 0 && index < count)
924 {
925 DWORD res = GetButton(index, &btn);
926 if (!res)
927 return E_FAIL;
928
929 if (btn.dwData)
930 {
931 if (prev != btn.idCommand)
932 {
933 TRACE("Setting Hot item to %d\n", index);
934 if (!(m_initFlags & SMINIT_VERTICAL) && m_isTrackingPopup)
935 {
936 HWND tlw;
937 m_menuBand->_GetTopLevelWindow(&tlw);
938 SendMessageW(tlw, WM_CANCELMODE, 0, 0);
939 PostMessageW(WM_USER_CHANGETRACKEDITEM, index, MAKELPARAM(m_isTrackingPopup, FALSE));
940 }
941 else
942 m_menuBand->_ChangeHotItem(this, btn.idCommand, 0);
943 }
944 return S_OK;
945 }
946
947 if (dwSelectType == VK_UP)
948 {
949 index--;
950 }
951 else if (dwSelectType == VK_DOWN)
952 {
953 index++;
954 }
955 }
956
957 return S_FALSE;
958 }
959
960 if (prev != -1)
961 {
962 TRACE("Setting Hot item to null\n");
963 m_menuBand->_ChangeHotItem(NULL, -1, 0);
964 }
965
966 return S_FALSE;
967 }
968
969 HRESULT CMenuToolbarBase::AddButton(DWORD commandId, LPCWSTR caption, BOOL hasSubMenu, INT iconId, DWORD_PTR buttonData, BOOL last)
970 {
971 TBBUTTON tbb = { 0 };
972
973 tbb.fsState = TBSTATE_ENABLED;
974 #if !USE_TBSTYLE_EX_VERTICAL
975 if (!last && (m_initFlags & SMINIT_VERTICAL))
976 tbb.fsState |= TBSTATE_WRAP;
977 #endif
978 tbb.fsStyle = BTNS_CHECKGROUP;
979
980 if (hasSubMenu && (m_initFlags & SMINIT_VERTICAL))
981 tbb.fsStyle |= BTNS_DROPDOWN;
982
983 if (!(m_initFlags & SMINIT_VERTICAL))
984 tbb.fsStyle |= BTNS_AUTOSIZE;
985
986 tbb.iString = (INT_PTR) caption;
987 tbb.idCommand = commandId;
988
989 tbb.iBitmap = iconId;
990 tbb.dwData = buttonData;
991
992 m_hasSizes = FALSE;
993
994 if (!AddButtons(1, &tbb))
995 return HRESULT_FROM_WIN32(GetLastError());
996 return S_OK;
997 }
998
999 HRESULT CMenuToolbarBase::AddSeparator(BOOL last)
1000 {
1001 TBBUTTON tbb = { 0 };
1002
1003 tbb.fsState = TBSTATE_ENABLED;
1004 #if !USE_TBSTYLE_EX_VERTICAL
1005 if (!last && (m_initFlags & SMINIT_VERTICAL))
1006 tbb.fsState |= TBSTATE_WRAP;
1007 #endif
1008 tbb.fsStyle = BTNS_SEP;
1009 tbb.iBitmap = 0;
1010
1011 m_hasSizes = FALSE;
1012
1013 if (!AddButtons(1, &tbb))
1014 return HRESULT_FROM_WIN32(GetLastError());
1015
1016 return S_OK;
1017 }
1018
1019 HRESULT CMenuToolbarBase::AddPlaceholder()
1020 {
1021 TBBUTTON tbb = { 0 };
1022 WCHAR MenuString[128];
1023
1024 LoadStringW(GetModuleHandle(L"shell32.dll"), IDS_MENU_EMPTY, MenuString, _countof(MenuString));
1025
1026 tbb.fsState = 0;
1027 tbb.fsStyle = 0;
1028 tbb.iString = (INT_PTR) MenuString;
1029 tbb.iBitmap = -1;
1030
1031 m_hasSizes = FALSE;
1032
1033 if (!AddButtons(1, &tbb))
1034 return HRESULT_FROM_WIN32(GetLastError());
1035
1036 return S_OK;
1037 }
1038
1039 HRESULT CMenuToolbarBase::ClearToolbar()
1040 {
1041 while (DeleteButton(0))
1042 {
1043 // empty;
1044 }
1045 m_hasSizes = FALSE;
1046 return S_OK;
1047 }
1048
1049 HRESULT CMenuToolbarBase::GetDataFromId(INT iItem, INT* pIndex, DWORD_PTR* pData)
1050 {
1051 if (pData)
1052 *pData = NULL;
1053
1054 if (pIndex)
1055 *pIndex = -1;
1056
1057 if (iItem < 0)
1058 return S_OK;
1059
1060 TBBUTTONINFO info = { 0 };
1061
1062 info.cbSize = sizeof(TBBUTTONINFO);
1063 info.dwMask = TBIF_COMMAND | TBIF_LPARAM;
1064
1065 int index = GetButtonInfo(iItem, &info);
1066 if (index < 0)
1067 return E_FAIL;
1068
1069 if (pIndex)
1070 *pIndex = index;
1071
1072 if (pData)
1073 *pData = info.lParam;
1074
1075 return S_OK;
1076 }
1077
1078 HRESULT CMenuToolbarBase::CancelCurrentPopup()
1079 {
1080 return m_menuBand->_CancelCurrentPopup();
1081 }
1082
1083 HRESULT CMenuToolbarBase::PopupItem(INT iItem, BOOL keyInitiated)
1084 {
1085 INT index;
1086 DWORD_PTR dwData;
1087
1088 if (iItem < 0)
1089 return S_OK;
1090
1091 if (m_popupBar == this && m_popupItem == iItem)
1092 return S_OK;
1093
1094 GetDataFromId(iItem, &index, &dwData);
1095
1096 HRESULT hr = InternalHasSubMenu(iItem, index, dwData);
1097 if (hr != S_OK)
1098 return hr;
1099
1100 if (m_popupBar)
1101 {
1102 HRESULT hr = CancelCurrentPopup();
1103 if (FAILED_UNEXPECTEDLY(hr))
1104 return hr;
1105 }
1106
1107 if (!(m_initFlags & SMINIT_VERTICAL))
1108 {
1109 TRACE("PopupItem non-vertical %d %d\n", index, iItem);
1110 m_menuBand->_ChangeHotItem(this, iItem, 0);
1111 }
1112
1113 return InternalPopupItem(iItem, index, dwData, keyInitiated);
1114 }
1115
1116 CMenuStaticToolbar::CMenuStaticToolbar(CMenuBand *menuBand) :
1117 CMenuToolbarBase(menuBand, FALSE),
1118 m_hmenu(NULL),
1119 m_hwndMenu(NULL)
1120 {
1121 }
1122
1123 CMenuStaticToolbar::~CMenuStaticToolbar()
1124 {
1125 }
1126
1127 HRESULT CMenuStaticToolbar::GetMenu(
1128 _Out_opt_ HMENU *phmenu,
1129 _Out_opt_ HWND *phwnd,
1130 _Out_opt_ DWORD *pdwFlags)
1131 {
1132 if (phmenu)
1133 *phmenu = m_hmenu;
1134 if (phwnd)
1135 *phwnd = m_hwndMenu;
1136 if (pdwFlags)
1137 *pdwFlags = m_dwMenuFlags;
1138
1139 return S_OK;
1140 }
1141
1142 HRESULT CMenuStaticToolbar::SetMenu(
1143 HMENU hmenu,
1144 HWND hwnd,
1145 DWORD dwFlags)
1146 {
1147 m_hmenu = hmenu;
1148 m_hwndMenu = hwnd;
1149 m_dwMenuFlags = dwFlags;
1150
1151 ClearToolbar();
1152
1153 return S_OK;
1154 }
1155
1156 HRESULT CMenuStaticToolbar::FillToolbar(BOOL clearFirst)
1157 {
1158 int i;
1159 int ic = GetMenuItemCount(m_hmenu);
1160
1161 if (clearFirst)
1162 {
1163 ClearToolbar();
1164 }
1165
1166 int count = 0;
1167 for (i = 0; i < ic; i++)
1168 {
1169 BOOL last = i + 1 == ic;
1170
1171 MENUITEMINFOW info;
1172
1173 info.cbSize = sizeof(info);
1174 info.dwTypeData = NULL;
1175 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1176
1177 if (!GetMenuItemInfoW(m_hmenu, i, TRUE, &info))
1178 {
1179 TRACE("Error obtaining info for menu item at pos=%d\n", i);
1180 continue;
1181 }
1182
1183 count++;
1184
1185 if (info.fType & MFT_SEPARATOR)
1186 {
1187 AddSeparator(last);
1188 }
1189 else if (!(info.fType & MFT_BITMAP))
1190 {
1191 info.cch++;
1192 info.dwTypeData = (PWSTR) HeapAlloc(GetProcessHeap(), 0, (info.cch + 1) * sizeof(WCHAR));
1193
1194 info.fMask = MIIM_STRING | MIIM_SUBMENU | MIIM_ID;
1195 GetMenuItemInfoW(m_hmenu, i, TRUE, &info);
1196
1197 SMINFO * sminfo = new SMINFO();
1198 sminfo->dwMask = SMIM_ICON | SMIM_FLAGS;
1199
1200 HRESULT hr = m_menuBand->_CallCBWithItemId(info.wID, SMC_GETINFO, 0, reinterpret_cast<LPARAM>(sminfo));
1201 if (FAILED_UNEXPECTEDLY(hr))
1202 {
1203 delete sminfo;
1204 return hr;
1205 }
1206
1207 AddButton(info.wID, info.dwTypeData, info.hSubMenu != NULL, sminfo->iIcon, reinterpret_cast<DWORD_PTR>(sminfo), last);
1208
1209 HeapFree(GetProcessHeap(), 0, info.dwTypeData);
1210 }
1211 }
1212
1213 return S_OK;
1214 }
1215
1216 HRESULT CMenuStaticToolbar::InternalGetTooltip(INT iItem, INT index, DWORD_PTR dwData, LPWSTR pszText, INT cchTextMax)
1217 {
1218 //SMINFO * info = reinterpret_cast<SMINFO*>(dwData);
1219 UNIMPLEMENTED;
1220 return E_NOTIMPL;
1221 }
1222
1223 HRESULT CMenuStaticToolbar::OnDeletingButton(const NMTOOLBAR * tb)
1224 {
1225 delete reinterpret_cast<SMINFO*>(tb->tbButton.dwData);
1226 return S_OK;
1227 }
1228
1229 HRESULT CMenuStaticToolbar::InternalContextMenu(INT iItem, INT index, DWORD_PTR dwData, POINT pt)
1230 {
1231 CComPtr<IContextMenu> contextMenu;
1232 HRESULT hr = m_menuBand->_CallCBWithItemId(iItem, SMC_GETOBJECT,
1233 reinterpret_cast<WPARAM>(&IID_IContextMenu), reinterpret_cast<LPARAM>(&contextMenu));
1234 if (hr != S_OK)
1235 return hr;
1236
1237 return TrackContextMenu(contextMenu, pt);
1238 }
1239
1240 HRESULT CMenuStaticToolbar::InternalExecuteItem(INT iItem, INT index, DWORD_PTR data)
1241 {
1242 return m_menuBand->_CallCBWithItemId(iItem, SMC_EXEC, 0, 0);
1243 }
1244
1245 HRESULT CMenuStaticToolbar::InternalPopupItem(INT iItem, INT index, DWORD_PTR dwData, BOOL keyInitiated)
1246 {
1247 SMINFO * nfo = reinterpret_cast<SMINFO*>(dwData);
1248 if (!nfo)
1249 return E_FAIL;
1250
1251 if (nfo->dwFlags&SMIF_TRACKPOPUP)
1252 {
1253 return PopupSubMenu(iItem, index, m_hmenu);
1254 }
1255 else
1256 {
1257 CComPtr<IShellMenu> shellMenu;
1258 HRESULT hr = m_menuBand->_CallCBWithItemId(iItem, SMC_GETOBJECT, reinterpret_cast<WPARAM>(&IID_IShellMenu), reinterpret_cast<LPARAM>(&shellMenu));
1259 if (FAILED_UNEXPECTEDLY(hr))
1260 return hr;
1261
1262 return PopupSubMenu(iItem, index, shellMenu, keyInitiated);
1263 }
1264 }
1265
1266 HRESULT CMenuStaticToolbar::InternalHasSubMenu(INT iItem, INT index, DWORD_PTR dwData)
1267 {
1268 return ::GetSubMenu(m_hmenu, index) ? S_OK : S_FALSE;
1269 }
1270
1271 CMenuSFToolbar::CMenuSFToolbar(CMenuBand * menuBand) :
1272 CMenuToolbarBase(menuBand, TRUE),
1273 m_shellFolder(NULL),
1274 m_idList(NULL),
1275 m_hKey(NULL)
1276 {
1277 }
1278
1279 CMenuSFToolbar::~CMenuSFToolbar()
1280 {
1281 }
1282
1283 int CALLBACK PidlListSort(void* item1, void* item2, LPARAM lParam)
1284 {
1285 IShellFolder * psf = (IShellFolder*) lParam;
1286 PCUIDLIST_RELATIVE pidl1 = (PCUIDLIST_RELATIVE) item1;
1287 PCUIDLIST_RELATIVE pidl2 = (PCUIDLIST_RELATIVE) item2;
1288 HRESULT hr = psf->CompareIDs(0, pidl1, pidl2);
1289 if (FAILED(hr))
1290 {
1291 // No way to cancel, so sort to equal.
1292 return 0;
1293 }
1294 return (int)(short)LOWORD(hr);
1295 }
1296
1297 HRESULT CMenuSFToolbar::FillToolbar(BOOL clearFirst)
1298 {
1299 HRESULT hr;
1300
1301 CComPtr<IEnumIDList> eidl;
1302 hr = m_shellFolder->EnumObjects(GetToolbar(), SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &eidl);
1303 if (FAILED_UNEXPECTEDLY(hr))
1304 return hr;
1305
1306 HDPA dpaSort = DPA_Create(10);
1307
1308 LPITEMIDLIST item = NULL;
1309 hr = eidl->Next(1, &item, NULL);
1310 while (hr == S_OK)
1311 {
1312 if (m_menuBand->_CallCBWithItemPidl(item, 0x10000000, 0, 0) == S_FALSE)
1313 {
1314 DPA_AppendPtr(dpaSort, ILClone(item));
1315 }
1316
1317 hr = eidl->Next(1, &item, NULL);
1318 }
1319
1320 // If no items were added, show the "empty" placeholder
1321 if (DPA_GetPtrCount(dpaSort) == 0)
1322 {
1323 DPA_Destroy(dpaSort);
1324 return AddPlaceholder();
1325 }
1326
1327 TRACE("FillToolbar added %d items to the DPA\n", DPA_GetPtrCount(dpaSort));
1328
1329 DPA_Sort(dpaSort, PidlListSort, (LPARAM) m_shellFolder.p);
1330
1331 for (int i = 0; i<DPA_GetPtrCount(dpaSort);)
1332 {
1333 PWSTR MenuString;
1334
1335 INT index = 0;
1336 INT indexOpen = 0;
1337
1338 STRRET sr = { STRRET_CSTR, { 0 } };
1339
1340 item = (LPITEMIDLIST)DPA_GetPtr(dpaSort, i);
1341
1342 hr = m_shellFolder->GetDisplayNameOf(item, SIGDN_NORMALDISPLAY, &sr);
1343 if (FAILED_UNEXPECTEDLY(hr))
1344 {
1345 DPA_Destroy(dpaSort);
1346 return hr;
1347 }
1348
1349 StrRetToStr(&sr, NULL, &MenuString);
1350
1351 index = SHMapPIDLToSystemImageListIndex(m_shellFolder, item, &indexOpen);
1352
1353 LPCITEMIDLIST itemc = item;
1354
1355 SFGAOF attrs = SFGAO_FOLDER;
1356 hr = m_shellFolder->GetAttributesOf(1, &itemc, &attrs);
1357
1358 DWORD_PTR dwData = reinterpret_cast<DWORD_PTR>(item);
1359
1360 // Fetch next item already, so we know if the current one is the last
1361 i++;
1362
1363 AddButton(i, MenuString, attrs & SFGAO_FOLDER, index, dwData, i >= DPA_GetPtrCount(dpaSort));
1364
1365 CoTaskMemFree(MenuString);
1366 }
1367
1368 DPA_Destroy(dpaSort);
1369 return hr;
1370 }
1371
1372 HRESULT CMenuSFToolbar::InternalGetTooltip(INT iItem, INT index, DWORD_PTR dwData, LPWSTR pszText, INT cchTextMax)
1373 {
1374 //ITEMIDLIST * pidl = reinterpret_cast<LPITEMIDLIST>(dwData);
1375 UNIMPLEMENTED;
1376 return E_NOTIMPL;
1377 }
1378
1379 HRESULT CMenuSFToolbar::OnDeletingButton(const NMTOOLBAR * tb)
1380 {
1381 ILFree(reinterpret_cast<LPITEMIDLIST>(tb->tbButton.dwData));
1382 return S_OK;
1383 }
1384
1385 HRESULT CMenuSFToolbar::SetShellFolder(IShellFolder *psf, LPCITEMIDLIST pidlFolder, HKEY hKey, DWORD dwFlags)
1386 {
1387 m_shellFolder = psf;
1388 m_idList = ILClone(pidlFolder);
1389 m_hKey = hKey;
1390 m_dwMenuFlags = dwFlags;
1391
1392 ClearToolbar();
1393
1394 return S_OK;
1395 }
1396
1397 HRESULT CMenuSFToolbar::GetShellFolder(DWORD *pdwFlags, LPITEMIDLIST *ppidl, REFIID riid, void **ppv)
1398 {
1399 HRESULT hr;
1400
1401 hr = m_shellFolder->QueryInterface(riid, ppv);
1402 if (FAILED_UNEXPECTEDLY(hr))
1403 return hr;
1404
1405 if (pdwFlags)
1406 *pdwFlags = m_dwMenuFlags;
1407
1408 if (ppidl)
1409 {
1410 LPITEMIDLIST pidl = NULL;
1411
1412 if (m_idList)
1413 {
1414 pidl = ILClone(m_idList);
1415 if (!pidl)
1416 {
1417 ERR("ILClone failed!\n");
1418 (*reinterpret_cast<IUnknown**>(ppv))->Release();
1419 return E_FAIL;
1420 }
1421 }
1422
1423 *ppidl = pidl;
1424 }
1425
1426 return hr;
1427 }
1428
1429 HRESULT CMenuSFToolbar::InternalContextMenu(INT iItem, INT index, DWORD_PTR dwData, POINT pt)
1430 {
1431 HRESULT hr;
1432 CComPtr<IContextMenu> contextMenu = NULL;
1433 LPCITEMIDLIST pidl = reinterpret_cast<LPCITEMIDLIST>(dwData);
1434
1435 hr = m_shellFolder->GetUIObjectOf(GetToolbar(), 1, &pidl, IID_NULL_PPV_ARG(IContextMenu, &contextMenu));
1436 if (FAILED_UNEXPECTEDLY(hr))
1437 {
1438 return hr;
1439 }
1440
1441 hr = TrackContextMenu(contextMenu, pt);
1442
1443 return hr;
1444 }
1445
1446 HRESULT CMenuSFToolbar::InternalExecuteItem(INT iItem, INT index, DWORD_PTR data)
1447 {
1448 return m_menuBand->_CallCBWithItemPidl(reinterpret_cast<LPITEMIDLIST>(data), SMC_SFEXEC, 0, 0);
1449 }
1450
1451 HRESULT CMenuSFToolbar::InternalPopupItem(INT iItem, INT index, DWORD_PTR dwData, BOOL keyInitiated)
1452 {
1453 HRESULT hr;
1454 UINT uId;
1455 UINT uIdAncestor;
1456 DWORD flags;
1457 CComPtr<IShellMenuCallback> psmc;
1458 CComPtr<IShellMenu> shellMenu;
1459
1460 LPITEMIDLIST pidl = reinterpret_cast<LPITEMIDLIST>(dwData);
1461
1462 if (!pidl)
1463 return E_FAIL;
1464
1465 hr = CMenuBand_Constructor(IID_PPV_ARG(IShellMenu, &shellMenu));
1466 if (FAILED_UNEXPECTEDLY(hr))
1467 return hr;
1468
1469 m_menuBand->GetMenuInfo(&psmc, &uId, &uIdAncestor, &flags);
1470
1471 // FIXME: not sure what to use as uId/uIdAncestor here
1472 hr = shellMenu->Initialize(psmc, 0, uId, SMINIT_VERTICAL);
1473 if (FAILED_UNEXPECTEDLY(hr))
1474 return hr;
1475
1476 CComPtr<IShellFolder> childFolder;
1477 hr = m_shellFolder->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &childFolder));
1478 if (FAILED_UNEXPECTEDLY(hr))
1479 return hr;
1480
1481 hr = shellMenu->SetShellFolder(childFolder, NULL, NULL, 0);
1482 if (FAILED_UNEXPECTEDLY(hr))
1483 return hr;
1484
1485 return PopupSubMenu(iItem, index, shellMenu, keyInitiated);
1486 }
1487
1488 HRESULT CMenuSFToolbar::InternalHasSubMenu(INT iItem, INT index, DWORD_PTR dwData)
1489 {
1490 HRESULT hr;
1491 LPCITEMIDLIST pidl = reinterpret_cast<LPITEMIDLIST>(dwData);
1492
1493 SFGAOF attrs = SFGAO_FOLDER;
1494 hr = m_shellFolder->GetAttributesOf(1, &pidl, &attrs);
1495 if (FAILED_UNEXPECTEDLY(hr))
1496 return hr;
1497
1498 return (attrs & SFGAO_FOLDER) ? S_OK : S_FALSE;
1499 }