4 * Copyright 2007 Hervé Poussineua
5 * Copyright 2009 Andrew Hill
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "shellbars.h"
25 /* The menu consists of 3 parts. The first is loaded from the resources,
26 the second is populated with the classes of the CATID_DeskBand comcat
27 and the third part consists of the entries for each CISFBand in the band side.
28 The first 5 ids are reserved for the resource menu, the following ids will be
29 for the CATID_DeskBand classes and the rest for the CISFBands.
30 The ids for the CISFBand menu items are not continuous, in this range
31 each menu id is calculated by adding the band id to the last id for the CATID_DeskBand range */
32 #define FIRST_COMCAT_MENU_ID 0x5
34 CBandSiteMenu::CBandSiteMenu():
42 CBandSiteMenu::~CBandSiteMenu()
48 DSA_Destroy(m_comcatDsa
);
51 ILFree(m_DesktopPidl
);
54 ILFree(m_QLaunchPidl
);
59 HRESULT WINAPI
CBandSiteMenu::FinalConstruct()
61 HRESULT hr
= SHGetFolderLocation(0, CSIDL_DESKTOP
, NULL
, 0, &m_DesktopPidl
);
62 if (FAILED_UNEXPECTEDLY(hr
))
65 WCHAR buffer
[MAX_PATH
];
66 hr
= SHGetFolderPathAndSubDirW(0, CSIDL_APPDATA
| CSIDL_FLAG_CREATE
, NULL
, 0, L
"Microsoft\\Internet Explorer\\Quick Launch", buffer
);
67 if (FAILED_UNEXPECTEDLY(hr
))
70 m_QLaunchPidl
= ILCreateFromPathW(buffer
);
71 if (m_QLaunchPidl
== NULL
)
77 HRESULT
CBandSiteMenu::_CreateMenuPart()
79 WCHAR wszBandName
[MAX_PATH
];
80 WCHAR wszBandGUID
[MAX_PATH
];
81 WCHAR wRegKey
[MAX_PATH
];
84 CATID category
= CATID_DeskBand
;
87 CComPtr
<IEnumGUID
> pEnumGUID
;
94 DSA_Destroy(m_comcatDsa
);
96 /* Load the template we will fill in */
97 m_hmenu
= LoadMenuW(GetModuleHandleW(L
"browseui.dll"), MAKEINTRESOURCEW(IDM_TASKBAR_TOOLBARS
));
99 return HRESULT_FROM_WIN32(GetLastError());
101 m_comcatDsa
= DSA_Create(sizeof(GUID
), 5);
103 return E_OUTOFMEMORY
;
105 /* Get the handle of the submenu where the available items will be shown */
106 hmenuToolbars
= GetSubMenu(m_hmenu
, 0);
108 /* Create the category enumerator */
109 hr
= SHEnumClassesOfCategories(1, &category
, 0, NULL
, &pEnumGUID
);
110 if (FAILED_UNEXPECTEDLY(hr
))
113 /* Enumerate the classes in the CATID_DeskBand category */
118 pEnumGUID
->Next(1, &iter
, &dwRead
);
122 if (!StringFromGUID2(iter
, wszBandGUID
, MAX_PATH
))
125 /* Get the band name */
126 StringCchPrintfW(wRegKey
, MAX_PATH
, L
"CLSID\\%s", wszBandGUID
);
127 dwDataSize
= MAX_PATH
;
128 SHGetValue(HKEY_CLASSES_ROOT
, wRegKey
, NULL
, NULL
, wszBandName
, &dwDataSize
);
131 InsertMenu(hmenuToolbars
, cBands
, MF_BYPOSITION
, DSA_GetItemCount(m_comcatDsa
) + FIRST_COMCAT_MENU_ID
, wszBandName
);
132 DSA_AppendItem(m_comcatDsa
, &iter
);
140 HRESULT
CBandSiteMenu::_CreateNewISFBand(HWND hwnd
, REFIID riid
, void** ppv
)
142 WCHAR path
[MAX_PATH
];
144 BROWSEINFOW bi
= { hwnd
, NULL
, path
};
146 if (LoadStringW(GetModuleHandleW(L
"browseui.dll"), IDS_BROWSEFORNEWTOOLAR
, message
, _countof(message
)))
147 bi
.lpszTitle
= message
;
149 bi
.lpszTitle
= L
"Choose a folder";
151 LPITEMIDLIST pidlSelected
= SHBrowseForFolderW(&bi
);
152 if (pidlSelected
== NULL
)
155 CComPtr
<IShellFolderBand
> pISFB
;
156 HRESULT hr
= CISFBand_CreateInstance(IID_IShellFolderBand
, (PVOID
*)&pISFB
);
157 if (FAILED_UNEXPECTEDLY(hr
))
160 hr
= pISFB
->InitializeSFB(NULL
, pidlSelected
);
161 if (FAILED_UNEXPECTEDLY(hr
))
164 hr
= pISFB
->QueryInterface(riid
, ppv
);
167 ILFree(pidlSelected
);
171 HRESULT
CBandSiteMenu::_CreateBuiltInISFBand(UINT uID
, REFIID riid
, void** ppv
)
176 pidl
= (uID
== IDM_TASKBAR_TOOLBARS_DESKTOP
) ? m_DesktopPidl
: m_QLaunchPidl
;
178 CComPtr
<IShellFolderBand
> pISFB
;
179 hr
= CISFBand_CreateInstance(IID_IShellFolderBand
, (PVOID
*)&pISFB
);
180 if (FAILED_UNEXPECTEDLY(hr
))
183 hr
= pISFB
->InitializeSFB(NULL
, pidl
);
184 if (FAILED_UNEXPECTEDLY(hr
))
187 /* HACK! We shouldn't pass ISFB_STATE_QLINKSMODE and CISFBand shouldn't handle it! */
188 if (uID
== IDM_TASKBAR_TOOLBARS_QUICKLAUNCH
)
190 BANDINFOSFB bisfb
= {ISFB_MASK_STATE
, ISFB_STATE_QLINKSMODE
, ISFB_STATE_QLINKSMODE
};
191 pISFB
->SetBandInfoSFB(&bisfb
);
194 return pISFB
->QueryInterface(riid
, ppv
);
197 HRESULT
CBandSiteMenu::_AddISFBandToMenu(HMENU hmenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, IUnknown
* pBand
, DWORD dwBandID
, UINT
*newMenuId
)
199 CComPtr
<IShellFolderBand
> psfb
;
200 HRESULT hr
= pBand
->QueryInterface(IID_PPV_ARG(IShellFolderBand
, &psfb
));
201 if (FAILED_UNEXPECTEDLY(hr
))
204 BANDINFOSFB bi
= {ISFB_MASK_IDLIST
};
205 hr
= psfb
->GetBandInfoSFB(&bi
);
206 if (FAILED_UNEXPECTEDLY(hr
))
210 return E_OUTOFMEMORY
;
212 WCHAR buffer
[MAX_PATH
];
213 hr
= ILGetDisplayNameEx(NULL
, bi
.pidl
, buffer
, ILGDN_INFOLDER
) ? S_OK
: E_FAIL
;
214 if (FAILED_UNEXPECTEDLY(hr
))
217 UINT id
= idCmdFirst
+ DSA_GetItemCount(m_comcatDsa
) + FIRST_COMCAT_MENU_ID
+ dwBandID
;
222 InsertMenu(hmenu
, indexMenu
, MF_BYPOSITION
, id
, buffer
);
226 UINT
CBandSiteMenu::_GetMenuIdFromISFBand(IUnknown
*pBand
)
230 CComPtr
<IShellFolderBand
> psfb
;
231 HRESULT hr
= pBand
->QueryInterface(IID_PPV_ARG(IShellFolderBand
, &psfb
));
232 if (FAILED_UNEXPECTEDLY(hr
))
235 BANDINFOSFB bi
= {ISFB_MASK_IDLIST
};
236 hr
= psfb
->GetBandInfoSFB(&bi
);
237 if (FAILED_UNEXPECTEDLY(hr
))
240 CComPtr
<IShellFolder
> psfDesktop
;
241 LPITEMIDLIST pidl
= bi
.pidl
;
245 if (pidl
->mkid
.cb
== 0)
247 ret
= IDM_TASKBAR_TOOLBARS_DESKTOP
;
251 hr
= SHGetDesktopFolder(&psfDesktop
);
252 if (FAILED_UNEXPECTEDLY(hr
))
255 hr
= psfDesktop
->CompareIDs(0, pidl
, m_QLaunchPidl
);
256 if (FAILED_UNEXPECTEDLY(hr
))
259 if (HRESULT_CODE(hr
) == 0)
260 ret
= IDM_TASKBAR_TOOLBARS_QUICKLAUNCH
;
269 UINT
CBandSiteMenu::_GetMenuIdFromBand(CLSID
*BandCLSID
)
271 /* Try to find the clsid of the band in the dsa */
272 UINT count
= DSA_GetItemCount(m_comcatDsa
);
273 for (UINT i
= 0; i
< count
; i
++)
275 GUID
* pdsaGUID
= (GUID
*)DSA_GetItemPtr(m_comcatDsa
, i
);
276 if (IsEqualGUID(*pdsaGUID
, *BandCLSID
))
278 /* The index in the dsa is also the index in the menu */
279 return i
+ FIRST_COMCAT_MENU_ID
;
286 UINT
CBandSiteMenu::_GetBandIdFromClsid(CLSID
* pclsid
)
288 CComPtr
<IPersist
> pBand
;
292 for (UINT uBand
= 0; SUCCEEDED(m_BandSite
->EnumBands(uBand
, &dwBandID
)); uBand
++)
294 if (FAILED(m_BandSite
->GetBandObject(dwBandID
, IID_PPV_ARG(IPersist
, &pBand
))))
297 if (FAILED(pBand
->GetClassID(&BandCLSID
)))
300 if (IsEqualGUID(*pclsid
, BandCLSID
))
307 UINT
CBandSiteMenu::_GetBandIdForBuiltinISFBand(UINT uID
)
309 CComPtr
<IPersist
> pBand
;
313 for (UINT uBand
= 0; SUCCEEDED(m_BandSite
->EnumBands(uBand
, &dwBandID
)); uBand
++)
315 if (FAILED(m_BandSite
->GetBandObject(dwBandID
, IID_PPV_ARG(IPersist
, &pBand
))))
318 if (FAILED(pBand
->GetClassID(&BandCLSID
)))
321 if (!IsEqualGUID(BandCLSID
, CLSID_ISFBand
))
324 UINT menuID
= _GetMenuIdFromISFBand(pBand
);
332 HRESULT STDMETHODCALLTYPE
CBandSiteMenu::SetOwner(IUnknown
*pOwner
)
334 TRACE("CBandSiteMenu::SetOwner(%p, %p)\n", this, pOwner
);
336 /* Cache the menu that will be merged every time QueryContextMenu is called */
339 return pOwner
->QueryInterface(IID_PPV_ARG(IBandSite
, &m_BandSite
));
342 HRESULT STDMETHODCALLTYPE
CBandSiteMenu::QueryContextMenu(
343 HMENU hmenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, UINT uFlags
)
345 CComPtr
<IPersist
> pBand
;
350 TRACE("CBandSiteMenu::QueryContextMenu(%p, %p, %u, %u, %u, 0x%x)\n", this, hmenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
352 /* First Merge the menu with the available bands */
353 idMax
= Shell_MergeMenus(hmenu
, m_hmenu
, indexMenu
, idCmdFirst
, idCmdLast
, MM_DONTREMOVESEPS
| MM_SUBMENUSHAVEIDS
);
355 HMENU hmenuToolbars
= GetSubMenu(hmenu
, indexMenu
);
357 /* Enumerate all present bands and mark them as checked in the menu */
358 for (UINT uBand
= 0; SUCCEEDED(m_BandSite
->EnumBands(uBand
, &dwBandID
)); uBand
++)
360 if (FAILED(m_BandSite
->GetBandObject(dwBandID
, IID_PPV_ARG(IPersist
, &pBand
))))
363 if (FAILED(pBand
->GetClassID(&BandCLSID
)))
367 if (IsEqualGUID(BandCLSID
, CLSID_ISFBand
))
369 menuID
= _GetMenuIdFromISFBand(pBand
);
370 if (menuID
== UINT_MAX
)
373 hr
= _AddISFBandToMenu(hmenuToolbars
, 0, idCmdFirst
, idCmdLast
, pBand
, dwBandID
, &menuID
);
374 if (SUCCEEDED(hr
) && menuID
> idMax
)
376 menuID
-= idCmdFirst
;
381 menuID
= _GetMenuIdFromBand(&BandCLSID
);
384 if (menuID
!= UINT_MAX
)
385 CheckMenuItem(hmenuToolbars
, menuID
+ idCmdFirst
, MF_CHECKED
);
388 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, USHORT(idMax
- idCmdFirst
+1));
391 HRESULT STDMETHODCALLTYPE
CBandSiteMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
396 /* FIXME: do we need to handle this and how? */
397 if (HIWORD(lpici
->lpVerb
) != NULL
)
400 UINT uID
= LOWORD(lpici
->lpVerb
);
401 if (uID
== IDM_TASKBAR_TOOLBARS_NEW
)
403 CComPtr
<IDeskBand
> pDeskBand
;
404 hRet
= _CreateNewISFBand(lpici
->hwnd
, IID_PPV_ARG(IDeskBand
, &pDeskBand
));
405 if (FAILED_UNEXPECTEDLY(hRet
))
408 hRet
= m_BandSite
->AddBand(pDeskBand
);
409 if (FAILED_UNEXPECTEDLY(hRet
))
414 else if (uID
> (UINT
)DSA_GetItemCount(m_comcatDsa
) + FIRST_COMCAT_MENU_ID
)
416 dwBandID
= uID
- (DSA_GetItemCount(m_comcatDsa
) + FIRST_COMCAT_MENU_ID
);
418 m_BandSite
->RemoveBand(dwBandID
);
422 else if (uID
== IDM_TASKBAR_TOOLBARS_DESKTOP
|| uID
== IDM_TASKBAR_TOOLBARS_QUICKLAUNCH
)
424 dwBandID
= _GetBandIdForBuiltinISFBand(uID
);
425 if (dwBandID
!= UINT_MAX
)
427 m_BandSite
->RemoveBand(dwBandID
);
431 CComPtr
<IDeskBand
> pDeskBand
;
432 hRet
= _CreateBuiltInISFBand(uID
, IID_PPV_ARG(IDeskBand
, &pDeskBand
));
433 if (FAILED_UNEXPECTEDLY(hRet
))
436 hRet
= m_BandSite
->AddBand(pDeskBand
);
437 if (FAILED_UNEXPECTEDLY(hRet
))
443 /* Get the GUID of the item that was clicked */
444 GUID
*pguidToolbar
= (GUID
*)DSA_GetItemPtr(m_comcatDsa
, uID
- FIRST_COMCAT_MENU_ID
);
448 /* Try to find if a band with a guid is present. If it is, remove it and return */
449 dwBandID
= _GetBandIdFromClsid(pguidToolbar
);
450 if (dwBandID
!= UINT_MAX
)
452 /* We found it, remove it */
453 m_BandSite
->RemoveBand(dwBandID
);
457 /* It is not present. Add it. */
458 CComPtr
<IDeskBand
> pDeskBand
;
459 hRet
= CoCreateInstance(*pguidToolbar
, NULL
, CLSCTX_INPROC_SERVER
, IID_PPV_ARG(IDeskBand
, &pDeskBand
));
460 if (FAILED_UNEXPECTEDLY(hRet
))
463 hRet
= m_BandSite
->AddBand(pDeskBand
);
464 if (FAILED_UNEXPECTEDLY(hRet
))
471 HRESULT STDMETHODCALLTYPE
CBandSiteMenu::GetCommandString(UINT_PTR idCmd
, UINT uType
,
472 UINT
*pwReserved
, LPSTR pszName
, UINT cchMax
)
474 FIXME("CBandSiteMenu::GetCommandString is UNIMPLEMENTED (%p, %p, %u, %p, %p, %u)\n", this, idCmd
, uType
, pwReserved
, pszName
, cchMax
);
478 HRESULT STDMETHODCALLTYPE
CBandSiteMenu::HandleMenuMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
480 FIXME("CBandSiteMenu::HandleMenuMsg is UNIMPLEMENTED (%p, %u, %p, %p)\n", this, uMsg
, wParam
, lParam
);
484 HRESULT STDMETHODCALLTYPE
CBandSiteMenu::HandleMenuMsg2(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*plResult
)
486 FIXME("CBandSiteMenu::HandleMenuMsg2 is UNIMPLEMENTED(%p, %u, %p, %p, %p)\n", this, uMsg
, wParam
, lParam
, plResult
);
491 HRESULT WINAPI
RSHELL_CBandSiteMenu_CreateInstance(REFIID riid
, void **ppv
)
493 return ShellObjectCreator
<CBandSiteMenu
>(riid
, ppv
);