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.

1068 lines
36 KiB

  1. #include "shellprv.h"
  2. #include "duiinfo.h"
  3. #include "ids.h"
  4. #include "datautil.h"
  5. DWORD FormatMessageArg(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageID, DWORD dwLangID,
  6. LPWSTR pwzBuffer, DWORD cchSize, ...)
  7. {
  8. va_list vaParamList;
  9. va_start(vaParamList, cchSize);
  10. DWORD dwResult = FormatMessageW(dwFlags, lpSource, dwMessageID, dwLangID, pwzBuffer,
  11. cchSize, &vaParamList);
  12. va_end(vaParamList);
  13. return dwResult;
  14. }
  15. CNameSpaceItemUIProperty::~CNameSpaceItemUIProperty()
  16. {
  17. }
  18. void CNameSpaceItemUIProperty::_SetParentAndItem(IShellFolder2 *psf, LPCITEMIDLIST pidl)
  19. {
  20. // set aliases to current values these are not refed
  21. // it is assumed that all helpers that use these variables won't be called
  22. // unless these have been set. since this can't fail this all works out fine
  23. m_psf = psf; // alias, not refed
  24. m_pidl = pidl; // alias, not cloned
  25. }
  26. STDMETHODIMP CNameSpaceItemUIProperty::GetPropertyDisplayName(SHCOLUMNID scid, WCHAR* pwszPropDisplayName, int cchPropDisplayName)
  27. {
  28. *pwszPropDisplayName = 0;
  29. CComPtr<IPropertyUI> spPropertyUI;
  30. HRESULT hr = _GetPropertyUI(&spPropertyUI);
  31. if (SUCCEEDED(hr))
  32. {
  33. hr = spPropertyUI->GetDisplayName(
  34. scid.fmtid,
  35. scid.pid,
  36. PUIFNF_DEFAULT,
  37. pwszPropDisplayName,
  38. cchPropDisplayName);
  39. }
  40. return hr;
  41. }
  42. STDMETHODIMP CNameSpaceItemUIProperty::GetPropertyDisplayValue(SHCOLUMNID scid, WCHAR* pszValue, int cch, PROPERTYUI_FORMAT_FLAGS flagsFormat)
  43. {
  44. *pszValue = 0;
  45. HRESULT hr = E_FAIL;
  46. // Use GetDisplayNameOf for the SCID_NAME property
  47. if (IsEqualSCID(scid, SCID_NAME))
  48. {
  49. hr = DisplayNameOf(m_psf, m_pidl, SHGDN_INFOLDER, pszValue, cch);
  50. }
  51. else
  52. { // Use GetDetailsEx to get the value
  53. CComVariant varPropDisplayValue;
  54. if (m_psf->GetDetailsEx(m_pidl, &scid, &varPropDisplayValue) == S_OK) // S_FALSE means property wasn't there.
  55. {
  56. if (IsEqualSCID(scid, SCID_SIZE) &&
  57. ((varPropDisplayValue.vt == VT_UI8) && (varPropDisplayValue.ullVal <= 0)))
  58. {
  59. hr = E_FAIL; // Don't display 0 byte sizes
  60. }
  61. else
  62. {
  63. CComPtr<IPropertyUI> spPropertyUI;
  64. hr = _GetPropertyUI(&spPropertyUI);
  65. if (SUCCEEDED(hr))
  66. {
  67. hr = spPropertyUI->FormatForDisplay(scid.fmtid, scid.pid,
  68. (PROPVARIANT*)&varPropDisplayValue, //cast from VARIANT to PROPVARIANT should be ok
  69. flagsFormat, pszValue, cch);
  70. }
  71. }
  72. }
  73. }
  74. return hr;
  75. }
  76. STDMETHODIMP CNameSpaceItemUIProperty::GetInfoString(SHCOLUMNID scid, WCHAR* pwszInfoString, int cchInfoString)
  77. {
  78. HRESULT hr = E_FAIL;
  79. // No DisplayName for the following properties
  80. if (IsEqualSCID(scid, SCID_NAME) ||
  81. IsEqualSCID(scid, SCID_TYPE) ||
  82. IsEqualSCID(scid, SCID_Comment))
  83. {
  84. hr = GetPropertyDisplayValue(scid, pwszInfoString, cchInfoString, PUIFFDF_DEFAULT);
  85. }
  86. else
  87. { // The other properties are in the format PropertyName: Value
  88. // Get the display name
  89. WCHAR wszPropertyDisplayName[50];
  90. hr = GetPropertyDisplayName(scid, wszPropertyDisplayName, ARRAYSIZE(wszPropertyDisplayName));
  91. if (SUCCEEDED(hr))
  92. {
  93. // Get the display value
  94. PROPERTYUI_FORMAT_FLAGS flagsFormat = IsEqualSCID(scid, SCID_WRITETIME) ? PUIFFDF_FRIENDLYDATE : PUIFFDF_DEFAULT;
  95. WCHAR wszPropertyDisplayValue[INTERNET_MAX_URL_LENGTH];
  96. hr = GetPropertyDisplayValue(scid, wszPropertyDisplayValue, ARRAYSIZE(wszPropertyDisplayValue), flagsFormat);
  97. // If the property display name or the property value is empty then we fail, so
  98. // this property will not be displayed.
  99. if (SUCCEEDED(hr))
  100. {
  101. hr = (wszPropertyDisplayName[0] && wszPropertyDisplayValue[0]) ? S_OK : E_FAIL;
  102. }
  103. // Now, combine the display name and value, seperated by a colon
  104. if (SUCCEEDED(hr))
  105. {
  106. // ShellConstructMessageString here to form the string
  107. WCHAR wszFormatStr[50];
  108. LoadStringW(HINST_THISDLL, IDS_COLONSEPERATED, wszFormatStr, ARRAYSIZE(wszFormatStr));
  109. if (FormatMessageArg(FORMAT_MESSAGE_FROM_STRING, wszFormatStr, 0, 0,
  110. pwszInfoString, cchInfoString, wszPropertyDisplayName,
  111. wszPropertyDisplayValue))
  112. {
  113. hr = S_OK;
  114. }
  115. else
  116. {
  117. hr = E_FAIL;
  118. }
  119. }
  120. }
  121. }
  122. return hr;
  123. }
  124. HRESULT CNameSpaceItemUIProperty::_GetPropertyUI(IPropertyUI **pppui)
  125. {
  126. HRESULT hr = E_FAIL;
  127. if (!m_spPropertyUI)
  128. {
  129. hr = SHCoCreateInstance(NULL, &CLSID_PropertiesUI, NULL, IID_PPV_ARG(IPropertyUI, &m_spPropertyUI));
  130. }
  131. *pppui = m_spPropertyUI;
  132. if (*pppui)
  133. {
  134. (*pppui)->AddRef();
  135. hr = S_OK;
  136. }
  137. return hr;
  138. }
  139. CNameSpaceItemInfoList::~CNameSpaceItemInfoList()
  140. {
  141. if (m_pDUIView)
  142. {
  143. m_pDUIView->SetDetailsInfoMsgWindowPtr(NULL, this);
  144. m_pDUIView->Release();
  145. }
  146. }
  147. STDMETHODIMP CNameSpaceItemInfoList::Create(CDUIView* pDUIView, Value* pvDetailsSheet,
  148. IShellItemArray *psiItemArray, Element** ppElement)
  149. {
  150. HRESULT hr;
  151. *ppElement = NULL;
  152. CNameSpaceItemInfoList* pNSIInfoList = HNewAndZero<CNameSpaceItemInfoList>();
  153. if (!pNSIInfoList)
  154. {
  155. hr = E_OUTOFMEMORY;
  156. }
  157. else
  158. {
  159. hr = pNSIInfoList->Initialize(pDUIView, pvDetailsSheet, psiItemArray);
  160. if (SUCCEEDED(hr))
  161. *ppElement = pNSIInfoList;
  162. else
  163. pNSIInfoList->Destroy();
  164. }
  165. return hr;
  166. }
  167. STDMETHODIMP CNameSpaceItemInfoList::Initialize(CDUIView* pDUIView, Value* pvDetailsSheet,
  168. IShellItemArray *psiItemArray)
  169. {
  170. HRESULT hr = Element::Initialize(0);
  171. if (SUCCEEDED(hr))
  172. {
  173. IDataObject *pdtobj = NULL;
  174. (m_pDUIView = pDUIView)->AddRef();
  175. Value* pvLayout = NULL;
  176. int arVLOptions[] = { FALSE, ALIGN_LEFT, ALIGN_JUSTIFY, ALIGN_TOP };
  177. hr = VerticalFlowLayout::Create(ARRAYSIZE(arVLOptions), arVLOptions, &pvLayout);
  178. if (SUCCEEDED(hr))
  179. {
  180. SetValue(LayoutProp, PI_Local, pvLayout);
  181. pvLayout->Release();
  182. }
  183. if (pvDetailsSheet)
  184. {
  185. SetValue(SheetProp, PI_Local, pvDetailsSheet);
  186. }
  187. // the HIDA format has 2 forms, one is each item in the array is a
  188. // fully qualified pidl. this is what the search folder produces
  189. // the other is the items are relative to a sigle folder pidl
  190. // the code below deals with both cases
  191. // Should just use use the ShellItemArray instead of getting the HIDA
  192. if (psiItemArray)
  193. {
  194. if (FAILED(psiItemArray->BindToHandler(NULL,BHID_DataObject,IID_PPV_ARG(IDataObject,&pdtobj))))
  195. {
  196. pdtobj = NULL;
  197. }
  198. }
  199. hr = S_OK;
  200. BOOL bDetailsAvailable = FALSE;
  201. if (pdtobj)
  202. {
  203. STGMEDIUM medium;
  204. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  205. if (pida)
  206. {
  207. IShellFolder2 *psfRoot;
  208. LPCITEMIDLIST pidlFolder = HIDA_GetPIDLFolder(pida);
  209. hr = SHBindToObjectEx(NULL, pidlFolder, NULL, IID_PPV_ARG(IShellFolder2, &psfRoot));
  210. if (SUCCEEDED(hr))
  211. {
  212. if (pida->cidl == 1)
  213. {
  214. LPCITEMIDLIST pidlItem = IDA_GetIDListPtr(pida, 0);
  215. IShellFolder2 *psf;
  216. LPCITEMIDLIST pidl;
  217. hr = SHBindToFolderIDListParent(psfRoot, pidlItem, IID_PPV_ARG(IShellFolder2, &psf), &pidl);
  218. if (SUCCEEDED(hr))
  219. {
  220. if (!SHGetAttributes(psf, pidl, SFGAO_ISSLOW | SFGAO_FOLDER) && m_pDUIView->ShouldShowMiniPreview())
  221. {
  222. _AddMiniPreviewerToList(psf, pidl);
  223. bDetailsAvailable = TRUE;
  224. }
  225. LPITEMIDLIST pidlFull;
  226. if (SUCCEEDED(SHILCombine(pidlFolder, pidlItem, &pidlFull)))
  227. {
  228. if (SUCCEEDED(m_pDUIView->InitializeDetailsInfo(
  229. CNameSpaceItemInfoList::WindowProc)))
  230. {
  231. m_pDUIView->SetDetailsInfoMsgWindowPtr(this, NULL);
  232. m_pDUIView->StartInfoExtraction(pidlFull);
  233. bDetailsAvailable = TRUE;
  234. }
  235. ILFree(pidlFull);
  236. }
  237. psf->Release();
  238. }
  239. }
  240. else
  241. {
  242. hr = _OnMultiSelect(psfRoot, pida);
  243. bDetailsAvailable = SUCCEEDED(hr);
  244. }
  245. psfRoot->Release();
  246. }
  247. HIDA_ReleaseStgMedium(pida, &medium);
  248. }
  249. pdtobj->Release();
  250. }
  251. if (!pdtobj || !bDetailsAvailable)
  252. {
  253. pDUIView->ShowDetails(FALSE);
  254. }
  255. }
  256. return hr;
  257. }
  258. LRESULT CALLBACK CNameSpaceItemInfoList::WindowProc(HWND hwnd, UINT uMsg,
  259. WPARAM wParam, LPARAM lParam)
  260. {
  261. CNameSpaceItemInfoList* pNSIInfoList = (CNameSpaceItemInfoList*)::GetWindowPtr(hwnd, 0);
  262. switch(uMsg)
  263. {
  264. case WM_DESTROY:
  265. // ignore late messages
  266. {
  267. MSG msg;
  268. while (PeekMessage(&msg, hwnd, WM_DETAILS_INFO, WM_DETAILS_INFO, PM_REMOVE))
  269. {
  270. if (msg.lParam)
  271. {
  272. CDetailsInfoList* pDetailsInfoList = (CDetailsInfoList*)msg.lParam;
  273. // The destructor will do the necessary cleanup
  274. delete pDetailsInfoList;
  275. }
  276. }
  277. SetWindowPtr(hwnd, 0, NULL);
  278. }
  279. break;
  280. case WM_DETAILS_INFO:
  281. {
  282. // Check that pDetailsInfo is still alive and that you have a CDetailsInfoList object of the requested pidl
  283. CDetailsInfoList* pDetailsInfoList = (CDetailsInfoList*)lParam;
  284. if (pDetailsInfoList && pNSIInfoList
  285. && (wParam == pNSIInfoList->m_pDUIView->_dwDetailsInfoID))
  286. {
  287. BOOL fShow = FALSE;
  288. StartDefer();
  289. Element * peDetailsInfoArea = pNSIInfoList->GetParent();
  290. if (peDetailsInfoArea)
  291. {
  292. peDetailsInfoArea->RemoveLocalValue(HeightProp);
  293. }
  294. for (int i = 0; i < pDetailsInfoList->_nProperties; i++)
  295. {
  296. if (!pDetailsInfoList->_diProperty[i].bstrValue)
  297. {
  298. continue;
  299. }
  300. // 253647 - surpress the comment field from showing in the
  301. // Details section. Note, I left the support for Comments
  302. // in the code below because this decision might be reversed.
  303. if (IsEqualSCID(pDetailsInfoList->_diProperty[i].scid, SCID_Comment))
  304. {
  305. continue;
  306. }
  307. WCHAR wszInfoString[INTERNET_MAX_URL_LENGTH];
  308. wszInfoString[0] = L'\0';
  309. SHCOLUMNID scid = pDetailsInfoList->_diProperty[i].scid;
  310. // No DisplayName if we don't have one
  311. // or if it is one of the following properties
  312. if ((!pDetailsInfoList->_diProperty[i].bstrDisplayName)
  313. || ( IsEqualSCID(scid, SCID_NAME)
  314. || IsEqualSCID(scid, SCID_TYPE)
  315. || IsEqualSCID(scid, SCID_Comment) ))
  316. {
  317. StrCpyNW(wszInfoString, pDetailsInfoList->_diProperty[i].bstrValue, ARRAYSIZE(wszInfoString));
  318. }
  319. else
  320. {
  321. // Now, combine the display name and value, seperated by a colon
  322. // ShellConstructMessageString here to form the string
  323. WCHAR wszFormatStr[50];
  324. LoadStringW(HINST_THISDLL, IDS_COLONSEPERATED, wszFormatStr, ARRAYSIZE(wszFormatStr));
  325. FormatMessageArg(FORMAT_MESSAGE_FROM_STRING, wszFormatStr, 0, 0,
  326. wszInfoString, ARRAYSIZE(wszInfoString),
  327. pDetailsInfoList->_diProperty[i].bstrDisplayName,
  328. pDetailsInfoList->_diProperty[i].bstrValue);
  329. }
  330. if (wszInfoString[0])
  331. {
  332. Element* pElement;
  333. HRESULT hr = CNameSpaceItemInfo::Create(wszInfoString, &pElement);
  334. if (SUCCEEDED(hr))
  335. {
  336. hr = pNSIInfoList->Add(pElement);
  337. if (IsEqualSCID(scid, SCID_NAME))
  338. {
  339. pElement->SetID(L"InfoName");
  340. }
  341. else if (IsEqualSCID(scid, SCID_TYPE))
  342. {
  343. pElement->SetID(L"InfoType");
  344. }
  345. else if (IsEqualSCID(scid, SCID_Comment))
  346. {
  347. pElement->SetID(L"InfoTip");
  348. }
  349. fShow = TRUE;
  350. }
  351. }
  352. }
  353. pNSIInfoList->m_pDUIView->ShowDetails(fShow);
  354. EndDefer();
  355. }
  356. if (pDetailsInfoList)
  357. {
  358. delete pDetailsInfoList; // The destructor will do the necessary cleanup
  359. }
  360. break;
  361. }
  362. default:
  363. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  364. }
  365. return (LRESULT)0;
  366. }
  367. HRESULT CNameSpaceItemInfoList::_AddMiniPreviewerToList(IShellFolder2 *psf, LPCITEMIDLIST pidl)
  368. {
  369. Element* pElement;
  370. HRESULT hr = CMiniPreviewer::Create(m_pDUIView, psf, pidl, &pElement);
  371. if (SUCCEEDED(hr))
  372. {
  373. hr = Add(pElement);
  374. }
  375. return E_NOTIMPL;
  376. }
  377. #define MAX_FILES_FOR_COMPUTING_SIZE 100
  378. HRESULT CNameSpaceItemInfoList::_OnMultiSelect(IShellFolder2 *psfRoot, LPIDA pida)
  379. {
  380. WCHAR wszText[INTERNET_MAX_URL_LENGTH];
  381. // Get the format string for n selection text
  382. WCHAR wszFormatStr[128];
  383. LoadStringW(HINST_THISDLL, IDS_NSELECTED, wszFormatStr, ARRAYSIZE(wszFormatStr));
  384. // Now, form the n selection text
  385. wnsprintfW(wszText, ARRAYSIZE(wszText), wszFormatStr, pida->cidl);
  386. WCHAR wszTemp[MAX_PATH];
  387. wszTemp[0] = 0;
  388. CComPtr<IPropertyUI> spPropertyUI;
  389. HRESULT hr = _GetPropertyUI(&spPropertyUI);
  390. if (SUCCEEDED(hr))
  391. {
  392. ULONGLONG ullSizeTotal = 0;
  393. if (pida->cidl <= MAX_FILES_FOR_COMPUTING_SIZE)
  394. {
  395. // Compute the total size and the names of the selected files
  396. for (UINT i = 0; i < pida->cidl; i++)
  397. {
  398. IShellFolder2 *psf;
  399. LPCITEMIDLIST pidl;
  400. hr = SHBindToFolderIDListParent(psfRoot, IDA_GetIDListPtr(pida, i), IID_PPV_ARG(IShellFolder2, &psf), &pidl);
  401. if (SUCCEEDED(hr))
  402. {
  403. ULONGLONG ullSize;
  404. if (SUCCEEDED(GetLongProperty(psf, pidl, &SCID_SIZE, &ullSize)))
  405. {
  406. ullSizeTotal += ullSize;
  407. }
  408. psf->Release();
  409. }
  410. }
  411. }
  412. // Get the display string for Total Size
  413. if (ullSizeTotal > 0)
  414. {
  415. // Convert ullSizeTotal to a string
  416. PROPVARIANT propvar;
  417. propvar.vt = VT_UI8;
  418. propvar.uhVal.QuadPart = ullSizeTotal;
  419. WCHAR wszFormattedTotalSize[128];
  420. if (SUCCEEDED(spPropertyUI->FormatForDisplay(SCID_SIZE.fmtid, SCID_SIZE.pid,
  421. &propvar, PUIFFDF_DEFAULT, wszFormattedTotalSize,
  422. ARRAYSIZE(wszFormattedTotalSize))))
  423. {
  424. // Get the format string for Total File Size text
  425. LoadStringW(HINST_THISDLL, IDS_TOTALFILESIZE, wszFormatStr, ARRAYSIZE(wszFormatStr));
  426. // Now, form the Total File Size text
  427. wnsprintfW(wszTemp, ARRAYSIZE(wszTemp), wszFormatStr, wszFormattedTotalSize);
  428. }
  429. }
  430. }
  431. if (wszTemp[0])
  432. {
  433. // Append two line breaks
  434. StrCatBuffW(wszText, L"\n\n", ARRAYSIZE(wszText));
  435. // Append the Total Size string
  436. StrCatBuffW(wszText, wszTemp, ARRAYSIZE(wszText));
  437. }
  438. // Now make a dui gadget for wszText
  439. Element* pElement;
  440. if (SUCCEEDED(CNameSpaceItemInfo::Create(wszText, &pElement)))
  441. {
  442. Add(pElement);
  443. }
  444. return S_OK;
  445. }
  446. IClassInfo* CNameSpaceItemInfoList::Class = NULL;
  447. HRESULT CNameSpaceItemInfoList::Register()
  448. {
  449. return ClassInfo<CNameSpaceItemInfoList,Element>::Register(L"NameSpaceItemInfoList", NULL, 0);
  450. }
  451. STDMETHODIMP CNameSpaceItemInfo::Create(WCHAR* pwszInfoString, Element** ppElement)
  452. {
  453. *ppElement = NULL;
  454. HRESULT hr = E_FAIL;
  455. CNameSpaceItemInfo* pNSIInfo = HNewAndZero<CNameSpaceItemInfo>();
  456. if (!pNSIInfo)
  457. {
  458. hr = E_OUTOFMEMORY;
  459. }
  460. else
  461. {
  462. hr = pNSIInfo->Initialize(pwszInfoString);
  463. if (SUCCEEDED(hr))
  464. *ppElement = pNSIInfo;
  465. else
  466. pNSIInfo->Destroy();
  467. }
  468. return hr;
  469. }
  470. STDMETHODIMP CNameSpaceItemInfo::Initialize(WCHAR* pwszInfoString)
  471. {
  472. HRESULT hr = Element::Initialize(0);
  473. if (SUCCEEDED(hr))
  474. {
  475. hr = SetContentString(pwszInfoString);
  476. }
  477. return hr;
  478. }
  479. IClassInfo* CNameSpaceItemInfo::Class = NULL;
  480. HRESULT CNameSpaceItemInfo::Register()
  481. {
  482. return ClassInfo<CNameSpaceItemInfo,Element>::Register(L"NameSpaceItemInfo", NULL, 0);
  483. }
  484. STDMETHODIMP CBitmapElement::Create(HBITMAP hBitmap, Element** ppElement)
  485. {
  486. *ppElement = NULL;
  487. HRESULT hr;
  488. CBitmapElement* pBitmapElement = HNewAndZero<CBitmapElement>();
  489. if (!pBitmapElement)
  490. {
  491. hr = E_OUTOFMEMORY;
  492. }
  493. else
  494. {
  495. hr = pBitmapElement->Initialize(hBitmap);
  496. if (SUCCEEDED(hr))
  497. *ppElement = pBitmapElement;
  498. else
  499. pBitmapElement->Destroy();
  500. }
  501. return hr;
  502. }
  503. STDMETHODIMP CBitmapElement::Initialize(HBITMAP hBitmap)
  504. {
  505. HRESULT hr = Element::Initialize(0);
  506. if (SUCCEEDED(hr))
  507. {
  508. if (hBitmap)
  509. {
  510. Value* pGraphic = Value::CreateGraphic(hBitmap);
  511. if (pGraphic)
  512. {
  513. SetValue(ContentProp, PI_Local, pGraphic);
  514. pGraphic->Release();
  515. }
  516. }
  517. }
  518. return hr;
  519. }
  520. IClassInfo* CBitmapElement::Class = NULL;
  521. HRESULT CBitmapElement::Register()
  522. {
  523. return ClassInfo<CBitmapElement,Element>::Register(L"BitmapElement", NULL, 0);
  524. }
  525. CMiniPreviewer::~CMiniPreviewer()
  526. {
  527. // We are going away
  528. if (m_pDUIView)
  529. {
  530. m_pDUIView->SetThumbnailMsgWindowPtr(NULL, this);
  531. m_pDUIView->Release();
  532. }
  533. }
  534. STDMETHODIMP CMiniPreviewer::Create(CDUIView* pDUIView, IShellFolder2* psf, LPCITEMIDLIST pidl, Element** ppElement)
  535. {
  536. HRESULT hr;
  537. *ppElement = NULL;
  538. CMiniPreviewer* pMiniPreviewer = HNewAndZero<CMiniPreviewer>();
  539. if (!pMiniPreviewer)
  540. {
  541. hr = E_OUTOFMEMORY;
  542. }
  543. else
  544. {
  545. hr = pMiniPreviewer->Initialize(pDUIView, psf, pidl);
  546. if (SUCCEEDED(hr))
  547. *ppElement = pMiniPreviewer;
  548. else
  549. pMiniPreviewer->Destroy();
  550. }
  551. return hr;
  552. }
  553. STDMETHODIMP CMiniPreviewer::Initialize(CDUIView* pDUIView, IShellFolder2 *psf, LPCITEMIDLIST pidl)
  554. {
  555. HRESULT hr = Element::Initialize(0);
  556. if (SUCCEEDED(hr))
  557. {
  558. (m_pDUIView = pDUIView)->AddRef();
  559. LPITEMIDLIST pidlFull;
  560. if (SUCCEEDED(SHFullIDListFromFolderAndItem(psf, pidl, &pidlFull)))
  561. {
  562. if (SUCCEEDED(m_pDUIView->InitializeThumbnail(CMiniPreviewer::WindowProc)))
  563. {
  564. m_pDUIView->SetThumbnailMsgWindowPtr(this, NULL);
  565. m_pDUIView->StartBitmapExtraction(pidlFull);
  566. }
  567. ILFree(pidlFull);
  568. }
  569. }
  570. return hr;
  571. }
  572. // Window procedure for catching the "image-extraction-done" message
  573. // from m_pDUIView->_spThumbnailExtractor2
  574. LRESULT CALLBACK CMiniPreviewer::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  575. {
  576. CMiniPreviewer* pMiniPreviewer = (CMiniPreviewer*)::GetWindowPtr(hwnd, 0);
  577. switch(uMsg)
  578. {
  579. case WM_DESTROY:
  580. // ignore late messages
  581. {
  582. MSG msg;
  583. while (PeekMessage(&msg, hwnd, WM_HTML_BITMAP, WM_HTML_BITMAP, PM_REMOVE))
  584. {
  585. if (msg.lParam)
  586. {
  587. DeleteObject((HBITMAP)msg.lParam);
  588. }
  589. }
  590. SetWindowPtr(hwnd, 0, NULL);
  591. }
  592. break;
  593. case WM_HTML_BITMAP:
  594. // Check that pMiniPreviewer is still alive and that you have an HBITMAP of the requested pidl
  595. if (pMiniPreviewer && (wParam == pMiniPreviewer->m_pDUIView->_dwThumbnailID))
  596. {
  597. if (lParam) // This is the HBITMAP of the extracted image
  598. {
  599. Element* pElement;
  600. HRESULT hr = CBitmapElement::Create((HBITMAP)lParam, &pElement);
  601. if (SUCCEEDED(hr))
  602. {
  603. // The addition of the thumbnail comes in late. DUI is
  604. // not currently set up to handle a DisableAnimations()/
  605. // EnableAnimations() here, which we were originally
  606. // doing to prevent jumpiness. This was discovered in
  607. // RAID 389343, because our coming off the background
  608. // thread and calling DisableAnimations() was screwing up
  609. // other animations that were already underway. Talking
  610. // with markfi, the problem is understood BUT not one to
  611. // be fixed because of the negative perf impact it would
  612. // have on DUI. So instead we'll StartDefer()/EndDefer()
  613. // to minimize jumpiness from our two layout ops below.
  614. StartDefer();
  615. // Set the VerticalFlowLayout for our element. Otherwise,
  616. // our control will not render.
  617. Value* pvLayout = NULL;
  618. hr = FillLayout::Create(0, NULL, &pvLayout);
  619. if (SUCCEEDED(hr))
  620. {
  621. hr = pMiniPreviewer->SetValue(LayoutProp, PI_Local, pvLayout);
  622. if (SUCCEEDED(hr))
  623. {
  624. hr = pMiniPreviewer->Add(pElement);
  625. }
  626. pvLayout->Release();
  627. }
  628. if (FAILED(hr))
  629. {
  630. pElement->Destroy();
  631. }
  632. EndDefer();
  633. }
  634. else
  635. {
  636. DeleteObject((HBITMAP)lParam);
  637. }
  638. }
  639. }
  640. else if (lParam) // This extraction got done too late.
  641. // So, just delete the wasted HBITMAP.
  642. {
  643. DeleteObject((HBITMAP)lParam);
  644. }
  645. break;
  646. default:
  647. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  648. }
  649. return (LRESULT)0;
  650. }
  651. IClassInfo* CMiniPreviewer::Class = NULL;
  652. HRESULT CMiniPreviewer::Register()
  653. {
  654. return ClassInfo<CMiniPreviewer,Element>::Register(L"MiniPreviewer", NULL, 0);
  655. }
  656. // ***** CDetailsInfoList *******
  657. CDetailsInfoList::CDetailsInfoList() : _nProperties(0)
  658. {
  659. }
  660. CDetailsInfoList::~CDetailsInfoList()
  661. {
  662. for (int i = 0; i < _nProperties; i++)
  663. {
  664. if (_diProperty[i].bstrValue)
  665. {
  666. SysFreeString(_diProperty[i].bstrValue);
  667. }
  668. if (_diProperty[i].bstrDisplayName)
  669. {
  670. SysFreeString(_diProperty[i].bstrDisplayName);
  671. }
  672. }
  673. }
  674. // ***** CDetailsSectionInfoTask *******
  675. CDetailsSectionInfoTask::CDetailsSectionInfoTask(HRESULT *phr,
  676. IShellFolder *psfContaining,
  677. LPCITEMIDLIST pidlAbsolute,
  678. HWND hwndMsg,
  679. UINT uMsg,
  680. DWORD dwDetailsInfoID)
  681. : CRunnableTask(RTF_DEFAULT),
  682. _hwndMsg(hwndMsg),
  683. _uMsg(uMsg),
  684. _dwDetailsInfoID(dwDetailsInfoID)
  685. {
  686. ASSERT(psfContaining && pidlAbsolute && hwndMsg);
  687. _psfContaining = psfContaining;
  688. _psfContaining->AddRef();
  689. *phr = SHILClone(pidlAbsolute, &_pidlAbsolute);
  690. }
  691. CDetailsSectionInfoTask::~CDetailsSectionInfoTask()
  692. {
  693. _psfContaining->Release();
  694. ILFree(_pidlAbsolute);
  695. }
  696. HRESULT CDetailsSectionInfoTask_CreateInstance(IShellFolder *psfContaining,
  697. LPCITEMIDLIST pidlAbsolute,
  698. HWND hwndMsg,
  699. UINT uMsg,
  700. DWORD dwDetailsInfoID,
  701. CDetailsSectionInfoTask **ppTask)
  702. {
  703. *ppTask = NULL;
  704. HRESULT hr;
  705. CDetailsSectionInfoTask* pNewTask = new CDetailsSectionInfoTask(
  706. &hr,
  707. psfContaining,
  708. pidlAbsolute,
  709. hwndMsg,
  710. uMsg,
  711. dwDetailsInfoID);
  712. if (pNewTask)
  713. {
  714. if (SUCCEEDED(hr))
  715. *ppTask = pNewTask;
  716. else
  717. pNewTask->Release();
  718. }
  719. else
  720. {
  721. hr = E_OUTOFMEMORY;
  722. }
  723. return hr;
  724. }
  725. STDMETHODIMP CDetailsSectionInfoTask::RunInitRT()
  726. {
  727. ASSERT(_pidlAbsolute);
  728. BOOL bMsgPosted = FALSE;
  729. HRESULT hr = E_FAIL;
  730. CDetailsInfoList* pCDetailsInfoList = new CDetailsInfoList;
  731. if (!pCDetailsInfoList)
  732. {
  733. hr = E_OUTOFMEMORY;
  734. }
  735. else
  736. {
  737. CComPtr<IShellFolder2> psf2;
  738. LPCITEMIDLIST pidlLast;
  739. hr = SHBindToIDListParent(_pidlAbsolute, IID_PPV_ARG(IShellFolder2, &psf2), &pidlLast);
  740. if (SUCCEEDED(hr))
  741. {
  742. _SetParentAndItem(psf2, pidlLast);
  743. WCHAR wszProperties[MAX_PATH];
  744. hr = _GetDisplayedDetailsProperties(psf2, pidlLast, wszProperties, ARRAYSIZE(wszProperties));
  745. if (SUCCEEDED(hr))
  746. {
  747. // pwszProperties is usually of the form "prop:Name;Type;Author"
  748. CComPtr<IPropertyUI> spPropertyUI;
  749. hr = _GetPropertyUI(&spPropertyUI);
  750. if (SUCCEEDED(hr))
  751. {
  752. SHCOLUMNID scid;
  753. WCHAR wszInfoString[INTERNET_MAX_URL_LENGTH];
  754. ULONG chEaten = 0; // loop var, incremented by ParsePropertyName
  755. for (pCDetailsInfoList->_nProperties = 0;
  756. pCDetailsInfoList->_nProperties < ARRAYSIZE(pCDetailsInfoList->_diProperty)
  757. && SUCCEEDED(spPropertyUI->ParsePropertyName(wszProperties, &scid.fmtid, &scid.pid, &chEaten));
  758. pCDetailsInfoList->_nProperties++)
  759. {
  760. pCDetailsInfoList->_diProperty[pCDetailsInfoList->_nProperties].scid = scid;
  761. PROPERTYUI_FORMAT_FLAGS flagsFormat = IsEqualSCID(scid, SCID_WRITETIME) ? PUIFFDF_FRIENDLYDATE : PUIFFDF_DEFAULT;
  762. // Get the display value
  763. hr = GetPropertyDisplayValue(scid, wszInfoString, ARRAYSIZE(wszInfoString), flagsFormat);
  764. if (SUCCEEDED(hr) && wszInfoString[0])
  765. {
  766. pCDetailsInfoList->_diProperty[pCDetailsInfoList->_nProperties].bstrValue = SysAllocString(wszInfoString);
  767. }
  768. // Get the display name
  769. hr = GetPropertyDisplayName(scid, wszInfoString, ARRAYSIZE(wszInfoString));
  770. if (SUCCEEDED(hr) && wszInfoString[0])
  771. {
  772. pCDetailsInfoList->_diProperty[pCDetailsInfoList->_nProperties].bstrDisplayName = SysAllocString(wszInfoString);
  773. }
  774. }
  775. //The extraction is done. Now post a message.
  776. if (PostMessage(_hwndMsg, WM_DETAILS_INFO,
  777. (WPARAM)_dwDetailsInfoID, (LPARAM)pCDetailsInfoList))
  778. {
  779. bMsgPosted = TRUE;
  780. }
  781. }
  782. }
  783. }
  784. }
  785. if (!bMsgPosted && pCDetailsInfoList)
  786. {
  787. delete pCDetailsInfoList;
  788. }
  789. return S_OK;
  790. }
  791. HRESULT CDetailsSectionInfoTask::_GetDisplayedDetailsProperties(IShellFolder2* psf,
  792. LPCITEMIDLIST pidl,
  793. WCHAR* pwszProperties,
  794. int cchProperties)
  795. {
  796. HRESULT hr = GetStringProperty(psf, pidl, &SCID_DetailsProperties, pwszProperties, cchProperties);
  797. if (FAILED(hr)) // Default properties
  798. {
  799. if (SHGetAttributes(psf, pidl, SFGAO_ISSLOW))
  800. {
  801. // SCID_NAME;SCID_TYPE
  802. StrCpyNW(pwszProperties, L"prop:Name;Type", cchProperties);
  803. }
  804. else
  805. {
  806. // SCID_NAME;SCID_TYPE;SCID_ATTRIBUTES_DESCRIPTION;SCID_Comment;SCID_WRITETIME;SCID_SIZE;SCID_Author;SCID_CSC_STATUS
  807. StrCpyNW(pwszProperties, L"prop:Name;Type;AttributesDescription;DocComments;Write;Size;DocAuthor;CSCStatus", cchProperties);
  808. }
  809. }
  810. // Augment properties to include "Location" if in CLSID_DocFindFolder.
  811. IPersist *pPersist;
  812. ASSERT(_psfContaining);
  813. if (SUCCEEDED(_psfContaining->QueryInterface(IID_IPersist, (void**)&pPersist)))
  814. {
  815. CLSID clsid;
  816. if (SUCCEEDED(pPersist->GetClassID(&clsid)) && IsEqualCLSID(clsid, CLSID_DocFindFolder))
  817. _AugmentDisplayedDetailsProperties(pwszProperties, cchProperties);
  818. pPersist->Release();
  819. }
  820. return S_OK;
  821. }
  822. void CDetailsSectionInfoTask::_AugmentDisplayedDetailsProperties(LPWSTR pszDetailsProperties, size_t cchDetailsProperties)
  823. {
  824. static WCHAR szDeclarator[] = L"prop:";
  825. static size_t lenDeclarator = lstrlen(szDeclarator);
  826. static WCHAR szName[64] = { 0 };
  827. static size_t lenName = 0;
  828. static WCHAR szType[64] = { 0 };
  829. static size_t lenType = 0;
  830. static WCHAR szDirectory[64] = { 0 };
  831. static size_t lenDirectory = 0;
  832. // Initialize statics once 'n only once.
  833. if (!szName[0] || !szType[0] || !szDirectory[0])
  834. {
  835. HRESULT hr;
  836. hr = SCIDCannonicalName((SHCOLUMNID *)&SCID_NAME, szName, ARRAYSIZE(szName));
  837. ASSERT(SUCCEEDED(hr));
  838. lenName = lstrlen(szName);
  839. hr = SCIDCannonicalName((SHCOLUMNID *)&SCID_TYPE, szType, ARRAYSIZE(szType));
  840. ASSERT(SUCCEEDED(hr));
  841. lenType = lstrlen(szType);
  842. hr = SCIDCannonicalName((SHCOLUMNID *)&SCID_DIRECTORY, szDirectory, ARRAYSIZE(szDirectory));
  843. ASSERT(SUCCEEDED(hr));
  844. lenDirectory = lstrlen(szDirectory);
  845. }
  846. // Attempt to merge the "Directory" property, in the following ways:
  847. // "prop:Name;Type;Directory;..."
  848. // "prop:Name;Directory;..."
  849. // "prop:Directory;..."
  850. //
  851. size_t lenDetailsProperties = lstrlen(pszDetailsProperties);
  852. size_t lenMerged = lenDetailsProperties + 1 + lenDirectory;
  853. if (lenMerged < cchDetailsProperties && 0 == StrCmpNI(pszDetailsProperties, szDeclarator, lenDeclarator))
  854. {
  855. // Search for "Directory" property (in case it is already specified).
  856. if (!_SearchDisplayedDetailsProperties(pszDetailsProperties, lenDetailsProperties, szDirectory, lenDirectory))
  857. {
  858. // Allocate a temporary buffer to merge into.
  859. size_t cchMerged = cchDetailsProperties;
  860. LPWSTR pszMerged = new WCHAR[cchMerged];
  861. if (pszMerged)
  862. {
  863. // Determine offset in pszDetailsProperties to merge at.
  864. size_t offsetInsert;
  865. if (lenDeclarator < lenDetailsProperties)
  866. {
  867. // Search for "Name" property.
  868. LPWSTR pszName = _SearchDisplayedDetailsProperties(
  869. &pszDetailsProperties[lenDeclarator],
  870. lenDetailsProperties - lenDeclarator,
  871. szName,
  872. lenName);
  873. if (pszName)
  874. {
  875. // Search for "Type" property (immediately following "Name").
  876. size_t offsetName = (pszName - pszDetailsProperties);
  877. size_t offsetType = offsetName + lenName + 1;
  878. size_t offsetRemainder = offsetType + lenType;
  879. if ((offsetRemainder == lenDetailsProperties || (offsetRemainder < lenDetailsProperties && pszDetailsProperties[offsetRemainder] == ';')) &&
  880. !StrCmpNI(&pszDetailsProperties[offsetType], szType, lenType))
  881. {
  882. offsetInsert = offsetRemainder;
  883. }
  884. else
  885. offsetInsert = offsetName + lenName;
  886. }
  887. else
  888. offsetInsert = lenDeclarator;
  889. }
  890. else
  891. offsetInsert = lenDeclarator;
  892. // Merge the "Directory" property.
  893. StrCpyN(pszMerged, pszDetailsProperties, offsetInsert + 1); // + 1 to account for null terminator.
  894. if (offsetInsert > lenDeclarator)
  895. StrCatBuff(pszMerged, L";", cchMerged); // ';' prepend if necessary
  896. StrCatBuff(pszMerged, szDirectory, cchMerged); // "Directory"
  897. if (offsetInsert < lenDetailsProperties)
  898. {
  899. if (pszDetailsProperties[offsetInsert] != ';')
  900. StrCatBuff(pszMerged, L";", cchMerged); // ';' append if necessary
  901. StrCatBuff(pszMerged, &pszDetailsProperties[offsetInsert], cchMerged);
  902. }
  903. // Update in/out pszDetailsProperties.
  904. StrCpyN(pszDetailsProperties, pszMerged, cchDetailsProperties);
  905. ASSERT(lenMerged == lstrlen(pszMerged));
  906. ASSERT(lenMerged < cchDetailsProperties);
  907. delete[] pszMerged;
  908. }
  909. }
  910. }
  911. else
  912. {
  913. // Invalid format.
  914. ASSERT(FALSE);
  915. }
  916. }
  917. LPWSTR CDetailsSectionInfoTask::_SearchDisplayedDetailsProperties(LPWSTR pszDetailsProperties, size_t lenDetailsProperties, LPWSTR pszProperty, size_t lenProperty)
  918. {
  919. LPWSTR psz = StrStrI(pszDetailsProperties, pszProperty);
  920. while (psz)
  921. {
  922. // Check start...
  923. if (psz == pszDetailsProperties || psz[-1] == ';')
  924. {
  925. // ... and end.
  926. size_t lenToEndOfProperty = (psz - pszDetailsProperties) + lenProperty;
  927. if (lenToEndOfProperty == lenDetailsProperties || pszDetailsProperties[lenToEndOfProperty] == ';')
  928. break;
  929. }
  930. psz = StrStrI(psz + lenProperty, pszProperty);
  931. }
  932. return psz;
  933. }