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