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
);
71 csize
= reinterpret_cast<LPNMPGCALCSIZE
>(hdr
);
74 if (csize
->dwFlag
== PGF_CALCHEIGHT
)
76 csize
->iHeight
= tbs
.cy
;
78 else if (csize
->dwFlag
== PGF_CALCWIDTH
)
80 csize
->iHeight
= tbs
.cx
;
85 wParam
= reinterpret_cast<LPNMTOOLBAR
>(hdr
)->iItem
;
86 return OnCommand(wParam
, 0, theResult
);
88 case TBN_HOTITEMCHANGE
:
89 hot
= reinterpret_cast<LPNMTBHOTITEM
>(hdr
);
90 return OnHotItemChange(hot
);
93 rclick
= reinterpret_cast<LPNMMOUSE
>(hdr
);
95 return OnContextMenu(rclick
);
98 cdraw
= reinterpret_cast<LPNMTBCUSTOMDRAW
>(hdr
);
99 switch (cdraw
->nmcd
.dwDrawStage
)
102 *theResult
= CDRF_NOTIFYITEMDRAW
;
105 case CDDS_ITEMPREPAINT
:
107 clrText
= GetSysColor(COLOR_MENUTEXT
);
108 clrTextHighlight
= GetSysColor(COLOR_HIGHLIGHTTEXT
);
110 bgBrush
= GetSysColorBrush(COLOR_MENU
);
111 hotBrush
= GetSysColorBrush(m_useFlatMenus
? COLOR_MENUHILIGHT
: COLOR_HIGHLIGHT
);
114 hdc
= cdraw
->nmcd
.hdc
;
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
);
119 if (isHot
|| (m_hotItem
< 0 && isPopup
))
121 cdraw
->nmcd
.uItemState
|= CDIS_HOT
;
125 cdraw
->nmcd
.uItemState
&= ~CDIS_HOT
;
128 if (cdraw
->nmcd
.uItemState
&CDIS_HOT
)
130 FillRect(hdc
, &rc
, hotBrush
);
131 SetTextColor(hdc
, clrTextHighlight
);
132 cdraw
->clrText
= clrTextHighlight
;
136 FillRect(hdc
, &rc
, bgBrush
);
137 SetTextColor(hdc
, clrText
);
138 cdraw
->clrText
= clrText
;
141 cdraw
->iListGap
+= 4;
143 *theResult
= CDRF_NOTIFYPOSTPAINT
| TBCDRF_NOBACKGROUND
| TBCDRF_NOEDGES
| TBCDRF_NOOFFSET
| TBCDRF_NOMARK
| 0x00800000; // FIXME: the last bit is Vista+, for debugging only
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
)
152 SelectObject(cdraw
->nmcd
.hdc
, m_marlett
);
154 SetBkMode(cdraw
->nmcd
.hdc
, TRANSPARENT
);
155 RECT rc
= cdraw
->nmcd
.rc
;
157 DrawTextEx(cdraw
->nmcd
.hdc
, text
, 1, &rc
, DT_NOCLIP
| DT_VCENTER
| DT_RIGHT
| DT_SINGLELINE
, NULL
);
170 CMenuToolbarBase::CMenuToolbarBase(CMenuBand
*menuBand
, BOOL usePager
) :
172 m_useFlatMenus(FALSE
),
174 m_menuBand(menuBand
),
177 m_hasIdealSize(FALSE
),
178 m_usePager(usePager
),
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");
188 CMenuToolbarBase::~CMenuToolbarBase()
190 DeleteObject(m_marlett
);
193 HRESULT
CMenuToolbarBase::IsWindowOwner(HWND hwnd
)
195 return (m_hwnd
&& m_hwnd
== hwnd
) ||
196 (m_hwndToolbar
&& m_hwndToolbar
== hwnd
) ? S_OK
: S_FALSE
;
199 void CMenuToolbarBase::InvalidateDraw()
201 InvalidateRect(m_hwnd
, NULL
, FALSE
);
204 HRESULT
CMenuToolbarBase::ShowWindow(BOOL fShow
)
206 ::ShowWindow(m_hwnd
, fShow
? SW_SHOW
: SW_HIDE
);
210 SystemParametersInfo(SPI_GETFLATMENU
, 0, &m_useFlatMenus
, 0);
215 HRESULT
CMenuToolbarBase::UpdateImageLists()
218 if (m_menuBand
->UseBigIcons())
221 SendMessageW(m_hwndToolbar
, TB_SETPADDING
, 0, MAKELPARAM(4, 0));
226 SendMessageW(m_hwndToolbar
, TB_SETPADDING
, 0, MAKELPARAM(4, 4));
230 HRESULT hr
= SHGetImageList(shiml
, IID_PPV_ARG(IImageList
, &piml
));
233 SendMessageW(m_hwndToolbar
, TB_SETIMAGELIST
, 0, reinterpret_cast<LPARAM
>(piml
));
237 SendMessageW(m_hwndToolbar
, TB_SETIMAGELIST
, 0, 0);
242 HRESULT
CMenuToolbarBase::Close()
244 DestroyWindow(m_hwndToolbar
);
245 if (m_hwndToolbar
!= m_hwnd
)
246 DestroyWindow(m_hwnd
);
247 m_hwndToolbar
= NULL
;
252 HRESULT
CMenuToolbarBase::CreateToolbar(HWND hwndParent
, DWORD dwFlags
)
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
;
259 if (dwFlags
& SMINIT_VERTICAL
)
261 tbStyles
|= CCS_VERT
;
263 #ifdef TBSTYLE_EX_VERTICAL
264 // FIXME: Use when it works in ros (?)
265 tbExStyles
|= TBSTYLE_EX_VERTICAL
| WS_EX_TOOLWINDOW
;
271 if (!::GetClientRect(hwndParent
, &rc
) || (rc
.left
== rc
.right
) || (rc
.top
== rc
.bottom
))
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);
284 if (hwndToolbar
== NULL
)
289 LONG pgStyles
= PGS_VERT
| WS_CHILD
| WS_VISIBLE
;
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);
297 ::SetParent(hwndToolbar
, hwndPager
);
298 ::SetParent(hwndPager
, hwndParent
);
300 SendMessage(hwndPager
, PGM_SETCHILD
, 0, reinterpret_cast<LPARAM
>(hwndToolbar
));
301 m_hwndToolbar
= hwndToolbar
;
306 ::SetParent(hwndToolbar
, hwndParent
);
307 m_hwndToolbar
= hwndToolbar
;
308 m_hwnd
= hwndToolbar
;
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);
314 //if (dwFlags & SMINIT_TOPLEVEL)
316 // /* Hide the placeholders for the button images */
317 // SendMessageW(m_hwnd, TB_SETIMAGELIST, 0, 0);
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
));
329 HRESULT
CMenuToolbarBase::GetIdealSize(SIZE
& size
)
331 size
.cx
= size
.cy
= 0;
333 if (m_hwndToolbar
&& !m_hasIdealSize
)
335 SendMessageW(m_hwndToolbar
, TB_AUTOSIZE
, 0, 0);
336 SendMessageW(m_hwndToolbar
, TB_GETMAXSIZE
, 0, reinterpret_cast<LPARAM
>(&m_idealSize
));
337 m_hasIdealSize
= TRUE
;
345 HRESULT
CMenuToolbarBase::SetPosSize(int x
, int y
, int cx
, int cy
)
347 if (m_hwnd
!= m_hwndToolbar
)
349 SetWindowPos(m_hwndToolbar
, NULL
, x
, y
, cx
, m_idealSize
.cy
, 0);
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
)));
357 HRESULT
CMenuToolbarBase::GetWindow(HWND
*phwnd
)
367 LRESULT CALLBACK
CMenuToolbarBase::s_SubclassProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
369 CMenuToolbarBase
* pthis
= reinterpret_cast<CMenuToolbarBase
*>(GetWindowLongPtr(hWnd
, GWLP_USERDATA
));
370 return pthis
->SubclassProc(hWnd
, uMsg
, wParam
, lParam
);
373 LRESULT
CMenuToolbarBase::SubclassProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
378 if (wParam
== TIMERID_HOTTRACK
)
380 KillTimer(hWnd
, TIMERID_HOTTRACK
);
382 m_menuBand
->_OnPopupSubMenu(NULL
, NULL
, NULL
, NULL
, -1);
384 if (HasSubMenu(m_hotItem
) == S_OK
)
386 PopupItem(m_hotItem
);
391 return m_SubclassOld(hWnd
, uMsg
, wParam
, lParam
);
394 HRESULT
CMenuToolbarBase::OnHotItemChange(const NMTBHOTITEM
* hot
)
396 if (hot
->dwFlags
& HICF_LEAVING
)
398 KillTimer(m_hwndToolbar
, TIMERID_HOTTRACK
);
400 m_menuBand
->_OnHotItemChanged(NULL
, -1);
401 m_menuBand
->_MenuItemHotTrack(MPOS_CHILDTRACKING
);
403 else if (m_hotItem
!= hot
->idNew
)
406 SystemParametersInfo(SPI_GETMENUSHOWDELAY
, 0, &elapsed
, 0);
407 SetTimer(m_hwndToolbar
, TIMERID_HOTTRACK
, elapsed
, NULL
);
409 m_hotItem
= hot
->idNew
;
410 m_menuBand
->_OnHotItemChanged(this, m_hotItem
);
411 m_menuBand
->_MenuItemHotTrack(MPOS_CHILDTRACKING
);
416 HRESULT
CMenuToolbarBase::OnHotItemChanged(CMenuToolbarBase
* toolbar
, INT item
)
424 HRESULT
CMenuToolbarBase::OnPopupItemChanged(CMenuToolbarBase
* toolbar
, INT item
)
426 m_popupBar
= toolbar
;
432 HRESULT
CMenuToolbarBase::PopupSubMenu(UINT uItem
, UINT index
, IShellMenu
* childShellMenu
)
434 IBandSite
* pBandSite
;
440 if (!SendMessage(m_hwndToolbar
, TB_GETITEMRECT
, index
, reinterpret_cast<LPARAM
>(&rc
)))
443 POINT a
= { rc
.left
, rc
.top
};
444 POINT b
= { rc
.right
, rc
.bottom
};
446 ClientToScreen(m_hwndToolbar
, &a
);
447 ClientToScreen(m_hwndToolbar
, &b
);
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?
452 #if USE_SYSTEM_MENUSITE
453 hr
= CoCreateInstance(CLSID_MenuBandSite
,
455 CLSCTX_INPROC_SERVER
,
456 IID_PPV_ARG(IBandSite
, &pBandSite
));
458 hr
= CMenuSite_Constructor(IID_PPV_ARG(IBandSite
, &pBandSite
));
460 if (FAILED_UNEXPECTEDLY(hr
))
463 hr
= CMenuSite_Wrapper(pBandSite
, IID_PPV_ARG(IBandSite
, &pBandSite
));
464 if (FAILED_UNEXPECTEDLY(hr
))
468 #if USE_SYSTEM_MENUDESKBAR
469 hr
= CoCreateInstance(CLSID_MenuDeskBar
,
471 CLSCTX_INPROC_SERVER
,
472 IID_PPV_ARG(IDeskBar
, &pDeskBar
));
474 hr
= CMenuDeskBar_Constructor(IID_PPV_ARG(IDeskBar
, &pDeskBar
));
476 if (FAILED_UNEXPECTEDLY(hr
))
479 hr
= CMenuDeskBar_Wrapper(pDeskBar
, IID_PPV_ARG(IDeskBar
, &pDeskBar
));
480 if (FAILED_UNEXPECTEDLY(hr
))
484 hr
= pDeskBar
->SetClient(pBandSite
);
485 if (FAILED_UNEXPECTEDLY(hr
))
488 hr
= pBandSite
->AddBand(childShellMenu
);
489 if (FAILED_UNEXPECTEDLY(hr
))
492 CComPtr
<IMenuPopup
> popup
;
493 hr
= pDeskBar
->QueryInterface(IID_PPV_ARG(IMenuPopup
, &popup
));
494 if (FAILED_UNEXPECTEDLY(hr
))
497 m_menuBand
->_OnPopupSubMenu(popup
, &pt
, &rcl
, this, m_popupItem
);
502 HRESULT
CMenuToolbarBase::PopupSubMenu(UINT index
, HMENU menu
)
506 if (!SendMessage(m_hwndToolbar
, TB_GETITEMRECT
, index
, reinterpret_cast<LPARAM
>(&rc
)))
509 POINT b
= { rc
.right
, rc
.bottom
};
511 ClientToScreen(m_hwndToolbar
, &b
);
513 HMENU popup
= GetSubMenu(menu
, index
);
515 m_menuBand
->_TrackSubMenuUsingTrackPopupMenu(popup
, b
.x
, b
.y
);
520 HRESULT
CMenuToolbarBase::DoContextMenu(IContextMenu
* contextMenu
)
523 HMENU hPopup
= CreatePopupMenu();
528 hr
= contextMenu
->QueryContextMenu(hPopup
, 0, 0, UINT_MAX
, CMF_NORMAL
);
529 if (FAILED_UNEXPECTEDLY(hr
))
535 DWORD dwPos
= GetMessagePos();
536 UINT uCommand
= ::TrackPopupMenu(hPopup
, TPM_RETURNCMD
, GET_X_LPARAM(dwPos
), GET_Y_LPARAM(dwPos
), 0, m_hwnd
, NULL
);
540 CMINVOKECOMMANDINFO cmi
= { 0 };
541 cmi
.cbSize
= sizeof(cmi
);
542 cmi
.lpVerb
= MAKEINTRESOURCEA(uCommand
);
544 hr
= contextMenu
->InvokeCommand(&cmi
);
550 HRESULT
CMenuToolbarBase::OnCommand(WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
553 if (HasSubMenu(wParam
) == S_OK
)
555 KillTimer(m_hwndToolbar
, TIMERID_HOTTRACK
);
559 return m_menuBand
->_MenuItemHotTrack(MPOS_EXECUTE
);
562 HRESULT
CMenuToolbarBase::ChangeHotItem(DWORD dwSelectType
)
564 int prev
= m_hotItem
;
567 if (dwSelectType
!= 0xFFFFFFFF)
569 int count
= SendMessage(m_hwndToolbar
, TB_BUTTONCOUNT
, 0, 0);
573 TBBUTTONINFO info
= { 0 };
574 info
.cbSize
= sizeof(TBBUTTONINFO
);
576 index
= SendMessage(m_hwndToolbar
, TB_GETBUTTONINFO
, m_hotItem
, reinterpret_cast<LPARAM
>(&info
));
579 if (dwSelectType
== VK_HOME
)
582 dwSelectType
= VK_DOWN
;
584 else if (dwSelectType
== VK_END
)
587 dwSelectType
= VK_UP
;
591 if (dwSelectType
== VK_UP
)
595 else if (dwSelectType
== VK_DOWN
)
602 if (dwSelectType
== VK_UP
)
606 else if (dwSelectType
== VK_DOWN
)
612 TBBUTTON btn
= { 0 };
613 while (index
>= 0 && index
< count
)
615 DWORD res
= SendMessage(m_hwndToolbar
, TB_GETBUTTON
, index
, reinterpret_cast<LPARAM
>(&btn
));
621 m_hotItem
= btn
.idCommand
;
622 if (prev
!= m_hotItem
)
624 SendMessage(m_hwndToolbar
, TB_SETHOTITEM
, index
, 0);
625 return m_menuBand
->_OnHotItemChanged(this, m_hotItem
);
630 if (dwSelectType
== VK_UP
)
634 else if (dwSelectType
== VK_DOWN
)
642 if (prev
!= m_hotItem
)
644 SendMessage(m_hwndToolbar
, TB_SETHOTITEM
, -1, 0);
645 m_menuBand
->_OnHotItemChanged(NULL
, -1);
650 HRESULT
CMenuToolbarBase::AddButton(DWORD commandId
, LPCWSTR caption
, BOOL hasSubMenu
, INT iconId
, DWORD_PTR buttonData
, BOOL last
)
652 TBBUTTON tbb
= { 0 };
654 tbb
.fsState
= TBSTATE_ENABLED
;
655 #ifndef TBSTYLE_EX_VERTICAL
657 tbb
.fsState
|= TBSTATE_WRAP
;
662 tbb
.fsStyle
|= BTNS_DROPDOWN
;
664 tbb
.iString
= (INT_PTR
) caption
;
665 tbb
.idCommand
= commandId
;
667 tbb
.iBitmap
= iconId
;
668 tbb
.dwData
= buttonData
;
670 DbgPrint("Trying to add a new button with id %d and caption '%S'...\n", commandId
, caption
);
672 if (!SendMessageW(m_hwndToolbar
, TB_ADDBUTTONS
, 1, reinterpret_cast<LPARAM
>(&tbb
)))
673 return HRESULT_FROM_WIN32(GetLastError());
678 HRESULT
CMenuToolbarBase::AddSeparator(BOOL last
)
680 TBBUTTON tbb
= { 0 };
682 tbb
.fsState
= TBSTATE_ENABLED
;
683 #ifndef TBSTYLE_EX_VERTICAL
685 tbb
.fsState
|= TBSTATE_WRAP
;
687 tbb
.fsStyle
= BTNS_SEP
;
690 DbgPrint("Trying to add a new separator...\n");
692 if (!SendMessageW(m_hwndToolbar
, TB_ADDBUTTONS
, 1, reinterpret_cast<LPARAM
>(&tbb
)))
693 return HRESULT_FROM_WIN32(GetLastError());
698 HRESULT
CMenuToolbarBase::AddPlaceholder()
700 TBBUTTON tbb
= { 0 };
701 PCWSTR MenuString
= L
"(Empty)";
705 tbb
.iString
= (INT_PTR
) MenuString
;
708 DbgPrint("Trying to add a new placeholder...\n");
710 if (!SendMessageW(m_hwndToolbar
, TB_ADDBUTTONS
, 1, reinterpret_cast<LPARAM
>(&tbb
)))
711 return HRESULT_FROM_WIN32(GetLastError());
716 HRESULT
CMenuToolbarBase::GetDataFromId(INT uItem
, INT
* pIndex
, DWORD_PTR
* pData
)
718 TBBUTTONINFO info
= { 0 };
719 info
.cbSize
= sizeof(TBBUTTONINFO
);
721 int index
= SendMessage(m_hwndToolbar
, TB_GETBUTTONINFO
, uItem
, reinterpret_cast<LPARAM
>(&info
));
730 TBBUTTON btn
= { 0 };
731 if (!SendMessage(m_hwndToolbar
, TB_GETBUTTON
, index
, reinterpret_cast<LPARAM
>(&btn
)))
740 HRESULT
CMenuToolbarBase::PopupItem(INT uItem
)
745 GetDataFromId(uItem
, &index
, &dwData
);
747 return InternalPopupItem(uItem
, index
, dwData
);
750 HRESULT
CMenuToolbarBase::HasSubMenu(INT uItem
)
755 GetDataFromId(uItem
, &index
, &dwData
);
757 return InternalHasSubMenu(uItem
, index
, dwData
);
760 CMenuStaticToolbar::CMenuStaticToolbar(CMenuBand
*menuBand
) :
761 CMenuToolbarBase(menuBand
, FALSE
),
766 HRESULT
CMenuStaticToolbar::GetMenu(
773 *pdwFlags
= m_dwMenuFlags
;
778 HRESULT
CMenuStaticToolbar::SetMenu(
784 m_dwMenuFlags
= dwFlags
;
789 HRESULT
CMenuStaticToolbar::FillToolbar()
792 int ic
= GetMenuItemCount(m_hmenu
);
795 for (i
= 0; i
< ic
; i
++)
797 BOOL last
= i
+ 1 == ic
;
801 info
.cbSize
= sizeof(info
);
802 info
.dwTypeData
= NULL
;
803 info
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_ID
;
805 if (!GetMenuItemInfoW(m_hmenu
, i
, TRUE
, &info
))
807 DbgPrint("Error obtaining info for menu item at pos=%d\n", i
);
813 DbgPrint("Found item with fType=%x, cmdId=%d\n", info
.fType
, info
.wID
);
815 if (info
.fType
& MFT_SEPARATOR
)
819 else if (!(info
.fType
& MFT_BITMAP
))
823 info
.dwTypeData
= (PWSTR
) HeapAlloc(GetProcessHeap(), 0, (info
.cch
+ 1) * sizeof(WCHAR
));
825 info
.fMask
= MIIM_STRING
| MIIM_SUBMENU
| MIIM_ID
;
826 GetMenuItemInfoW(m_hmenu
, i
, TRUE
, &info
);
828 SMINFO
* sminfo
= new SMINFO();
829 sminfo
->dwMask
= SMIM_ICON
| SMIM_FLAGS
;
830 // FIXME: remove before deleting the toolbar or it will leak
832 HRESULT hr
= m_menuBand
->_CallCBWithItemId(info
.wID
, SMC_GETINFO
, 0, reinterpret_cast<LPARAM
>(sminfo
));
833 if (FAILED_UNEXPECTEDLY(hr
))
836 AddButton(info
.wID
, info
.dwTypeData
, info
.hSubMenu
!= NULL
, sminfo
->iIcon
, reinterpret_cast<DWORD_PTR
>(sminfo
), last
);
838 HeapFree(GetProcessHeap(), 0, info
.dwTypeData
);
842 DbgPrint("Created toolbar with %d buttons.\n", count
);
847 HRESULT
CMenuStaticToolbar::OnContextMenu(NMMOUSE
* rclick
)
849 CComPtr
<IContextMenu
> contextMenu
;
850 HRESULT hr
= m_menuBand
->_CallCBWithItemId(rclick
->dwItemSpec
, SMC_GETOBJECT
, reinterpret_cast<WPARAM
>(&IID_IContextMenu
), reinterpret_cast<LPARAM
>(&contextMenu
));
854 return DoContextMenu(contextMenu
);
857 HRESULT
CMenuStaticToolbar::OnCommand(WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
860 hr
= CMenuToolbarBase::OnCommand(wParam
, lParam
, theResult
);
861 if (FAILED_UNEXPECTEDLY(hr
))
864 // in case the clicked item has a submenu, we do not need to execute the item
868 return m_menuBand
->_CallCBWithItemId(wParam
, SMC_EXEC
, 0, 0);
871 HRESULT
CMenuStaticToolbar::InternalPopupItem(INT uItem
, INT index
, DWORD_PTR dwData
)
873 SMINFO
* nfo
= reinterpret_cast<SMINFO
*>(dwData
);
877 if (nfo
->dwFlags
&SMIF_TRACKPOPUP
)
879 return PopupSubMenu(index
, m_hmenu
);
883 CComPtr
<IShellMenu
> shellMenu
;
884 HRESULT hr
= m_menuBand
->_CallCBWithItemId(uItem
, SMC_GETOBJECT
, reinterpret_cast<WPARAM
>(&IID_IShellMenu
), reinterpret_cast<LPARAM
>(&shellMenu
));
885 if (FAILED_UNEXPECTEDLY(hr
))
888 return PopupSubMenu(uItem
, index
, shellMenu
);
892 HRESULT
CMenuStaticToolbar::InternalHasSubMenu(INT uItem
, INT index
, DWORD_PTR dwData
)
894 return ::GetSubMenu(m_hmenu
, index
) ? S_OK
: S_FALSE
;
897 CMenuSFToolbar::CMenuSFToolbar(CMenuBand
* menuBand
) :
898 CMenuToolbarBase(menuBand
, TRUE
),
903 CMenuSFToolbar::~CMenuSFToolbar()
907 HRESULT
CMenuSFToolbar::FillToolbar()
914 m_shellFolder
->EnumObjects(m_hwndToolbar
, SHCONTF_FOLDERS
| SHCONTF_NONFOLDERS
, &eidl
);
916 LPITEMIDLIST item
= static_cast<LPITEMIDLIST
>(CoTaskMemAlloc(sizeof(ITEMIDLIST
)));
918 hr
= eidl
->Next(1, &item
, &fetched
);
919 while (SUCCEEDED(hr
) && fetched
> 0)
924 STRRET sr
= { STRRET_CSTR
, { 0 } };
926 hr
= m_shellFolder
->GetDisplayNameOf(item
, SIGDN_NORMALDISPLAY
, &sr
);
927 if (FAILED_UNEXPECTEDLY(hr
))
930 StrRetToStr(&sr
, NULL
, &MenuString
);
932 index
= SHMapPIDLToSystemImageListIndex(m_shellFolder
, item
, &indexOpen
);
934 LPCITEMIDLIST itemc
= item
;
936 SFGAOF attrs
= SFGAO_FOLDER
;
937 hr
= m_shellFolder
->GetAttributesOf(1, &itemc
, &attrs
);
939 DWORD_PTR dwData
= reinterpret_cast<DWORD_PTR
>(ILClone(item
));
940 // FIXME: remove before deleting the toolbar or it will leak
942 // Fetch next item already, so we know if the current one is the last
943 hr
= eidl
->Next(1, &item
, &fetched
);
945 AddButton(++i
, MenuString
, attrs
& SFGAO_FOLDER
, index
, dwData
, FAILED(hr
) || fetched
== 0);
947 CoTaskMemFree(MenuString
);
951 // If no items were added, show the "empty" placeholder
954 DbgPrint("The toolbar is empty, adding placeholder.\n");
956 return AddPlaceholder();
959 DbgPrint("Created toolbar with %d buttons.\n", i
);
964 HRESULT
CMenuSFToolbar::SetShellFolder(IShellFolder
*psf
, LPCITEMIDLIST pidlFolder
, HKEY hKey
, DWORD dwFlags
)
967 m_idList
= pidlFolder
;
969 m_dwMenuFlags
= dwFlags
;
973 HRESULT
CMenuSFToolbar::GetShellFolder(DWORD
*pdwFlags
, LPITEMIDLIST
*ppidl
, REFIID riid
, void **ppv
)
977 hr
= m_shellFolder
->QueryInterface(riid
, ppv
);
978 if (FAILED_UNEXPECTEDLY(hr
))
982 *pdwFlags
= m_dwMenuFlags
;
986 LPITEMIDLIST pidl
= NULL
;
990 pidl
= ILClone(m_idList
);
993 (*(IUnknown
**) ppv
)->Release();
1004 HRESULT
CMenuSFToolbar::OnContextMenu(NMMOUSE
* rclick
)
1007 CComPtr
<IContextMenu
> contextMenu
;
1008 LPCITEMIDLIST pidl
= reinterpret_cast<LPCITEMIDLIST
>(rclick
->dwItemData
);
1010 hr
= m_shellFolder
->GetUIObjectOf(m_hwndToolbar
, 1, &pidl
, IID_IContextMenu
, NULL
, reinterpret_cast<VOID
**>(&contextMenu
));
1014 return DoContextMenu(contextMenu
);
1017 HRESULT
CMenuSFToolbar::OnCommand(WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
1020 hr
= CMenuToolbarBase::OnCommand(wParam
, lParam
, theResult
);
1021 if (FAILED_UNEXPECTEDLY(hr
))
1024 // in case the clicked item has a submenu, we do not need to execute the item
1029 GetDataFromId(wParam
, NULL
, &data
);
1031 return m_menuBand
->_CallCBWithItemPidl(reinterpret_cast<LPITEMIDLIST
>(data
), SMC_SFEXEC
, 0, 0);
1034 HRESULT
CMenuSFToolbar::InternalPopupItem(INT uItem
, INT index
, DWORD_PTR dwData
)
1040 CComPtr
<IShellMenuCallback
> psmc
;
1041 CComPtr
<IShellMenu
> shellMenu
;
1043 LPITEMIDLIST pidl
= reinterpret_cast<LPITEMIDLIST
>(dwData
);
1048 #if USE_SYSTEM_MENUBAND
1049 hr
= CoCreateInstance(CLSID_MenuBand
,
1051 CLSCTX_INPROC_SERVER
,
1052 IID_PPV_ARG(IShellMenu
, &shellMenu
));
1054 hr
= CMenuBand_Constructor(IID_PPV_ARG(IShellMenu
, &shellMenu
));
1056 if (FAILED_UNEXPECTEDLY(hr
))
1059 hr
= CMenuBand_Wrapper(shellMenu
, IID_PPV_ARG(IShellMenu
, &shellMenu
));
1060 if (FAILED_UNEXPECTEDLY(hr
))
1064 m_menuBand
->GetMenuInfo(&psmc
, &uId
, &uIdAncestor
, &flags
);
1066 // FIXME: not sure what to use as uId/uIdAncestor here
1067 hr
= shellMenu
->Initialize(psmc
, 0, uId
, SMINIT_VERTICAL
);
1068 if (FAILED_UNEXPECTEDLY(hr
))
1071 CComPtr
<IShellFolder
> childFolder
;
1072 hr
= m_shellFolder
->BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder
, &childFolder
));
1073 if (FAILED_UNEXPECTEDLY(hr
))
1076 hr
= shellMenu
->SetShellFolder(childFolder
, NULL
, NULL
, 0);
1077 if (FAILED_UNEXPECTEDLY(hr
))
1080 return PopupSubMenu(uItem
, index
, shellMenu
);
1083 HRESULT
CMenuSFToolbar::InternalHasSubMenu(INT uItem
, INT index
, DWORD_PTR dwData
)
1086 LPCITEMIDLIST pidl
= reinterpret_cast<LPITEMIDLIST
>(dwData
);
1088 SFGAOF attrs
= SFGAO_FOLDER
;
1089 hr
= m_shellFolder
->GetAttributesOf(1, &pidl
, &attrs
);
1090 if (FAILED_UNEXPECTEDLY(hr
))
1093 return (attrs
& SFGAO_FOLDER
) ? S_OK
: S_FALSE
;