4 * Copyright 2014 David Quintana
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.
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.
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
22 #include <CommonControls.h>
23 #include <shlwapi_undoc.h>
25 #include "CMenuBand.h"
26 #include "CMenuToolbars.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(CMenuToolbars
);
31 HRESULT WINAPI
SHGetImageList(
37 // FIXME: Enable if/when wine comctl supports this flag properly
38 #define USE_TBSTYLE_EX_VERTICAL 0
40 #define TIMERID_HOTTRACK 1
41 #define SUBCLASS_ID_MENUBAND 1
43 HRESULT
CMenuToolbarBase::DisableMouseTrack(BOOL bDisable
)
45 m_disableMouseTrack
= bDisable
;
49 HRESULT
CMenuToolbarBase::OnWinEvent(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
59 return OnCommand(wParam
, lParam
, theResult
);
62 hdr
= reinterpret_cast<LPNMHDR
>(lParam
);
65 case TTN_GETDISPINFOA
:
66 case TTN_GETDISPINFOW
:
69 case TBN_DELETINGBUTTON
:
70 return OnDeletingButton(reinterpret_cast<LPNMTOOLBAR
>(hdr
));
73 csize
= reinterpret_cast<LPNMPGCALCSIZE
>(hdr
);
76 if (csize
->dwFlag
== PGF_CALCHEIGHT
)
78 csize
->iHeight
= tbs
.cy
;
80 else if (csize
->dwFlag
== PGF_CALCWIDTH
)
82 csize
->iHeight
= tbs
.cx
;
87 wParam
= reinterpret_cast<LPNMTOOLBAR
>(hdr
)->iItem
;
88 return OnCommand(wParam
, 0, theResult
);
90 case TBN_HOTITEMCHANGE
:
91 return OnHotItemChange(reinterpret_cast<LPNMTBHOTITEM
>(hdr
), theResult
);
94 return OnContextMenu(reinterpret_cast<LPNMMOUSE
>(hdr
));
97 return OnCustomDraw(reinterpret_cast<LPNMTBCUSTOMDRAW
>(hdr
), theResult
);
103 DbgPrint("WM_NOTIFY unknown code %d, %d\n", hdr
->code
, hdr
->idFrom
);
111 HRESULT
CMenuToolbarBase::OnCustomDraw(LPNMTBCUSTOMDRAW cdraw
, LRESULT
* theResult
)
118 COLORREF clrTextHighlight
;
122 switch (cdraw
->nmcd
.dwDrawStage
)
125 if (m_toolbarFlags
& SMINIT_VERTICAL
)
126 *theResult
= CDRF_NOTIFYITEMDRAW
;
129 case CDDS_ITEMPREPAINT
:
131 clrText
= GetSysColor(COLOR_MENUTEXT
);
132 clrTextHighlight
= GetSysColor(COLOR_HIGHLIGHTTEXT
);
134 bgBrush
= GetSysColorBrush(COLOR_MENU
);
135 hotBrush
= GetSysColorBrush(m_useFlatMenus
? COLOR_MENUHILIGHT
: COLOR_HIGHLIGHT
);
138 hdc
= cdraw
->nmcd
.hdc
;
140 isHot
= m_hotBar
== this && m_hotItem
== static_cast<INT
>(cdraw
->nmcd
.dwItemSpec
);
141 isPopup
= m_popupBar
== this && m_popupItem
== static_cast<INT
>(cdraw
->nmcd
.dwItemSpec
);
143 if (isHot
|| (m_hotItem
< 0 && isPopup
))
145 cdraw
->nmcd
.uItemState
|= CDIS_HOT
;
149 cdraw
->nmcd
.uItemState
&= ~CDIS_HOT
;
152 if (cdraw
->nmcd
.uItemState
&CDIS_HOT
)
154 FillRect(hdc
, &rc
, hotBrush
);
155 SetTextColor(hdc
, clrTextHighlight
);
156 cdraw
->clrText
= clrTextHighlight
;
160 FillRect(hdc
, &rc
, bgBrush
);
161 SetTextColor(hdc
, clrText
);
162 cdraw
->clrText
= clrText
;
165 cdraw
->iListGap
+= 4;
167 *theResult
= CDRF_NOTIFYPOSTPAINT
| TBCDRF_NOBACKGROUND
| TBCDRF_NOEDGES
| TBCDRF_NOOFFSET
| TBCDRF_NOMARK
| 0x00800000; // FIXME: the last bit is Vista+, for debugging only
170 case CDDS_ITEMPOSTPAINT
:
171 btni
.cbSize
= sizeof(btni
);
172 btni
.dwMask
= TBIF_STYLE
;
173 SendMessage(m_hwndToolbar
, TB_GETBUTTONINFO
, cdraw
->nmcd
.dwItemSpec
, reinterpret_cast<LPARAM
>(&btni
));
174 if (btni
.fsStyle
& BTNS_DROPDOWN
)
176 SelectObject(cdraw
->nmcd
.hdc
, m_marlett
);
178 SetBkMode(cdraw
->nmcd
.hdc
, TRANSPARENT
);
179 RECT rc
= cdraw
->nmcd
.rc
;
181 DrawTextEx(cdraw
->nmcd
.hdc
, text
, 1, &rc
, DT_NOCLIP
| DT_VCENTER
| DT_RIGHT
| DT_SINGLELINE
, NULL
);
189 CMenuToolbarBase::CMenuToolbarBase(CMenuBand
*menuBand
, BOOL usePager
) :
191 m_useFlatMenus(FALSE
),
193 m_disableMouseTrack(FALSE
),
194 m_menuBand(menuBand
),
197 m_hasIdealSize(FALSE
),
198 m_usePager(usePager
),
203 m_marlett
= CreateFont(
204 0, 0, 0, 0, 0, 0, 0, 0, DEFAULT_CHARSET
,
205 OUT_DEFAULT_PRECIS
, CLIP_DEFAULT_PRECIS
,
206 DEFAULT_QUALITY
, FF_DONTCARE
, L
"Marlett");
209 CMenuToolbarBase::~CMenuToolbarBase()
211 DeleteObject(m_marlett
);
214 HRESULT
CMenuToolbarBase::IsWindowOwner(HWND hwnd
)
216 return (m_hwnd
&& m_hwnd
== hwnd
) ||
217 (m_hwndToolbar
&& m_hwndToolbar
== hwnd
) ? S_OK
: S_FALSE
;
220 void CMenuToolbarBase::InvalidateDraw()
222 InvalidateRect(m_hwnd
, NULL
, FALSE
);
225 HRESULT
CMenuToolbarBase::ShowWindow(BOOL fShow
)
227 ::ShowWindow(m_hwnd
, fShow
? SW_SHOW
: SW_HIDE
);
231 SystemParametersInfo(SPI_GETFLATMENU
, 0, &m_useFlatMenus
, 0);
236 HRESULT
CMenuToolbarBase::UpdateImageLists()
238 if ((m_toolbarFlags
& (SMINIT_TOPLEVEL
| SMINIT_VERTICAL
)) == SMINIT_TOPLEVEL
) // not vertical.
240 /* Hide the placeholders for the button images */
241 SendMessageW(m_hwnd
, TB_SETIMAGELIST
, 0, 0);
246 if (m_menuBand
->UseBigIcons())
249 SendMessageW(m_hwndToolbar
, TB_SETPADDING
, 0, MAKELPARAM(4, 0));
254 SendMessageW(m_hwndToolbar
, TB_SETPADDING
, 0, MAKELPARAM(4, 4));
258 HRESULT hr
= SHGetImageList(shiml
, IID_PPV_ARG(IImageList
, &piml
));
261 SendMessageW(m_hwndToolbar
, TB_SETIMAGELIST
, 0, reinterpret_cast<LPARAM
>(piml
));
265 SendMessageW(m_hwndToolbar
, TB_SETIMAGELIST
, 0, 0);
270 HRESULT
CMenuToolbarBase::Close()
272 DestroyWindow(m_hwndToolbar
);
273 if (m_hwndToolbar
!= m_hwnd
)
274 DestroyWindow(m_hwnd
);
275 m_hwndToolbar
= NULL
;
280 HRESULT
CMenuToolbarBase::CreateToolbar(HWND hwndParent
, DWORD dwFlags
)
282 LONG tbStyles
= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
|
283 TBSTYLE_TOOLTIPS
| TBSTYLE_TRANSPARENT
| TBSTYLE_REGISTERDROP
| TBSTYLE_LIST
| TBSTYLE_FLAT
| TBSTYLE_CUSTOMERASE
|
284 CCS_NODIVIDER
| CCS_NOPARENTALIGN
| CCS_NORESIZE
| CCS_TOP
;
285 LONG tbExStyles
= TBSTYLE_EX_DOUBLEBUFFER
;
287 if (dwFlags
& SMINIT_VERTICAL
)
289 tbStyles
|= CCS_VERT
;
291 #if USE_TBSTYLE_EX_VERTICAL
292 // FIXME: Use when it works in ros (?)
293 tbExStyles
|= TBSTYLE_EX_VERTICAL
| WS_EX_TOOLWINDOW
;
297 m_toolbarFlags
= dwFlags
;
301 if (!::GetClientRect(hwndParent
, &rc
) || (rc
.left
== rc
.right
) || (rc
.top
== rc
.bottom
))
309 HWND hwndToolbar
= CreateWindowEx(
310 tbExStyles
, TOOLBARCLASSNAMEW
, NULL
,
311 tbStyles
, rc
.left
, rc
.top
, rc
.right
- rc
.left
, rc
.bottom
- rc
.top
,
312 hwndParent
, NULL
, _AtlBaseModule
.GetModuleInstance(), 0);
314 if (hwndToolbar
== NULL
)
319 LONG pgStyles
= PGS_VERT
| WS_CHILD
| WS_VISIBLE
;
322 HWND hwndPager
= CreateWindowEx(
323 pgExStyles
, WC_PAGESCROLLER
, NULL
,
324 pgStyles
, rc
.left
, rc
.top
, rc
.right
- rc
.left
, rc
.bottom
- rc
.top
,
325 hwndParent
, NULL
, _AtlBaseModule
.GetModuleInstance(), 0);
327 ::SetParent(hwndToolbar
, hwndPager
);
328 ::SetParent(hwndPager
, hwndParent
);
330 SendMessage(hwndPager
, PGM_SETCHILD
, 0, reinterpret_cast<LPARAM
>(hwndToolbar
));
331 m_hwndToolbar
= hwndToolbar
;
336 ::SetParent(hwndToolbar
, hwndParent
);
337 m_hwndToolbar
= hwndToolbar
;
338 m_hwnd
= hwndToolbar
;
341 /* Identify the version of the used Common Controls DLL by sending the size of the TBBUTTON structure */
342 SendMessageW(hwndToolbar
, TB_BUTTONSTRUCTSIZE
, sizeof(TBBUTTON
), 0);
344 SetWindowLongPtr(hwndToolbar
, GWLP_USERDATA
, reinterpret_cast<LONG_PTR
>(this));
345 m_SubclassOld
= (WNDPROC
) SetWindowLongPtr(hwndToolbar
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(CMenuToolbarBase::s_SubclassProc
));
352 HRESULT
CMenuToolbarBase::GetIdealSize(SIZE
& size
)
354 size
.cx
= size
.cy
= 0;
356 if (m_hwndToolbar
&& !m_hasIdealSize
)
358 SendMessageW(m_hwndToolbar
, TB_AUTOSIZE
, 0, 0);
359 SendMessageW(m_hwndToolbar
, TB_GETMAXSIZE
, 0, reinterpret_cast<LPARAM
>(&m_idealSize
));
360 m_hasIdealSize
= TRUE
;
368 HRESULT
CMenuToolbarBase::SetPosSize(int x
, int y
, int cx
, int cy
)
370 if (m_hwnd
!= m_hwndToolbar
)
372 SetWindowPos(m_hwndToolbar
, NULL
, x
, y
, cx
, m_idealSize
.cy
, 0);
374 SetWindowPos(m_hwnd
, NULL
, x
, y
, cx
, cy
, 0);
375 if (m_toolbarFlags
& SMINIT_VERTICAL
)
377 DWORD btnSize
= SendMessage(m_hwndToolbar
, TB_GETBUTTONSIZE
, 0, 0);
378 SendMessage(m_hwndToolbar
, TB_SETBUTTONSIZE
, 0, MAKELPARAM(cx
, HIWORD(btnSize
)));
383 HRESULT
CMenuToolbarBase::GetWindow(HWND
*phwnd
)
393 LRESULT CALLBACK
CMenuToolbarBase::s_SubclassProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
395 CMenuToolbarBase
* pthis
= reinterpret_cast<CMenuToolbarBase
*>(GetWindowLongPtr(hWnd
, GWLP_USERDATA
));
396 return pthis
->SubclassProc(hWnd
, uMsg
, wParam
, lParam
);
399 LRESULT
CMenuToolbarBase::SubclassProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
405 case WM_USER_ISTRACKEDITEM
:
406 m_SubclassOld(hWnd
, uMsg
, wParam
, lParam
);
407 return IsTrackedItem(wParam
);
408 case WM_USER_CHANGETRACKEDITEM
:
410 m_SubclassOld(hWnd
, uMsg
, wParam
, lParam
);
411 return ChangeTrackedItem(wParam
);
414 OnWinEvent(hWnd
, uMsg
, wParam
, lParam
, &lr
);
417 OnWinEvent(hWnd
, uMsg
, wParam
, lParam
, &lr
);
420 if (wParam
== TIMERID_HOTTRACK
)
422 KillTimer(hWnd
, TIMERID_HOTTRACK
);
424 m_menuBand
->_OnPopupSubMenu(NULL
, NULL
, NULL
, NULL
, -1);
426 if (HasSubMenu(m_hotItem
) == S_OK
)
428 PopupItem(m_hotItem
);
433 return m_SubclassOld(hWnd
, uMsg
, wParam
, lParam
);
436 HRESULT
CMenuToolbarBase::OnHotItemChange(const NMTBHOTITEM
* hot
, LRESULT
* theResult
)
438 if (m_disableMouseTrack
&& hot
->dwFlags
& HICF_MOUSE
)
444 if (hot
->dwFlags
& HICF_LEAVING
)
446 KillTimer(m_hwndToolbar
, TIMERID_HOTTRACK
);
448 if (m_menuBand
->_OnHotItemChanged(NULL
, -1) == S_FALSE
)
456 m_menuBand
->_MenuItemHotTrack(MPOS_CHILDTRACKING
);
460 else if (m_hotItem
!= hot
->idNew
)
462 if (hot
->dwFlags
& HICF_MOUSE
&&
463 m_toolbarFlags
& SMINIT_VERTICAL
)
466 SystemParametersInfo(SPI_GETMENUSHOWDELAY
, 0, &elapsed
, 0);
467 SetTimer(m_hwndToolbar
, TIMERID_HOTTRACK
, elapsed
, NULL
);
470 m_hotItem
= hot
->idNew
;
471 m_menuBand
->_OnHotItemChanged(this, m_hotItem
);
472 m_menuBand
->_MenuItemHotTrack(MPOS_CHILDTRACKING
);
474 if (m_isTracking
&& !(m_toolbarFlags
& SMINIT_VERTICAL
))
476 KillTimer(m_hwndToolbar
, TIMERID_HOTTRACK
);
478 m_menuBand
->_OnPopupSubMenu(NULL
, NULL
, NULL
, NULL
, -1);
480 if (HasSubMenu(m_hotItem
) == S_OK
)
482 PopupItem(m_hotItem
);
490 HRESULT
CMenuToolbarBase::OnHotItemChanged(CMenuToolbarBase
* toolbar
, INT item
)
492 BOOL wasChecked
= FALSE
;
493 if (m_hotBar
== this && !(m_toolbarFlags
& SMINIT_VERTICAL
))
495 wasChecked
= SendMessage(m_hwndToolbar
, TB_ISBUTTONCHECKED
, m_hotItem
, 0);
498 SendMessage(m_hwndToolbar
, TB_CHECKBUTTON
, m_hotItem
, FALSE
);
503 if (wasChecked
&& m_hotBar
== this && !(m_toolbarFlags
& SMINIT_VERTICAL
))
505 SendMessage(m_hwndToolbar
, TB_CHECKBUTTON
, m_hotItem
, TRUE
);
511 HRESULT
CMenuToolbarBase::OnPopupItemChanged(CMenuToolbarBase
* toolbar
, INT item
)
513 if (toolbar
== NULL
&& m_popupBar
== this)
515 SendMessage(m_hwndToolbar
, TB_CHECKBUTTON
, m_popupItem
, FALSE
);
516 m_isTracking
= FALSE
;
518 m_popupBar
= toolbar
;
524 HRESULT
CMenuToolbarBase::IsTrackedItem(INT index
)
528 if (m_hotBar
!= this)
531 SendMessage(m_hwndToolbar
, TB_GETBUTTON
, index
, reinterpret_cast<LPARAM
>(&btn
));
533 if (m_hotItem
== btn
.idCommand
)
538 HRESULT
CMenuToolbarBase::ChangeTrackedItem(INT index
)
541 SendMessage(m_hwndToolbar
, TB_GETBUTTON
, index
, reinterpret_cast<LPARAM
>(&btn
));
543 if (m_hotItem
!= btn
.idCommand
)
545 SendMessage(m_hwndToolbar
, TB_SETHOTITEM
, index
, 0);
550 HRESULT
CMenuToolbarBase::PopupSubMenu(UINT uItem
, UINT index
, IShellMenu
* childShellMenu
)
552 IBandSite
* pBandSite
;
559 if (!SendMessage(m_hwndToolbar
, TB_GETITEMRECT
, index
, reinterpret_cast<LPARAM
>(&rc
)))
562 GetWindowRect(m_hwnd
, &rcx
);
564 POINT a
= { rc
.left
, rc
.top
};
565 POINT b
= { rc
.right
, rc
.bottom
};
566 POINT c
= { rcx
.left
, rcx
.top
};
567 POINT d
= { rcx
.right
, rcx
.bottom
};
569 ClientToScreen(m_hwndToolbar
, &a
);
570 ClientToScreen(m_hwndToolbar
, &b
);
571 ClientToScreen(m_hwnd
, &c
);
572 ClientToScreen(m_hwnd
, &d
);
574 POINTL pt
= { a
.x
, b
.y
};
575 RECTL rcl
= { c
.x
, c
.y
, d
.x
, d
.y
};
577 if(m_toolbarFlags
& SMINIT_VERTICAL
)
583 #if USE_SYSTEM_MENUSITE
584 hr
= CoCreateInstance(CLSID_MenuBandSite
,
586 CLSCTX_INPROC_SERVER
,
587 IID_PPV_ARG(IBandSite
, &pBandSite
));
589 hr
= CMenuSite_Constructor(IID_PPV_ARG(IBandSite
, &pBandSite
));
591 if (FAILED_UNEXPECTEDLY(hr
))
594 hr
= CMenuSite_Wrapper(pBandSite
, IID_PPV_ARG(IBandSite
, &pBandSite
));
595 if (FAILED_UNEXPECTEDLY(hr
))
599 #if USE_SYSTEM_MENUDESKBAR
600 hr
= CoCreateInstance(CLSID_MenuDeskBar
,
602 CLSCTX_INPROC_SERVER
,
603 IID_PPV_ARG(IDeskBar
, &pDeskBar
));
605 hr
= CMenuDeskBar_Constructor(IID_PPV_ARG(IDeskBar
, &pDeskBar
));
607 if (FAILED_UNEXPECTEDLY(hr
))
610 hr
= CMenuDeskBar_Wrapper(pDeskBar
, IID_PPV_ARG(IDeskBar
, &pDeskBar
));
611 if (FAILED_UNEXPECTEDLY(hr
))
615 hr
= pDeskBar
->SetClient(pBandSite
);
616 if (FAILED_UNEXPECTEDLY(hr
))
619 hr
= pBandSite
->AddBand(childShellMenu
);
620 if (FAILED_UNEXPECTEDLY(hr
))
623 CComPtr
<IMenuPopup
> popup
;
624 hr
= pDeskBar
->QueryInterface(IID_PPV_ARG(IMenuPopup
, &popup
));
625 if (FAILED_UNEXPECTEDLY(hr
))
629 m_menuBand
->_OnPopupSubMenu(popup
, &pt
, &rcl
, this, uItem
);
634 HRESULT
CMenuToolbarBase::PopupSubMenu(UINT uItem
, UINT index
, HMENU menu
)
639 if (!SendMessage(m_hwndToolbar
, TB_GETITEMRECT
, index
, reinterpret_cast<LPARAM
>(&rc
)))
642 GetClientRect(m_hwndToolbar
, &rcx
);
644 POINT a
= { rc
.left
, rc
.top
};
645 POINT b
= { rc
.right
, rc
.bottom
};
646 POINT c
= { rc
.left
, rc
.top
};
647 POINT d
= { rc
.right
, rc
.bottom
};
649 ClientToScreen(m_hwndToolbar
, &a
);
650 ClientToScreen(m_hwndToolbar
, &b
);
651 ClientToScreen(m_hwndToolbar
, &c
);
652 ClientToScreen(m_hwndToolbar
, &d
);
654 POINT pt
= { a
.x
, b
.y
};
655 RECT rcl
= { c
.x
, c
.y
, d
.x
, d
.y
};
657 if (m_toolbarFlags
& SMINIT_VERTICAL
)
663 HMENU popup
= GetSubMenu(menu
, index
);
666 m_menuBand
->_TrackSubMenuUsingTrackPopupMenu(popup
, pt
.x
, pt
.y
, rcl
);
668 SendMessage(m_hwndToolbar
, TB_CHECKBUTTON
, uItem
, FALSE
);
669 m_isTracking
= FALSE
;
674 HRESULT
CMenuToolbarBase::DoContextMenu(IContextMenu
* contextMenu
)
677 HMENU hPopup
= CreatePopupMenu();
682 hr
= contextMenu
->QueryContextMenu(hPopup
, 0, 0, UINT_MAX
, CMF_NORMAL
);
683 if (FAILED_UNEXPECTEDLY(hr
))
689 DWORD dwPos
= GetMessagePos();
690 UINT uCommand
= ::TrackPopupMenu(hPopup
, TPM_RETURNCMD
, GET_X_LPARAM(dwPos
), GET_Y_LPARAM(dwPos
), 0, m_hwnd
, NULL
);
694 CMINVOKECOMMANDINFO cmi
= { 0 };
695 cmi
.cbSize
= sizeof(cmi
);
696 cmi
.lpVerb
= MAKEINTRESOURCEA(uCommand
);
698 hr
= contextMenu
->InvokeCommand(&cmi
);
704 HRESULT
CMenuToolbarBase::OnCommand(WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
707 if (HasSubMenu(wParam
) == S_OK
)
709 KillTimer(m_hwndToolbar
, TIMERID_HOTTRACK
);
713 HRESULT hr
= m_menuBand
->_MenuItemHotTrack(MPOS_EXECUTE
);
714 if (FAILED_UNEXPECTEDLY(hr
))
716 return S_OK
; // filter out a possible S_FALSE from here.
719 HRESULT
CMenuToolbarBase::ChangeHotItem(DWORD dwSelectType
)
721 int prev
= m_hotItem
;
724 if (dwSelectType
!= 0xFFFFFFFF)
726 int count
= SendMessage(m_hwndToolbar
, TB_BUTTONCOUNT
, 0, 0);
730 TBBUTTONINFO info
= { 0 };
731 info
.cbSize
= sizeof(TBBUTTONINFO
);
733 index
= SendMessage(m_hwndToolbar
, TB_GETBUTTONINFO
, m_hotItem
, reinterpret_cast<LPARAM
>(&info
));
736 if (dwSelectType
== VK_HOME
)
739 dwSelectType
= VK_DOWN
;
741 else if (dwSelectType
== VK_END
)
744 dwSelectType
= VK_UP
;
748 if (dwSelectType
== VK_UP
)
752 else if (dwSelectType
== VK_DOWN
)
759 if (dwSelectType
== VK_UP
)
763 else if (dwSelectType
== VK_DOWN
)
769 TBBUTTON btn
= { 0 };
770 while (index
>= 0 && index
< count
)
772 DWORD res
= SendMessage(m_hwndToolbar
, TB_GETBUTTON
, index
, reinterpret_cast<LPARAM
>(&btn
));
778 if (prev
!= btn
.idCommand
)
780 SendMessage(m_hwndToolbar
, TB_SETHOTITEM
, index
, 0);
785 if (dwSelectType
== VK_UP
)
789 else if (dwSelectType
== VK_DOWN
)
798 SendMessage(m_hwndToolbar
, TB_SETHOTITEM
, -1, 0);
803 HRESULT
CMenuToolbarBase::AddButton(DWORD commandId
, LPCWSTR caption
, BOOL hasSubMenu
, INT iconId
, DWORD_PTR buttonData
, BOOL last
)
805 TBBUTTON tbb
= { 0 };
807 tbb
.fsState
= TBSTATE_ENABLED
;
808 #if !USE_TBSTYLE_EX_VERTICAL
809 if (!last
&& (m_toolbarFlags
& SMINIT_VERTICAL
))
810 tbb
.fsState
|= TBSTATE_WRAP
;
814 if (hasSubMenu
&& (m_toolbarFlags
& SMINIT_VERTICAL
))
815 tbb
.fsStyle
|= BTNS_DROPDOWN
;
817 if (!(m_toolbarFlags
& SMINIT_VERTICAL
))
818 tbb
.fsStyle
|= BTNS_AUTOSIZE
| BTNS_CHECKGROUP
;
820 tbb
.iString
= (INT_PTR
) caption
;
821 tbb
.idCommand
= commandId
;
823 tbb
.iBitmap
= iconId
;
824 tbb
.dwData
= buttonData
;
826 if (!SendMessageW(m_hwndToolbar
, TB_ADDBUTTONS
, 1, reinterpret_cast<LPARAM
>(&tbb
)))
827 return HRESULT_FROM_WIN32(GetLastError());
832 HRESULT
CMenuToolbarBase::AddSeparator(BOOL last
)
834 TBBUTTON tbb
= { 0 };
836 tbb
.fsState
= TBSTATE_ENABLED
;
837 #if !USE_TBSTYLE_EX_VERTICAL
838 if (!last
&& (m_toolbarFlags
& SMINIT_VERTICAL
))
839 tbb
.fsState
|= TBSTATE_WRAP
;
841 tbb
.fsStyle
= BTNS_SEP
;
844 if (!SendMessageW(m_hwndToolbar
, TB_ADDBUTTONS
, 1, reinterpret_cast<LPARAM
>(&tbb
)))
845 return HRESULT_FROM_WIN32(GetLastError());
850 HRESULT
CMenuToolbarBase::AddPlaceholder()
852 TBBUTTON tbb
= { 0 };
853 PCWSTR MenuString
= L
"(Empty)";
857 tbb
.iString
= (INT_PTR
) MenuString
;
860 if (!SendMessageW(m_hwndToolbar
, TB_ADDBUTTONS
, 1, reinterpret_cast<LPARAM
>(&tbb
)))
861 return HRESULT_FROM_WIN32(GetLastError());
866 HRESULT
CMenuToolbarBase::GetDataFromId(INT uItem
, INT
* pIndex
, DWORD_PTR
* pData
)
868 TBBUTTONINFO info
= { 0 };
869 info
.cbSize
= sizeof(TBBUTTONINFO
);
871 int index
= SendMessage(m_hwndToolbar
, TB_GETBUTTONINFO
, uItem
, reinterpret_cast<LPARAM
>(&info
));
880 TBBUTTON btn
= { 0 };
881 if (!SendMessage(m_hwndToolbar
, TB_GETBUTTON
, index
, reinterpret_cast<LPARAM
>(&btn
)))
890 HRESULT
CMenuToolbarBase::PopupItem(INT uItem
)
895 if (!(m_toolbarFlags
& SMINIT_VERTICAL
))
897 SendMessage(m_hwndToolbar
, TB_SETHOTITEM
, uItem
, 0);
898 SendMessage(m_hwndToolbar
, TB_CHECKBUTTON
, uItem
, TRUE
);
901 GetDataFromId(uItem
, &index
, &dwData
);
903 return InternalPopupItem(uItem
, index
, dwData
);
906 HRESULT
CMenuToolbarBase::HasSubMenu(INT uItem
)
911 GetDataFromId(uItem
, &index
, &dwData
);
913 return InternalHasSubMenu(uItem
, index
, dwData
);
916 CMenuStaticToolbar::CMenuStaticToolbar(CMenuBand
*menuBand
) :
917 CMenuToolbarBase(menuBand
, FALSE
),
922 HRESULT
CMenuStaticToolbar::GetMenu(
923 _Out_opt_ HMENU
*phmenu
,
924 _Out_opt_ HWND
*phwnd
,
925 _Out_opt_ DWORD
*pdwFlags
)
932 *pdwFlags
= m_dwMenuFlags
;
937 HRESULT
CMenuStaticToolbar::SetMenu(
943 m_dwMenuFlags
= dwFlags
;
948 HRESULT
CMenuStaticToolbar::FillToolbar(BOOL clearFirst
)
951 int ic
= GetMenuItemCount(m_hmenu
);
955 while (SendMessage(m_hwndToolbar
, TB_DELETEBUTTON
, 0, 0))
962 for (i
= 0; i
< ic
; i
++)
964 BOOL last
= i
+ 1 == ic
;
968 info
.cbSize
= sizeof(info
);
969 info
.dwTypeData
= NULL
;
970 info
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_ID
;
972 if (!GetMenuItemInfoW(m_hmenu
, i
, TRUE
, &info
))
974 DbgPrint("Error obtaining info for menu item at pos=%d\n", i
);
980 if (info
.fType
& MFT_SEPARATOR
)
984 else if (!(info
.fType
& MFT_BITMAP
))
988 info
.dwTypeData
= (PWSTR
) HeapAlloc(GetProcessHeap(), 0, (info
.cch
+ 1) * sizeof(WCHAR
));
990 info
.fMask
= MIIM_STRING
| MIIM_SUBMENU
| MIIM_ID
;
991 GetMenuItemInfoW(m_hmenu
, i
, TRUE
, &info
);
993 SMINFO
* sminfo
= new SMINFO();
994 sminfo
->dwMask
= SMIM_ICON
| SMIM_FLAGS
;
995 // FIXME: remove before deleting the toolbar or it will leak
997 HRESULT hr
= m_menuBand
->_CallCBWithItemId(info
.wID
, SMC_GETINFO
, 0, reinterpret_cast<LPARAM
>(sminfo
));
998 if (FAILED_UNEXPECTEDLY(hr
))
1001 AddButton(info
.wID
, info
.dwTypeData
, info
.hSubMenu
!= NULL
, sminfo
->iIcon
, reinterpret_cast<DWORD_PTR
>(sminfo
), last
);
1003 HeapFree(GetProcessHeap(), 0, info
.dwTypeData
);
1007 DbgPrint("Created toolbar with %d buttons.\n", count
);
1012 HRESULT
CMenuStaticToolbar::OnDeletingButton(const NMTOOLBAR
* tb
)
1014 delete reinterpret_cast<SMINFO
*>(tb
->tbButton
.dwData
);
1018 HRESULT
CMenuStaticToolbar::OnContextMenu(NMMOUSE
* rclick
)
1020 CComPtr
<IContextMenu
> contextMenu
;
1021 HRESULT hr
= m_menuBand
->_CallCBWithItemId(rclick
->dwItemSpec
, SMC_GETOBJECT
, reinterpret_cast<WPARAM
>(&IID_IContextMenu
), reinterpret_cast<LPARAM
>(&contextMenu
));
1025 return DoContextMenu(contextMenu
);
1028 HRESULT
CMenuStaticToolbar::OnCommand(WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
1031 hr
= CMenuToolbarBase::OnCommand(wParam
, lParam
, theResult
);
1032 if (FAILED_UNEXPECTEDLY(hr
))
1035 // in case the clicked item has a submenu, we do not need to execute the item
1038 DbgPrint("CMenuToolbarBase::OnCommand told us to cancel.\n");
1042 return m_menuBand
->_CallCBWithItemId(wParam
, SMC_EXEC
, 0, 0);
1045 HRESULT
CMenuStaticToolbar::InternalPopupItem(INT uItem
, INT index
, DWORD_PTR dwData
)
1047 SMINFO
* nfo
= reinterpret_cast<SMINFO
*>(dwData
);
1051 if (nfo
->dwFlags
&SMIF_TRACKPOPUP
)
1053 return PopupSubMenu(uItem
, index
, m_hmenu
);
1057 CComPtr
<IShellMenu
> shellMenu
;
1058 HRESULT hr
= m_menuBand
->_CallCBWithItemId(uItem
, SMC_GETOBJECT
, reinterpret_cast<WPARAM
>(&IID_IShellMenu
), reinterpret_cast<LPARAM
>(&shellMenu
));
1059 if (FAILED_UNEXPECTEDLY(hr
))
1062 return PopupSubMenu(uItem
, index
, shellMenu
);
1066 HRESULT
CMenuStaticToolbar::InternalHasSubMenu(INT uItem
, INT index
, DWORD_PTR dwData
)
1068 return ::GetSubMenu(m_hmenu
, index
) ? S_OK
: S_FALSE
;
1071 CMenuSFToolbar::CMenuSFToolbar(CMenuBand
* menuBand
) :
1072 CMenuToolbarBase(menuBand
, TRUE
),
1073 m_shellFolder(NULL
),
1079 CMenuSFToolbar::~CMenuSFToolbar()
1083 HRESULT
CMenuSFToolbar::FillToolbar(BOOL clearFirst
)
1090 m_shellFolder
->EnumObjects(m_hwndToolbar
, SHCONTF_FOLDERS
| SHCONTF_NONFOLDERS
, &eidl
);
1092 LPITEMIDLIST item
= static_cast<LPITEMIDLIST
>(CoTaskMemAlloc(sizeof(ITEMIDLIST
)));
1094 hr
= eidl
->Next(1, &item
, &fetched
);
1095 while (SUCCEEDED(hr
) && fetched
> 0)
1100 STRRET sr
= { STRRET_CSTR
, { 0 } };
1102 hr
= m_shellFolder
->GetDisplayNameOf(item
, SIGDN_NORMALDISPLAY
, &sr
);
1103 if (FAILED_UNEXPECTEDLY(hr
))
1106 StrRetToStr(&sr
, NULL
, &MenuString
);
1108 index
= SHMapPIDLToSystemImageListIndex(m_shellFolder
, item
, &indexOpen
);
1110 LPCITEMIDLIST itemc
= item
;
1112 SFGAOF attrs
= SFGAO_FOLDER
;
1113 hr
= m_shellFolder
->GetAttributesOf(1, &itemc
, &attrs
);
1115 DWORD_PTR dwData
= reinterpret_cast<DWORD_PTR
>(ILClone(item
));
1116 // FIXME: remove before deleting the toolbar or it will leak
1118 // Fetch next item already, so we know if the current one is the last
1119 hr
= eidl
->Next(1, &item
, &fetched
);
1121 AddButton(++i
, MenuString
, attrs
& SFGAO_FOLDER
, index
, dwData
, FAILED(hr
) || fetched
== 0);
1123 CoTaskMemFree(MenuString
);
1125 CoTaskMemFree(item
);
1127 // If no items were added, show the "empty" placeholder
1130 return AddPlaceholder();
1133 DbgPrint("Created toolbar with %d buttons.\n", i
);
1138 HRESULT
CMenuSFToolbar::OnDeletingButton(const NMTOOLBAR
* tb
)
1140 ILFree(reinterpret_cast<LPITEMIDLIST
>(tb
->tbButton
.dwData
));
1144 HRESULT
CMenuSFToolbar::SetShellFolder(IShellFolder
*psf
, LPCITEMIDLIST pidlFolder
, HKEY hKey
, DWORD dwFlags
)
1146 m_shellFolder
= psf
;
1147 m_idList
= ILClone(pidlFolder
);
1149 m_dwMenuFlags
= dwFlags
;
1153 HRESULT
CMenuSFToolbar::GetShellFolder(DWORD
*pdwFlags
, LPITEMIDLIST
*ppidl
, REFIID riid
, void **ppv
)
1157 hr
= m_shellFolder
->QueryInterface(riid
, ppv
);
1158 if (FAILED_UNEXPECTEDLY(hr
))
1162 *pdwFlags
= m_dwMenuFlags
;
1166 LPITEMIDLIST pidl
= NULL
;
1170 pidl
= ILClone(m_idList
);
1173 (*(IUnknown
**) ppv
)->Release();
1184 HRESULT
CMenuSFToolbar::OnContextMenu(NMMOUSE
* rclick
)
1187 CComPtr
<IContextMenu
> contextMenu
;
1188 LPCITEMIDLIST pidl
= reinterpret_cast<LPCITEMIDLIST
>(rclick
->dwItemData
);
1190 hr
= m_shellFolder
->GetUIObjectOf(m_hwndToolbar
, 1, &pidl
, IID_IContextMenu
, NULL
, reinterpret_cast<VOID
**>(&contextMenu
));
1194 return DoContextMenu(contextMenu
);
1197 HRESULT
CMenuSFToolbar::OnCommand(WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
1200 hr
= CMenuToolbarBase::OnCommand(wParam
, lParam
, theResult
);
1201 if (FAILED_UNEXPECTEDLY(hr
))
1204 // in case the clicked item has a submenu, we do not need to execute the item
1207 DbgPrint("CMenuToolbarBase::OnCommand told us to cancel.\n");
1212 GetDataFromId(wParam
, NULL
, &data
);
1214 return m_menuBand
->_CallCBWithItemPidl(reinterpret_cast<LPITEMIDLIST
>(data
), SMC_SFEXEC
, 0, 0);
1217 HRESULT
CMenuSFToolbar::InternalPopupItem(INT uItem
, INT index
, DWORD_PTR dwData
)
1223 CComPtr
<IShellMenuCallback
> psmc
;
1224 CComPtr
<IShellMenu
> shellMenu
;
1226 LPITEMIDLIST pidl
= reinterpret_cast<LPITEMIDLIST
>(dwData
);
1231 #if USE_SYSTEM_MENUBAND
1232 hr
= CoCreateInstance(CLSID_MenuBand
,
1234 CLSCTX_INPROC_SERVER
,
1235 IID_PPV_ARG(IShellMenu
, &shellMenu
));
1237 hr
= CMenuBand_Constructor(IID_PPV_ARG(IShellMenu
, &shellMenu
));
1239 if (FAILED_UNEXPECTEDLY(hr
))
1242 hr
= CMenuBand_Wrapper(shellMenu
, IID_PPV_ARG(IShellMenu
, &shellMenu
));
1243 if (FAILED_UNEXPECTEDLY(hr
))
1247 m_menuBand
->GetMenuInfo(&psmc
, &uId
, &uIdAncestor
, &flags
);
1249 // FIXME: not sure what to use as uId/uIdAncestor here
1250 hr
= shellMenu
->Initialize(psmc
, 0, uId
, SMINIT_VERTICAL
);
1251 if (FAILED_UNEXPECTEDLY(hr
))
1254 CComPtr
<IShellFolder
> childFolder
;
1255 hr
= m_shellFolder
->BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder
, &childFolder
));
1256 if (FAILED_UNEXPECTEDLY(hr
))
1259 hr
= shellMenu
->SetShellFolder(childFolder
, NULL
, NULL
, 0);
1260 if (FAILED_UNEXPECTEDLY(hr
))
1263 return PopupSubMenu(uItem
, index
, shellMenu
);
1266 HRESULT
CMenuSFToolbar::InternalHasSubMenu(INT uItem
, INT index
, DWORD_PTR dwData
)
1269 LPCITEMIDLIST pidl
= reinterpret_cast<LPITEMIDLIST
>(dwData
);
1271 SFGAOF attrs
= SFGAO_FOLDER
;
1272 hr
= m_shellFolder
->GetAttributesOf(1, &pidl
, &attrs
);
1273 if (FAILED_UNEXPECTEDLY(hr
))
1276 return (attrs
& SFGAO_FOLDER
) ? S_OK
: S_FALSE
;