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