Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

531 lines
16 KiB

  1. #include "shellprv.h"
  2. #include "common.h"
  3. #include "resource.h"
  4. #include "dpastuff.h"
  5. #include "bands.h"
  6. #include "isfband.h"
  7. #include "legacy.h"
  8. #include "uemapp.h"
  9. #define SUPERCLASS CISFBand
  10. #define DM_PERSIST DM_TRACE // trace IPS::Load, ::Save, etc.
  11. class CQuickLinks : public CISFBand
  12. {
  13. public:
  14. // *** IUnknown ***
  15. virtual STDMETHODIMP_(ULONG) AddRef(void) { return CISFBand::AddRef(); };
  16. virtual STDMETHODIMP_(ULONG) Release(void){ return CISFBand::Release(); };
  17. virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  18. // *** IPersistStream methods ***
  19. virtual STDMETHODIMP GetClassID(CLSID *pClassID);
  20. virtual STDMETHODIMP Load(IStream *pStm);
  21. virtual STDMETHODIMP Save(IStream *pstm, BOOL fClearDirty);
  22. // *** IDockingWindow methods (override) ***
  23. virtual STDMETHODIMP ShowDW(BOOL fShow);
  24. virtual STDMETHODIMP CloseDW(DWORD dw) { return CISFBand::CloseDW(dw); };
  25. // *** IObjectWithSite methods ***
  26. virtual STDMETHODIMP SetSite(IUnknown* punkSite) { return CISFBand::SetSite(punkSite); };
  27. // *** IOleCommandTarget ***
  28. virtual STDMETHODIMP Exec(const GUID *pguidCmdGroup,
  29. DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn,
  30. VARIANTARG *pvarargOut);
  31. // *** IDeskBand methods ***
  32. virtual STDMETHODIMP GetBandInfo(DWORD dwBandID, DWORD fViewMode,
  33. DESKBANDINFO* pdbi);
  34. // *** IDelegateDropTarget ***
  35. virtual HRESULT OnDropDDT(IDropTarget *pdt, IDataObject *pdtobj, DWORD * pgrfKeyState, POINTL pt, DWORD *pdwEffect);
  36. protected:
  37. CQuickLinks();
  38. virtual ~CQuickLinks();
  39. HRESULT _GetTitleW(LPWSTR pwzTitle, DWORD cchSize);
  40. HRESULT _InternalInit(void);
  41. virtual HRESULT _LoadOrderStream();
  42. virtual HRESULT _SaveOrderStream();
  43. virtual BOOL _AllowDropOnTitle() { return TRUE; };
  44. virtual HRESULT _GetIEnumIDList(DWORD dwEnumFlags, IEnumIDList **ppenum);
  45. private:
  46. BITBOOL _fIsInited :1;
  47. BITBOOL _fSingleLine :1;
  48. friend HRESULT CQuickLinks_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv); // for ctor
  49. };
  50. #define MAX_QL_SITES 5 // Number of Sites on the quick link bar
  51. #define SZ_REGKEY_SPECIALFOLDERS TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders")
  52. HRESULT SHGetSpecialFolderPathEx(LPTSTR pszPath, DWORD cchSize, DWORD dwCSIDL, LPCTSTR pszFolderName)
  53. {
  54. HRESULT hr = S_OK;
  55. if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE, NULL, 0, pszPath)))
  56. return hr;
  57. cchSize *= sizeof(TCHAR); // Count of chars to count of bytes.
  58. if (ERROR_SUCCESS != SHGetValue(HKEY_CURRENT_USER, SZ_REGKEY_SPECIALFOLDERS, pszFolderName, NULL, pszPath, &cchSize))
  59. hr = E_FAIL;
  60. TraceMsg(TF_BAND|TF_GENERAL, "CQuickLinks SHGetSpecialFolderPath(CSIDL_FAVORITES), Failed so getting Fav dir from registry. Path=%s; hr=%#8lx", pszPath, hr);
  61. return hr;
  62. }
  63. #define LINKS_FOLDERNAME_KEY TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar")
  64. #define LINKS_FOLDERNAME_VALUE TEXT("LinksFolderName")
  65. // _GetTitleW and QuickLinks_GetFolder call this.
  66. // if we ever go back to ANSI days we'll get a build break
  67. // right now we are saving some space by not having A version that's not used
  68. void QuickLinks_GetName(LPTSTR pszName, DWORD cchSize, BOOL bSetup)
  69. {
  70. DWORD cb = cchSize * SIZEOF(TCHAR);
  71. // try to get the name of the folder from the registry (in case of upgrade to a different
  72. // language we cannot use the resource)
  73. if (SHGetValue(HKEY_CURRENT_USER, LINKS_FOLDERNAME_KEY, LINKS_FOLDERNAME_VALUE, NULL, (void *)pszName, &cb) != ERROR_SUCCESS)
  74. {
  75. // no luck, try the HKLM if we are doing per user registration, maybe setup stored the old links folder name there
  76. cb = cchSize * SIZEOF(TCHAR);
  77. if (!bSetup || SHGetValue(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion"), TEXT("LinkFolderName"), NULL, (void *)pszName, &cb) != ERROR_SUCCESS)
  78. {
  79. // if everything else fails load it from the resource
  80. LoadString(HINST_THISDLL, IDS_QLINKS, pszName, cchSize);
  81. }
  82. }
  83. }
  84. HRESULT QuickLinks_GetFolder(LPTSTR pszPath, DWORD cchSize, BOOL bSetup = FALSE)
  85. {
  86. TCHAR szQuickLinks[MAX_PATH];
  87. if (SUCCEEDED(SHGetSpecialFolderPathEx(pszPath, cchSize, CSIDL_FAVORITES, TEXT("Favorites"))))
  88. {
  89. QuickLinks_GetName(szQuickLinks, ARRAYSIZE(szQuickLinks), bSetup);
  90. PathCombine(pszPath, pszPath, szQuickLinks);
  91. return S_OK;
  92. }
  93. return E_FAIL;
  94. }
  95. // Length of the text under each quick links
  96. #define MAX_QL_TEXT_LENGTH 256
  97. #define MAX_QL_WIDTH 92
  98. #define QL_BUFFER (MAX_QL_TEXT_LENGTH + MAX_URL_STRING + MAX_TOOLTIP_STRING + 3)
  99. //
  100. // Load strings needed for quick links
  101. //
  102. // returns TRUE/FALSE if it was user specified (false used to load the default
  103. // urls, but now we leave that to branding dll
  104. BOOL QLLoadLinkName(HUSKEY hUSKey, int i, LPTSTR pszTitle, UINT cchTitle, LPTSTR pszURL, UINT cchURL)
  105. {
  106. CHAR szScratch[QL_BUFFER];
  107. DWORD dwcbData = SIZEOF(szScratch);
  108. CHAR * pszTemp;
  109. TCHAR szRegValues[5];
  110. // In IE3, links did not have its own folder. Instead, links were stored in the registry as a
  111. // set of binary streams of ANSI strings.
  112. wnsprintf(szRegValues, ARRAYSIZE(szRegValues), TEXT("%d"), i+1);
  113. if (hUSKey &&
  114. (ERROR_SUCCESS == SHRegQueryUSValue(hUSKey, szRegValues, NULL, (LPBYTE)szScratch, &dwcbData, FALSE, NULL, 0)))
  115. {
  116. int nNULLs = 0;
  117. pszTemp = szScratch;
  118. DWORD j;
  119. for (j = 0; j < dwcbData; j++)
  120. {
  121. #ifdef MAINWIN
  122. // Because of the limitations of the MainWin registry, we'll put '*' instead of '\0'.
  123. if (*pszTemp == TEXT('*'))
  124. *pszTemp = '\0';
  125. #endif
  126. nNULLs += (UINT)(*pszTemp++ == TEXT('\0'));
  127. }
  128. // make sure we have 3 strings with a double NULL at the end
  129. if (nNULLs > 3)
  130. {
  131. pszTemp = szScratch;
  132. SHAnsiToTChar(pszTemp, pszTitle, cchTitle);
  133. pszTemp += lstrlenA(pszTemp) + 1;
  134. SHAnsiToTChar(pszTemp, pszURL, cchURL);
  135. return TRUE;
  136. }
  137. }
  138. return FALSE;
  139. }
  140. void ImportQuickLinks()
  141. {
  142. TCHAR szQuickLinksDir[MAX_PATH];
  143. if (FAILED(QuickLinks_GetFolder(szQuickLinksDir, ARRAYSIZE(szQuickLinksDir), TRUE)))
  144. return;
  145. // need to write the folder name to the registry so we can use it w/ different plug ui languages
  146. LPTSTR pszQLinks;
  147. DWORD cb;
  148. PathRemoveBackslash(szQuickLinksDir);
  149. pszQLinks = PathFindFileName(szQuickLinksDir);
  150. if (pszQLinks)
  151. {
  152. cb = (lstrlen(pszQLinks)+1) * sizeof(TCHAR);
  153. SHSetValue(HKEY_CURRENT_USER, LINKS_FOLDERNAME_KEY, LINKS_FOLDERNAME_VALUE, REG_SZ, (void *)pszQLinks, cb);
  154. }
  155. if (!PathFileExists(szQuickLinksDir) &&
  156. CreateDirectory(szQuickLinksDir, NULL))
  157. {
  158. HUSKEY hUSKey = NULL;
  159. SHRegOpenUSKey(TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar\\Links"), KEY_READ, NULL, &hUSKey, FALSE);
  160. for (int i = 0; i < MAX_QL_SITES; i++)
  161. {
  162. // this was user specified... convert it
  163. LPITEMIDLIST pidl;
  164. TCHAR szURLTemp[MAX_URL_STRING];
  165. TCHAR szTitle[MAX_QL_TEXT_LENGTH];
  166. if (QLLoadLinkName(hUSKey, i, szTitle, ARRAYSIZE(szTitle), szURLTemp, ARRAYSIZE(szURLTemp)))
  167. {
  168. WCHAR szURL[MAX_URL_STRING];
  169. if (SUCCEEDED(URLSubstitution(szURLTemp, szURL, ARRAYSIZE(szURL), URLSUB_ALL)) &&
  170. SUCCEEDED(IECreateFromPath(szURL, &pidl)))
  171. {
  172. CreateShortcutInDir(pidl, szTitle, szQuickLinksDir, NULL, FALSE);
  173. ILFree(pidl);
  174. }
  175. }
  176. }
  177. // we found the key, there's something to migrate
  178. if (hUSKey)
  179. SHRegCloseUSKey(hUSKey);
  180. // all converted, delete the key
  181. SHDeleteKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar\\Links"));
  182. }
  183. else
  184. {
  185. // ie4 -> ieX upgrade
  186. // create a value in hkcu\sw\ms\ie\toolbar to indicate that we should preserve the order from the links stream
  187. // not the one from the favorites\links that we are using for links starting w/ ie5
  188. BOOL bVal = TRUE;
  189. // we don't care if this fails. if it does we'll just just favorites\links order stream
  190. SHSetValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar"),
  191. TEXT("SaveLinksOrder"), REG_BINARY, (DWORD *)&bVal, SIZEOF(bVal));
  192. }
  193. }
  194. HRESULT CQuickLinks::_InternalInit(void)
  195. {
  196. if (!_fIsInited && !_psf)
  197. {
  198. LPITEMIDLIST pidlQLinks;
  199. TCHAR szPath[MAX_PATH];
  200. QuickLinks_GetFolder(szPath, ARRAYSIZE(szPath));
  201. if (!PathFileExists(szPath))
  202. CreateDirectory(szPath, NULL);
  203. if (SUCCEEDED(IECreateFromPath(szPath, &pidlQLinks)))
  204. {
  205. InitializeSFB(NULL, pidlQLinks);
  206. ILFree(pidlQLinks);
  207. }
  208. }
  209. _fIsInited = TRUE;
  210. return S_OK;
  211. }
  212. CQuickLinks::CQuickLinks() :
  213. SUPERCLASS()
  214. {
  215. #ifdef DEBUG
  216. if (IsFlagSet(g_dwBreakFlags, BF_ONAPIENTER))
  217. {
  218. TraceMsg(TF_ALWAYS, "Stopping in CQuickLinks ctor");
  219. DEBUG_BREAK;
  220. }
  221. #endif
  222. ASSERT(!_fIsInited);
  223. _fCascadeFolder = TRUE;
  224. _fVariableWidth = TRUE;
  225. _pguidUEMGroup = &UEMIID_BROWSER;
  226. }
  227. CQuickLinks::~CQuickLinks()
  228. {
  229. }
  230. HRESULT CQuickLinks_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  231. {
  232. // aggregation checking is handled in class factory
  233. HRESULT hr = E_OUTOFMEMORY;
  234. CQuickLinks *pObj = new CQuickLinks();
  235. if (pObj)
  236. {
  237. hr = pObj->QueryInterface(riid, ppv);
  238. pObj->Release();
  239. }
  240. return hr;
  241. }
  242. HRESULT CQuickLinks::_LoadOrderStream()
  243. {
  244. HRESULT hr = E_FAIL;
  245. if (_pidl && _psf)
  246. {
  247. IStream* pstm = OpenPidlOrderStream((LPCITEMIDLIST)CSIDL_FAVORITES, _pidl, REG_SUBKEY_FAVORITESA, STGM_READ);
  248. if (pstm)
  249. {
  250. OrderList_Destroy(&_hdpaOrder);
  251. hr = OrderList_LoadFromStream(pstm, &_hdpaOrder, _psf);
  252. pstm->Release();
  253. }
  254. }
  255. return hr;
  256. }
  257. HRESULT CQuickLinks::_SaveOrderStream()
  258. {
  259. HRESULT hr = E_FAIL;
  260. if (_pidl && (_hdpa || _hdpaOrder))
  261. {
  262. IStream* pstm = OpenPidlOrderStream((LPCITEMIDLIST)CSIDL_FAVORITES, _pidl, REG_SUBKEY_FAVORITESA, STGM_CREATE | STGM_WRITE);
  263. if (pstm)
  264. {
  265. hr = OrderList_SaveToStream(pstm, (_hdpa ? _hdpa : _hdpaOrder), _psf);
  266. pstm->Release();
  267. }
  268. }
  269. if (SUCCEEDED(hr))
  270. hr = SUPERCLASS::_SaveOrderStream();
  271. return hr;
  272. }
  273. HRESULT CQuickLinks::_GetIEnumIDList(DWORD dwEnumFlags, IEnumIDList **ppenum)
  274. {
  275. HRESULT hres;
  276. ASSERT(_psf);
  277. // Pass in a NULL hwnd so the enumerator does not show any UI while
  278. // we're filling a band.
  279. hres = IShellFolder_EnumObjects(_psf, NULL, dwEnumFlags, ppenum);
  280. // we could have failed because our folder does not exist
  281. // that can happen if someone delted/renamed links while there is
  282. // stream in the registry that saves the pidl - we get the pidl and
  283. // bind to it (bind does not hit the disk so it succeeds even though
  284. // the file does not exist
  285. if (FAILED(hres) && hres != E_OUTOFMEMORY)
  286. {
  287. TCHAR szPath[MAX_PATH];
  288. ASSERT(_pidl);
  289. if (SHGetPathFromIDList(_pidl, szPath) && !PathFileExists(szPath))
  290. {
  291. LPITEMIDLIST pidlQLinks;
  292. QuickLinks_GetFolder(szPath, ARRAYSIZE(szPath));
  293. if (!PathFileExists(szPath))
  294. CreateDirectory(szPath, NULL);
  295. if (SUCCEEDED(IECreateFromPath(szPath, &pidlQLinks)))
  296. {
  297. if (SUCCEEDED(InitializeSFB(NULL, pidlQLinks)))
  298. {
  299. hres = _psf->EnumObjects(NULL, dwEnumFlags, ppenum);
  300. }
  301. ILFree(pidlQLinks);
  302. }
  303. }
  304. }
  305. return hres;
  306. }
  307. //*** CQuickLinks::IPersistStream
  308. HRESULT CQuickLinks::Load(IStream *pstm)
  309. {
  310. HRESULT hr = S_OK;
  311. hr = SUPERCLASS::Load(pstm);
  312. // This forces a refresh
  313. _fIsInited = FALSE;
  314. ATOMICRELEASE(_psf);
  315. _InternalInit();
  316. // if we are on our first run through (i.e. this reg key exists)
  317. // we load the order stream from our old location (used in ie4) and avoid overwriting it w/ favorites stream
  318. // so user can have their custom order preserved on upgrade (they are more likely to customize links bar
  319. // order then favorites/links so we picked that one)
  320. if (SHGetValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar"),
  321. TEXT("SaveLinksOrder"), NULL, NULL, NULL) == ERROR_SUCCESS)
  322. {
  323. SHDeleteValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar"), TEXT("SaveLinksOrder"));
  324. // must persist old order stream in the new location (fav/links)
  325. _SaveOrderStream();
  326. }
  327. else
  328. {
  329. _LoadOrderStream();
  330. }
  331. return hr;
  332. }
  333. HRESULT CQuickLinks::Save(IStream *pstm, BOOL fClearDirty)
  334. {
  335. HRESULT hr = SUPERCLASS::Save(pstm, fClearDirty);
  336. _SaveOrderStream();
  337. return hr;
  338. }
  339. HRESULT CQuickLinks::GetClassID(CLSID *pClassID)
  340. {
  341. *pClassID = CLSID_QuickLinks;
  342. return S_OK;
  343. }
  344. // *** IUnknown Interface ***
  345. HRESULT CQuickLinks::QueryInterface(REFIID riid, void **ppvObj)
  346. {
  347. return SUPERCLASS::QueryInterface(riid, ppvObj);
  348. }
  349. // command target
  350. STDMETHODIMP CQuickLinks::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
  351. DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  352. {
  353. HRESULT hres = S_FALSE;
  354. if (pguidCmdGroup)
  355. {
  356. if (IsEqualGUID(*pguidCmdGroup, CLSID_QuickLinks))
  357. {
  358. switch (nCmdID)
  359. {
  360. case QLCMD_SINGLELINE:
  361. _fSingleLine = (nCmdexecopt == 1);
  362. return S_OK;
  363. }
  364. }
  365. else if (IsEqualGUID(*pguidCmdGroup, CGID_ISFBand))
  366. {
  367. switch(nCmdID)
  368. {
  369. case ISFBID_SETORDERSTREAM:
  370. hres = SUPERCLASS::Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
  371. _SaveOrderStream();
  372. break;
  373. }
  374. }
  375. }
  376. if (hres == S_FALSE)
  377. hres = SUPERCLASS::Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
  378. return hres;
  379. }
  380. // *** IDockingWindow Interface ***
  381. HRESULT CQuickLinks::ShowDW(BOOL fShow)
  382. {
  383. if (fShow)
  384. _InternalInit();
  385. return SUPERCLASS::ShowDW(fShow);
  386. }
  387. HRESULT CQuickLinks::_GetTitleW(LPWSTR pwzTitle, DWORD cchSize)
  388. {
  389. QuickLinks_GetName(pwzTitle, cchSize, FALSE);
  390. return S_OK;
  391. }
  392. HRESULT CQuickLinks::GetBandInfo(DWORD dwBandID, DWORD fViewMode, DESKBANDINFO* pdbi)
  393. {
  394. HRESULT hres = SUPERCLASS::GetBandInfo(dwBandID, fViewMode, pdbi);
  395. if (_hwndTB && _fSingleLine)
  396. {
  397. LRESULT lButtonSize = SendMessage(_hwndTB, TB_GETBUTTONSIZE, 0, 0L);
  398. pdbi->ptMinSize.y = HIWORD(lButtonSize);
  399. pdbi->dwModeFlags &= ~DBIMF_VARIABLEHEIGHT;
  400. }
  401. return hres;
  402. }
  403. HRESULT CQuickLinks::OnDropDDT(IDropTarget *pdt, IDataObject *pdtobj, DWORD * pgrfKeyState, POINTL pt, DWORD *pdwEffect)
  404. {
  405. HRESULT hr;
  406. BOOL fIsSafe = TRUE;
  407. // if we are not the source of the drop and the Links folder does not exist we need to create it
  408. if (_iDragSource == -1)
  409. {
  410. TCHAR szPath[MAX_PATH];
  411. QuickLinks_GetFolder(szPath, ARRAYSIZE(szPath));
  412. if (!PathFileExists(szPath))
  413. CreateDirectory(szPath, NULL);
  414. LPITEMIDLIST pidl;
  415. if (SUCCEEDED(SHPidlFromDataObject(pdtobj, &pidl, NULL, 0)))
  416. {
  417. fIsSafe = IEIsLinkSafe(_hwnd, pidl, ILS_LINK);
  418. ILFree(pidl);
  419. }
  420. }
  421. if (fIsSafe)
  422. {
  423. hr = SUPERCLASS::OnDropDDT(pdt, pdtobj, pgrfKeyState, pt, pdwEffect);
  424. }
  425. else
  426. {
  427. hr = S_FALSE;
  428. }
  429. return hr;
  430. }