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