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 TBSTYLE_EX_VERTICAL 4
40 #define TIMERID_HOTTRACK 1
41 #define SUBCLASS_ID_MENUBAND 1
43 HRESULT
CMenuToolbarBase::OnWinEvent(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
50 NMTBCUSTOMDRAW
* cdraw
;
56 COLORREF clrTextHighlight
;
64 return OnCommand(wParam
, lParam
, theResult
);
67 hdr
= reinterpret_cast<LPNMHDR
>(lParam
);
70 case TTN_GETDISPINFOA
:
71 case TTN_GETDISPINFOW
:
75 csize
= reinterpret_cast<LPNMPGCALCSIZE
>(hdr
);
78 if (csize
->dwFlag
== PGF_CALCHEIGHT
)
80 csize
->iHeight
= tbs
.cy
;
82 else if (csize
->dwFlag
== PGF_CALCWIDTH
)
84 csize
->iHeight
= tbs
.cx
;
89 wParam
= reinterpret_cast<LPNMTOOLBAR
>(hdr
)->iItem
;
90 return OnCommand(wParam
, 0, theResult
);
92 case TBN_HOTITEMCHANGE
:
93 hot
= reinterpret_cast<LPNMTBHOTITEM
>(hdr
);
94 return OnHotItemChange(hot
);
97 rclick
= reinterpret_cast<LPNMMOUSE
>(hdr
);
99 return OnContextMenu(rclick
);
102 cdraw
= reinterpret_cast<LPNMTBCUSTOMDRAW
>(hdr
);
103 switch (cdraw
->nmcd
.dwDrawStage
)
106 if (m_toolbarFlags
& SMINIT_VERTICAL
)
107 *theResult
= CDRF_NOTIFYITEMDRAW
;
110 case CDDS_ITEMPREPAINT
:
112 clrText
= GetSysColor(COLOR_MENUTEXT
);
113 clrTextHighlight
= GetSysColor(COLOR_HIGHLIGHTTEXT
);
115 bgBrush
= GetSysColorBrush(COLOR_MENU
);
116 hotBrush
= GetSysColorBrush(m_useFlatMenus
? COLOR_MENUHILIGHT
: COLOR_HIGHLIGHT
);
119 hdc
= cdraw
->nmcd
.hdc
;
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
);
124 if (isHot
|| (m_hotItem
< 0 && isPopup
))
126 cdraw
->nmcd
.uItemState
|= CDIS_HOT
;
130 cdraw
->nmcd
.uItemState
&= ~CDIS_HOT
;
133 if (cdraw
->nmcd
.uItemState
&CDIS_HOT
)
135 FillRect(hdc
, &rc
, hotBrush
);
136 SetTextColor(hdc
, clrTextHighlight
);
137 cdraw
->clrText
= clrTextHighlight
;
141 FillRect(hdc
, &rc
, bgBrush
);
142 SetTextColor(hdc
, clrText
);
143 cdraw
->clrText
= clrText
;
146 cdraw
->iListGap
+= 4;
148 *theResult
= CDRF_NOTIFYPOSTPAINT
| TBCDRF_NOBACKGROUND
| TBCDRF_NOEDGES
| TBCDRF_NOOFFSET
| TBCDRF_NOMARK
| 0x00800000; // FIXME: the last bit is Vista+, for debugging only
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
)
157 SelectObject(cdraw
->nmcd
.hdc
, m_marlett
);
159 SetBkMode(cdraw
->nmcd
.hdc
, TRANSPARENT
);
160 RECT rc
= cdraw
->nmcd
.rc
;
162 DrawTextEx(cdraw
->nmcd
.hdc
, text
, 1, &rc
, DT_NOCLIP
| DT_VCENTER
| DT_RIGHT
| DT_SINGLELINE
, NULL
);
171 DbgPrint("WM_NOTIFY unknown code %d, %d\n", hdr
->code
, hdr
->idFrom
);
179 CMenuToolbarBase::CMenuToolbarBase(CMenuBand
*menuBand
, BOOL usePager
) :
181 m_useFlatMenus(FALSE
),
183 m_menuBand(menuBand
),
186 m_hasIdealSize(FALSE
),
187 m_usePager(usePager
),
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");
197 CMenuToolbarBase::~CMenuToolbarBase()
199 DeleteObject(m_marlett
);
202 HRESULT
CMenuToolbarBase::IsWindowOwner(HWND hwnd
)
204 return (m_hwnd
&& m_hwnd
== hwnd
) ||
205 (m_hwndToolbar
&& m_hwndToolbar
== hwnd
) ? S_OK
: S_FALSE
;
208 void CMenuToolbarBase::InvalidateDraw()
210 InvalidateRect(m_hwnd
, NULL
, FALSE
);
213 HRESULT
CMenuToolbarBase::ShowWindow(BOOL fShow
)
215 ::ShowWindow(m_hwnd
, fShow
? SW_SHOW
: SW_HIDE
);
219 SystemParametersInfo(SPI_GETFLATMENU
, 0, &m_useFlatMenus
, 0);
224 HRESULT
CMenuToolbarBase::UpdateImageLists()
226 if ((m_toolbarFlags
& (SMINIT_TOPLEVEL
| SMINIT_VERTICAL
)) == SMINIT_TOPLEVEL
) // not vertical.
228 /* Hide the placeholders for the button images */
229 SendMessageW(m_hwnd
, TB_SETIMAGELIST
, 0, 0);
234 if (m_menuBand
->UseBigIcons())
237 SendMessageW(m_hwndToolbar
, TB_SETPADDING
, 0, MAKELPARAM(4, 0));
242 SendMessageW(m_hwndToolbar
, TB_SETPADDING
, 0, MAKELPARAM(4, 4));
246 HRESULT hr
= SHGetImageList(shiml
, IID_PPV_ARG(IImageList
, &piml
));
249 SendMessageW(m_hwndToolbar
, TB_SETIMAGELIST
, 0, reinterpret_cast<LPARAM
>(piml
));
253 SendMessageW(m_hwndToolbar
, TB_SETIMAGELIST
, 0, 0);
258 HRESULT
CMenuToolbarBase::Close()
260 DestroyWindow(m_hwndToolbar
);
261 if (m_hwndToolbar
!= m_hwnd
)
262 DestroyWindow(m_hwnd
);
263 m_hwndToolbar
= NULL
;
268 HRESULT
CMenuToolbarBase::CreateToolbar(HWND hwndParent
, DWORD dwFlags
)
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
;
275 if (dwFlags
& SMINIT_VERTICAL
)
277 tbStyles
|= CCS_VERT
;
279 #ifdef TBSTYLE_EX_VERTICAL
280 // FIXME: Use when it works in ros (?)
281 tbExStyles
|= TBSTYLE_EX_VERTICAL
| WS_EX_TOOLWINDOW
;
285 m_toolbarFlags
= dwFlags
;
289 if (!::GetClientRect(hwndParent
, &rc
) || (rc
.left
== rc
.right
) || (rc
.top
== rc
.bottom
))
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);
302 if (hwndToolbar
== NULL
)
307 LONG pgStyles
= PGS_VERT
| WS_CHILD
| WS_VISIBLE
;
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);
315 ::SetParent(hwndToolbar
, hwndPager
);
316 ::SetParent(hwndPager
, hwndParent
);
318 SendMessage(hwndPager
, PGM_SETCHILD
, 0, reinterpret_cast<LPARAM
>(hwndToolbar
));
319 m_hwndToolbar
= hwndToolbar
;
324 ::SetParent(hwndToolbar
, hwndParent
);
325 m_hwndToolbar
= hwndToolbar
;
326 m_hwnd
= hwndToolbar
;
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);
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
));
340 HRESULT
CMenuToolbarBase::GetIdealSize(SIZE
& size
)
342 size
.cx
= size
.cy
= 0;
344 if (m_hwndToolbar
&& !m_hasIdealSize
)
346 SendMessageW(m_hwndToolbar
, TB_AUTOSIZE
, 0, 0);
347 SendMessageW(m_hwndToolbar
, TB_GETMAXSIZE
, 0, reinterpret_cast<LPARAM
>(&m_idealSize
));
348 m_hasIdealSize
= TRUE
;
356 HRESULT
CMenuToolbarBase::SetPosSize(int x
, int y
, int cx
, int cy
)
358 if (m_hwnd
!= m_hwndToolbar
)
360 SetWindowPos(m_hwndToolbar
, NULL
, x
, y
, cx
, m_idealSize
.cy
, 0);
362 SetWindowPos(m_hwnd
, NULL
, x
, y
, cx
, cy
, 0);
363 if (m_toolbarFlags
& SMINIT_VERTICAL
)
365 DWORD btnSize
= SendMessage(m_hwndToolbar
, TB_GETBUTTONSIZE
, 0, 0);
366 SendMessage(m_hwndToolbar
, TB_SETBUTTONSIZE
, 0, MAKELPARAM(cx
, HIWORD(btnSize
)));
371 HRESULT
CMenuToolbarBase::GetWindow(HWND
*phwnd
)
381 LRESULT CALLBACK
CMenuToolbarBase::s_SubclassProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
383 CMenuToolbarBase
* pthis
= reinterpret_cast<CMenuToolbarBase
*>(GetWindowLongPtr(hWnd
, GWLP_USERDATA
));
384 return pthis
->SubclassProc(hWnd
, uMsg
, wParam
, lParam
);
387 LRESULT
CMenuToolbarBase::SubclassProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
394 OnWinEvent(hWnd
, uMsg
, wParam
, lParam
, &lr
);
397 OnWinEvent(hWnd
, uMsg
, wParam
, lParam
, &lr
);
400 if (wParam
== TIMERID_HOTTRACK
)
402 KillTimer(hWnd
, TIMERID_HOTTRACK
);
404 m_menuBand
->_OnPopupSubMenu(NULL
, NULL
, NULL
, NULL
, -1);
406 if (HasSubMenu(m_hotItem
) == S_OK
)
408 PopupItem(m_hotItem
);
413 return m_SubclassOld(hWnd
, uMsg
, wParam
, lParam
);
416 HRESULT
CMenuToolbarBase::OnHotItemChange(const NMTBHOTITEM
* hot
)
418 if (hot
->dwFlags
& HICF_LEAVING
)
420 KillTimer(m_hwndToolbar
, TIMERID_HOTTRACK
);
422 m_menuBand
->_OnHotItemChanged(NULL
, -1);
423 m_menuBand
->_MenuItemHotTrack(MPOS_CHILDTRACKING
);
425 else if (m_hotItem
!= hot
->idNew
)
427 if (m_toolbarFlags
& SMINIT_VERTICAL
)
430 SystemParametersInfo(SPI_GETMENUSHOWDELAY
, 0, &elapsed
, 0);
431 SetTimer(m_hwndToolbar
, TIMERID_HOTTRACK
, elapsed
, NULL
);
434 m_hotItem
= hot
->idNew
;
435 m_menuBand
->_OnHotItemChanged(this, m_hotItem
);
436 m_menuBand
->_MenuItemHotTrack(MPOS_CHILDTRACKING
);
441 HRESULT
CMenuToolbarBase::OnHotItemChanged(CMenuToolbarBase
* toolbar
, INT item
)
443 BOOL wasChecked
= FALSE
;
444 if (m_hotBar
== this && !(m_toolbarFlags
& SMINIT_VERTICAL
))
446 wasChecked
= SendMessage(m_hwndToolbar
, TB_ISBUTTONCHECKED
, m_hotItem
, 0);
447 SendMessage(m_hwndToolbar
, TB_CHECKBUTTON
, m_hotItem
, FALSE
);
451 if (wasChecked
&& m_hotBar
== this && !(m_toolbarFlags
& SMINIT_VERTICAL
))
453 SendMessage(m_hwndToolbar
, TB_CHECKBUTTON
, m_hotItem
, TRUE
);
459 HRESULT
CMenuToolbarBase::OnPopupItemChanged(CMenuToolbarBase
* toolbar
, INT item
)
461 m_popupBar
= toolbar
;
467 HRESULT
CMenuToolbarBase::PopupSubMenu(UINT uItem
, UINT index
, IShellMenu
* childShellMenu
)
469 IBandSite
* pBandSite
;
476 if (!SendMessage(m_hwndToolbar
, TB_GETITEMRECT
, index
, reinterpret_cast<LPARAM
>(&rc
)))
479 GetWindowRect(m_hwnd
, &rcx
);
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
};
486 ClientToScreen(m_hwndToolbar
, &a
);
487 ClientToScreen(m_hwndToolbar
, &b
);
488 ClientToScreen(m_hwnd
, &c
);
489 ClientToScreen(m_hwnd
, &d
);
491 POINTL pt
= { b
.x
- 3, a
.y
- 3 };
492 RECTL rcl
= { c
.x
, c
.y
, d
.x
, d
.y
};
494 #if USE_SYSTEM_MENUSITE
495 hr
= CoCreateInstance(CLSID_MenuBandSite
,
497 CLSCTX_INPROC_SERVER
,
498 IID_PPV_ARG(IBandSite
, &pBandSite
));
500 hr
= CMenuSite_Constructor(IID_PPV_ARG(IBandSite
, &pBandSite
));
502 if (FAILED_UNEXPECTEDLY(hr
))
505 hr
= CMenuSite_Wrapper(pBandSite
, IID_PPV_ARG(IBandSite
, &pBandSite
));
506 if (FAILED_UNEXPECTEDLY(hr
))
510 #if USE_SYSTEM_MENUDESKBAR
511 hr
= CoCreateInstance(CLSID_MenuDeskBar
,
513 CLSCTX_INPROC_SERVER
,
514 IID_PPV_ARG(IDeskBar
, &pDeskBar
));
516 hr
= CMenuDeskBar_Constructor(IID_PPV_ARG(IDeskBar
, &pDeskBar
));
518 if (FAILED_UNEXPECTEDLY(hr
))
521 hr
= CMenuDeskBar_Wrapper(pDeskBar
, IID_PPV_ARG(IDeskBar
, &pDeskBar
));
522 if (FAILED_UNEXPECTEDLY(hr
))
526 hr
= pDeskBar
->SetClient(pBandSite
);
527 if (FAILED_UNEXPECTEDLY(hr
))
530 hr
= pBandSite
->AddBand(childShellMenu
);
531 if (FAILED_UNEXPECTEDLY(hr
))
534 CComPtr
<IMenuPopup
> popup
;
535 hr
= pDeskBar
->QueryInterface(IID_PPV_ARG(IMenuPopup
, &popup
));
536 if (FAILED_UNEXPECTEDLY(hr
))
539 m_menuBand
->_OnPopupSubMenu(popup
, &pt
, &rcl
, this, m_popupItem
);
544 HRESULT
CMenuToolbarBase::PopupSubMenu(UINT uItem
, UINT index
, HMENU menu
)
549 if (!SendMessage(m_hwndToolbar
, TB_GETITEMRECT
, index
, reinterpret_cast<LPARAM
>(&rc
)))
552 GetWindowRect(m_hwnd
, &rcx
);
554 POINT a
= { rc
.left
, rc
.top
};
555 POINT b
= { rc
.right
, rc
.bottom
};
556 POINT c
= { rcx
.left
, rcx
.top
};
557 POINT d
= { rcx
.right
, rcx
.bottom
};
559 ClientToScreen(m_hwndToolbar
, &a
);
560 ClientToScreen(m_hwndToolbar
, &b
);
561 ClientToScreen(m_hwnd
, &c
);
562 ClientToScreen(m_hwnd
, &d
);
564 POINT pt
= { a
.x
, b
.y
};
565 RECT rcl
= { c
.x
, c
.y
, d
.x
, d
.y
};
567 if (m_toolbarFlags
& SMINIT_VERTICAL
)
573 HMENU popup
= GetSubMenu(menu
, index
);
575 m_menuBand
->_TrackSubMenuUsingTrackPopupMenu(popup
, pt
.x
, pt
.y
, rcl
);
577 SendMessage(m_hwndToolbar
, TB_CHECKBUTTON
, uItem
, FALSE
);
582 HRESULT
CMenuToolbarBase::DoContextMenu(IContextMenu
* contextMenu
)
585 HMENU hPopup
= CreatePopupMenu();
590 hr
= contextMenu
->QueryContextMenu(hPopup
, 0, 0, UINT_MAX
, CMF_NORMAL
);
591 if (FAILED_UNEXPECTEDLY(hr
))
597 DWORD dwPos
= GetMessagePos();
598 UINT uCommand
= ::TrackPopupMenu(hPopup
, TPM_RETURNCMD
, GET_X_LPARAM(dwPos
), GET_Y_LPARAM(dwPos
), 0, m_hwnd
, NULL
);
602 CMINVOKECOMMANDINFO cmi
= { 0 };
603 cmi
.cbSize
= sizeof(cmi
);
604 cmi
.lpVerb
= MAKEINTRESOURCEA(uCommand
);
606 hr
= contextMenu
->InvokeCommand(&cmi
);
612 HRESULT
CMenuToolbarBase::OnCommand(WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
615 if (HasSubMenu(wParam
) == S_OK
)
617 KillTimer(m_hwndToolbar
, TIMERID_HOTTRACK
);
621 return m_menuBand
->_MenuItemHotTrack(MPOS_EXECUTE
);
624 HRESULT
CMenuToolbarBase::ChangeHotItem(DWORD dwSelectType
)
626 int prev
= m_hotItem
;
629 if (dwSelectType
!= 0xFFFFFFFF)
631 int count
= SendMessage(m_hwndToolbar
, TB_BUTTONCOUNT
, 0, 0);
635 TBBUTTONINFO info
= { 0 };
636 info
.cbSize
= sizeof(TBBUTTONINFO
);
638 index
= SendMessage(m_hwndToolbar
, TB_GETBUTTONINFO
, m_hotItem
, reinterpret_cast<LPARAM
>(&info
));
641 if (dwSelectType
== VK_HOME
)
644 dwSelectType
= VK_DOWN
;
646 else if (dwSelectType
== VK_END
)
649 dwSelectType
= VK_UP
;
653 if (dwSelectType
== VK_UP
)
657 else if (dwSelectType
== VK_DOWN
)
664 if (dwSelectType
== VK_UP
)
668 else if (dwSelectType
== VK_DOWN
)
674 TBBUTTON btn
= { 0 };
675 while (index
>= 0 && index
< count
)
677 DWORD res
= SendMessage(m_hwndToolbar
, TB_GETBUTTON
, index
, reinterpret_cast<LPARAM
>(&btn
));
683 m_hotItem
= btn
.idCommand
;
684 if (prev
!= m_hotItem
)
686 SendMessage(m_hwndToolbar
, TB_SETHOTITEM
, index
, 0);
687 return m_menuBand
->_OnHotItemChanged(this, m_hotItem
);
692 if (dwSelectType
== VK_UP
)
696 else if (dwSelectType
== VK_DOWN
)
704 if (prev
!= m_hotItem
)
706 SendMessage(m_hwndToolbar
, TB_SETHOTITEM
, -1, 0);
707 m_menuBand
->_OnHotItemChanged(NULL
, -1);
712 HRESULT
CMenuToolbarBase::AddButton(DWORD commandId
, LPCWSTR caption
, BOOL hasSubMenu
, INT iconId
, DWORD_PTR buttonData
, BOOL last
)
714 TBBUTTON tbb
= { 0 };
716 tbb
.fsState
= TBSTATE_ENABLED
;
717 #ifndef TBSTYLE_EX_VERTICAL
718 if (!last
&& (m_toolbarFlags
& SMINIT_VERTICAL
))
719 tbb
.fsState
|= TBSTATE_WRAP
;
723 if (hasSubMenu
&& (m_toolbarFlags
& SMINIT_VERTICAL
))
724 tbb
.fsStyle
|= BTNS_DROPDOWN
;
726 if (!(m_toolbarFlags
& SMINIT_VERTICAL
))
727 tbb
.fsStyle
|= BTNS_AUTOSIZE
| BTNS_CHECKGROUP
;
729 tbb
.iString
= (INT_PTR
) caption
;
730 tbb
.idCommand
= commandId
;
732 tbb
.iBitmap
= iconId
;
733 tbb
.dwData
= buttonData
;
735 if (!SendMessageW(m_hwndToolbar
, TB_ADDBUTTONS
, 1, reinterpret_cast<LPARAM
>(&tbb
)))
736 return HRESULT_FROM_WIN32(GetLastError());
741 HRESULT
CMenuToolbarBase::AddSeparator(BOOL last
)
743 TBBUTTON tbb
= { 0 };
745 tbb
.fsState
= TBSTATE_ENABLED
;
746 #ifndef TBSTYLE_EX_VERTICAL
747 if (!last
&& (m_toolbarFlags
& SMINIT_VERTICAL
))
748 tbb
.fsState
|= TBSTATE_WRAP
;
750 tbb
.fsStyle
= BTNS_SEP
;
753 if (!SendMessageW(m_hwndToolbar
, TB_ADDBUTTONS
, 1, reinterpret_cast<LPARAM
>(&tbb
)))
754 return HRESULT_FROM_WIN32(GetLastError());
759 HRESULT
CMenuToolbarBase::AddPlaceholder()
761 TBBUTTON tbb
= { 0 };
762 PCWSTR MenuString
= L
"(Empty)";
766 tbb
.iString
= (INT_PTR
) MenuString
;
769 if (!SendMessageW(m_hwndToolbar
, TB_ADDBUTTONS
, 1, reinterpret_cast<LPARAM
>(&tbb
)))
770 return HRESULT_FROM_WIN32(GetLastError());
775 HRESULT
CMenuToolbarBase::GetDataFromId(INT uItem
, INT
* pIndex
, DWORD_PTR
* pData
)
777 TBBUTTONINFO info
= { 0 };
778 info
.cbSize
= sizeof(TBBUTTONINFO
);
780 int index
= SendMessage(m_hwndToolbar
, TB_GETBUTTONINFO
, uItem
, reinterpret_cast<LPARAM
>(&info
));
789 TBBUTTON btn
= { 0 };
790 if (!SendMessage(m_hwndToolbar
, TB_GETBUTTON
, index
, reinterpret_cast<LPARAM
>(&btn
)))
799 HRESULT
CMenuToolbarBase::PopupItem(INT uItem
)
804 GetDataFromId(uItem
, &index
, &dwData
);
806 return InternalPopupItem(uItem
, index
, dwData
);
809 HRESULT
CMenuToolbarBase::HasSubMenu(INT uItem
)
814 GetDataFromId(uItem
, &index
, &dwData
);
816 return InternalHasSubMenu(uItem
, index
, dwData
);
819 CMenuStaticToolbar::CMenuStaticToolbar(CMenuBand
*menuBand
) :
820 CMenuToolbarBase(menuBand
, FALSE
),
825 HRESULT
CMenuStaticToolbar::GetMenu(
832 *pdwFlags
= m_dwMenuFlags
;
837 HRESULT
CMenuStaticToolbar::SetMenu(
843 m_dwMenuFlags
= dwFlags
;
848 HRESULT
CMenuStaticToolbar::FillToolbar()
851 int ic
= GetMenuItemCount(m_hmenu
);
854 for (i
= 0; i
< ic
; i
++)
856 BOOL last
= i
+ 1 == ic
;
860 info
.cbSize
= sizeof(info
);
861 info
.dwTypeData
= NULL
;
862 info
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_ID
;
864 if (!GetMenuItemInfoW(m_hmenu
, i
, TRUE
, &info
))
866 DbgPrint("Error obtaining info for menu item at pos=%d\n", i
);
872 if (info
.fType
& MFT_SEPARATOR
)
876 else if (!(info
.fType
& MFT_BITMAP
))
880 info
.dwTypeData
= (PWSTR
) HeapAlloc(GetProcessHeap(), 0, (info
.cch
+ 1) * sizeof(WCHAR
));
882 info
.fMask
= MIIM_STRING
| MIIM_SUBMENU
| MIIM_ID
;
883 GetMenuItemInfoW(m_hmenu
, i
, TRUE
, &info
);
885 SMINFO
* sminfo
= new SMINFO();
886 sminfo
->dwMask
= SMIM_ICON
| SMIM_FLAGS
;
887 // FIXME: remove before deleting the toolbar or it will leak
889 HRESULT hr
= m_menuBand
->_CallCBWithItemId(info
.wID
, SMC_GETINFO
, 0, reinterpret_cast<LPARAM
>(sminfo
));
890 if (FAILED_UNEXPECTEDLY(hr
))
893 AddButton(info
.wID
, info
.dwTypeData
, info
.hSubMenu
!= NULL
, sminfo
->iIcon
, reinterpret_cast<DWORD_PTR
>(sminfo
), last
);
895 HeapFree(GetProcessHeap(), 0, info
.dwTypeData
);
902 HRESULT
CMenuStaticToolbar::OnContextMenu(NMMOUSE
* rclick
)
904 CComPtr
<IContextMenu
> contextMenu
;
905 HRESULT hr
= m_menuBand
->_CallCBWithItemId(rclick
->dwItemSpec
, SMC_GETOBJECT
, reinterpret_cast<WPARAM
>(&IID_IContextMenu
), reinterpret_cast<LPARAM
>(&contextMenu
));
909 return DoContextMenu(contextMenu
);
912 HRESULT
CMenuStaticToolbar::OnCommand(WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
915 hr
= CMenuToolbarBase::OnCommand(wParam
, lParam
, theResult
);
916 if (FAILED_UNEXPECTEDLY(hr
))
919 // in case the clicked item has a submenu, we do not need to execute the item
923 return m_menuBand
->_CallCBWithItemId(wParam
, SMC_EXEC
, 0, 0);
926 HRESULT
CMenuStaticToolbar::InternalPopupItem(INT uItem
, INT index
, DWORD_PTR dwData
)
928 SMINFO
* nfo
= reinterpret_cast<SMINFO
*>(dwData
);
932 if (nfo
->dwFlags
&SMIF_TRACKPOPUP
)
934 return PopupSubMenu(uItem
, index
, m_hmenu
);
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
))
943 return PopupSubMenu(uItem
, index
, shellMenu
);
947 HRESULT
CMenuStaticToolbar::InternalHasSubMenu(INT uItem
, INT index
, DWORD_PTR dwData
)
949 return ::GetSubMenu(m_hmenu
, index
) ? S_OK
: S_FALSE
;
952 CMenuSFToolbar::CMenuSFToolbar(CMenuBand
* menuBand
) :
953 CMenuToolbarBase(menuBand
, TRUE
),
958 CMenuSFToolbar::~CMenuSFToolbar()
962 HRESULT
CMenuSFToolbar::FillToolbar()
969 m_shellFolder
->EnumObjects(m_hwndToolbar
, SHCONTF_FOLDERS
| SHCONTF_NONFOLDERS
, &eidl
);
971 LPITEMIDLIST item
= static_cast<LPITEMIDLIST
>(CoTaskMemAlloc(sizeof(ITEMIDLIST
)));
973 hr
= eidl
->Next(1, &item
, &fetched
);
974 while (SUCCEEDED(hr
) && fetched
> 0)
979 STRRET sr
= { STRRET_CSTR
, { 0 } };
981 hr
= m_shellFolder
->GetDisplayNameOf(item
, SIGDN_NORMALDISPLAY
, &sr
);
982 if (FAILED_UNEXPECTEDLY(hr
))
985 StrRetToStr(&sr
, NULL
, &MenuString
);
987 index
= SHMapPIDLToSystemImageListIndex(m_shellFolder
, item
, &indexOpen
);
989 LPCITEMIDLIST itemc
= item
;
991 SFGAOF attrs
= SFGAO_FOLDER
;
992 hr
= m_shellFolder
->GetAttributesOf(1, &itemc
, &attrs
);
994 DWORD_PTR dwData
= reinterpret_cast<DWORD_PTR
>(ILClone(item
));
995 // FIXME: remove before deleting the toolbar or it will leak
997 // Fetch next item already, so we know if the current one is the last
998 hr
= eidl
->Next(1, &item
, &fetched
);
1000 AddButton(++i
, MenuString
, attrs
& SFGAO_FOLDER
, index
, dwData
, FAILED(hr
) || fetched
== 0);
1002 CoTaskMemFree(MenuString
);
1004 CoTaskMemFree(item
);
1006 // If no items were added, show the "empty" placeholder
1009 return AddPlaceholder();
1015 HRESULT
CMenuSFToolbar::SetShellFolder(IShellFolder
*psf
, LPCITEMIDLIST pidlFolder
, HKEY hKey
, DWORD dwFlags
)
1017 m_shellFolder
= psf
;
1018 m_idList
= pidlFolder
;
1020 m_dwMenuFlags
= dwFlags
;
1024 HRESULT
CMenuSFToolbar::GetShellFolder(DWORD
*pdwFlags
, LPITEMIDLIST
*ppidl
, REFIID riid
, void **ppv
)
1028 hr
= m_shellFolder
->QueryInterface(riid
, ppv
);
1029 if (FAILED_UNEXPECTEDLY(hr
))
1033 *pdwFlags
= m_dwMenuFlags
;
1037 LPITEMIDLIST pidl
= NULL
;
1041 pidl
= ILClone(m_idList
);
1044 (*(IUnknown
**) ppv
)->Release();
1055 HRESULT
CMenuSFToolbar::OnContextMenu(NMMOUSE
* rclick
)
1058 CComPtr
<IContextMenu
> contextMenu
;
1059 LPCITEMIDLIST pidl
= reinterpret_cast<LPCITEMIDLIST
>(rclick
->dwItemData
);
1061 hr
= m_shellFolder
->GetUIObjectOf(m_hwndToolbar
, 1, &pidl
, IID_IContextMenu
, NULL
, reinterpret_cast<VOID
**>(&contextMenu
));
1065 return DoContextMenu(contextMenu
);
1068 HRESULT
CMenuSFToolbar::OnCommand(WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
1071 hr
= CMenuToolbarBase::OnCommand(wParam
, lParam
, theResult
);
1072 if (FAILED_UNEXPECTEDLY(hr
))
1075 // in case the clicked item has a submenu, we do not need to execute the item
1080 GetDataFromId(wParam
, NULL
, &data
);
1082 return m_menuBand
->_CallCBWithItemPidl(reinterpret_cast<LPITEMIDLIST
>(data
), SMC_SFEXEC
, 0, 0);
1085 HRESULT
CMenuSFToolbar::InternalPopupItem(INT uItem
, INT index
, DWORD_PTR dwData
)
1091 CComPtr
<IShellMenuCallback
> psmc
;
1092 CComPtr
<IShellMenu
> shellMenu
;
1094 LPITEMIDLIST pidl
= reinterpret_cast<LPITEMIDLIST
>(dwData
);
1099 #if USE_SYSTEM_MENUBAND
1100 hr
= CoCreateInstance(CLSID_MenuBand
,
1102 CLSCTX_INPROC_SERVER
,
1103 IID_PPV_ARG(IShellMenu
, &shellMenu
));
1105 hr
= CMenuBand_Constructor(IID_PPV_ARG(IShellMenu
, &shellMenu
));
1107 if (FAILED_UNEXPECTEDLY(hr
))
1110 hr
= CMenuBand_Wrapper(shellMenu
, IID_PPV_ARG(IShellMenu
, &shellMenu
));
1111 if (FAILED_UNEXPECTEDLY(hr
))
1115 m_menuBand
->GetMenuInfo(&psmc
, &uId
, &uIdAncestor
, &flags
);
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
))
1122 CComPtr
<IShellFolder
> childFolder
;
1123 hr
= m_shellFolder
->BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder
, &childFolder
));
1124 if (FAILED_UNEXPECTEDLY(hr
))
1127 hr
= shellMenu
->SetShellFolder(childFolder
, NULL
, NULL
, 0);
1128 if (FAILED_UNEXPECTEDLY(hr
))
1131 return PopupSubMenu(uItem
, index
, shellMenu
);
1134 HRESULT
CMenuSFToolbar::InternalHasSubMenu(INT uItem
, INT index
, DWORD_PTR dwData
)
1137 LPCITEMIDLIST pidl
= reinterpret_cast<LPITEMIDLIST
>(dwData
);
1139 SFGAOF attrs
= SFGAO_FOLDER
;
1140 hr
= m_shellFolder
->GetAttributesOf(1, &pidl
, &attrs
);
1141 if (FAILED_UNEXPECTEDLY(hr
))
1144 return (attrs
& SFGAO_FOLDER
) ? S_OK
: S_FALSE
;