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