62afe5d3d2040b7d6f6e76a58214a3490a8861a5
[reactos.git] / reactos / base / shell / rshell / CMenuToolbars.cpp
1 /*
2 * Shell Menu Band
3 *
4 * Copyright 2014 David Quintana
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20 #include "precomp.h"
21 #include <commoncontrols.h>
22 #include <shlwapi_undoc.h>
23 #include <uxtheme.h>
24 #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 if (IsAppThemed())
300 GetThemeSysBool(GetWindowTheme(m_hWnd), TMT_FLATMENUS);
301 else
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 if (IsAppThemed())
391 GetThemeSysBool(GetWindowTheme(m_hWnd), TMT_FLATMENUS);
392 else
393 SystemParametersInfo(SPI_GETFLATMENU, 0, &m_useFlatMenus, 0);
394
395 m_menuBand->AdjustForTheme(m_useFlatMenus);
396
397 // If needed, create the pager.
398 if (m_usePager)
399 {
400 LONG pgStyles = PGS_VERT | WS_CHILD | WS_VISIBLE;
401 LONG pgExStyles = 0;
402
403 HWND hwndPager = CreateWindowEx(
404 pgExStyles, WC_PAGESCROLLER, NULL,
405 pgStyles, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
406 hwndParent, NULL, _AtlBaseModule.GetModuleInstance(), 0);
407
408 m_pager.SubclassWindow(hwndPager);
409
410 ::SetParent(m_hWnd, hwndPager);
411
412 m_pager.SendMessageW(PGM_SETCHILD, 0, reinterpret_cast<LPARAM>(m_hWnd));
413 }
414
415 // Configure the image lists
416 UpdateImageLists();
417
418 return S_OK;
419 }
420
421 HRESULT CMenuToolbarBase::GetSizes(SIZE* pMinSize, SIZE* pMaxSize, SIZE* pIntegralSize)
422 {
423 if (pMinSize)
424 *pMinSize = m_idealSize;
425 if (pMaxSize)
426 *pMaxSize = m_idealSize;
427 if (pIntegralSize)
428 *pIntegralSize = m_itemSize;
429
430 if (m_hasSizes)
431 return S_OK;
432
433 TRACE("Sizes out of date, recalculating.\n");
434
435 if (!m_hWnd)
436 {
437 return S_OK;
438 }
439
440 // Obtain the ideal size, to be used as min and max
441 GetMaxSize(&m_idealSize);
442 GetIdealSize((m_initFlags & SMINIT_VERTICAL) != 0, &m_idealSize);
443
444 TRACE("Ideal Size: (%d, %d) for %d buttons\n", m_idealSize, GetButtonCount());
445
446 // Obtain the button size, to be used as the integral size
447 DWORD size = GetButtonSize();
448 m_itemSize.cx = GET_X_LPARAM(size);
449 m_itemSize.cy = GET_Y_LPARAM(size);
450 m_hasSizes = TRUE;
451
452 if (pMinSize)
453 *pMinSize = m_idealSize;
454 if (pMaxSize)
455 *pMaxSize = m_idealSize;
456 if (pIntegralSize)
457 *pIntegralSize = m_itemSize;
458
459 return S_OK;
460 }
461
462 HRESULT CMenuToolbarBase::SetPosSize(int x, int y, int cx, int cy)
463 {
464 // Update the toolbar or pager to fit the requested rect
465 // If we have a pager, set the toolbar height to the ideal height of the toolbar
466 if (m_pager.m_hWnd)
467 {
468 SetWindowPos(NULL, x, y, cx, m_idealSize.cy, 0);
469 m_pager.SetWindowPos(NULL, x, y, cx, cy, 0);
470 }
471 else
472 {
473 SetWindowPos(NULL, x, y, cx, cy, 0);
474 }
475
476 // In a vertical menu, resize the buttons to fit the width
477 if (m_initFlags & SMINIT_VERTICAL)
478 {
479 DWORD btnSize = GetButtonSize();
480 SetButtonSize(cx, GET_Y_LPARAM(btnSize));
481 }
482
483 return S_OK;
484 }
485
486 HRESULT CMenuToolbarBase::IsWindowOwner(HWND hwnd)
487 {
488 if (m_hWnd && m_hWnd == hwnd) return S_OK;
489 if (m_pager.m_hWnd && m_pager.m_hWnd == hwnd) return S_OK;
490 return S_FALSE;
491 }
492
493 HRESULT CMenuToolbarBase::GetWindow(HWND *phwnd)
494 {
495 if (!phwnd)
496 return E_FAIL;
497
498 if (m_pager.m_hWnd)
499 *phwnd = m_pager.m_hWnd;
500 else
501 *phwnd = m_hWnd;
502
503 return S_OK;
504 }
505
506 HRESULT CMenuToolbarBase::OnGetInfoTip(NMTBGETINFOTIP * tip)
507 {
508 INT index;
509 DWORD_PTR dwData;
510
511 INT iItem = tip->iItem;
512
513 GetDataFromId(iItem, &index, &dwData);
514
515 return InternalGetTooltip(iItem, index, dwData, tip->pszText, tip->cchTextMax);
516 }
517
518 HRESULT CMenuToolbarBase::OnPopupTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
519 {
520 if (wParam != TIMERID_HOTTRACK)
521 {
522 bHandled = FALSE;
523 return 0;
524 }
525
526 KillTimer(TIMERID_HOTTRACK);
527
528 if (!m_timerEnabled)
529 return 0;
530
531 m_timerEnabled = FALSE;
532
533 if (m_hotItem < 0)
534 return 0;
535
536 // Returns S_FALSE if the current item did not show a submenu
537 HRESULT hr = PopupItem(m_hotItem, FALSE);
538 if (hr != S_FALSE)
539 return 0;
540
541 // If we didn't switch submenus, cancel the current popup regardless
542 if (m_popupBar)
543 {
544 HRESULT hr = CancelCurrentPopup();
545 if (FAILED_UNEXPECTEDLY(hr))
546 return 0;
547 }
548
549 return 0;
550 }
551
552 HRESULT CMenuToolbarBase::KillPopupTimer()
553 {
554 if (m_timerEnabled)
555 {
556 m_timerEnabled = FALSE;
557 KillTimer(TIMERID_HOTTRACK);
558 return S_OK;
559 }
560 return S_FALSE;
561 }
562
563 HRESULT CMenuToolbarBase::ChangeHotItem(CMenuToolbarBase * toolbar, INT item, DWORD dwFlags)
564 {
565 // Ignore the change if it already matches the stored info
566 if (m_hotBar == toolbar && m_hotItem == item)
567 return S_FALSE;
568
569 // Prevent a change of hot item if the change was triggered by the mouse,
570 // and mouse tracking is disabled.
571 if (m_disableMouseTrack && dwFlags & HICF_MOUSE)
572 {
573 TRACE("Hot item change prevented by DisableMouseTrack\n");
574 return S_OK;
575 }
576
577 // Notify the toolbar if the hot-tracking left this toolbar
578 if (m_hotBar == this && toolbar != this)
579 {
580 SetHotItem(-1);
581 }
582
583 TRACE("Hot item changed from %p %p, to %p %p\n", m_hotBar, m_hotItem, toolbar, item);
584 m_hotBar = toolbar;
585 m_hotItem = item;
586
587 if (m_hotBar == this)
588 {
589 if (m_isTrackingPopup && !(m_initFlags & SMINIT_VERTICAL))
590 {
591 // If the menubar has an open submenu, switch to the new item's submenu immediately
592 PopupItem(m_hotItem, FALSE);
593 }
594 else if (dwFlags & HICF_MOUSE)
595 {
596 // Vertical menus show/hide the submenu after a delay,
597 // but only with the mouse.
598 if (m_initFlags & SMINIT_VERTICAL)
599 {
600 DWORD elapsed = 0;
601 SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &elapsed, 0);
602 SetTimer(TIMERID_HOTTRACK, elapsed);
603 m_timerEnabled = TRUE;
604 TRACE("SetTimer called with m_hotItem=%d\n", m_hotItem);
605 }
606 }
607 else
608 {
609 TBBUTTONINFO info;
610 info.cbSize = sizeof(info);
611 info.dwMask = 0;
612
613 int index = GetButtonInfo(item, &info);
614
615 SetHotItem(index);
616 }
617 }
618
619 InvalidateDraw();
620 return S_OK;
621 }
622
623 HRESULT CMenuToolbarBase::ChangePopupItem(CMenuToolbarBase * toolbar, INT item)
624 {
625 // Ignore the change if it already matches the stored info
626 if (m_popupBar == toolbar && m_popupItem == item)
627 return S_FALSE;
628
629 // Notify the toolbar if the popup-tracking this toolbar
630 if (m_popupBar == this && toolbar != this)
631 {
632 CheckButton(m_popupItem, FALSE);
633 m_isTrackingPopup = FALSE;
634 }
635
636 m_popupBar = toolbar;
637 m_popupItem = item;
638
639 if (m_popupBar == this)
640 {
641 CheckButton(m_popupItem, TRUE);
642 }
643
644 InvalidateDraw();
645 return S_OK;
646 }
647
648 LRESULT CMenuToolbarBase::IsTrackedItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
649 {
650 TBBUTTON btn;
651 INT idx = (INT)wParam;
652
653 if (m_hotBar != this)
654 return S_FALSE;
655
656 if (idx < 0)
657 return S_FALSE;
658
659 if (!GetButton(idx, &btn))
660 return E_FAIL;
661
662 if (m_hotItem == btn.idCommand)
663 return S_OK;
664
665 if (m_popupItem == btn.idCommand)
666 return S_OK;
667
668 return S_FALSE;
669 }
670
671 LRESULT CMenuToolbarBase::ChangeTrackedItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
672 {
673 TBBUTTON btn;
674 BOOL wasTracking = LOWORD(lParam);
675 BOOL mouse = HIWORD(lParam);
676 INT idx = (INT)wParam;
677
678 if (idx < 0)
679 {
680 m_isTrackingPopup = FALSE;
681 return m_menuBand->_ChangeHotItem(NULL, -1, HICF_MOUSE);
682 }
683
684 if (!GetButton(idx, &btn))
685 return E_FAIL;
686
687 TRACE("ChangeTrackedItem %d, %d\n", idx, wasTracking);
688 m_isTrackingPopup = wasTracking;
689 return m_menuBand->_ChangeHotItem(this, btn.idCommand, mouse ? HICF_MOUSE : 0);
690 }
691
692 HRESULT CMenuToolbarBase::PopupSubMenu(UINT iItem, UINT index, IShellMenu* childShellMenu, BOOL keyInitiated)
693 {
694 // Calculate the submenu position and exclude area
695 RECT rc = { 0 };
696
697 if (!GetItemRect(index, &rc))
698 return E_FAIL;
699
700 POINT a = { rc.left, rc.top };
701 POINT b = { rc.right, rc.bottom };
702
703 ::ClientToScreen(m_hWnd, &a);
704 ::ClientToScreen(m_hWnd, &b);
705
706 POINTL pt = { a.x, b.y };
707 RECTL rcl = { a.x, a.y, b.x, b.y };
708
709 if (m_initFlags & SMINIT_VERTICAL)
710 {
711 // FIXME: Hardcoding this here feels hacky.
712 if (IsAppThemed())
713 {
714 pt.x = b.x - 1;
715 pt.y = a.y - 1;
716 }
717 else
718 {
719 pt.x = b.x - 3;
720 pt.y = a.y - 3;
721 }
722 }
723
724 // Display the submenu
725 m_isTrackingPopup = TRUE;
726
727 m_menuBand->_ChangePopupItem(this, iItem);
728 m_menuBand->_OnPopupSubMenu(childShellMenu, &pt, &rcl, keyInitiated);
729
730 return S_OK;
731 }
732
733 HRESULT CMenuToolbarBase::PopupSubMenu(UINT iItem, UINT index, HMENU menu)
734 {
735 // Calculate the submenu position and exclude area
736 RECT rc = { 0 };
737
738 if (!GetItemRect(index, &rc))
739 return E_FAIL;
740
741 POINT a = { rc.left, rc.top };
742 POINT b = { rc.right, rc.bottom };
743
744 ::ClientToScreen(m_hWnd, &a);
745 ::ClientToScreen(m_hWnd, &b);
746
747 POINT pt = { a.x, b.y };
748 RECT rcl = { a.x, a.y, b.x, b.y };
749
750 if (m_initFlags & SMINIT_VERTICAL)
751 {
752 pt.x = b.x;
753 pt.y = a.y;
754 }
755
756 HMENU popup = GetSubMenu(menu, index);
757
758 // Display the submenu
759 m_isTrackingPopup = TRUE;
760 m_menuBand->_ChangePopupItem(this, iItem);
761 m_menuBand->_TrackSubMenu(popup, pt.x, pt.y, rcl);
762 m_menuBand->_ChangePopupItem(NULL, -1);
763 m_isTrackingPopup = FALSE;
764
765 return S_OK;
766 }
767
768 HRESULT CMenuToolbarBase::TrackContextMenu(IContextMenu* contextMenu, POINT pt)
769 {
770 // Cancel submenus
771 m_menuBand->_KillPopupTimers();
772 if (m_popupBar)
773 m_menuBand->_CancelCurrentPopup();
774
775 // Display the context menu
776 return m_menuBand->_TrackContextMenu(contextMenu, pt.x, pt.y);
777 }
778
779 HRESULT CMenuToolbarBase::BeforeCancelPopup()
780 {
781 m_cancelingPopup = TRUE;
782 TRACE("BeforeCancelPopup\n");
783 return S_OK;
784 }
785
786 HRESULT CMenuToolbarBase::ProcessClick(INT iItem)
787 {
788 if (m_disableMouseTrack)
789 {
790 TRACE("Item click prevented by DisableMouseTrack\n");
791 return S_OK;
792 }
793
794 // If a button is clicked while a submenu was open, cancel the submenu.
795 if (!(m_initFlags & SMINIT_VERTICAL) && m_isTrackingPopup)
796 {
797 TRACE("OnCommand cancelled because it was tracking submenu.\n");
798 return S_FALSE;
799 }
800
801 if (PopupItem(iItem, FALSE) == S_OK)
802 {
803 TRACE("PopupItem returned S_OK\n");
804 return S_FALSE;
805 }
806
807 TRACE("Executing...\n");
808
809 return m_menuBand->_MenuItemSelect(MPOS_EXECUTE);
810 }
811
812 HRESULT CMenuToolbarBase::ProcessContextMenu(INT iItem)
813 {
814 INT index;
815 DWORD_PTR data;
816
817 GetDataFromId(iItem, &index, &data);
818
819 DWORD pos = GetMessagePos();
820 POINT pt = { GET_X_LPARAM(pos), GET_Y_LPARAM(pos) };
821
822 return InternalContextMenu(iItem, index, data, pt);
823 }
824
825 HRESULT CMenuToolbarBase::MenuBarMouseDown(INT iIndex, BOOL isLButton)
826 {
827 TBBUTTON btn;
828
829 GetButton(iIndex, &btn);
830 if (!isLButton)
831 return ProcessContextMenu(btn.idCommand);
832
833 if ((m_initFlags & SMINIT_VERTICAL)
834 || m_popupBar
835 || m_cancelingPopup)
836 {
837 m_cancelingPopup = FALSE;
838 return S_OK;
839 }
840
841 return ProcessClick(btn.idCommand);
842 }
843
844 HRESULT CMenuToolbarBase::MenuBarMouseUp(INT iIndex)
845 {
846 TBBUTTON btn;
847
848 m_cancelingPopup = FALSE;
849
850 if (!(m_initFlags & SMINIT_VERTICAL))
851 return S_OK;
852
853 GetButton(iIndex, &btn);
854 return ProcessClick(btn.idCommand);
855 }
856
857 HRESULT CMenuToolbarBase::PrepareExecuteItem(INT iItem)
858 {
859 this->m_menuBand->_KillPopupTimers();
860
861 m_executeItem = iItem;
862 return GetDataFromId(iItem, &m_executeIndex, &m_executeData);
863 }
864
865 HRESULT CMenuToolbarBase::ExecuteItem()
866 {
867 return InternalExecuteItem(m_executeItem, m_executeItem, m_executeData);
868 }
869
870 HRESULT CMenuToolbarBase::KeyboardItemChange(DWORD dwSelectType)
871 {
872 int prev = m_hotItem;
873 int index = -1;
874
875 if (dwSelectType != 0xFFFFFFFF)
876 {
877 int count = GetButtonCount();
878
879 if (dwSelectType == VK_HOME)
880 {
881 index = 0;
882 dwSelectType = VK_DOWN;
883 }
884 else if (dwSelectType == VK_END)
885 {
886 index = count - 1;
887 dwSelectType = VK_UP;
888 }
889 else
890 {
891 if (m_hotItem >= 0)
892 {
893 TBBUTTONINFO info = { 0 };
894 info.cbSize = sizeof(TBBUTTONINFO);
895 info.dwMask = 0;
896 index = GetButtonInfo(m_hotItem, &info);
897 }
898
899 if (index < 0)
900 {
901 if (dwSelectType == VK_UP)
902 {
903 index = count - 1;
904 }
905 else if (dwSelectType == VK_DOWN)
906 {
907 index = 0;
908 }
909 }
910 else
911 {
912 if (dwSelectType == VK_UP)
913 {
914 index--;
915 }
916 else if (dwSelectType == VK_DOWN)
917 {
918 index++;
919 }
920 }
921 }
922
923 TBBUTTON btn = { 0 };
924 while (index >= 0 && index < count)
925 {
926 DWORD res = GetButton(index, &btn);
927 if (!res)
928 return E_FAIL;
929
930 if (btn.dwData)
931 {
932 if (prev != btn.idCommand)
933 {
934 TRACE("Setting Hot item to %d\n", index);
935 if (!(m_initFlags & SMINIT_VERTICAL) && m_isTrackingPopup)
936 {
937 HWND tlw;
938 m_menuBand->_GetTopLevelWindow(&tlw);
939 SendMessageW(tlw, WM_CANCELMODE, 0, 0);
940 ::PostMessageW(m_hWnd, WM_USER_CHANGETRACKEDITEM, index, MAKELPARAM(m_isTrackingPopup, FALSE));
941 }
942 else
943 m_menuBand->_ChangeHotItem(this, btn.idCommand, 0);
944 }
945 return S_OK;
946 }
947
948 if (dwSelectType == VK_UP)
949 {
950 index--;
951 }
952 else if (dwSelectType == VK_DOWN)
953 {
954 index++;
955 }
956 }
957
958 return S_FALSE;
959 }
960
961 if (prev != -1)
962 {
963 TRACE("Setting Hot item to null\n");
964 m_menuBand->_ChangeHotItem(NULL, -1, 0);
965 }
966
967 return S_FALSE;
968 }
969
970 HRESULT CMenuToolbarBase::AddButton(DWORD commandId, LPCWSTR caption, BOOL hasSubMenu, INT iconId, DWORD_PTR buttonData, BOOL last)
971 {
972 TBBUTTON tbb = { 0 };
973
974 tbb.fsState = TBSTATE_ENABLED;
975 #if !USE_TBSTYLE_EX_VERTICAL
976 if (!last && (m_initFlags & SMINIT_VERTICAL))
977 tbb.fsState |= TBSTATE_WRAP;
978 #endif
979 tbb.fsStyle = BTNS_CHECKGROUP;
980
981 if (hasSubMenu && (m_initFlags & SMINIT_VERTICAL))
982 tbb.fsStyle |= BTNS_DROPDOWN;
983
984 if (!(m_initFlags & SMINIT_VERTICAL))
985 tbb.fsStyle |= BTNS_AUTOSIZE;
986
987 tbb.iString = (INT_PTR) caption;
988 tbb.idCommand = commandId;
989
990 tbb.iBitmap = iconId;
991 tbb.dwData = buttonData;
992
993 m_hasSizes = FALSE;
994
995 if (!AddButtons(1, &tbb))
996 return HRESULT_FROM_WIN32(GetLastError());
997 return S_OK;
998 }
999
1000 HRESULT CMenuToolbarBase::AddSeparator(BOOL last)
1001 {
1002 TBBUTTON tbb = { 0 };
1003
1004 tbb.fsState = TBSTATE_ENABLED;
1005 #if !USE_TBSTYLE_EX_VERTICAL
1006 if (!last && (m_initFlags & SMINIT_VERTICAL))
1007 tbb.fsState |= TBSTATE_WRAP;
1008 #endif
1009 tbb.fsStyle = BTNS_SEP;
1010 tbb.iBitmap = 0;
1011
1012 m_hasSizes = FALSE;
1013
1014 if (!AddButtons(1, &tbb))
1015 return HRESULT_FROM_WIN32(GetLastError());
1016
1017 return S_OK;
1018 }
1019
1020 HRESULT CMenuToolbarBase::AddPlaceholder()
1021 {
1022 TBBUTTON tbb = { 0 };
1023 WCHAR MenuString[128];
1024
1025 LoadStringW(GetModuleHandle(L"shell32.dll"), IDS_MENU_EMPTY, MenuString, _countof(MenuString));
1026
1027 tbb.fsState = 0;
1028 tbb.fsStyle = 0;
1029 tbb.iString = (INT_PTR) MenuString;
1030 tbb.iBitmap = -1;
1031
1032 m_hasSizes = FALSE;
1033
1034 if (!AddButtons(1, &tbb))
1035 return HRESULT_FROM_WIN32(GetLastError());
1036
1037 return S_OK;
1038 }
1039
1040 HRESULT CMenuToolbarBase::ClearToolbar()
1041 {
1042 while (DeleteButton(0))
1043 {
1044 // empty;
1045 }
1046 m_hasSizes = FALSE;
1047 return S_OK;
1048 }
1049
1050 HRESULT CMenuToolbarBase::GetDataFromId(INT iItem, INT* pIndex, DWORD_PTR* pData)
1051 {
1052 if (pData)
1053 *pData = NULL;
1054
1055 if (pIndex)
1056 *pIndex = -1;
1057
1058 if (iItem < 0)
1059 return S_OK;
1060
1061 TBBUTTONINFO info = { 0 };
1062
1063 info.cbSize = sizeof(TBBUTTONINFO);
1064 info.dwMask = TBIF_COMMAND | TBIF_LPARAM;
1065
1066 int index = GetButtonInfo(iItem, &info);
1067 if (index < 0)
1068 return E_FAIL;
1069
1070 if (pIndex)
1071 *pIndex = index;
1072
1073 if (pData)
1074 *pData = info.lParam;
1075
1076 return S_OK;
1077 }
1078
1079 HRESULT CMenuToolbarBase::CancelCurrentPopup()
1080 {
1081 return m_menuBand->_CancelCurrentPopup();
1082 }
1083
1084 HRESULT CMenuToolbarBase::PopupItem(INT iItem, BOOL keyInitiated)
1085 {
1086 INT index;
1087 DWORD_PTR dwData;
1088
1089 if (iItem < 0)
1090 return S_OK;
1091
1092 if (m_popupBar == this && m_popupItem == iItem)
1093 return S_OK;
1094
1095 GetDataFromId(iItem, &index, &dwData);
1096
1097 HRESULT hr = InternalHasSubMenu(iItem, index, dwData);
1098 if (hr != S_OK)
1099 return hr;
1100
1101 if (m_popupBar)
1102 {
1103 HRESULT hr = CancelCurrentPopup();
1104 if (FAILED_UNEXPECTEDLY(hr))
1105 return hr;
1106 }
1107
1108 if (!(m_initFlags & SMINIT_VERTICAL))
1109 {
1110 TRACE("PopupItem non-vertical %d %d\n", index, iItem);
1111 m_menuBand->_ChangeHotItem(this, iItem, 0);
1112 }
1113
1114 return InternalPopupItem(iItem, index, dwData, keyInitiated);
1115 }
1116
1117 CMenuStaticToolbar::CMenuStaticToolbar(CMenuBand *menuBand) :
1118 CMenuToolbarBase(menuBand, FALSE),
1119 m_hmenu(NULL),
1120 m_hwndMenu(NULL)
1121 {
1122 }
1123
1124 CMenuStaticToolbar::~CMenuStaticToolbar()
1125 {
1126 }
1127
1128 HRESULT CMenuStaticToolbar::GetMenu(
1129 _Out_opt_ HMENU *phmenu,
1130 _Out_opt_ HWND *phwnd,
1131 _Out_opt_ DWORD *pdwFlags)
1132 {
1133 if (phmenu)
1134 *phmenu = m_hmenu;
1135 if (phwnd)
1136 *phwnd = m_hwndMenu;
1137 if (pdwFlags)
1138 *pdwFlags = m_dwMenuFlags;
1139
1140 return S_OK;
1141 }
1142
1143 HRESULT CMenuStaticToolbar::SetMenu(
1144 HMENU hmenu,
1145 HWND hwnd,
1146 DWORD dwFlags)
1147 {
1148 m_hmenu = hmenu;
1149 m_hwndMenu = hwnd;
1150 m_dwMenuFlags = dwFlags;
1151
1152 ClearToolbar();
1153
1154 return S_OK;
1155 }
1156
1157 HRESULT CMenuStaticToolbar::FillToolbar(BOOL clearFirst)
1158 {
1159 int i;
1160 int ic = GetMenuItemCount(m_hmenu);
1161
1162 if (clearFirst)
1163 {
1164 ClearToolbar();
1165 }
1166
1167 int count = 0;
1168 for (i = 0; i < ic; i++)
1169 {
1170 BOOL last = i + 1 == ic;
1171
1172 MENUITEMINFOW info;
1173
1174 info.cbSize = sizeof(info);
1175 info.dwTypeData = NULL;
1176 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1177
1178 if (!GetMenuItemInfoW(m_hmenu, i, TRUE, &info))
1179 {
1180 TRACE("Error obtaining info for menu item at pos=%d\n", i);
1181 continue;
1182 }
1183
1184 count++;
1185
1186 if (info.fType & MFT_SEPARATOR)
1187 {
1188 AddSeparator(last);
1189 }
1190 else if (!(info.fType & MFT_BITMAP))
1191 {
1192 info.cch++;
1193 info.dwTypeData = (PWSTR) HeapAlloc(GetProcessHeap(), 0, (info.cch + 1) * sizeof(WCHAR));
1194
1195 info.fMask = MIIM_STRING | MIIM_SUBMENU | MIIM_ID;
1196 GetMenuItemInfoW(m_hmenu, i, TRUE, &info);
1197
1198 SMINFO * sminfo = new SMINFO();
1199 sminfo->dwMask = SMIM_ICON | SMIM_FLAGS;
1200
1201 HRESULT hr = m_menuBand->_CallCBWithItemId(info.wID, SMC_GETINFO, 0, reinterpret_cast<LPARAM>(sminfo));
1202 if (FAILED_UNEXPECTEDLY(hr))
1203 {
1204 delete sminfo;
1205 return hr;
1206 }
1207
1208 AddButton(info.wID, info.dwTypeData, info.hSubMenu != NULL, sminfo->iIcon, reinterpret_cast<DWORD_PTR>(sminfo), last);
1209
1210 HeapFree(GetProcessHeap(), 0, info.dwTypeData);
1211 }
1212 }
1213
1214 return S_OK;
1215 }
1216
1217 HRESULT CMenuStaticToolbar::InternalGetTooltip(INT iItem, INT index, DWORD_PTR dwData, LPWSTR pszText, INT cchTextMax)
1218 {
1219 //SMINFO * info = reinterpret_cast<SMINFO*>(dwData);
1220 UNIMPLEMENTED;
1221 return E_NOTIMPL;
1222 }
1223
1224 HRESULT CMenuStaticToolbar::OnDeletingButton(const NMTOOLBAR * tb)
1225 {
1226 delete reinterpret_cast<SMINFO*>(tb->tbButton.dwData);
1227 return S_OK;
1228 }
1229
1230 HRESULT CMenuStaticToolbar::InternalContextMenu(INT iItem, INT index, DWORD_PTR dwData, POINT pt)
1231 {
1232 CComPtr<IContextMenu> contextMenu;
1233 HRESULT hr = m_menuBand->_CallCBWithItemId(iItem, SMC_GETOBJECT,
1234 reinterpret_cast<WPARAM>(&IID_IContextMenu), reinterpret_cast<LPARAM>(&contextMenu));
1235 if (hr != S_OK)
1236 return hr;
1237
1238 return TrackContextMenu(contextMenu, pt);
1239 }
1240
1241 HRESULT CMenuStaticToolbar::InternalExecuteItem(INT iItem, INT index, DWORD_PTR data)
1242 {
1243 return m_menuBand->_CallCBWithItemId(iItem, SMC_EXEC, 0, 0);
1244 }
1245
1246 HRESULT CMenuStaticToolbar::InternalPopupItem(INT iItem, INT index, DWORD_PTR dwData, BOOL keyInitiated)
1247 {
1248 SMINFO * nfo = reinterpret_cast<SMINFO*>(dwData);
1249 if (!nfo)
1250 return E_FAIL;
1251
1252 if (nfo->dwFlags&SMIF_TRACKPOPUP)
1253 {
1254 return PopupSubMenu(iItem, index, m_hmenu);
1255 }
1256 else
1257 {
1258 CComPtr<IShellMenu> shellMenu;
1259 HRESULT hr = m_menuBand->_CallCBWithItemId(iItem, SMC_GETOBJECT, reinterpret_cast<WPARAM>(&IID_IShellMenu), reinterpret_cast<LPARAM>(&shellMenu));
1260 if (FAILED_UNEXPECTEDLY(hr))
1261 return hr;
1262
1263 return PopupSubMenu(iItem, index, shellMenu, keyInitiated);
1264 }
1265 }
1266
1267 HRESULT CMenuStaticToolbar::InternalHasSubMenu(INT iItem, INT index, DWORD_PTR dwData)
1268 {
1269 return ::GetSubMenu(m_hmenu, index) ? S_OK : S_FALSE;
1270 }
1271
1272 CMenuSFToolbar::CMenuSFToolbar(CMenuBand * menuBand) :
1273 CMenuToolbarBase(menuBand, TRUE),
1274 m_shellFolder(NULL),
1275 m_idList(NULL),
1276 m_hKey(NULL)
1277 {
1278 }
1279
1280 CMenuSFToolbar::~CMenuSFToolbar()
1281 {
1282 }
1283
1284 int CALLBACK PidlListSort(void* item1, void* item2, LPARAM lParam)
1285 {
1286 IShellFolder * psf = (IShellFolder*) lParam;
1287 PCUIDLIST_RELATIVE pidl1 = (PCUIDLIST_RELATIVE) item1;
1288 PCUIDLIST_RELATIVE pidl2 = (PCUIDLIST_RELATIVE) item2;
1289 HRESULT hr = psf->CompareIDs(0, pidl1, pidl2);
1290 if (FAILED(hr))
1291 {
1292 // No way to cancel, so sort to equal.
1293 return 0;
1294 }
1295 return (int)(short)LOWORD(hr);
1296 }
1297
1298 HRESULT CMenuSFToolbar::FillToolbar(BOOL clearFirst)
1299 {
1300 HRESULT hr;
1301
1302 CComPtr<IEnumIDList> eidl;
1303 hr = m_shellFolder->EnumObjects(GetToolbar(), SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &eidl);
1304 if (FAILED_UNEXPECTEDLY(hr))
1305 return hr;
1306
1307 HDPA dpaSort = DPA_Create(10);
1308
1309 LPITEMIDLIST item = NULL;
1310 hr = eidl->Next(1, &item, NULL);
1311 while (hr == S_OK)
1312 {
1313 if (m_menuBand->_CallCBWithItemPidl(item, 0x10000000, 0, 0) == S_FALSE)
1314 {
1315 DPA_AppendPtr(dpaSort, ILClone(item));
1316 }
1317
1318 hr = eidl->Next(1, &item, NULL);
1319 }
1320
1321 // If no items were added, show the "empty" placeholder
1322 if (DPA_GetPtrCount(dpaSort) == 0)
1323 {
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 return hr;
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 }