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