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