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.

525 lines
16 KiB

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