[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
25 #include "CMenuBand.h"
26 #include "CMenuToolbars.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(CMenuToolbars);
29
30 extern "C"
31 HRESULT WINAPI SHGetImageList(
32 _In_ int iImageList,
33 _In_ REFIID riid,
34 _Out_ void **ppv
35 );
36
37 #define TBSTYLE_EX_VERTICAL 4
38
39
40 #define TIMERID_HOTTRACK 1
41 #define SUBCLASS_ID_MENUBAND 1
42
43 CMenuToolbarBase::CMenuToolbarBase(CMenuBand *menuBand, BOOL usePager) :
44 m_hwnd(NULL),
45 m_menuBand(menuBand),
46 m_hwndToolbar(NULL),
47 m_dwMenuFlags(0),
48 m_hasIdealSize(FALSE)
49 {
50 }
51
52 HRESULT CMenuToolbarBase::IsWindowOwner(HWND hwnd)
53 {
54 return (m_hwnd && m_hwnd == hwnd) ||
55 (m_hwndToolbar && m_hwndToolbar == hwnd) ? S_OK : S_FALSE;
56 }
57
58 void CMenuToolbarBase::InvalidateDraw()
59 {
60 InvalidateRect(m_hwnd, NULL, FALSE);
61 }
62
63 HRESULT CMenuToolbarBase::ShowWindow(BOOL fShow)
64 {
65 ::ShowWindow(m_hwnd, fShow ? SW_SHOW : SW_HIDE);
66
67 UpdateImageLists();
68
69 return S_OK;
70 }
71
72 HRESULT CMenuToolbarBase::UpdateImageLists()
73 {
74 int shiml;
75 if (m_menuBand->UseBigIcons())
76 {
77 shiml = SHIL_LARGE;
78 SendMessageW(m_hwndToolbar, TB_SETPADDING, 0, MAKELPARAM(0, 0));
79 }
80 else
81 {
82 shiml = SHIL_SMALL;
83 }
84
85 IImageList * piml;
86 HRESULT hr = SHGetImageList(shiml, IID_PPV_ARG(IImageList, &piml));
87 if (SUCCEEDED(hr))
88 {
89 SendMessageW(m_hwndToolbar, TB_SETIMAGELIST, 0, reinterpret_cast<LPARAM>(piml));
90 }
91 else
92 {
93 SendMessageW(m_hwndToolbar, TB_SETIMAGELIST, 0, 0);
94 }
95 return S_OK;
96 }
97
98 HRESULT CMenuToolbarBase::Close()
99 {
100 DestroyWindow(m_hwndToolbar);
101 if (m_hwndToolbar != m_hwnd)
102 DestroyWindow(m_hwnd);
103 m_hwndToolbar = NULL;
104 m_hwnd = NULL;
105 return S_OK;
106 }
107
108 HRESULT CMenuToolbarBase::CreateToolbar(HWND hwndParent, DWORD dwFlags)
109 {
110 LONG tbStyles = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
111 TBSTYLE_TOOLTIPS | TBSTYLE_TRANSPARENT | TBSTYLE_REGISTERDROP | TBSTYLE_LIST | TBSTYLE_FLAT | TBSTYLE_CUSTOMERASE |
112 CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | CCS_TOP;
113 LONG tbExStyles = TBSTYLE_EX_DOUBLEBUFFER;
114
115 if (dwFlags & SMINIT_VERTICAL)
116 {
117 tbStyles |= CCS_VERT;
118 tbExStyles |= TBSTYLE_EX_VERTICAL | WS_EX_TOOLWINDOW;
119 }
120
121 RECT rc;
122
123 if (!::GetClientRect(hwndParent, &rc) || (rc.left == rc.right) || (rc.top == rc.bottom))
124 {
125 rc.left = 0;
126 rc.top = 0;
127 rc.right = 1;
128 rc.bottom = 1;
129 }
130
131 HWND hwndToolbar = CreateWindowEx(
132 tbExStyles, TOOLBARCLASSNAMEW, NULL,
133 tbStyles, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
134 hwndParent, NULL, _AtlBaseModule.GetModuleInstance(), 0);
135
136 if (hwndToolbar == NULL)
137 return E_FAIL;
138
139 if (m_usePager)
140 {
141 LONG pgStyles = PGS_VERT | WS_CHILD | WS_VISIBLE;
142 LONG pgExStyles = 0;
143
144 HWND hwndPager = CreateWindowEx(
145 pgExStyles, WC_PAGESCROLLER, NULL,
146 pgStyles, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
147 hwndParent, NULL, _AtlBaseModule.GetModuleInstance(), 0);
148
149 ::SetParent(hwndToolbar, hwndPager);
150 ::SetParent(hwndPager, hwndParent);
151
152 SendMessage(hwndPager, PGM_SETCHILD, 0, reinterpret_cast<LPARAM>(hwndToolbar));
153 m_hwndToolbar = hwndToolbar;
154 m_hwnd = hwndPager;
155 }
156 else
157 {
158 ::SetParent(hwndToolbar, hwndParent);
159 m_hwndToolbar = hwndToolbar;
160 m_hwnd = hwndToolbar;
161 }
162
163 /* Identify the version of the used Common Controls DLL by sending the size of the TBBUTTON structure */
164 SendMessageW(hwndToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
165
166 //if (dwFlags & SMINIT_TOPLEVEL)
167 //{
168 // /* Hide the placeholders for the button images */
169 // SendMessageW(m_hwnd, TB_SETIMAGELIST, 0, 0);
170 //}
171 //else
172
173 SetWindowLongPtr(hwndToolbar, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
174 m_SubclassOld = (WNDPROC) SetWindowLongPtr(hwndToolbar, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(CMenuToolbarBase::s_SubclassProc));
175
176 UpdateImageLists();
177
178 return S_OK;
179 }
180
181 HRESULT CMenuToolbarBase::GetIdealSize(SIZE& size)
182 {
183 size.cx = size.cy = 0;
184
185 if (m_hwndToolbar && !m_hasIdealSize)
186 {
187 SendMessageW(m_hwndToolbar, TB_AUTOSIZE, 0, 0);
188 SendMessageW(m_hwndToolbar, TB_GETMAXSIZE, 0, reinterpret_cast<LPARAM>(&m_idealSize));
189 m_hasIdealSize = TRUE;
190 }
191
192 size = m_idealSize;
193
194 return S_OK;
195 }
196
197 HRESULT CMenuToolbarBase::SetPosSize(int x, int y, int cx, int cy)
198 {
199 if (m_hwnd != m_hwndToolbar)
200 {
201 SetWindowPos(m_hwndToolbar, NULL, x, y, cx, m_idealSize.cy, 0);
202 }
203 SetWindowPos(m_hwnd, NULL, x, y, cx, cy, 0);
204 DWORD btnSize = SendMessage(m_hwndToolbar, TB_GETBUTTONSIZE, 0, 0);
205 SendMessage(m_hwndToolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(cx, HIWORD(btnSize)));
206 return S_OK;
207 }
208
209 HRESULT CMenuToolbarBase::GetWindow(HWND *phwnd)
210 {
211 if (!phwnd)
212 return E_FAIL;
213
214 *phwnd = m_hwnd;
215
216 return S_OK;
217 }
218
219 LRESULT CALLBACK CMenuToolbarBase::s_SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
220 {
221 CMenuToolbarBase * pthis = reinterpret_cast<CMenuToolbarBase *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
222 return pthis->SubclassProc(hWnd, uMsg, wParam, lParam);
223 }
224
225 LRESULT CMenuToolbarBase::SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
226 {
227 switch (uMsg)
228 {
229 case WM_TIMER:
230 if (wParam == TIMERID_HOTTRACK)
231 {
232 KillTimer(hWnd, TIMERID_HOTTRACK);
233
234 m_menuBand->_OnPopupSubMenu(-1, NULL, NULL, NULL);
235
236 if (HasSubMenu(m_hotItem) == S_OK)
237 {
238 PopupItem(m_hotItem);
239 }
240 }
241 }
242
243 return m_SubclassOld(hWnd, uMsg, wParam, lParam);
244 }
245
246 HRESULT CMenuToolbarBase::OnHotItemChange(const NMTBHOTITEM * hot)
247 {
248 if (hot->dwFlags & HICF_LEAVING)
249 {
250 KillTimer(m_hwndToolbar, TIMERID_HOTTRACK);
251 m_hotItem = -1;
252 m_menuBand->_OnHotItemChanged(NULL, -1);
253 m_menuBand->_MenuItemHotTrack(MPOS_CHILDTRACKING);
254 }
255 else if (m_hotItem != hot->idNew)
256 {
257 DWORD elapsed = 0;
258 SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &elapsed, 0);
259 SetTimer(m_hwndToolbar, TIMERID_HOTTRACK, elapsed, NULL);
260
261 m_hotItem = hot->idNew;
262 m_menuBand->_OnHotItemChanged(this, m_hotItem);
263 m_menuBand->_MenuItemHotTrack(MPOS_CHILDTRACKING);
264 }
265 return S_OK;
266 }
267
268 HRESULT CMenuToolbarBase::PopupSubMenu(UINT itemId, UINT index, IShellMenu* childShellMenu)
269 {
270 IBandSite* pBandSite;
271 IDeskBar* pDeskBar;
272
273 HRESULT hr = 0;
274 RECT rc = { 0 };
275
276 if (!SendMessage(m_hwndToolbar, TB_GETITEMRECT, index, reinterpret_cast<LPARAM>(&rc)))
277 return E_FAIL;
278
279 POINT a = { rc.left, rc.top };
280 POINT b = { rc.right, rc.bottom };
281
282 ClientToScreen(m_hwndToolbar, &a);
283 ClientToScreen(m_hwndToolbar, &b);
284
285 POINTL pt = { b.x - 4, a.y };
286 RECTL rcl = { a.x, a.y, b.x, b.y }; // maybe-TODO: fetch client area of deskbar?
287
288
289 #if USE_SYSTEM_MENUSITE
290 hr = CoCreateInstance(CLSID_MenuBandSite,
291 NULL,
292 CLSCTX_INPROC_SERVER,
293 IID_PPV_ARG(IBandSite, &pBandSite));
294 #else
295 hr = CMenuSite_Constructor(IID_PPV_ARG(IBandSite, &pBandSite));
296 #endif
297 if (FAILED(hr))
298 return hr;
299 #if WRAP_MENUSITE
300 hr = CMenuSite_Wrapper(pBandSite, IID_PPV_ARG(IBandSite, &pBandSite));
301 if (FAILED(hr))
302 return hr;
303 #endif
304
305 #if USE_SYSTEM_MENUDESKBAR
306 hr = CoCreateInstance(CLSID_MenuDeskBar,
307 NULL,
308 CLSCTX_INPROC_SERVER,
309 IID_PPV_ARG(IDeskBar, &pDeskBar));
310 #else
311 hr = CMenuDeskBar_Constructor(IID_PPV_ARG(IDeskBar, &pDeskBar));
312 #endif
313 if (FAILED(hr))
314 return hr;
315 #if WRAP_MENUDESKBAR
316 hr = CMenuDeskBar_Wrapper(pDeskBar, IID_PPV_ARG(IDeskBar, &pDeskBar));
317 if (FAILED(hr))
318 return hr;
319 #endif
320
321 hr = pDeskBar->SetClient(pBandSite);
322 if (FAILED(hr))
323 return hr;
324
325 hr = pBandSite->AddBand(childShellMenu);
326 if (FAILED(hr))
327 return hr;
328
329 CComPtr<IMenuPopup> popup;
330 hr = pDeskBar->QueryInterface(IID_PPV_ARG(IMenuPopup, &popup));
331 if (FAILED(hr))
332 return hr;
333
334 m_menuBand->_OnPopupSubMenu(itemId, popup, &pt, &rcl);
335
336 return S_OK;
337 }
338
339 HRESULT CMenuToolbarBase::PopupSubMenu(UINT index, HMENU menu)
340 {
341 RECT rc = { 0 };
342
343 if (!SendMessage(m_hwndToolbar, TB_GETITEMRECT, index, reinterpret_cast<LPARAM>(&rc)))
344 return E_FAIL;
345
346 POINT b = { rc.right, rc.bottom };
347
348 ClientToScreen(m_hwndToolbar, &b);
349
350 HMENU popup = GetSubMenu(menu, index);
351
352 m_menuBand->_TrackSubMenuUsingTrackPopupMenu(popup, b.x, b.y);
353
354 return S_OK;
355 }
356
357 HRESULT CMenuToolbarBase::DoContextMenu(IContextMenu* contextMenu)
358 {
359 HRESULT hr;
360 HMENU hPopup = CreatePopupMenu();
361
362 if (hPopup == NULL)
363 return E_FAIL;
364
365 hr = contextMenu->QueryContextMenu(hPopup, 0, 0, UINT_MAX, CMF_NORMAL);
366 if (FAILED(hr))
367 {
368 DestroyMenu(hPopup);
369 return hr;
370 }
371
372 DWORD dwPos = GetMessagePos();
373 UINT uCommand = ::TrackPopupMenu(hPopup, TPM_RETURNCMD, GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos), 0, m_hwnd, NULL);
374 if (uCommand == 0)
375 return S_FALSE;
376
377 CMINVOKECOMMANDINFO cmi = { 0 };
378 cmi.cbSize = sizeof(cmi);
379 cmi.lpVerb = MAKEINTRESOURCEA(uCommand);
380 cmi.hwnd = m_hwnd;
381 hr = contextMenu->InvokeCommand(&cmi);
382
383 DestroyMenu(hPopup);
384 return hr;
385 }
386
387 HRESULT CMenuToolbarBase::OnCommand(WPARAM wParam, LPARAM lParam, LRESULT *theResult)
388 {
389 theResult = 0;
390 if (HasSubMenu(wParam) == S_OK)
391 {
392 KillTimer(m_hwndToolbar, TIMERID_HOTTRACK);
393 PopupItem(wParam);
394 return S_FALSE;
395 }
396 return m_menuBand->_MenuItemHotTrack(MPOS_EXECUTE);
397 }
398
399 HRESULT CMenuToolbarBase::ChangeHotItem(DWORD dwSelectType)
400 {
401 int prev = m_hotItem;
402 int index = -1;
403
404 if (dwSelectType != 0xFFFFFFFF)
405 {
406 int count = SendMessage(m_hwndToolbar, TB_BUTTONCOUNT, 0, 0);
407
408 if (m_hotItem >= 0)
409 {
410 TBBUTTONINFO info = { 0 };
411 info.cbSize = sizeof(TBBUTTONINFO);
412 info.dwMask = 0;
413 index = SendMessage(m_hwndToolbar, TB_GETBUTTONINFO, m_hotItem, reinterpret_cast<LPARAM>(&info));
414 }
415
416 if (dwSelectType == VK_HOME)
417 {
418 index = 0;
419 dwSelectType = VK_DOWN;
420 }
421 else if (dwSelectType == VK_END)
422 {
423 index = count - 1;
424 dwSelectType = VK_UP;
425 }
426 else if (index < 0)
427 {
428 if (dwSelectType == VK_UP)
429 {
430 index = count - 1;
431 }
432 else if (dwSelectType == VK_DOWN)
433 {
434 index = 0;
435 }
436 }
437 else
438 {
439 if (dwSelectType == VK_UP)
440 {
441 index--;
442 }
443 else if (dwSelectType == VK_DOWN)
444 {
445 index++;
446 }
447 }
448
449 TBBUTTON btn = { 0 };
450 while (index >= 0 && index < count)
451 {
452 DWORD res = SendMessage(m_hwndToolbar, TB_GETBUTTON, index, reinterpret_cast<LPARAM>(&btn));
453 if (!res)
454 return E_FAIL;
455
456 if (btn.dwData)
457 {
458 m_hotItem = btn.idCommand;
459 if (prev != m_hotItem)
460 {
461 SendMessage(m_hwndToolbar, TB_SETHOTITEM, index, 0);
462 return m_menuBand->_OnHotItemChanged(this, m_hotItem);
463 }
464 return S_OK;
465 }
466
467 if (dwSelectType == VK_UP)
468 {
469 index--;
470 }
471 else if (dwSelectType == VK_DOWN)
472 {
473 index++;
474 }
475 }
476 }
477
478 m_hotItem = -1;
479 if (prev != m_hotItem)
480 {
481 SendMessage(m_hwndToolbar, TB_SETHOTITEM, -1, 0);
482 m_menuBand->_OnHotItemChanged(NULL, -1);
483 }
484 return S_FALSE;
485 }
486
487 BOOL
488 AllocAndGetMenuString(HMENU hMenu, UINT ItemIDByPosition, WCHAR** String)
489 {
490 int Length;
491
492 Length = GetMenuStringW(hMenu, ItemIDByPosition, NULL, 0, MF_BYPOSITION);
493
494 if (!Length)
495 return FALSE;
496
497 /* Also allocate space for the terminating NULL character */
498 ++Length;
499 *String = (PWSTR) HeapAlloc(GetProcessHeap(), 0, Length * sizeof(WCHAR));
500
501 GetMenuStringW(hMenu, ItemIDByPosition, *String, Length, MF_BYPOSITION);
502
503 return TRUE;
504 }
505
506 CMenuStaticToolbar::CMenuStaticToolbar(CMenuBand *menuBand) :
507 CMenuToolbarBase(menuBand, FALSE),
508 m_hmenu(NULL)
509 {
510 }
511
512 HRESULT CMenuStaticToolbar::GetMenu(
513 HMENU *phmenu,
514 HWND *phwnd,
515 DWORD *pdwFlags)
516 {
517 *phmenu = m_hmenu;
518 *phwnd = NULL;
519 *pdwFlags = m_dwMenuFlags;
520
521 return S_OK;
522 }
523
524 HRESULT CMenuStaticToolbar::SetMenu(
525 HMENU hmenu,
526 HWND hwnd,
527 DWORD dwFlags)
528 {
529 m_hmenu = hmenu;
530 m_dwMenuFlags = dwFlags;
531
532 return S_OK;
533 }
534
535 HRESULT CMenuStaticToolbar::FillToolbar()
536 {
537 int i;
538 int ic = GetMenuItemCount(m_hmenu);
539
540 for (i = 0; i < ic; i++)
541 {
542 MENUITEMINFOW info;
543 TBBUTTON tbb = { 0 };
544 PWSTR MenuString = NULL;
545
546 tbb.fsState = TBSTATE_ENABLED;
547 tbb.fsStyle = 0;
548
549 info.cbSize = sizeof(info);
550 info.fMask = MIIM_FTYPE | MIIM_ID;
551
552 GetMenuItemInfoW(m_hmenu, i, TRUE, &info);
553
554 if (info.fType == MFT_STRING)
555 {
556 if (!AllocAndGetMenuString(m_hmenu, i, &MenuString))
557 return E_OUTOFMEMORY;
558 if (::GetSubMenu(m_hmenu, i) != NULL)
559 tbb.fsStyle |= BTNS_DROPDOWN;
560 tbb.iString = (INT_PTR) MenuString;
561 tbb.idCommand = info.wID;
562
563 SMINFO * sminfo = new SMINFO();
564 sminfo->dwMask = SMIM_ICON | SMIM_FLAGS;
565 if (SUCCEEDED(m_menuBand->_CallCBWithItemId(info.wID, SMC_GETINFO, 0, reinterpret_cast<LPARAM>(sminfo))))
566 {
567 tbb.iBitmap = sminfo->iIcon;
568 tbb.dwData = reinterpret_cast<DWORD_PTR>(sminfo);
569 // FIXME: remove before deleting the toolbar or it will leak
570 }
571 }
572 else
573 {
574 tbb.fsStyle |= BTNS_SEP;
575 }
576
577 SendMessageW(m_hwndToolbar, TB_ADDBUTTONS, 1, reinterpret_cast<LPARAM>(&tbb));
578
579 if (MenuString)
580 HeapFree(GetProcessHeap(), 0, MenuString);
581 }
582
583 return S_OK;
584 }
585
586 HRESULT CMenuStaticToolbar::OnContextMenu(NMMOUSE * rclick)
587 {
588 CComPtr<IContextMenu> contextMenu;
589 HRESULT hr = m_menuBand->_CallCBWithItemId(rclick->dwItemSpec, SMC_GETOBJECT, reinterpret_cast<WPARAM>(&IID_IContextMenu), reinterpret_cast<LPARAM>(&contextMenu));
590 if (hr != S_OK)
591 return hr;
592
593 return DoContextMenu(contextMenu);
594 }
595
596 HRESULT CMenuStaticToolbar::OnCommand(WPARAM wParam, LPARAM lParam, LRESULT *theResult)
597 {
598 HRESULT hr;
599 hr = CMenuToolbarBase::OnCommand(wParam, lParam, theResult);
600 if (FAILED(hr))
601 return hr;
602
603 // in case the clicked item has a submenu, we do not need to execute the item
604 if (hr == S_FALSE)
605 return hr;
606
607 return m_menuBand->_CallCBWithItemId(wParam, SMC_EXEC, 0, 0);
608 }
609
610 HRESULT CMenuStaticToolbar::PopupItem(UINT uItem)
611 {
612 TBBUTTONINFO info = { 0 };
613 info.cbSize = sizeof(TBBUTTONINFO);
614 info.dwMask = 0;
615 int index = SendMessage(m_hwndToolbar, TB_GETBUTTONINFO, uItem, reinterpret_cast<LPARAM>(&info));
616 if (index < 0)
617 return E_FAIL;
618
619 TBBUTTON btn = { 0 };
620 SendMessage(m_hwndToolbar, TB_GETBUTTON, index, reinterpret_cast<LPARAM>(&btn));
621
622 SMINFO * nfo = reinterpret_cast<SMINFO*>(btn.dwData);
623 if (!nfo)
624 return E_FAIL;
625
626 if (nfo->dwFlags&SMIF_TRACKPOPUP)
627 {
628 return PopupSubMenu(index, m_hmenu);
629 }
630 else
631 {
632 CComPtr<IShellMenu> shellMenu;
633 HRESULT hr = m_menuBand->_CallCBWithItemId(uItem, SMC_GETOBJECT, reinterpret_cast<WPARAM>(&IID_IShellMenu), reinterpret_cast<LPARAM>(&shellMenu));
634 if (FAILED(hr))
635 return hr;
636
637 return PopupSubMenu(uItem, index, shellMenu);
638 }
639 }
640
641 HRESULT CMenuStaticToolbar::HasSubMenu(UINT uItem)
642 {
643 TBBUTTONINFO info = { 0 };
644 info.cbSize = sizeof(TBBUTTONINFO);
645 info.dwMask = 0;
646 int index = SendMessage(m_hwndToolbar, TB_GETBUTTONINFO, uItem, reinterpret_cast<LPARAM>(&info));
647 if (index < 0)
648 return E_FAIL;
649 return ::GetSubMenu(m_hmenu, index) ? S_OK : S_FALSE;
650 }
651
652 CMenuSFToolbar::CMenuSFToolbar(CMenuBand * menuBand) :
653 CMenuToolbarBase(menuBand, TRUE),
654 m_shellFolder(NULL)
655 {
656 }
657
658 CMenuSFToolbar::~CMenuSFToolbar()
659 {
660 }
661
662 HRESULT CMenuSFToolbar::FillToolbar()
663 {
664 HRESULT hr;
665 int i = 0;
666 PWSTR MenuString;
667
668 IEnumIDList * eidl;
669 m_shellFolder->EnumObjects(m_hwndToolbar, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &eidl);
670
671 LPITEMIDLIST item = static_cast<LPITEMIDLIST>(CoTaskMemAlloc(sizeof(ITEMIDLIST)));
672 ULONG fetched;
673 while ((hr = eidl->Next(1, &item, &fetched)) == S_OK)
674 {
675 INT index = 0;
676 INT indexOpen = 0;
677
678 TBBUTTON tbb = { 0 };
679 tbb.fsState = TBSTATE_ENABLED;
680 tbb.fsStyle = 0;
681
682 CComPtr<IShellItem> psi;
683 hr = SHCreateShellItem(NULL, m_shellFolder, item, &psi);
684 if (FAILED(hr))
685 return hr;
686
687 hr = psi->GetDisplayName(SIGDN_NORMALDISPLAY, &MenuString);
688 if (FAILED(hr))
689 return hr;
690
691 index = SHMapPIDLToSystemImageListIndex(m_shellFolder, item, &indexOpen);
692
693 SFGAOF attrs;
694 hr = psi->GetAttributes(SFGAO_FOLDER, &attrs);
695
696 if (attrs != 0)
697 {
698 tbb.fsStyle |= BTNS_DROPDOWN;
699 }
700
701 tbb.idCommand = ++i;
702 tbb.iString = (INT_PTR) MenuString;
703 tbb.iBitmap = index;
704 tbb.dwData = reinterpret_cast<DWORD_PTR>(ILClone(item));
705 // FIXME: remove before deleting the toolbar or it will leak
706
707 SendMessageW(m_hwndToolbar, TB_ADDBUTTONS, 1, reinterpret_cast<LPARAM>(&tbb));
708 HeapFree(GetProcessHeap(), 0, MenuString);
709
710 }
711 CoTaskMemFree(item);
712
713 // If no items were added, show the "empty" placeholder
714 if (i == 0)
715 {
716 TBBUTTON tbb = { 0 };
717 PCWSTR MenuString = L"(Empty)";
718
719 tbb.fsState = 0/*TBSTATE_DISABLED*/;
720 tbb.fsStyle = 0;
721 tbb.iString = (INT_PTR) MenuString;
722 tbb.iBitmap = -1;
723
724 SendMessageW(m_hwndToolbar, TB_ADDBUTTONS, 1, reinterpret_cast<LPARAM>(&tbb));
725
726 return S_OK;
727 }
728
729 return hr;
730 }
731
732 HRESULT CMenuSFToolbar::SetShellFolder(IShellFolder *psf, LPCITEMIDLIST pidlFolder, HKEY hKey, DWORD dwFlags)
733 {
734 m_shellFolder = psf;
735 m_idList = pidlFolder;
736 m_hKey = hKey;
737 m_dwMenuFlags = dwFlags;
738 return S_OK;
739 }
740
741 HRESULT CMenuSFToolbar::GetShellFolder(DWORD *pdwFlags, LPITEMIDLIST *ppidl, REFIID riid, void **ppv)
742 {
743 HRESULT hr;
744
745 hr = m_shellFolder->QueryInterface(riid, ppv);
746 if (FAILED(hr))
747 return hr;
748
749 if (pdwFlags)
750 *pdwFlags = m_dwMenuFlags;
751
752 if (ppidl)
753 {
754 LPITEMIDLIST pidl = NULL;
755
756 if (m_idList)
757 {
758 pidl = ILClone(m_idList);
759 if (!pidl)
760 {
761 (*(IUnknown**) ppv)->Release();
762 return E_FAIL;
763 }
764 }
765
766 *ppidl = pidl;
767 }
768
769 return hr;
770 }
771
772 LPITEMIDLIST CMenuSFToolbar::GetPidlFromId(UINT uItem, INT* pIndex)
773 {
774 TBBUTTONINFO info = { 0 };
775 info.cbSize = sizeof(TBBUTTONINFO);
776 info.dwMask = 0;
777 int index = SendMessage(m_hwndToolbar, TB_GETBUTTONINFO, uItem, reinterpret_cast<LPARAM>(&info));
778 if (index < 0)
779 return NULL;
780
781 if (pIndex)
782 *pIndex = index;
783
784 TBBUTTON btn = { 0 };
785 if (!SendMessage(m_hwndToolbar, TB_GETBUTTON, index, reinterpret_cast<LPARAM>(&btn)))
786 return NULL;
787
788 return reinterpret_cast<LPITEMIDLIST>(btn.dwData);
789 }
790
791 HRESULT CMenuSFToolbar::OnContextMenu(NMMOUSE * rclick)
792 {
793 HRESULT hr;
794 CComPtr<IContextMenu> contextMenu;
795 LPCITEMIDLIST pidl = reinterpret_cast<LPCITEMIDLIST>(rclick->dwItemData);
796
797 hr = m_shellFolder->GetUIObjectOf(m_hwndToolbar, 1, &pidl, IID_IContextMenu, NULL, reinterpret_cast<VOID **>(&contextMenu));
798 if (hr != S_OK)
799 return hr;
800
801 return DoContextMenu(contextMenu);
802 }
803
804 HRESULT CMenuSFToolbar::OnCommand(WPARAM wParam, LPARAM lParam, LRESULT *theResult)
805 {
806 HRESULT hr;
807 hr = CMenuToolbarBase::OnCommand(wParam, lParam, theResult);
808 if (FAILED(hr))
809 return hr;
810
811 // in case the clicked item has a submenu, we do not need to execute the item
812 if (hr == S_FALSE)
813 return hr;
814
815 return m_menuBand->_CallCBWithItemPidl(GetPidlFromId(wParam), SMC_SFEXEC, 0, 0);
816 }
817
818 HRESULT CMenuSFToolbar::PopupItem(UINT uItem)
819 {
820 HRESULT hr;
821 UINT uId;
822 UINT uIdAncestor;
823 DWORD flags;
824 int index;
825 CComPtr<IShellMenuCallback> psmc;
826 CComPtr<IShellMenu> shellMenu;
827
828 LPITEMIDLIST pidl = GetPidlFromId(uItem, &index);
829
830 if (!pidl)
831 return E_FAIL;
832
833 #if USE_SYSTEM_MENUBAND
834 hr = CoCreateInstance(CLSID_MenuBand,
835 NULL,
836 CLSCTX_INPROC_SERVER,
837 IID_PPV_ARG(IShellMenu, &shellMenu));
838 #else
839 hr = CMenuBand_Constructor(IID_PPV_ARG(IShellMenu, &shellMenu));
840 #endif
841 if (FAILED(hr))
842 return hr;
843 #if WRAP_MENUBAND
844 hr = CMenuBand_Wrapper(shellMenu, IID_PPV_ARG(IShellMenu, &shellMenu));
845 if (FAILED(hr))
846 return hr;
847 #endif
848
849 m_menuBand->GetMenuInfo(&psmc, &uId, &uIdAncestor, &flags);
850
851 // FIXME: not sure what to use as uId/uIdAncestor here
852 hr = shellMenu->Initialize(psmc, 0, uId, SMINIT_VERTICAL);
853 if (FAILED(hr))
854 return hr;
855
856 CComPtr<IShellFolder> childFolder;
857 hr = m_shellFolder->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &childFolder));
858 if (FAILED(hr))
859 return hr;
860
861 hr = shellMenu->SetShellFolder(childFolder, NULL, NULL, 0);
862 if (FAILED(hr))
863 return hr;
864
865 return PopupSubMenu(uItem, index, shellMenu);
866 }
867
868 HRESULT CMenuSFToolbar::HasSubMenu(UINT uItem)
869 {
870 HRESULT hr;
871 CComPtr<IShellItem> psi;
872 hr = SHCreateShellItem(NULL, m_shellFolder, GetPidlFromId(uItem), &psi);
873 if (FAILED(hr))
874 return S_FALSE;
875
876 SFGAOF attrs;
877 hr = psi->GetAttributes(SFGAO_FOLDER, &attrs);
878 if (FAILED(hr))
879 return hr;
880
881 return (attrs != 0) ? S_OK : S_FALSE;
882 }