[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 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_SubclassOld(NULL),
174 m_menuBand(menuBand),
175 m_hwndToolbar(NULL),
176 m_dwMenuFlags(0),
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 #ifdef TBSTYLE_EX_VERTICAL
264 // FIXME: Use when it works in ros (?)
265 tbExStyles |= TBSTYLE_EX_VERTICAL | WS_EX_TOOLWINDOW;
266 #endif
267 }
268
269 RECT rc;
270
271 if (!::GetClientRect(hwndParent, &rc) || (rc.left == rc.right) || (rc.top == rc.bottom))
272 {
273 rc.left = 0;
274 rc.top = 0;
275 rc.right = 1;
276 rc.bottom = 1;
277 }
278
279 HWND hwndToolbar = CreateWindowEx(
280 tbExStyles, TOOLBARCLASSNAMEW, NULL,
281 tbStyles, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
282 hwndParent, NULL, _AtlBaseModule.GetModuleInstance(), 0);
283
284 if (hwndToolbar == NULL)
285 return E_FAIL;
286
287 if (m_usePager)
288 {
289 LONG pgStyles = PGS_VERT | WS_CHILD | WS_VISIBLE;
290 LONG pgExStyles = 0;
291
292 HWND hwndPager = CreateWindowEx(
293 pgExStyles, WC_PAGESCROLLER, NULL,
294 pgStyles, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
295 hwndParent, NULL, _AtlBaseModule.GetModuleInstance(), 0);
296
297 ::SetParent(hwndToolbar, hwndPager);
298 ::SetParent(hwndPager, hwndParent);
299
300 SendMessage(hwndPager, PGM_SETCHILD, 0, reinterpret_cast<LPARAM>(hwndToolbar));
301 m_hwndToolbar = hwndToolbar;
302 m_hwnd = hwndPager;
303 }
304 else
305 {
306 ::SetParent(hwndToolbar, hwndParent);
307 m_hwndToolbar = hwndToolbar;
308 m_hwnd = hwndToolbar;
309 }
310
311 /* Identify the version of the used Common Controls DLL by sending the size of the TBBUTTON structure */
312 SendMessageW(hwndToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
313
314 //if (dwFlags & SMINIT_TOPLEVEL)
315 //{
316 // /* Hide the placeholders for the button images */
317 // SendMessageW(m_hwnd, TB_SETIMAGELIST, 0, 0);
318 //}
319 //else
320
321 SetWindowLongPtr(hwndToolbar, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
322 m_SubclassOld = (WNDPROC) SetWindowLongPtr(hwndToolbar, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(CMenuToolbarBase::s_SubclassProc));
323
324 UpdateImageLists();
325
326 return S_OK;
327 }
328
329 HRESULT CMenuToolbarBase::GetIdealSize(SIZE& size)
330 {
331 size.cx = size.cy = 0;
332
333 if (m_hwndToolbar && !m_hasIdealSize)
334 {
335 SendMessageW(m_hwndToolbar, TB_AUTOSIZE, 0, 0);
336 SendMessageW(m_hwndToolbar, TB_GETMAXSIZE, 0, reinterpret_cast<LPARAM>(&m_idealSize));
337 m_hasIdealSize = TRUE;
338 }
339
340 size = m_idealSize;
341
342 return S_OK;
343 }
344
345 HRESULT CMenuToolbarBase::SetPosSize(int x, int y, int cx, int cy)
346 {
347 if (m_hwnd != m_hwndToolbar)
348 {
349 SetWindowPos(m_hwndToolbar, NULL, x, y, cx, m_idealSize.cy, 0);
350 }
351 SetWindowPos(m_hwnd, NULL, x, y, cx, cy, 0);
352 DWORD btnSize = SendMessage(m_hwndToolbar, TB_GETBUTTONSIZE, 0, 0);
353 SendMessage(m_hwndToolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(cx, HIWORD(btnSize)));
354 return S_OK;
355 }
356
357 HRESULT CMenuToolbarBase::GetWindow(HWND *phwnd)
358 {
359 if (!phwnd)
360 return E_FAIL;
361
362 *phwnd = m_hwnd;
363
364 return S_OK;
365 }
366
367 LRESULT CALLBACK CMenuToolbarBase::s_SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
368 {
369 CMenuToolbarBase * pthis = reinterpret_cast<CMenuToolbarBase *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
370 return pthis->SubclassProc(hWnd, uMsg, wParam, lParam);
371 }
372
373 LRESULT CMenuToolbarBase::SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
374 {
375 switch (uMsg)
376 {
377 case WM_TIMER:
378 if (wParam == TIMERID_HOTTRACK)
379 {
380 KillTimer(hWnd, TIMERID_HOTTRACK);
381
382 m_menuBand->_OnPopupSubMenu(NULL, NULL, NULL, NULL, -1);
383
384 if (HasSubMenu(m_hotItem) == S_OK)
385 {
386 PopupItem(m_hotItem);
387 }
388 }
389 }
390
391 return m_SubclassOld(hWnd, uMsg, wParam, lParam);
392 }
393
394 HRESULT CMenuToolbarBase::OnHotItemChange(const NMTBHOTITEM * hot)
395 {
396 if (hot->dwFlags & HICF_LEAVING)
397 {
398 KillTimer(m_hwndToolbar, TIMERID_HOTTRACK);
399 m_hotItem = -1;
400 m_menuBand->_OnHotItemChanged(NULL, -1);
401 m_menuBand->_MenuItemHotTrack(MPOS_CHILDTRACKING);
402 }
403 else if (m_hotItem != hot->idNew)
404 {
405 DWORD elapsed = 0;
406 SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &elapsed, 0);
407 SetTimer(m_hwndToolbar, TIMERID_HOTTRACK, elapsed, NULL);
408
409 m_hotItem = hot->idNew;
410 m_menuBand->_OnHotItemChanged(this, m_hotItem);
411 m_menuBand->_MenuItemHotTrack(MPOS_CHILDTRACKING);
412 }
413 return S_OK;
414 }
415
416 HRESULT CMenuToolbarBase::OnHotItemChanged(CMenuToolbarBase * toolbar, INT item)
417 {
418 m_hotBar = toolbar;
419 m_hotItem = item;
420 InvalidateDraw();
421 return S_OK;
422 }
423
424 HRESULT CMenuToolbarBase::OnPopupItemChanged(CMenuToolbarBase * toolbar, INT item)
425 {
426 m_popupBar = toolbar;
427 m_popupItem = item;
428 InvalidateDraw();
429 return S_OK;
430 }
431
432 HRESULT CMenuToolbarBase::PopupSubMenu(UINT uItem, UINT index, IShellMenu* childShellMenu)
433 {
434 IBandSite* pBandSite;
435 IDeskBar* pDeskBar;
436
437 HRESULT hr = 0;
438 RECT rc = { 0 };
439
440 if (!SendMessage(m_hwndToolbar, TB_GETITEMRECT, index, reinterpret_cast<LPARAM>(&rc)))
441 return E_FAIL;
442
443 POINT a = { rc.left, rc.top };
444 POINT b = { rc.right, rc.bottom };
445
446 ClientToScreen(m_hwndToolbar, &a);
447 ClientToScreen(m_hwndToolbar, &b);
448
449 POINTL pt = { b.x - 3, a.y - 3 };
450 RECTL rcl = { a.x, a.y, b.x, b.y }; // maybe-TODO: fetch client area of deskbar?
451
452 #if USE_SYSTEM_MENUSITE
453 hr = CoCreateInstance(CLSID_MenuBandSite,
454 NULL,
455 CLSCTX_INPROC_SERVER,
456 IID_PPV_ARG(IBandSite, &pBandSite));
457 #else
458 hr = CMenuSite_Constructor(IID_PPV_ARG(IBandSite, &pBandSite));
459 #endif
460 if (FAILED(hr))
461 return hr;
462 #if WRAP_MENUSITE
463 hr = CMenuSite_Wrapper(pBandSite, IID_PPV_ARG(IBandSite, &pBandSite));
464 if (FAILED(hr))
465 return hr;
466 #endif
467
468 #if USE_SYSTEM_MENUDESKBAR
469 hr = CoCreateInstance(CLSID_MenuDeskBar,
470 NULL,
471 CLSCTX_INPROC_SERVER,
472 IID_PPV_ARG(IDeskBar, &pDeskBar));
473 #else
474 hr = CMenuDeskBar_Constructor(IID_PPV_ARG(IDeskBar, &pDeskBar));
475 #endif
476 if (FAILED(hr))
477 return hr;
478 #if WRAP_MENUDESKBAR
479 hr = CMenuDeskBar_Wrapper(pDeskBar, IID_PPV_ARG(IDeskBar, &pDeskBar));
480 if (FAILED(hr))
481 return hr;
482 #endif
483
484 hr = pDeskBar->SetClient(pBandSite);
485 if (FAILED(hr))
486 return hr;
487
488 hr = pBandSite->AddBand(childShellMenu);
489 if (FAILED(hr))
490 return hr;
491
492 CComPtr<IMenuPopup> popup;
493 hr = pDeskBar->QueryInterface(IID_PPV_ARG(IMenuPopup, &popup));
494 if (FAILED(hr))
495 return hr;
496
497 m_menuBand->_OnPopupSubMenu(popup, &pt, &rcl, this, m_popupItem);
498
499 return S_OK;
500 }
501
502 HRESULT CMenuToolbarBase::PopupSubMenu(UINT index, HMENU menu)
503 {
504 RECT rc = { 0 };
505
506 if (!SendMessage(m_hwndToolbar, TB_GETITEMRECT, index, reinterpret_cast<LPARAM>(&rc)))
507 return E_FAIL;
508
509 POINT b = { rc.right, rc.bottom };
510
511 ClientToScreen(m_hwndToolbar, &b);
512
513 HMENU popup = GetSubMenu(menu, index);
514
515 m_menuBand->_TrackSubMenuUsingTrackPopupMenu(popup, b.x, b.y);
516
517 return S_OK;
518 }
519
520 HRESULT CMenuToolbarBase::DoContextMenu(IContextMenu* contextMenu)
521 {
522 HRESULT hr;
523 HMENU hPopup = CreatePopupMenu();
524
525 if (hPopup == NULL)
526 return E_FAIL;
527
528 hr = contextMenu->QueryContextMenu(hPopup, 0, 0, UINT_MAX, CMF_NORMAL);
529 if (FAILED(hr))
530 {
531 DestroyMenu(hPopup);
532 return hr;
533 }
534
535 DWORD dwPos = GetMessagePos();
536 UINT uCommand = ::TrackPopupMenu(hPopup, TPM_RETURNCMD, GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos), 0, m_hwnd, NULL);
537 if (uCommand == 0)
538 return S_FALSE;
539
540 CMINVOKECOMMANDINFO cmi = { 0 };
541 cmi.cbSize = sizeof(cmi);
542 cmi.lpVerb = MAKEINTRESOURCEA(uCommand);
543 cmi.hwnd = m_hwnd;
544 hr = contextMenu->InvokeCommand(&cmi);
545
546 DestroyMenu(hPopup);
547 return hr;
548 }
549
550 HRESULT CMenuToolbarBase::OnCommand(WPARAM wParam, LPARAM lParam, LRESULT *theResult)
551 {
552 theResult = 0;
553 if (HasSubMenu(wParam) == S_OK)
554 {
555 KillTimer(m_hwndToolbar, TIMERID_HOTTRACK);
556 PopupItem(wParam);
557 return S_FALSE;
558 }
559 return m_menuBand->_MenuItemHotTrack(MPOS_EXECUTE);
560 }
561
562 HRESULT CMenuToolbarBase::ChangeHotItem(DWORD dwSelectType)
563 {
564 int prev = m_hotItem;
565 int index = -1;
566
567 if (dwSelectType != 0xFFFFFFFF)
568 {
569 int count = SendMessage(m_hwndToolbar, TB_BUTTONCOUNT, 0, 0);
570
571 if (m_hotItem >= 0)
572 {
573 TBBUTTONINFO info = { 0 };
574 info.cbSize = sizeof(TBBUTTONINFO);
575 info.dwMask = 0;
576 index = SendMessage(m_hwndToolbar, TB_GETBUTTONINFO, m_hotItem, reinterpret_cast<LPARAM>(&info));
577 }
578
579 if (dwSelectType == VK_HOME)
580 {
581 index = 0;
582 dwSelectType = VK_DOWN;
583 }
584 else if (dwSelectType == VK_END)
585 {
586 index = count - 1;
587 dwSelectType = VK_UP;
588 }
589 else if (index < 0)
590 {
591 if (dwSelectType == VK_UP)
592 {
593 index = count - 1;
594 }
595 else if (dwSelectType == VK_DOWN)
596 {
597 index = 0;
598 }
599 }
600 else
601 {
602 if (dwSelectType == VK_UP)
603 {
604 index--;
605 }
606 else if (dwSelectType == VK_DOWN)
607 {
608 index++;
609 }
610 }
611
612 TBBUTTON btn = { 0 };
613 while (index >= 0 && index < count)
614 {
615 DWORD res = SendMessage(m_hwndToolbar, TB_GETBUTTON, index, reinterpret_cast<LPARAM>(&btn));
616 if (!res)
617 return E_FAIL;
618
619 if (btn.dwData)
620 {
621 m_hotItem = btn.idCommand;
622 if (prev != m_hotItem)
623 {
624 SendMessage(m_hwndToolbar, TB_SETHOTITEM, index, 0);
625 return m_menuBand->_OnHotItemChanged(this, m_hotItem);
626 }
627 return S_OK;
628 }
629
630 if (dwSelectType == VK_UP)
631 {
632 index--;
633 }
634 else if (dwSelectType == VK_DOWN)
635 {
636 index++;
637 }
638 }
639 }
640
641 m_hotItem = -1;
642 if (prev != m_hotItem)
643 {
644 SendMessage(m_hwndToolbar, TB_SETHOTITEM, -1, 0);
645 m_menuBand->_OnHotItemChanged(NULL, -1);
646 }
647 return S_FALSE;
648 }
649
650 HRESULT CMenuToolbarBase::AddButton(DWORD commandId, LPCWSTR caption, BOOL hasSubMenu, INT iconId, DWORD_PTR buttonData, BOOL last)
651 {
652 TBBUTTON tbb = { 0 };
653
654 tbb.fsState = TBSTATE_ENABLED;
655 #ifndef TBSTYLE_EX_VERTICAL
656 if (!last)
657 tbb.fsState |= TBSTATE_WRAP;
658 #endif
659 tbb.fsStyle = 0;
660
661 if (hasSubMenu)
662 tbb.fsStyle |= BTNS_DROPDOWN;
663
664 tbb.iString = (INT_PTR) caption;
665 tbb.idCommand = commandId;
666
667 tbb.iBitmap = iconId;
668 tbb.dwData = buttonData;
669
670 SendMessageW(m_hwndToolbar, TB_ADDBUTTONS, 1, reinterpret_cast<LPARAM>(&tbb));
671
672 return S_OK;
673 }
674
675 HRESULT CMenuToolbarBase::AddSeparator(BOOL last)
676 {
677 TBBUTTON tbb = { 0 };
678
679 tbb.fsState = TBSTATE_ENABLED;
680 #ifndef TBSTYLE_EX_VERTICAL
681 if (!last)
682 tbb.fsState |= TBSTATE_WRAP;
683 #endif
684 tbb.fsStyle = BTNS_SEP;
685 tbb.iBitmap = 0;
686
687 SendMessageW(m_hwndToolbar, TB_ADDBUTTONS, 1, reinterpret_cast<LPARAM>(&tbb));
688
689 return S_OK;
690 }
691
692 HRESULT CMenuToolbarBase::AddPlaceholder()
693 {
694 TBBUTTON tbb = { 0 };
695 PCWSTR MenuString = L"(Empty)";
696
697 tbb.fsState = 0;
698 tbb.fsStyle = 0;
699 tbb.iString = (INT_PTR) MenuString;
700 tbb.iBitmap = -1;
701
702 SendMessageW(m_hwndToolbar, TB_ADDBUTTONS, 1, reinterpret_cast<LPARAM>(&tbb));
703
704 return S_OK;
705 }
706
707 HRESULT CMenuToolbarBase::GetDataFromId(INT uItem, INT* pIndex, DWORD_PTR* pData)
708 {
709 TBBUTTONINFO info = { 0 };
710 info.cbSize = sizeof(TBBUTTONINFO);
711 info.dwMask = 0;
712 int index = SendMessage(m_hwndToolbar, TB_GETBUTTONINFO, uItem, reinterpret_cast<LPARAM>(&info));
713 if (index < 0)
714 return E_FAIL;
715
716 if (pIndex)
717 *pIndex = index;
718
719 if (pData)
720 {
721 TBBUTTON btn = { 0 };
722 if (!SendMessage(m_hwndToolbar, TB_GETBUTTON, index, reinterpret_cast<LPARAM>(&btn)))
723 return E_FAIL;
724 *pData = btn.dwData;
725 }
726
727 return S_OK;
728 }
729
730
731 HRESULT CMenuToolbarBase::PopupItem(INT uItem)
732 {
733 INT index;
734 DWORD_PTR dwData;
735
736 GetDataFromId(uItem, &index, &dwData);
737
738 return InternalPopupItem(uItem, index, dwData);
739 }
740
741 HRESULT CMenuToolbarBase::HasSubMenu(INT uItem)
742 {
743 INT index;
744 DWORD_PTR dwData;
745
746 GetDataFromId(uItem, &index, &dwData);
747
748 return InternalHasSubMenu(uItem, index, dwData);
749 }
750
751 CMenuStaticToolbar::CMenuStaticToolbar(CMenuBand *menuBand) :
752 CMenuToolbarBase(menuBand, FALSE),
753 m_hmenu(NULL)
754 {
755 }
756
757 HRESULT CMenuStaticToolbar::GetMenu(
758 HMENU *phmenu,
759 HWND *phwnd,
760 DWORD *pdwFlags)
761 {
762 *phmenu = m_hmenu;
763 *phwnd = NULL;
764 *pdwFlags = m_dwMenuFlags;
765
766 return S_OK;
767 }
768
769 HRESULT CMenuStaticToolbar::SetMenu(
770 HMENU hmenu,
771 HWND hwnd,
772 DWORD dwFlags)
773 {
774 m_hmenu = hmenu;
775 m_dwMenuFlags = dwFlags;
776
777 return S_OK;
778 }
779
780 HRESULT CMenuStaticToolbar::FillToolbar()
781 {
782 int i;
783 int ic = GetMenuItemCount(m_hmenu);
784
785 for (i = 0; i < ic; i++)
786 {
787 BOOL last = i + 1 == ic;
788
789 MENUITEMINFOW info;
790
791 info.cbSize = sizeof(info);
792 info.dwTypeData = NULL;
793 info.fMask = MIIM_FTYPE | MIIM_STRING;
794
795 GetMenuItemInfoW(m_hmenu, i, TRUE, &info);
796
797 if (info.fType == MFT_STRING)
798 {
799 info.cch++;
800 info.dwTypeData = (PWSTR) HeapAlloc(GetProcessHeap(), 0, (info.cch + 1) * sizeof(WCHAR));
801
802 info.fMask = MIIM_STRING | MIIM_SUBMENU | MIIM_ID;
803 GetMenuItemInfoW(m_hmenu, i, TRUE, &info);
804
805 SMINFO * sminfo = new SMINFO();
806 sminfo->dwMask = SMIM_ICON | SMIM_FLAGS;
807 // FIXME: remove before deleting the toolbar or it will leak
808
809 HRESULT hr = m_menuBand->_CallCBWithItemId(info.wID, SMC_GETINFO, 0, reinterpret_cast<LPARAM>(sminfo));
810 if (FAILED(hr))
811 return hr;
812
813 AddButton(info.wID, info.dwTypeData, info.hSubMenu != NULL, sminfo->iIcon, reinterpret_cast<DWORD_PTR>(sminfo), last);
814
815 HeapFree(GetProcessHeap(), 0, info.dwTypeData);
816 }
817 else
818 {
819 AddSeparator(last);
820 }
821 }
822
823 return S_OK;
824 }
825
826 HRESULT CMenuStaticToolbar::OnContextMenu(NMMOUSE * rclick)
827 {
828 CComPtr<IContextMenu> contextMenu;
829 HRESULT hr = m_menuBand->_CallCBWithItemId(rclick->dwItemSpec, SMC_GETOBJECT, reinterpret_cast<WPARAM>(&IID_IContextMenu), reinterpret_cast<LPARAM>(&contextMenu));
830 if (hr != S_OK)
831 return hr;
832
833 return DoContextMenu(contextMenu);
834 }
835
836 HRESULT CMenuStaticToolbar::OnCommand(WPARAM wParam, LPARAM lParam, LRESULT *theResult)
837 {
838 HRESULT hr;
839 hr = CMenuToolbarBase::OnCommand(wParam, lParam, theResult);
840 if (FAILED(hr))
841 return hr;
842
843 // in case the clicked item has a submenu, we do not need to execute the item
844 if (hr == S_FALSE)
845 return hr;
846
847 return m_menuBand->_CallCBWithItemId(wParam, SMC_EXEC, 0, 0);
848 }
849
850 HRESULT CMenuStaticToolbar::InternalPopupItem(INT uItem, INT index, DWORD_PTR dwData)
851 {
852 SMINFO * nfo = reinterpret_cast<SMINFO*>(dwData);
853 if (!nfo)
854 return E_FAIL;
855
856 if (nfo->dwFlags&SMIF_TRACKPOPUP)
857 {
858 return PopupSubMenu(index, m_hmenu);
859 }
860 else
861 {
862 CComPtr<IShellMenu> shellMenu;
863 HRESULT hr = m_menuBand->_CallCBWithItemId(uItem, SMC_GETOBJECT, reinterpret_cast<WPARAM>(&IID_IShellMenu), reinterpret_cast<LPARAM>(&shellMenu));
864 if (FAILED(hr))
865 return hr;
866
867 return PopupSubMenu(uItem, index, shellMenu);
868 }
869 }
870
871 HRESULT CMenuStaticToolbar::InternalHasSubMenu(INT uItem, INT index, DWORD_PTR dwData)
872 {
873 return ::GetSubMenu(m_hmenu, index) ? S_OK : S_FALSE;
874 }
875
876 CMenuSFToolbar::CMenuSFToolbar(CMenuBand * menuBand) :
877 CMenuToolbarBase(menuBand, TRUE),
878 m_shellFolder(NULL)
879 {
880 }
881
882 CMenuSFToolbar::~CMenuSFToolbar()
883 {
884 }
885
886 HRESULT CMenuSFToolbar::FillToolbar()
887 {
888 HRESULT hr;
889 int i = 0;
890 PWSTR MenuString;
891
892 IEnumIDList * eidl;
893 m_shellFolder->EnumObjects(m_hwndToolbar, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &eidl);
894
895 LPITEMIDLIST item = static_cast<LPITEMIDLIST>(CoTaskMemAlloc(sizeof(ITEMIDLIST)));
896 ULONG fetched;
897 hr = eidl->Next(1, &item, &fetched);
898 while (SUCCEEDED(hr) && fetched > 0)
899 {
900 INT index = 0;
901 INT indexOpen = 0;
902
903 STRRET sr = { STRRET_CSTR, { 0 } };
904
905 hr = m_shellFolder->GetDisplayNameOf(item, SIGDN_NORMALDISPLAY, &sr);
906 if (FAILED(hr))
907 return hr;
908
909 StrRetToStr(&sr, NULL, &MenuString);
910
911 index = SHMapPIDLToSystemImageListIndex(m_shellFolder, item, &indexOpen);
912
913 LPCITEMIDLIST itemc = item;
914
915 SFGAOF attrs = SFGAO_FOLDER;
916 hr = m_shellFolder->GetAttributesOf(1, &itemc, &attrs);
917
918 DWORD_PTR dwData = reinterpret_cast<DWORD_PTR>(ILClone(item));
919 // FIXME: remove before deleting the toolbar or it will leak
920
921 // Fetch next item already, so we know if the current one is the last
922 hr = eidl->Next(1, &item, &fetched);
923
924 AddButton(++i, MenuString, attrs & SFGAO_FOLDER, index, dwData, SUCCEEDED(hr) && fetched > 0);
925
926 CoTaskMemFree(MenuString);
927 }
928 CoTaskMemFree(item);
929
930 // If no items were added, show the "empty" placeholder
931 if (i == 0)
932 {
933 return AddPlaceholder();
934 }
935
936 return hr;
937 }
938
939 HRESULT CMenuSFToolbar::SetShellFolder(IShellFolder *psf, LPCITEMIDLIST pidlFolder, HKEY hKey, DWORD dwFlags)
940 {
941 m_shellFolder = psf;
942 m_idList = pidlFolder;
943 m_hKey = hKey;
944 m_dwMenuFlags = dwFlags;
945 return S_OK;
946 }
947
948 HRESULT CMenuSFToolbar::GetShellFolder(DWORD *pdwFlags, LPITEMIDLIST *ppidl, REFIID riid, void **ppv)
949 {
950 HRESULT hr;
951
952 hr = m_shellFolder->QueryInterface(riid, ppv);
953 if (FAILED(hr))
954 return hr;
955
956 if (pdwFlags)
957 *pdwFlags = m_dwMenuFlags;
958
959 if (ppidl)
960 {
961 LPITEMIDLIST pidl = NULL;
962
963 if (m_idList)
964 {
965 pidl = ILClone(m_idList);
966 if (!pidl)
967 {
968 (*(IUnknown**) ppv)->Release();
969 return E_FAIL;
970 }
971 }
972
973 *ppidl = pidl;
974 }
975
976 return hr;
977 }
978
979 HRESULT CMenuSFToolbar::OnContextMenu(NMMOUSE * rclick)
980 {
981 HRESULT hr;
982 CComPtr<IContextMenu> contextMenu;
983 LPCITEMIDLIST pidl = reinterpret_cast<LPCITEMIDLIST>(rclick->dwItemData);
984
985 hr = m_shellFolder->GetUIObjectOf(m_hwndToolbar, 1, &pidl, IID_IContextMenu, NULL, reinterpret_cast<VOID **>(&contextMenu));
986 if (hr != S_OK)
987 return hr;
988
989 return DoContextMenu(contextMenu);
990 }
991
992 HRESULT CMenuSFToolbar::OnCommand(WPARAM wParam, LPARAM lParam, LRESULT *theResult)
993 {
994 HRESULT hr;
995 hr = CMenuToolbarBase::OnCommand(wParam, lParam, theResult);
996 if (FAILED(hr))
997 return hr;
998
999 // in case the clicked item has a submenu, we do not need to execute the item
1000 if (hr == S_FALSE)
1001 return hr;
1002
1003 DWORD_PTR data;
1004 GetDataFromId(wParam, NULL, &data);
1005
1006 return m_menuBand->_CallCBWithItemPidl(reinterpret_cast<LPITEMIDLIST>(data), SMC_SFEXEC, 0, 0);
1007 }
1008
1009 HRESULT CMenuSFToolbar::InternalPopupItem(INT uItem, INT index, DWORD_PTR dwData)
1010 {
1011 HRESULT hr;
1012 UINT uId;
1013 UINT uIdAncestor;
1014 DWORD flags;
1015 CComPtr<IShellMenuCallback> psmc;
1016 CComPtr<IShellMenu> shellMenu;
1017
1018 LPITEMIDLIST pidl = reinterpret_cast<LPITEMIDLIST>(dwData);
1019
1020 if (!pidl)
1021 return E_FAIL;
1022
1023 #if USE_SYSTEM_MENUBAND
1024 hr = CoCreateInstance(CLSID_MenuBand,
1025 NULL,
1026 CLSCTX_INPROC_SERVER,
1027 IID_PPV_ARG(IShellMenu, &shellMenu));
1028 #else
1029 hr = CMenuBand_Constructor(IID_PPV_ARG(IShellMenu, &shellMenu));
1030 #endif
1031 if (FAILED(hr))
1032 return hr;
1033 #if WRAP_MENUBAND
1034 hr = CMenuBand_Wrapper(shellMenu, IID_PPV_ARG(IShellMenu, &shellMenu));
1035 if (FAILED(hr))
1036 return hr;
1037 #endif
1038
1039 m_menuBand->GetMenuInfo(&psmc, &uId, &uIdAncestor, &flags);
1040
1041 // FIXME: not sure what to use as uId/uIdAncestor here
1042 hr = shellMenu->Initialize(psmc, 0, uId, SMINIT_VERTICAL);
1043 if (FAILED(hr))
1044 return hr;
1045
1046 CComPtr<IShellFolder> childFolder;
1047 hr = m_shellFolder->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &childFolder));
1048 if (FAILED(hr))
1049 return hr;
1050
1051 hr = shellMenu->SetShellFolder(childFolder, NULL, NULL, 0);
1052 if (FAILED(hr))
1053 return hr;
1054
1055 return PopupSubMenu(uItem, index, shellMenu);
1056 }
1057
1058 HRESULT CMenuSFToolbar::InternalHasSubMenu(INT uItem, INT index, DWORD_PTR dwData)
1059 {
1060 HRESULT hr;
1061 LPCITEMIDLIST pidl = reinterpret_cast<LPITEMIDLIST>(dwData);
1062
1063 SFGAOF attrs = SFGAO_FOLDER;
1064 hr = m_shellFolder->GetAttributesOf(1, &pidl, &attrs);
1065 if (FAILED(hr))
1066 return hr;
1067
1068 return (attrs & SFGAO_FOLDER) ? S_OK : S_FALSE;
1069 }