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