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