[BROWSEUI] CBandSiteMenu: Use CSimpleArray and CComHeapPtr.
[reactos.git] / dll / win32 / browseui / shellbars / CBandSiteMenu.cpp
1 /*
2 * Band site menu
3 *
4 * Copyright 2007 Hervé Poussineua
5 * Copyright 2009 Andrew Hill
6 *
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.
11 *
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.
16 *
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
20 */
21
22 #include "shellbars.h"
23 #include <strsafe.h>
24
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
33
34 CBandSiteMenu::CBandSiteMenu():
35 m_hmenu(NULL),
36 m_DesktopPidl(NULL),
37 m_QLaunchPidl(NULL)
38 {
39 }
40
41 CBandSiteMenu::~CBandSiteMenu()
42 {
43 if (m_hmenu)
44 DestroyMenu(m_hmenu);
45
46 m_BandSite = NULL;
47 }
48
49 HRESULT WINAPI CBandSiteMenu::FinalConstruct()
50 {
51 HRESULT hr = SHGetFolderLocation(0, CSIDL_DESKTOP, NULL, 0, &m_DesktopPidl);
52 if (FAILED_UNEXPECTEDLY(hr))
53 return hr;
54
55 WCHAR buffer[MAX_PATH];
56 hr = SHGetFolderPathAndSubDirW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, L"Microsoft\\Internet Explorer\\Quick Launch", buffer);
57 if (FAILED_UNEXPECTEDLY(hr))
58 return hr;
59
60 m_QLaunchPidl.Attach(ILCreateFromPathW(buffer));
61 if (m_QLaunchPidl == NULL)
62 return E_FAIL;
63
64 return S_OK;
65 }
66
67 HRESULT CBandSiteMenu::_CreateMenuPart()
68 {
69 WCHAR wszBandName[MAX_PATH];
70 WCHAR wszBandGUID[MAX_PATH];
71 WCHAR wRegKey[MAX_PATH];
72 UINT cBands;
73 DWORD dwDataSize;
74 CATID category = CATID_DeskBand;
75 HMENU hmenuToolbars;
76 DWORD dwRead;
77 CComPtr<IEnumGUID> pEnumGUID;
78 HRESULT hr;
79
80 if (m_hmenu)
81 DestroyMenu(m_hmenu);
82
83 /* Load the template we will fill in */
84 m_hmenu = LoadMenuW(GetModuleHandleW(L"browseui.dll"), MAKEINTRESOURCEW(IDM_TASKBAR_TOOLBARS));
85 if (!m_hmenu)
86 return HRESULT_FROM_WIN32(GetLastError());
87
88 /* Get the handle of the submenu where the available items will be shown */
89 hmenuToolbars = GetSubMenu(m_hmenu, 0);
90
91 /* Create the category enumerator */
92 hr = SHEnumClassesOfCategories(1, &category, 0, NULL, &pEnumGUID);
93 if (FAILED_UNEXPECTEDLY(hr))
94 return hr;
95
96 m_ComCatGuids.RemoveAll();
97
98 /* Enumerate the classes in the CATID_DeskBand category */
99 cBands = 0;
100 do
101 {
102 GUID iter;
103 pEnumGUID->Next(1, &iter, &dwRead);
104 if (!dwRead)
105 continue;
106
107 if (!StringFromGUID2(iter, wszBandGUID, MAX_PATH))
108 continue;
109
110 /* Get the band name */
111 StringCchPrintfW(wRegKey, MAX_PATH, L"CLSID\\%s", wszBandGUID);
112 dwDataSize = MAX_PATH;
113 SHGetValue(HKEY_CLASSES_ROOT, wRegKey, NULL, NULL, wszBandName, &dwDataSize);
114
115 /* Insert it */
116 InsertMenu(hmenuToolbars, cBands, MF_BYPOSITION, m_ComCatGuids.GetSize() + FIRST_COMCAT_MENU_ID, wszBandName);
117 m_ComCatGuids.Add(iter);
118 cBands++;
119 }
120 while (dwRead > 0);
121
122 return S_OK;
123 }
124
125 HRESULT CBandSiteMenu::_CreateNewISFBand(HWND hwnd, REFIID riid, void** ppv)
126 {
127 WCHAR path[MAX_PATH];
128 WCHAR message[256];
129 BROWSEINFOW bi = { hwnd, NULL, path };
130
131 if (LoadStringW(GetModuleHandleW(L"browseui.dll"), IDS_BROWSEFORNEWTOOLAR, message, _countof(message)))
132 bi.lpszTitle = message;
133 else
134 bi.lpszTitle = L"Choose a folder";
135
136 CComHeapPtr<ITEMIDLIST> pidlSelected;
137 pidlSelected.Attach(SHBrowseForFolderW(&bi));
138 if (pidlSelected == NULL)
139 return S_FALSE;
140
141 CComPtr<IShellFolderBand> pISFB;
142 HRESULT hr = CISFBand_CreateInstance(IID_IShellFolderBand, (PVOID*)&pISFB);
143 if (FAILED_UNEXPECTEDLY(hr))
144 return hr;
145
146 hr = pISFB->InitializeSFB(NULL, pidlSelected);
147 if (FAILED_UNEXPECTEDLY(hr))
148 return hr;
149
150 return pISFB->QueryInterface(riid, ppv);
151 }
152
153 HRESULT CBandSiteMenu::_CreateBuiltInISFBand(UINT uID, REFIID riid, void** ppv)
154 {
155 LPITEMIDLIST pidl;
156 HRESULT hr;
157
158 pidl = (uID == IDM_TASKBAR_TOOLBARS_DESKTOP) ? m_DesktopPidl : m_QLaunchPidl;
159
160 CComPtr<IShellFolderBand> pISFB;
161 hr = CISFBand_CreateInstance(IID_IShellFolderBand, (PVOID*)&pISFB);
162 if (FAILED_UNEXPECTEDLY(hr))
163 return hr;
164
165 hr = pISFB->InitializeSFB(NULL, pidl);
166 if (FAILED_UNEXPECTEDLY(hr))
167 return hr;
168
169 /* HACK! We shouldn't pass ISFB_STATE_QLINKSMODE and CISFBand shouldn't handle it! */
170 if (uID == IDM_TASKBAR_TOOLBARS_QUICKLAUNCH)
171 {
172 BANDINFOSFB bisfb = {ISFB_MASK_STATE, ISFB_STATE_QLINKSMODE, ISFB_STATE_QLINKSMODE};
173 pISFB->SetBandInfoSFB(&bisfb);
174 }
175
176 return pISFB->QueryInterface(riid, ppv);
177 }
178
179 HRESULT CBandSiteMenu::_AddISFBandToMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, IUnknown* pBand, DWORD dwBandID, UINT *newMenuId)
180 {
181 CComPtr<IShellFolderBand> psfb;
182 HRESULT hr = pBand->QueryInterface(IID_PPV_ARG(IShellFolderBand, &psfb));
183 if (FAILED_UNEXPECTEDLY(hr))
184 return hr;
185
186 BANDINFOSFB bi = {ISFB_MASK_IDLIST};
187 hr = psfb->GetBandInfoSFB(&bi);
188 if (FAILED_UNEXPECTEDLY(hr))
189 return hr;
190
191 CComHeapPtr<ITEMIDLIST> pidl(bi.pidl);
192 if (!pidl)
193 {
194 ERR("Failed to get the pidl of the CISFBand\n");
195 return E_OUTOFMEMORY;
196 }
197
198 WCHAR buffer[MAX_PATH];
199 hr = ILGetDisplayNameEx(NULL, pidl, buffer, ILGDN_INFOLDER) ? S_OK : E_FAIL;
200 if (FAILED_UNEXPECTEDLY(hr))
201 return hr;
202
203 UINT id = idCmdFirst + m_ComCatGuids.GetSize() + FIRST_COMCAT_MENU_ID + dwBandID;
204 if (id >= idCmdLast)
205 return E_FAIL;
206
207 *newMenuId = id;
208 InsertMenu(hmenu, indexMenu, MF_BYPOSITION, id, buffer);
209 return S_OK;
210 }
211
212 UINT CBandSiteMenu::_GetMenuIdFromISFBand(IUnknown *pBand)
213 {
214 CComPtr<IShellFolderBand> psfb;
215 HRESULT hr = pBand->QueryInterface(IID_PPV_ARG(IShellFolderBand, &psfb));
216 if (FAILED_UNEXPECTEDLY(hr))
217 return UINT_MAX;
218
219 BANDINFOSFB bi = {ISFB_MASK_IDLIST};
220 hr = psfb->GetBandInfoSFB(&bi);
221 if (FAILED_UNEXPECTEDLY(hr))
222 return UINT_MAX;
223
224 CComHeapPtr<ITEMIDLIST> pidl(bi.pidl);
225 if (!pidl)
226 {
227 ERR("Failed to get the pidl of the CISFBand\n");
228 return UINT_MAX;
229 }
230
231 if (pidl->mkid.cb == 0)
232 {
233 return IDM_TASKBAR_TOOLBARS_DESKTOP;
234 }
235
236 CComPtr<IShellFolder> psfDesktop;
237 hr = SHGetDesktopFolder(&psfDesktop);
238 if (FAILED_UNEXPECTEDLY(hr))
239 return UINT_MAX;
240
241 hr = psfDesktop->CompareIDs(0, pidl, m_QLaunchPidl);
242 if (FAILED_UNEXPECTEDLY(hr))
243 return UINT_MAX;
244
245 if (HRESULT_CODE(hr) == 0)
246 return IDM_TASKBAR_TOOLBARS_QUICKLAUNCH;
247
248 return UINT_MAX;
249 }
250
251 UINT CBandSiteMenu::_GetBandIdFromClsid(CLSID* pclsid)
252 {
253 CComPtr<IPersist> pBand;
254 CLSID BandCLSID;
255 DWORD dwBandID;
256
257 for (UINT uBand = 0; SUCCEEDED(m_BandSite->EnumBands(uBand, &dwBandID)); uBand++)
258 {
259 if (FAILED(m_BandSite->GetBandObject(dwBandID, IID_PPV_ARG(IPersist, &pBand))))
260 continue;
261
262 if (FAILED_UNEXPECTEDLY(pBand->GetClassID(&BandCLSID)))
263 continue;
264
265 if (IsEqualGUID(*pclsid, BandCLSID))
266 return dwBandID;
267 }
268
269 return UINT_MAX;
270 }
271
272 UINT CBandSiteMenu::_GetBandIdForBuiltinISFBand(UINT uID)
273 {
274 CComPtr<IPersist> pBand;
275 CLSID BandCLSID;
276 DWORD dwBandID;
277
278 for (UINT uBand = 0; SUCCEEDED(m_BandSite->EnumBands(uBand, &dwBandID)); uBand++)
279 {
280 if (FAILED(m_BandSite->GetBandObject(dwBandID, IID_PPV_ARG(IPersist, &pBand))))
281 continue;
282
283 if (FAILED_UNEXPECTEDLY(pBand->GetClassID(&BandCLSID)))
284 continue;
285
286 if (!IsEqualGUID(BandCLSID, CLSID_ISFBand))
287 continue;
288
289 UINT menuID = _GetMenuIdFromISFBand(pBand);
290 if (menuID == uID)
291 return dwBandID;
292 }
293
294 return UINT_MAX;
295 }
296
297 HRESULT STDMETHODCALLTYPE CBandSiteMenu::SetOwner(IUnknown *pOwner)
298 {
299 TRACE("CBandSiteMenu::SetOwner(%p, %p)\n", this, pOwner);
300
301 /* Cache the menu that will be merged every time QueryContextMenu is called */
302 _CreateMenuPart();
303
304 return pOwner->QueryInterface(IID_PPV_ARG(IBandSite, &m_BandSite));
305 }
306
307 HRESULT STDMETHODCALLTYPE CBandSiteMenu::QueryContextMenu(
308 HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
309 {
310 CComPtr<IPersist> pBand;
311 CLSID BandCLSID;
312 DWORD dwBandID;
313 UINT idMax;
314
315 TRACE("CBandSiteMenu::QueryContextMenu(%p, %p, %u, %u, %u, 0x%x)\n", this, hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
316
317 /* First Merge the menu with the available bands */
318 idMax = Shell_MergeMenus(hmenu, m_hmenu, indexMenu, idCmdFirst, idCmdLast, MM_DONTREMOVESEPS | MM_SUBMENUSHAVEIDS);
319
320 HMENU hmenuToolbars = GetSubMenu(hmenu, indexMenu);
321
322 /* Enumerate all present bands and mark them as checked in the menu */
323 for (UINT uBand = 0; SUCCEEDED(m_BandSite->EnumBands(uBand, &dwBandID)); uBand++)
324 {
325 if (FAILED(m_BandSite->GetBandObject(dwBandID, IID_PPV_ARG(IPersist, &pBand))))
326 continue;
327
328 if (FAILED_UNEXPECTEDLY(pBand->GetClassID(&BandCLSID)))
329 continue;
330
331 UINT menuID;
332 if (IsEqualGUID(BandCLSID, CLSID_ISFBand))
333 {
334 menuID = _GetMenuIdFromISFBand(pBand);
335 if (menuID == UINT_MAX)
336 {
337 HRESULT hr;
338 hr = _AddISFBandToMenu(hmenuToolbars, 0, idCmdFirst, idCmdLast, pBand, dwBandID, &menuID);
339 if (SUCCEEDED(hr) && menuID > idMax)
340 idMax = menuID;
341 menuID -= idCmdFirst;
342 }
343 }
344 else
345 {
346 int i = m_ComCatGuids.Find(BandCLSID);
347 menuID = (i == -1 ? UINT_MAX : i + FIRST_COMCAT_MENU_ID);
348 }
349
350 if (menuID != UINT_MAX)
351 CheckMenuItem(hmenuToolbars, menuID + idCmdFirst, MF_CHECKED);
352 }
353
354 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(idMax - idCmdFirst +1));
355 }
356
357 HRESULT STDMETHODCALLTYPE CBandSiteMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
358 {
359 HRESULT hRet;
360 DWORD dwBandID;
361
362 /* FIXME: do we need to handle this and how? */
363 if (HIWORD(lpici->lpVerb) != NULL)
364 return E_FAIL;
365
366 UINT uID = LOWORD(lpici->lpVerb);
367 if (uID == IDM_TASKBAR_TOOLBARS_NEW)
368 {
369 CComPtr<IDeskBand> pDeskBand;
370 hRet = _CreateNewISFBand(lpici->hwnd, IID_PPV_ARG(IDeskBand, &pDeskBand));
371 if (FAILED_UNEXPECTEDLY(hRet))
372 return hRet;
373
374 hRet = m_BandSite->AddBand(pDeskBand);
375 if (FAILED_UNEXPECTEDLY(hRet))
376 return hRet;
377
378 return S_OK;
379 }
380 else if (uID > (UINT) m_ComCatGuids.GetSize() + FIRST_COMCAT_MENU_ID )
381 {
382 dwBandID = uID - (m_ComCatGuids.GetSize() + FIRST_COMCAT_MENU_ID );
383
384 m_BandSite->RemoveBand(dwBandID);
385
386 return S_OK;
387 }
388 else if (uID == IDM_TASKBAR_TOOLBARS_DESKTOP || uID == IDM_TASKBAR_TOOLBARS_QUICKLAUNCH)
389 {
390 dwBandID = _GetBandIdForBuiltinISFBand(uID);
391 if (dwBandID != UINT_MAX)
392 {
393 m_BandSite->RemoveBand(dwBandID);
394 }
395 else
396 {
397 CComPtr<IDeskBand> pDeskBand;
398 hRet = _CreateBuiltInISFBand(uID, IID_PPV_ARG(IDeskBand, &pDeskBand));
399 if (FAILED_UNEXPECTEDLY(hRet))
400 return hRet;
401
402 hRet = m_BandSite->AddBand(pDeskBand);
403 if (FAILED_UNEXPECTEDLY(hRet))
404 return hRet;
405 }
406 return S_OK;
407 }
408
409 /* Get the GUID of the item that was clicked */
410 GUID *pguidToolbar = &m_ComCatGuids[uID - FIRST_COMCAT_MENU_ID];
411 if (!pguidToolbar)
412 return E_FAIL;
413
414 /* Try to find if a band with a guid is present. If it is, remove it and return */
415 dwBandID = _GetBandIdFromClsid(pguidToolbar);
416 if (dwBandID != UINT_MAX)
417 {
418 /* We found it, remove it */
419 m_BandSite->RemoveBand(dwBandID);
420 }
421 else
422 {
423 /* It is not present. Add it. */
424 CComPtr<IDeskBand> pDeskBand;
425 hRet = CoCreateInstance(*pguidToolbar, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IDeskBand, &pDeskBand));
426 if (FAILED_UNEXPECTEDLY(hRet))
427 return hRet;
428
429 hRet = m_BandSite->AddBand(pDeskBand);
430 if (FAILED_UNEXPECTEDLY(hRet))
431 return hRet;
432 }
433
434 return S_OK;
435 }
436
437 HRESULT STDMETHODCALLTYPE CBandSiteMenu::GetCommandString(UINT_PTR idCmd, UINT uType,
438 UINT *pwReserved, LPSTR pszName, UINT cchMax)
439 {
440 FIXME("CBandSiteMenu::GetCommandString is UNIMPLEMENTED (%p, %p, %u, %p, %p, %u)\n", this, idCmd, uType, pwReserved, pszName, cchMax);
441 return E_NOTIMPL;
442 }
443
444 HRESULT STDMETHODCALLTYPE CBandSiteMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
445 {
446 FIXME("CBandSiteMenu::HandleMenuMsg is UNIMPLEMENTED (%p, %u, %p, %p)\n", this, uMsg, wParam, lParam);
447 return E_NOTIMPL;
448 }
449
450 HRESULT STDMETHODCALLTYPE CBandSiteMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
451 {
452 FIXME("CBandSiteMenu::HandleMenuMsg2 is UNIMPLEMENTED(%p, %u, %p, %p, %p)\n", this, uMsg, wParam, lParam, plResult);
453 return E_NOTIMPL;
454 }
455
456 extern "C"
457 HRESULT WINAPI RSHELL_CBandSiteMenu_CreateInstance(REFIID riid, void **ppv)
458 {
459 return ShellObjectCreator<CBandSiteMenu>(riid, ppv);
460 }