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.

289 lines
7.6 KiB

  1. #include "shellprv.h"
  2. #include "infotip.h"
  3. #include "ids.h"
  4. #include "prop.h"
  5. #include <mluisupp.h>
  6. // generic info tip object
  7. class CInfoTip : public IQueryInfo, public ICustomizeInfoTip, public IParentAndItem
  8. {
  9. public:
  10. // IUnknown
  11. STDMETHODIMP QueryInterface(REFIID, void **);
  12. STDMETHODIMP_(ULONG) AddRef(void);
  13. STDMETHODIMP_(ULONG) Release(void);
  14. // IQueryInfo methods.
  15. STDMETHODIMP GetInfoTip(DWORD dwFlags, WCHAR** ppwszTip);
  16. STDMETHODIMP GetInfoFlags(DWORD *pdwFlags);
  17. // ICustomizeInfoTip
  18. STDMETHODIMP SetPrefixText(LPCWSTR pszPrefix);
  19. STDMETHODIMP SetExtraProperties(const SHCOLUMNID *pscid, UINT cscid);
  20. // IParentAndItem
  21. STDMETHODIMP SetParentAndItem(LPCITEMIDLIST pidlParent, IShellFolder *psf, LPCITEMIDLIST pidlChild);
  22. STDMETHODIMP GetParentAndItem(LPITEMIDLIST *ppidlParent, IShellFolder **ppsf, LPITEMIDLIST *ppidlChild);
  23. CInfoTip(IShellFolder2 *psf, LPCITEMIDLIST pidl, LPCWSTR pszProp);
  24. private:
  25. ~CInfoTip();
  26. HRESULT _GetInfoTipFromItem(WCHAR **ppszText);
  27. BOOL _InExtraList(const SHCOLUMNID *pscid);
  28. LONG _cRef;
  29. IShellFolder2 *_psf;
  30. LPITEMIDLIST _pidl;
  31. TCHAR _szText[INFOTIPSIZE];
  32. LPWSTR _pszPrefix;
  33. SHCOLUMNID _rgcols[8];
  34. UINT _cscid;
  35. };
  36. #define PROP_PREFIX TEXT("prop:")
  37. #define PROP_PREFIX_LEN (ARRAYSIZE(PROP_PREFIX) - 1)
  38. CInfoTip::CInfoTip(IShellFolder2 *psf, LPCITEMIDLIST pidl, LPCWSTR pszText) : _cRef(1)
  39. {
  40. if (IS_INTRESOURCE(pszText))
  41. LoadString(HINST_THISDLL, LOWORD((UINT_PTR)pszText), _szText, ARRAYSIZE(_szText));
  42. else
  43. SHUnicodeToTChar(pszText, _szText, ARRAYSIZE(_szText));
  44. if (psf && pidl && (StrCmpNI(_szText, PROP_PREFIX, PROP_PREFIX_LEN) == 0))
  45. {
  46. // list of properties, we need the psf and pidl for this
  47. psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &_psf));
  48. _pidl = ILClone(pidl);
  49. }
  50. }
  51. CInfoTip::~CInfoTip()
  52. {
  53. if (_psf)
  54. _psf->Release();
  55. ILFree(_pidl);
  56. Str_SetPtr(&_pszPrefix, NULL);
  57. }
  58. HRESULT CInfoTip::QueryInterface(REFIID riid, void **ppv)
  59. {
  60. static const QITAB qit[] = {
  61. QITABENT(CInfoTip, IQueryInfo),
  62. QITABENT(CInfoTip, ICustomizeInfoTip),
  63. QITABENT(CInfoTip, IParentAndItem),
  64. { 0 },
  65. };
  66. return QISearch(this, qit, riid, ppv);
  67. }
  68. ULONG CInfoTip::AddRef()
  69. {
  70. return InterlockedIncrement(&_cRef);
  71. }
  72. ULONG CInfoTip::Release()
  73. {
  74. if (InterlockedDecrement(&_cRef))
  75. return _cRef;
  76. delete this;
  77. return 0;
  78. }
  79. BOOL CInfoTip::_InExtraList(const SHCOLUMNID *pscid)
  80. {
  81. for (UINT i = 0; i < _cscid; i++)
  82. {
  83. if (IsEqualSCID(*pscid, _rgcols[i]))
  84. return TRUE;
  85. }
  86. return FALSE;
  87. }
  88. void _AppendTipText(LPTSTR pszBuf, int cch, LPCTSTR pszCRLF, LPCTSTR pszPropName, LPCTSTR pszValue)
  89. {
  90. TCHAR szFmt[64], szProp[128];
  91. if (*pszPropName)
  92. LoadString(g_hinst, IDS_EXCOL_TEMPLATE, szFmt, SIZECHARS(szFmt));
  93. else
  94. lstrcpy(szFmt, TEXT("%s%s%s"));
  95. wnsprintf(szProp, ARRAYSIZE(szProp), szFmt, pszCRLF, pszPropName, pszValue);
  96. StrCatBuff(pszBuf, szProp, cch);
  97. }
  98. HRESULT CInfoTip::_GetInfoTipFromItem(WCHAR **ppszText)
  99. {
  100. TCHAR szTip[INFOTIPSIZE];
  101. szTip[0] = 0;
  102. IPropertyUI *ppui;
  103. if (SUCCEEDED(CoCreateInstance(CLSID_PropertiesUI, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IPropertyUI, &ppui))))
  104. {
  105. LPCTSTR pszCRLF = TEXT("");
  106. if (_pszPrefix)
  107. {
  108. _AppendTipText(szTip, ARRAYSIZE(szTip), pszCRLF, TEXT(""), _pszPrefix);
  109. pszCRLF = TEXT("\n");
  110. }
  111. UINT iCurrentExtra = 0;
  112. BOOL bContinue = TRUE;
  113. ULONG chEaten = 0; // gets incremented by ParsePropertyName
  114. while (bContinue)
  115. {
  116. SHCOLUMNID scid;
  117. BOOL bDoThisOne = TRUE;
  118. if (iCurrentExtra < _cscid)
  119. {
  120. scid = _rgcols[iCurrentExtra++];
  121. }
  122. else
  123. {
  124. if (SUCCEEDED(ppui->ParsePropertyName(_szText, &scid.fmtid, &scid.pid, &chEaten)))
  125. {
  126. bDoThisOne = !_InExtraList(&scid);
  127. }
  128. else
  129. {
  130. bContinue = FALSE;
  131. }
  132. }
  133. if (bContinue)
  134. {
  135. VARIANT v = {0};
  136. if (bDoThisOne && (S_OK == _psf->GetDetailsEx(_pidl, &scid, &v)))
  137. {
  138. TCHAR szPropName[128], szValue[128];
  139. ppui->FormatForDisplay(scid.fmtid, scid.pid, (PROPVARIANT*)&v, PUIFFDF_DEFAULT, szValue, ARRAYSIZE(szValue));
  140. if (IsEqualSCID(scid, SCID_Comment))
  141. {
  142. szPropName[0] = 0; // comment property, don't use the label
  143. }
  144. else
  145. {
  146. ppui->GetDisplayName(scid.fmtid, scid.pid, PUIFNF_DEFAULT, szPropName, ARRAYSIZE(szPropName));
  147. }
  148. // if we got a value, and that value is different from
  149. // the prefix of the current tip string we append it.
  150. // that is don't dupe the same string where the comment == name
  151. if (szValue[0] && (0 != StrCmpNI(szTip, szValue, lstrlen(szValue))))
  152. {
  153. _AppendTipText(szTip, ARRAYSIZE(szTip), pszCRLF, szPropName, szValue);
  154. pszCRLF = TEXT("\n");
  155. }
  156. VariantClear(&v);
  157. }
  158. }
  159. }
  160. ppui->Release();
  161. }
  162. return SHStrDup(szTip, ppszText);
  163. }
  164. STDMETHODIMP CInfoTip::GetInfoTip(DWORD dwFlags, WCHAR** ppszText)
  165. {
  166. HRESULT hr;
  167. if (_psf && _pidl)
  168. hr = _GetInfoTipFromItem(ppszText);
  169. else if (_szText[0])
  170. hr = SHStrDup(_szText, ppszText);
  171. else
  172. hr = E_FAIL;
  173. return hr;
  174. }
  175. STDMETHODIMP CInfoTip::GetInfoFlags(DWORD *pdwFlags)
  176. {
  177. *pdwFlags = 0;
  178. return E_NOTIMPL;
  179. }
  180. // ICustomizeInfoTip
  181. STDMETHODIMP CInfoTip::SetPrefixText(LPCWSTR pszPrefix)
  182. {
  183. Str_SetPtr(&_pszPrefix, pszPrefix);
  184. return S_OK;
  185. }
  186. // IParentAndItem
  187. STDMETHODIMP CInfoTip::SetParentAndItem(LPCITEMIDLIST pidlParent, IShellFolder *psf, LPCITEMIDLIST pidl)
  188. {
  189. if (psf)
  190. {
  191. ATOMICRELEASE(_psf);
  192. psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &_psf));
  193. }
  194. if (pidl)
  195. Pidl_Set(&_pidl, pidl);
  196. return _psf && _pidl ? S_OK : E_FAIL;
  197. }
  198. STDMETHODIMP CInfoTip::GetParentAndItem(LPITEMIDLIST *ppidlParent, IShellFolder **ppsf, LPITEMIDLIST *ppidl)
  199. {
  200. return E_NOTIMPL;
  201. }
  202. STDMETHODIMP CInfoTip::SetExtraProperties(const SHCOLUMNID *pscid, UINT cscid)
  203. {
  204. _cscid = min(cscid, ARRAYSIZE(_rgcols));
  205. CopyMemory(_rgcols, pscid, _cscid * sizeof(_rgcols[0]));
  206. return S_OK;
  207. }
  208. // in:
  209. // pszText - description of info tip. either
  210. // 1) a semi separated list of property names, "Author;Size" or "{fmtid},pid;{fmtid},pid"
  211. // 2) if no semis the tip to create
  212. // MAKEINTRESOURCE(id) of a resource ID
  213. STDAPI CreateInfoTipFromItem(IShellFolder2 *psf, LPCITEMIDLIST pidl, LPCWSTR pszText, REFIID riid, void **ppv)
  214. {
  215. HRESULT hr;
  216. CInfoTip* pit = new CInfoTip(psf, pidl, pszText);
  217. if (pit)
  218. {
  219. hr = pit->QueryInterface(riid, ppv);
  220. pit->Release();
  221. }
  222. else
  223. {
  224. hr = E_OUTOFMEMORY;
  225. *ppv = NULL;
  226. }
  227. return hr;
  228. }
  229. STDAPI CreateInfoTipFromText(LPCTSTR pszText, REFIID riid, void **ppv)
  230. {
  231. if (IS_INTRESOURCE(pszText))
  232. return CreateInfoTipFromItem(NULL, NULL, (LPCWSTR)pszText, riid, ppv);
  233. else
  234. {
  235. WCHAR szBuf[INFOTIPSIZE];
  236. SHTCharToUnicode(pszText, szBuf, ARRAYSIZE(szBuf));
  237. return CreateInfoTipFromItem(NULL, NULL, szBuf, riid, ppv);
  238. }
  239. }