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