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