* Sync with trunk r64401.
[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::ExecuteItem(INT iItem)
850 {
851 this->m_menuBand->_KillPopupTimers();
852
853 INT index;
854 DWORD_PTR data;
855
856 GetDataFromId(iItem, &index, &data);
857
858 return InternalExecuteItem(iItem, index, data);
859 }
860
861 HRESULT CMenuToolbarBase::OnContextMenu(NMMOUSE * rclick)
862 {
863 INT iItem = rclick->dwItemSpec;
864 INT index = rclick->dwHitInfo;
865 DWORD_PTR data = rclick->dwItemData;
866
867 GetDataFromId(iItem, &index, &data);
868
869 return InternalContextMenu(iItem, index, data, rclick->pt);
870 }
871
872 HRESULT CMenuToolbarBase::KeyboardItemChange(DWORD dwSelectType)
873 {
874 int prev = m_hotItem;
875 int index = -1;
876
877 if (dwSelectType != 0xFFFFFFFF)
878 {
879 int count = SendMessage(m_hwndToolbar, TB_BUTTONCOUNT, 0, 0);
880
881 if (dwSelectType == VK_HOME)
882 {
883 index = 0;
884 dwSelectType = VK_DOWN;
885 }
886 else if (dwSelectType == VK_END)
887 {
888 index = count - 1;
889 dwSelectType = VK_UP;
890 }
891 else
892 {
893 if (m_hotItem >= 0)
894 {
895 TBBUTTONINFO info = { 0 };
896 info.cbSize = sizeof(TBBUTTONINFO);
897 info.dwMask = 0;
898 index = SendMessage(m_hwndToolbar, TB_GETBUTTONINFO, m_hotItem, reinterpret_cast<LPARAM>(&info));
899 }
900
901 if (index < 0)
902 {
903 if (dwSelectType == VK_UP)
904 {
905 index = count - 1;
906 }
907 else if (dwSelectType == VK_DOWN)
908 {
909 index = 0;
910 }
911 }
912 else
913 {
914 if (dwSelectType == VK_UP)
915 {
916 index--;
917 }
918 else if (dwSelectType == VK_DOWN)
919 {
920 index++;
921 }
922 }
923 }
924
925 TBBUTTON btn = { 0 };
926 while (index >= 0 && index < count)
927 {
928 DWORD res = SendMessage(m_hwndToolbar, TB_GETBUTTON, index, reinterpret_cast<LPARAM>(&btn));
929 if (!res)
930 return E_FAIL;
931
932 if (btn.dwData)
933 {
934 if (prev != btn.idCommand)
935 {
936 TRACE("Setting Hot item to %d\n", index);
937 if (!(m_initFlags & SMINIT_VERTICAL) && m_isTrackingPopup)
938 {
939 HWND tlw;
940 m_menuBand->_GetTopLevelWindow(&tlw);
941 SendMessage(tlw, WM_CANCELMODE, 0, 0);
942 PostMessage(m_hwndToolbar, WM_USER_CHANGETRACKEDITEM, index, MAKELPARAM(m_isTrackingPopup, FALSE));
943 }
944 else
945 m_menuBand->_ChangeHotItem(this, btn.idCommand, 0);
946 }
947 return S_OK;
948 }
949
950 if (dwSelectType == VK_UP)
951 {
952 index--;
953 }
954 else if (dwSelectType == VK_DOWN)
955 {
956 index++;
957 }
958 }
959
960 return S_FALSE;
961 }
962
963 if (prev != -1)
964 {
965 TRACE("Setting Hot item to null\n");
966 m_menuBand->_ChangeHotItem(NULL, -1, 0);
967 }
968
969 return S_FALSE;
970 }
971
972 HRESULT CMenuToolbarBase::AddButton(DWORD commandId, LPCWSTR caption, BOOL hasSubMenu, INT iconId, DWORD_PTR buttonData, BOOL last)
973 {
974 TBBUTTON tbb = { 0 };
975
976 tbb.fsState = TBSTATE_ENABLED;
977 #if !USE_TBSTYLE_EX_VERTICAL
978 if (!last && (m_initFlags & SMINIT_VERTICAL))
979 tbb.fsState |= TBSTATE_WRAP;
980 #endif
981 tbb.fsStyle = BTNS_CHECKGROUP;
982
983 if (hasSubMenu && (m_initFlags & SMINIT_VERTICAL))
984 tbb.fsStyle |= BTNS_DROPDOWN;
985
986 if (!(m_initFlags & SMINIT_VERTICAL))
987 tbb.fsStyle |= BTNS_AUTOSIZE;
988
989 tbb.iString = (INT_PTR) caption;
990 tbb.idCommand = commandId;
991
992 tbb.iBitmap = iconId;
993 tbb.dwData = buttonData;
994
995 if (!SendMessageW(m_hwndToolbar, TB_ADDBUTTONS, 1, reinterpret_cast<LPARAM>(&tbb)))
996 return HRESULT_FROM_WIN32(GetLastError());
997
998 return S_OK;
999 }
1000
1001 HRESULT CMenuToolbarBase::AddSeparator(BOOL last)
1002 {
1003 TBBUTTON tbb = { 0 };
1004
1005 tbb.fsState = TBSTATE_ENABLED;
1006 #if !USE_TBSTYLE_EX_VERTICAL
1007 if (!last && (m_initFlags & SMINIT_VERTICAL))
1008 tbb.fsState |= TBSTATE_WRAP;
1009 #endif
1010 tbb.fsStyle = BTNS_SEP;
1011 tbb.iBitmap = 0;
1012
1013 if (!SendMessageW(m_hwndToolbar, TB_ADDBUTTONS, 1, reinterpret_cast<LPARAM>(&tbb)))
1014 return HRESULT_FROM_WIN32(GetLastError());
1015
1016 return S_OK;
1017 }
1018
1019 HRESULT CMenuToolbarBase::AddPlaceholder()
1020 {
1021 TBBUTTON tbb = { 0 };
1022 PCWSTR MenuString = L"(Empty)";
1023
1024 tbb.fsState = 0;
1025 tbb.fsStyle = 0;
1026 tbb.iString = (INT_PTR) MenuString;
1027 tbb.iBitmap = -1;
1028
1029 if (!SendMessageW(m_hwndToolbar, TB_ADDBUTTONS, 1, reinterpret_cast<LPARAM>(&tbb)))
1030 return HRESULT_FROM_WIN32(GetLastError());
1031
1032 return S_OK;
1033 }
1034
1035 HRESULT CMenuToolbarBase::ClearToolbar()
1036 {
1037 while (SendMessage(m_hwndToolbar, TB_DELETEBUTTON, 0, 0))
1038 {
1039 // empty;
1040 }
1041 return S_OK;
1042 }
1043
1044 HRESULT CMenuToolbarBase::GetDataFromId(INT iItem, INT* pIndex, DWORD_PTR* pData)
1045 {
1046 if (pData)
1047 *pData = NULL;
1048
1049 if (pIndex)
1050 *pIndex = -1;
1051
1052 if (iItem < 0)
1053 return S_OK;
1054
1055 TBBUTTONINFO info = { 0 };
1056
1057 info.cbSize = sizeof(TBBUTTONINFO);
1058 info.dwMask = TBIF_COMMAND | TBIF_LPARAM;
1059
1060 int index = SendMessage(m_hwndToolbar, TB_GETBUTTONINFO, iItem, reinterpret_cast<LPARAM>(&info));
1061 if (index < 0)
1062 return E_FAIL;
1063
1064 if (pIndex)
1065 *pIndex = index;
1066
1067 if (pData)
1068 *pData = info.lParam;
1069
1070 return S_OK;
1071 }
1072
1073 HRESULT CMenuToolbarBase::CancelCurrentPopup()
1074 {
1075 return m_menuBand->_CancelCurrentPopup();
1076 }
1077
1078 HRESULT CMenuToolbarBase::PopupItem(INT iItem, BOOL keyInitiated)
1079 {
1080 INT index;
1081 DWORD_PTR dwData;
1082
1083 if (iItem < 0)
1084 return S_OK;
1085
1086 if (m_popupBar == this && m_popupItem == iItem)
1087 return S_OK;
1088
1089 GetDataFromId(iItem, &index, &dwData);
1090
1091 HRESULT hr = InternalHasSubMenu(iItem, index, dwData);
1092 if (hr != S_OK)
1093 return hr;
1094
1095 if (m_popupBar)
1096 {
1097 HRESULT hr = CancelCurrentPopup();
1098 if (FAILED_UNEXPECTEDLY(hr))
1099 return hr;
1100 }
1101
1102 if (!(m_initFlags & SMINIT_VERTICAL))
1103 {
1104 TRACE("PopupItem non-vertical %d %d\n", index, iItem);
1105 m_menuBand->_ChangeHotItem(this, iItem, 0);
1106 }
1107
1108 return InternalPopupItem(iItem, index, dwData, keyInitiated);
1109 }
1110
1111 CMenuStaticToolbar::CMenuStaticToolbar(CMenuBand *menuBand) :
1112 CMenuToolbarBase(menuBand, FALSE),
1113 m_hmenu(NULL)
1114 {
1115 }
1116
1117 CMenuStaticToolbar::~CMenuStaticToolbar()
1118 {
1119 }
1120
1121 HRESULT CMenuStaticToolbar::GetMenu(
1122 _Out_opt_ HMENU *phmenu,
1123 _Out_opt_ HWND *phwnd,
1124 _Out_opt_ DWORD *pdwFlags)
1125 {
1126 if (phmenu)
1127 *phmenu = m_hmenu;
1128 if (phwnd)
1129 *phwnd = NULL;
1130 if (pdwFlags)
1131 *pdwFlags = m_dwMenuFlags;
1132
1133 return S_OK;
1134 }
1135
1136 HRESULT CMenuStaticToolbar::SetMenu(
1137 HMENU hmenu,
1138 HWND hwnd,
1139 DWORD dwFlags)
1140 {
1141 m_hmenu = hmenu;
1142 m_dwMenuFlags = dwFlags;
1143
1144 return S_OK;
1145 }
1146
1147 HRESULT CMenuStaticToolbar::FillToolbar(BOOL clearFirst)
1148 {
1149 int i;
1150 int ic = GetMenuItemCount(m_hmenu);
1151
1152 if (clearFirst)
1153 {
1154 ClearToolbar();
1155 }
1156
1157 int count = 0;
1158 for (i = 0; i < ic; i++)
1159 {
1160 BOOL last = i + 1 == ic;
1161
1162 MENUITEMINFOW info;
1163
1164 info.cbSize = sizeof(info);
1165 info.dwTypeData = NULL;
1166 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1167
1168 if (!GetMenuItemInfoW(m_hmenu, i, TRUE, &info))
1169 {
1170 TRACE("Error obtaining info for menu item at pos=%d\n", i);
1171 continue;
1172 }
1173
1174 count++;
1175
1176 if (info.fType & MFT_SEPARATOR)
1177 {
1178 AddSeparator(last);
1179 }
1180 else if (!(info.fType & MFT_BITMAP))
1181 {
1182 info.cch++;
1183 info.dwTypeData = (PWSTR) HeapAlloc(GetProcessHeap(), 0, (info.cch + 1) * sizeof(WCHAR));
1184
1185 info.fMask = MIIM_STRING | MIIM_SUBMENU | MIIM_ID;
1186 GetMenuItemInfoW(m_hmenu, i, TRUE, &info);
1187
1188 SMINFO * sminfo = new SMINFO();
1189 sminfo->dwMask = SMIM_ICON | SMIM_FLAGS;
1190 // FIXME: remove before deleting the toolbar or it will leak
1191
1192 HRESULT hr = m_menuBand->_CallCBWithItemId(info.wID, SMC_GETINFO, 0, reinterpret_cast<LPARAM>(sminfo));
1193 if (FAILED_UNEXPECTEDLY(hr))
1194 return hr;
1195
1196 AddButton(info.wID, info.dwTypeData, info.hSubMenu != NULL, sminfo->iIcon, reinterpret_cast<DWORD_PTR>(sminfo), last);
1197
1198 HeapFree(GetProcessHeap(), 0, info.dwTypeData);
1199 }
1200 }
1201
1202 return S_OK;
1203 }
1204
1205 HRESULT CMenuStaticToolbar::InternalGetTooltip(INT iItem, INT index, DWORD_PTR dwData, LPWSTR pszText, INT cchTextMax)
1206 {
1207 //SMINFO * info = reinterpret_cast<SMINFO*>(dwData);
1208 UNIMPLEMENTED;
1209 return E_NOTIMPL;
1210 }
1211
1212 HRESULT CMenuStaticToolbar::OnDeletingButton(const NMTOOLBAR * tb)
1213 {
1214 delete reinterpret_cast<SMINFO*>(tb->tbButton.dwData);
1215 return S_OK;
1216 }
1217
1218 HRESULT CMenuStaticToolbar::InternalContextMenu(INT iItem, INT index, DWORD_PTR dwData, POINT pt)
1219 {
1220 CComPtr<IContextMenu> contextMenu;
1221 HRESULT hr = m_menuBand->_CallCBWithItemId(iItem, SMC_GETOBJECT,
1222 reinterpret_cast<WPARAM>(&IID_IContextMenu), reinterpret_cast<LPARAM>(&contextMenu));
1223 if (hr != S_OK)
1224 return hr;
1225
1226 return TrackContextMenu(contextMenu, pt);
1227 }
1228
1229 HRESULT CMenuStaticToolbar::InternalExecuteItem(INT iItem, INT index, DWORD_PTR data)
1230 {
1231 return m_menuBand->_CallCBWithItemId(iItem, SMC_EXEC, 0, 0);
1232 }
1233
1234 HRESULT CMenuStaticToolbar::InternalPopupItem(INT iItem, INT index, DWORD_PTR dwData, BOOL keyInitiated)
1235 {
1236 SMINFO * nfo = reinterpret_cast<SMINFO*>(dwData);
1237 if (!nfo)
1238 return E_FAIL;
1239
1240 if (nfo->dwFlags&SMIF_TRACKPOPUP)
1241 {
1242 return PopupSubMenu(iItem, index, m_hmenu);
1243 }
1244 else
1245 {
1246 CComPtr<IShellMenu> shellMenu;
1247 HRESULT hr = m_menuBand->_CallCBWithItemId(iItem, SMC_GETOBJECT, reinterpret_cast<WPARAM>(&IID_IShellMenu), reinterpret_cast<LPARAM>(&shellMenu));
1248 if (FAILED_UNEXPECTEDLY(hr))
1249 return hr;
1250
1251 return PopupSubMenu(iItem, index, shellMenu, keyInitiated);
1252 }
1253 }
1254
1255 HRESULT CMenuStaticToolbar::InternalHasSubMenu(INT iItem, INT index, DWORD_PTR dwData)
1256 {
1257 return ::GetSubMenu(m_hmenu, index) ? S_OK : S_FALSE;
1258 }
1259
1260 CMenuSFToolbar::CMenuSFToolbar(CMenuBand * menuBand) :
1261 CMenuToolbarBase(menuBand, TRUE),
1262 m_shellFolder(NULL),
1263 m_idList(NULL),
1264 m_hKey(NULL)
1265 {
1266 }
1267
1268 CMenuSFToolbar::~CMenuSFToolbar()
1269 {
1270 }
1271
1272 HRESULT CMenuSFToolbar::FillToolbar(BOOL clearFirst)
1273 {
1274 HRESULT hr;
1275 int i = 0;
1276 PWSTR MenuString;
1277
1278 IEnumIDList * eidl;
1279 m_shellFolder->EnumObjects(GetToolbar(), SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &eidl);
1280
1281 LPITEMIDLIST item = NULL;
1282 hr = eidl->Next(1, &item, NULL);
1283 while (hr == S_OK)
1284 {
1285 INT index = 0;
1286 INT indexOpen = 0;
1287
1288 if (m_menuBand->_CallCBWithItemPidl(item, 0x10000000, 0, 0) == S_OK)
1289 {
1290 STRRET sr = { STRRET_CSTR, { 0 } };
1291
1292 hr = m_shellFolder->GetDisplayNameOf(item, SIGDN_NORMALDISPLAY, &sr);
1293 if (FAILED_UNEXPECTEDLY(hr))
1294 return hr;
1295
1296 StrRetToStr(&sr, NULL, &MenuString);
1297
1298 index = SHMapPIDLToSystemImageListIndex(m_shellFolder, item, &indexOpen);
1299
1300 LPCITEMIDLIST itemc = item;
1301
1302 SFGAOF attrs = SFGAO_FOLDER;
1303 hr = m_shellFolder->GetAttributesOf(1, &itemc, &attrs);
1304
1305 DWORD_PTR dwData = reinterpret_cast<DWORD_PTR>(ILClone(item));
1306
1307 // Fetch next item already, so we know if the current one is the last
1308 hr = eidl->Next(1, &item, NULL);
1309
1310 AddButton(++i, MenuString, attrs & SFGAO_FOLDER, index, dwData, hr != S_OK);
1311
1312 CoTaskMemFree(MenuString);
1313 }
1314 else
1315 {
1316 // Fetch next item here also
1317 hr = eidl->Next(1, &item, NULL);
1318 }
1319 }
1320 ILFree(item);
1321
1322 // If no items were added, show the "empty" placeholder
1323 if (i == 0)
1324 {
1325 return AddPlaceholder();
1326 }
1327
1328 return hr;
1329 }
1330
1331 HRESULT CMenuSFToolbar::InternalGetTooltip(INT iItem, INT index, DWORD_PTR dwData, LPWSTR pszText, INT cchTextMax)
1332 {
1333 //ITEMIDLIST * pidl = reinterpret_cast<LPITEMIDLIST>(dwData);
1334 UNIMPLEMENTED;
1335 return E_NOTIMPL;
1336 }
1337
1338 HRESULT CMenuSFToolbar::OnDeletingButton(const NMTOOLBAR * tb)
1339 {
1340 ILFree(reinterpret_cast<LPITEMIDLIST>(tb->tbButton.dwData));
1341 return S_OK;
1342 }
1343
1344 HRESULT CMenuSFToolbar::SetShellFolder(IShellFolder *psf, LPCITEMIDLIST pidlFolder, HKEY hKey, DWORD dwFlags)
1345 {
1346 m_shellFolder = psf;
1347 m_idList = ILClone(pidlFolder);
1348 m_hKey = hKey;
1349 m_dwMenuFlags = dwFlags;
1350 return S_OK;
1351 }
1352
1353 HRESULT CMenuSFToolbar::GetShellFolder(DWORD *pdwFlags, LPITEMIDLIST *ppidl, REFIID riid, void **ppv)
1354 {
1355 HRESULT hr;
1356
1357 hr = m_shellFolder->QueryInterface(riid, ppv);
1358 if (FAILED_UNEXPECTEDLY(hr))
1359 return hr;
1360
1361 if (pdwFlags)
1362 *pdwFlags = m_dwMenuFlags;
1363
1364 if (ppidl)
1365 {
1366 LPITEMIDLIST pidl = NULL;
1367
1368 if (m_idList)
1369 {
1370 pidl = ILClone(m_idList);
1371 if (!pidl)
1372 {
1373 (*reinterpret_cast<IUnknown**>(ppv))->Release();
1374 return E_FAIL;
1375 }
1376 }
1377
1378 *ppidl = pidl;
1379 }
1380
1381 return hr;
1382 }
1383
1384 HRESULT CMenuSFToolbar::InternalContextMenu(INT iItem, INT index, DWORD_PTR dwData, POINT pt)
1385 {
1386 HRESULT hr;
1387 CComPtr<IContextMenu> contextMenu = NULL;
1388 LPCITEMIDLIST pidl = reinterpret_cast<LPCITEMIDLIST>(dwData);
1389
1390 hr = m_shellFolder->GetUIObjectOf(GetToolbar(), 1, &pidl, IID_NULL_PPV_ARG(IContextMenu, &contextMenu));
1391 if (FAILED_UNEXPECTEDLY(hr))
1392 {
1393 return hr;
1394 }
1395
1396 hr = TrackContextMenu(contextMenu, pt);
1397
1398 return hr;
1399 }
1400
1401 HRESULT CMenuSFToolbar::InternalExecuteItem(INT iItem, INT index, DWORD_PTR data)
1402 {
1403 return m_menuBand->_CallCBWithItemPidl(reinterpret_cast<LPITEMIDLIST>(data), SMC_SFEXEC, 0, 0);
1404 }
1405
1406 HRESULT CMenuSFToolbar::InternalPopupItem(INT iItem, INT index, DWORD_PTR dwData, BOOL keyInitiated)
1407 {
1408 HRESULT hr;
1409 UINT uId;
1410 UINT uIdAncestor;
1411 DWORD flags;
1412 CComPtr<IShellMenuCallback> psmc;
1413 CComPtr<IShellMenu> shellMenu;
1414
1415 LPITEMIDLIST pidl = reinterpret_cast<LPITEMIDLIST>(dwData);
1416
1417 if (!pidl)
1418 return E_FAIL;
1419
1420 hr = CMenuBand_Constructor(IID_PPV_ARG(IShellMenu, &shellMenu));
1421 if (FAILED_UNEXPECTEDLY(hr))
1422 return hr;
1423
1424 m_menuBand->GetMenuInfo(&psmc, &uId, &uIdAncestor, &flags);
1425
1426 // FIXME: not sure what to use as uId/uIdAncestor here
1427 hr = shellMenu->Initialize(psmc, 0, uId, SMINIT_VERTICAL);
1428 if (FAILED_UNEXPECTEDLY(hr))
1429 return hr;
1430
1431 CComPtr<IShellFolder> childFolder;
1432 hr = m_shellFolder->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &childFolder));
1433 if (FAILED_UNEXPECTEDLY(hr))
1434 return hr;
1435
1436 hr = shellMenu->SetShellFolder(childFolder, NULL, NULL, 0);
1437 if (FAILED_UNEXPECTEDLY(hr))
1438 return hr;
1439
1440 return PopupSubMenu(iItem, index, shellMenu, keyInitiated);
1441 }
1442
1443 HRESULT CMenuSFToolbar::InternalHasSubMenu(INT iItem, INT index, DWORD_PTR dwData)
1444 {
1445 HRESULT hr;
1446 LPCITEMIDLIST pidl = reinterpret_cast<LPITEMIDLIST>(dwData);
1447
1448 SFGAOF attrs = SFGAO_FOLDER;
1449 hr = m_shellFolder->GetAttributesOf(1, &pidl, &attrs);
1450 if (FAILED_UNEXPECTEDLY(hr))
1451 return hr;
1452
1453 return (attrs & SFGAO_FOLDER) ? S_OK : S_FALSE;
1454 }