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