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.

1213 lines
37 KiB

  1. #include "shellprv.h"
  2. #include "defviewp.h"
  3. #include "ViewState.h"
  4. CViewState::CViewState()
  5. {
  6. ASSERT(_lParamSort == NULL);
  7. ASSERT(_iDirection == 0);
  8. ASSERT(_iLastColumnClick == 0);
  9. ASSERT(_ViewMode == 0);
  10. ASSERT(_ptScroll.x == 0 && _ptScroll.y == 0);
  11. ASSERT(_guidGroupID == GUID_NULL);
  12. ASSERT(_scidDetails.fmtid == GUID_NULL);
  13. ASSERT(_scidDetails.pid == 0);
  14. ASSERT(_hdsaColumnOrder == NULL);
  15. ASSERT(_hdsaColumnWidths == NULL);
  16. ASSERT(_hdsaColumnStates == NULL);
  17. ASSERT(_hdpaItemPos == NULL);
  18. ASSERT(_pbPositionData == NULL);
  19. _iDirection = 1;
  20. _fFirstViewed = TRUE; // Assume this is the first time we are looking at a folder.
  21. }
  22. CViewState::~CViewState()
  23. {
  24. if (_hdsaColumnOrder)
  25. DSA_Destroy(_hdsaColumnOrder);
  26. if (_hdsaColumnWidths)
  27. DSA_Destroy(_hdsaColumnWidths);
  28. if (_hdsaColumnStates)
  29. DSA_Destroy(_hdsaColumnStates);
  30. if (_hdsaColumns)
  31. DSA_Destroy(_hdsaColumns);
  32. ClearPositionData();
  33. LocalFree(_pbPositionData); // accepts NULL
  34. }
  35. // When initializing a new DefView, see if we can
  36. // propogate information from the previous one.
  37. void CViewState::InitFromPreviousView(IUnknown* pPrevView)
  38. {
  39. CDefView *pdsvPrev;
  40. if (SUCCEEDED(pPrevView->QueryInterface(IID_PPV_ARG(CDefView, &pdsvPrev))))
  41. {
  42. // preserve stuff like sort order
  43. _lParamSort = pdsvPrev->_vs._lParamSort;
  44. _iDirection = pdsvPrev->_vs._iDirection;
  45. _iLastColumnClick = pdsvPrev->_vs._iLastColumnClick;
  46. pdsvPrev->Release();
  47. }
  48. }
  49. void CViewState::InitFromHeader(DVSAVEHEADER_COMBO* pdv)
  50. {
  51. _lParamSort = pdv->dvSaveHeader.dvState.lParamSort;
  52. _iDirection = pdv->dvSaveHeader.dvState.iDirection;
  53. // Patch this up. I guess at one time we persisted this wrong.
  54. if (_iDirection == 0)
  55. _iDirection = 1;
  56. _iLastColumnClick = pdv->dvSaveHeader.dvState.iLastColumnClick;
  57. _ViewMode = pdv->dvSaveHeader.ViewMode;
  58. _ptScroll = pdv->dvSaveHeader.ptScroll;
  59. }
  60. void CViewState::GetDefaults(CDefView* pdv, LPARAM* plParamSort, int* piDirection, int* piLastColumnClick)
  61. {
  62. SHELLSTATE ss;
  63. SHGetSetSettings(&ss, SSF_SORTCOLUMNS, FALSE);
  64. if (plParamSort)
  65. *plParamSort = ss.lParamSort;
  66. if (piDirection)
  67. *piDirection = ss.iSortDirection ? ss.iSortDirection : 1;
  68. if (piLastColumnClick)
  69. *piLastColumnClick = -1;
  70. pdv->CallCB(SFVM_GETSORTDEFAULTS, (LPARAM)piDirection, (WPARAM)plParamSort);
  71. }
  72. void CViewState::InitWithDefaults(CDefView* pdv)
  73. {
  74. GetDefaults(pdv, &_lParamSort, &_iDirection, &_iLastColumnClick);
  75. }
  76. int CALLBACK CViewState::_SavedItemCompare(void *p1, void *p2, LPARAM lParam)
  77. {
  78. CDefView *pdv = reinterpret_cast<CDefView*>(lParam);
  79. UNALIGNED VIEWSTATE_POSITION *pdvi1 = (UNALIGNED VIEWSTATE_POSITION *)p1;
  80. UNALIGNED VIEWSTATE_POSITION *pdvi2 = (UNALIGNED VIEWSTATE_POSITION *)p2;
  81. // manually terminate these pidls because they are packed together
  82. // in the save buffer
  83. LPITEMIDLIST pFakeEnd1 = _ILNext(&pdvi1->idl);
  84. USHORT uSave1 = pFakeEnd1->mkid.cb;
  85. pFakeEnd1->mkid.cb = 0;
  86. LPITEMIDLIST pFakeEnd2 = _ILNext(&pdvi2->idl);
  87. USHORT uSave2 = pFakeEnd2->mkid.cb;
  88. pFakeEnd2->mkid.cb = 0;
  89. int nCmp = pdv->_Compare(&pdvi1->idl, &pdvi2->idl, reinterpret_cast<LPARAM>(pdv));
  90. pFakeEnd2->mkid.cb = uSave2;
  91. pFakeEnd1->mkid.cb = uSave1;
  92. return nCmp;
  93. }
  94. BOOL CViewState::SyncPositions(CDefView* pdv)
  95. {
  96. if (_ViewMode != pdv->_fs.ViewMode)
  97. {
  98. return FALSE;
  99. }
  100. if (_hdpaItemPos == NULL || DPA_GetPtrCount(_hdpaItemPos) == 0)
  101. {
  102. return FALSE;
  103. }
  104. if (DPA_Sort(_hdpaItemPos, _SavedItemCompare, (LPARAM)pdv))
  105. {
  106. UNALIGNED VIEWSTATE_POSITION * UNALIGNED * ppDVItem = (UNALIGNED VIEWSTATE_POSITION * UNALIGNED *)DPA_GetPtrPtr(_hdpaItemPos);
  107. UNALIGNED VIEWSTATE_POSITION * UNALIGNED *ppEndDVItems = ppDVItem + DPA_GetPtrCount(_hdpaItemPos);
  108. // Turn off auto-arrange and snap-to-grid if it's on at the mo.
  109. DWORD dwStyle = GetWindowStyle(pdv->_hwndListview);
  110. if (dwStyle & LVS_AUTOARRANGE)
  111. SetWindowLong(pdv->_hwndListview, GWL_STYLE, dwStyle & ~LVS_AUTOARRANGE);
  112. DWORD dwLVExStyle = ListView_GetExtendedListViewStyle(pdv->_hwndListview);
  113. if (dwLVExStyle & LVS_EX_SNAPTOGRID)
  114. ListView_SetExtendedListViewStyle(pdv->_hwndListview, dwLVExStyle & ~LVS_EX_SNAPTOGRID);
  115. HDSA hdsaPositionlessItems = NULL;
  116. int iCount = ListView_GetItemCount(pdv->_hwndListview);
  117. for (int i = 0; i < iCount; i++)
  118. {
  119. LPCITEMIDLIST pidl = pdv->_GetPIDL(i);
  120. // need to check for pidl because this could be on a background
  121. // thread and an fsnotify could be coming through to blow it away
  122. for ( ; pidl ; )
  123. {
  124. int nCmp;
  125. if (ppDVItem < ppEndDVItems)
  126. {
  127. // We terminate the IDList manually after saving
  128. // the needed information. Note we will not GP fault
  129. // since we added sizeof(ITEMIDLIST) onto the Alloc
  130. LPITEMIDLIST pFakeEnd = _ILNext(&(*ppDVItem)->idl);
  131. USHORT uSave = pFakeEnd->mkid.cb;
  132. pFakeEnd->mkid.cb = 0;
  133. nCmp = pdv->_Compare(&((*ppDVItem)->idl), (void *)pidl, (LPARAM)pdv);
  134. pFakeEnd->mkid.cb = uSave;
  135. }
  136. else
  137. {
  138. // do this by default. this prevents overlap of icons
  139. //
  140. // i.e. if we've run out of saved positions information,
  141. // we need to just loop through and set all remaining items
  142. // to position 0x7FFFFFFFF so that when it's really shown,
  143. // the listview will pick a new (unoccupied) spot.
  144. // breaking out now would leave it were the _Sort
  145. // put it, but another item with saved state info could
  146. // have come and be placed on top of it.
  147. nCmp = 1;
  148. }
  149. if (nCmp > 0)
  150. {
  151. // We did not find the item
  152. // reset it's position to be recomputed
  153. if (NULL == hdsaPositionlessItems)
  154. hdsaPositionlessItems = DSA_Create(sizeof(int), 16);
  155. if (hdsaPositionlessItems)
  156. DSA_AppendItem(hdsaPositionlessItems, (void*)&i);
  157. break;
  158. }
  159. else if (nCmp == 0) // They are equal
  160. {
  161. UNALIGNED VIEWSTATE_POSITION * pDVItem = *ppDVItem;
  162. pdv->_SetItemPosition(i, pDVItem->pt.x, pDVItem->pt.y);
  163. ppDVItem++; // move on to the next
  164. break;
  165. }
  166. ppDVItem++; // move to the next
  167. }
  168. }
  169. if (hdsaPositionlessItems)
  170. {
  171. for (i = 0; i < DSA_GetItemCount(hdsaPositionlessItems); i++)
  172. {
  173. int* pIndex = (int*)DSA_GetItemPtr(hdsaPositionlessItems, i);
  174. pdv->_SetItemPosition(*pIndex, 0x7FFFFFFF, 0x7FFFFFFF);
  175. }
  176. DSA_Destroy(hdsaPositionlessItems);
  177. }
  178. // Turn auto-arrange and snap to grid back on if needed...
  179. if (dwLVExStyle & LVS_EX_SNAPTOGRID)
  180. ListView_SetExtendedListViewStyle(pdv->_hwndListview, dwLVExStyle);
  181. if (dwStyle & LVS_AUTOARRANGE)
  182. SetWindowLong(pdv->_hwndListview, GWL_STYLE, dwStyle);
  183. }
  184. return TRUE;
  185. }
  186. void CViewState::LoadPositionBlob(CDefView* pdv, DWORD cbSizeofStream, IStream* pstm)
  187. {
  188. // Allocate a blob of memory to hold the position info.
  189. if (_pbPositionData)
  190. LocalFree(_pbPositionData);
  191. _pbPositionData = (BYTE*)LocalAlloc(LPTR, cbSizeofStream);
  192. if (_pbPositionData == NULL)
  193. return;
  194. // Read into that blob.
  195. if (SUCCEEDED(pstm->Read(_pbPositionData, cbSizeofStream, NULL)))
  196. {
  197. // Walk the blob, and append to the DPA.
  198. UNALIGNED VIEWSTATE_POSITION *pDVItem = (UNALIGNED VIEWSTATE_POSITION *)(_pbPositionData);
  199. UNALIGNED VIEWSTATE_POSITION *pDVEnd = (UNALIGNED VIEWSTATE_POSITION *)(_pbPositionData + cbSizeofStream - sizeof(VIEWSTATE_POSITION));
  200. ClearPositionData(); // destroy _hdpaItemPos
  201. // Grow every 16 items
  202. _hdpaItemPos = DPA_Create(16);
  203. if (_hdpaItemPos)
  204. {
  205. for ( ; ; pDVItem = (UNALIGNED VIEWSTATE_POSITION *)_ILNext(&pDVItem->idl))
  206. {
  207. if (pDVItem > pDVEnd)
  208. {
  209. break; // Invalid list
  210. }
  211. // End normally when we reach a NULL IDList
  212. if (pDVItem->idl.mkid.cb == 0)
  213. {
  214. break;
  215. }
  216. if (DPA_AppendPtr(_hdpaItemPos, pDVItem) < 0)
  217. {
  218. break;
  219. }
  220. }
  221. }
  222. }
  223. }
  224. HRESULT CViewState::SavePositionBlob(CDefView* pdv, IStream* pstm)
  225. {
  226. HRESULT hr = S_FALSE; // success, but did nothing
  227. if (pdv->_fUserPositionedItems && pdv->_IsPositionedView())
  228. {
  229. VIEWSTATE_POSITION dvitem = {0};
  230. int iCount = ListView_GetItemCount(pdv->_hwndListview);
  231. for (int i = 0; SUCCEEDED(hr) && (i < iCount); i++)
  232. {
  233. ListView_GetItemPosition(pdv->_hwndListview, i, &dvitem.pt);
  234. hr = pstm->Write(&dvitem.pt, sizeof(dvitem.pt), NULL);
  235. if (SUCCEEDED(hr))
  236. {
  237. LPCITEMIDLIST pidl = pdv->_GetPIDL(i);
  238. if (pidl)
  239. hr = pstm->Write(pidl, pidl->mkid.cb, NULL);
  240. else
  241. hr = E_FAIL;
  242. }
  243. }
  244. if (SUCCEEDED(hr))
  245. {
  246. // Terminate the list with a NULL IDList
  247. dvitem.idl.mkid.cb = 0;
  248. hr = pstm->Write(&dvitem, sizeof(dvitem), NULL);
  249. }
  250. }
  251. return hr;
  252. }
  253. void CViewState::ClearPositionData()
  254. {
  255. if (_hdpaItemPos)
  256. {
  257. DPA_Destroy(_hdpaItemPos);
  258. _hdpaItemPos = NULL;
  259. }
  260. }
  261. UINT CViewState::GetColumnCount()
  262. {
  263. if (!_hdsaColumns)
  264. return 0;
  265. return DSA_GetItemCount(_hdsaColumns);
  266. }
  267. DWORD CViewState::GetColumnState(UINT uCol)
  268. {
  269. if (_hdsaColumns && (uCol < GetColumnCount()))
  270. {
  271. COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, uCol);
  272. return pci->csFlags;
  273. }
  274. return 0;
  275. }
  276. DWORD CViewState::GetTransientColumnState(UINT uCol)
  277. {
  278. if (_hdsaColumns && (uCol < GetColumnCount()))
  279. {
  280. COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, uCol);
  281. return pci->tsFlags;
  282. }
  283. return 0;
  284. }
  285. void CViewState::SetColumnState(UINT uCol, DWORD dwMask, DWORD dwNewBits)
  286. {
  287. if (_hdsaColumns && uCol < GetColumnCount())
  288. {
  289. COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, uCol);
  290. pci->csFlags = (pci->csFlags & ~dwMask) | (dwNewBits & dwMask);
  291. }
  292. }
  293. void CViewState::SetTransientColumnState(UINT uCol, DWORD dwMask, DWORD dwNewBits)
  294. {
  295. if (_hdsaColumns && uCol < GetColumnCount())
  296. {
  297. COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, uCol);
  298. pci->tsFlags = (pci->tsFlags & ~dwMask) | (dwNewBits & dwMask);
  299. }
  300. }
  301. LPTSTR CViewState::GetColumnName(UINT uCol)
  302. {
  303. if (_hdsaColumns && (uCol < GetColumnCount()))
  304. {
  305. COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, uCol);
  306. return pci->szName;
  307. }
  308. return NULL;
  309. }
  310. UINT CViewState::GetColumnCharCount(UINT uCol)
  311. {
  312. if (_hdsaColumns && (uCol < GetColumnCount()))
  313. {
  314. COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, uCol);
  315. return pci->cChars;
  316. }
  317. return 0;
  318. }
  319. int CViewState::GetColumnFormat(UINT uCol)
  320. {
  321. if (_hdsaColumns && (uCol < GetColumnCount()))
  322. {
  323. COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, uCol);
  324. return pci->fmt;
  325. }
  326. return 0;
  327. }
  328. HRESULT CViewState::InitializeColumns(CDefView* pdv)
  329. {
  330. if (_hdsaColumns != NULL)
  331. return S_OK;
  332. _hdsaColumns = DSA_Create(sizeof(COL_INFO), 6);
  333. if (!_hdsaColumns)
  334. return E_OUTOFMEMORY;
  335. for (UINT iReal = 0; ; iReal++)
  336. {
  337. DETAILSINFO di = {0};
  338. di.fmt = LVCFMT_LEFT;
  339. di.cxChar = 20;
  340. di.str.uType = (UINT)-1;
  341. if (SUCCEEDED(pdv->_GetDetailsHelper(iReal, &di)))
  342. {
  343. COL_INFO ci = {0};
  344. StrRetToBuf(&di.str, NULL, ci.szName, ARRAYSIZE(ci.szName));
  345. ci.cChars = di.cxChar;
  346. ci.csFlags = pdv->_DefaultColumnState(iReal);
  347. ci.fmt = di.fmt;
  348. DSA_AppendItem(_hdsaColumns, &ci);
  349. }
  350. else
  351. break;
  352. }
  353. // Set up saved column state only if the saved state
  354. // contains information other than "nothing".
  355. if (_hdsaColumnStates)
  356. {
  357. UINT cStates = DSA_GetItemCount(_hdsaColumnStates);
  358. if (cStates > 0)
  359. {
  360. // 99/02/05 vtan: If there is a saved column state then
  361. // clear all the column "on" states to "off" and only
  362. // display what columns are specified. Start at 1 so
  363. // that name is always on.
  364. for (iReal = 1; iReal < GetColumnCount(); iReal++)
  365. {
  366. COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, iReal);
  367. pci->csFlags &= ~SHCOLSTATE_ONBYDEFAULT;
  368. }
  369. for (UINT i = 0; i < cStates; i++)
  370. {
  371. DWORD dw;
  372. DSA_GetItem(_hdsaColumnStates, i, &dw);
  373. SetColumnState(dw, SHCOLSTATE_ONBYDEFAULT, SHCOLSTATE_ONBYDEFAULT);
  374. }
  375. }
  376. }
  377. return S_OK;
  378. }
  379. // When Loading or Saving from the View State Stream
  380. HRESULT CViewState::LoadFromStream(CDefView* pdv, IStream* pstm)
  381. {
  382. ULONG cbRead;
  383. DVSAVEHEADER_COMBO dv;
  384. ULARGE_INTEGER libStartPos;
  385. LARGE_INTEGER dlibMove = {0};
  386. pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libStartPos);
  387. // See what format the persisted view is in:
  388. HRESULT hr = pstm->Read(&dv, sizeof(dv), &cbRead);
  389. if (SUCCEEDED(hr) &&
  390. sizeof(DVSAVEHEADER_COMBO) == cbRead &&
  391. dv.dvSaveHeader.cbSize == sizeof(WIN95HEADER) &&
  392. dv.dvSaveHeader.cbColOffset == 0 &&
  393. dv.dvSaveHeaderEx.dwSignature == IE4HEADER_SIGNATURE &&
  394. dv.dvSaveHeaderEx.cbSize >= sizeof(IE4HEADER))
  395. {
  396. InitFromHeader(&dv);
  397. if (dv.dvSaveHeaderEx.wVersion < IE4HEADER_VERSION)
  398. {
  399. // We used to store szExtended in here -- not any more
  400. dv.dvSaveHeaderEx.dwUnused = 0;
  401. }
  402. if (dv.dvSaveHeaderEx.cbColOffset >= sizeof(dv))
  403. {
  404. dlibMove.QuadPart = libStartPos.QuadPart + dv.dvSaveHeaderEx.cbColOffset;
  405. hr = pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  406. if (SUCCEEDED(hr))
  407. {
  408. hr = LoadColumns(pdv, pstm);
  409. }
  410. }
  411. if (SUCCEEDED(hr))
  412. {
  413. dlibMove.QuadPart = libStartPos.QuadPart + dv.dvSaveHeader.cbPosOffset;
  414. hr = pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  415. if (SUCCEEDED(hr))
  416. {
  417. LoadPositionBlob(pdv, dv.dvSaveHeaderEx.cbStreamSize, pstm);
  418. }
  419. }
  420. }
  421. return S_OK;
  422. }
  423. void SetSize(ULARGE_INTEGER libCurPosition, IStream* pstm)
  424. {
  425. LARGE_INTEGER dlibMove;
  426. dlibMove.QuadPart = libCurPosition.QuadPart;
  427. pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  428. pstm->SetSize(libCurPosition);
  429. }
  430. DWORD CViewState::_GetStreamSize(IStream* pstm)
  431. {
  432. DWORD dwRet = 0;
  433. ULARGE_INTEGER uli;
  434. if (SUCCEEDED(IStream_Size(pstm, &uli)))
  435. {
  436. if (0 == uli.HighPart)
  437. {
  438. dwRet = uli.LowPart;
  439. }
  440. }
  441. return dwRet;
  442. }
  443. HRESULT CViewState::SaveToStream(CDefView* pdv, IStream* pstm)
  444. {
  445. ULONG ulWrite;
  446. DVSAVEHEADER_COMBO dv = {0};
  447. LARGE_INTEGER dlibMove = {0};
  448. ULARGE_INTEGER libCurPosition;
  449. // Get the current info.
  450. Sync(pdv, FALSE);
  451. // Position the stream right after the headers, and save the starting
  452. // position at the same time
  453. dlibMove.QuadPart = sizeof(dv);
  454. pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libCurPosition);
  455. // Avoid 2 calls to seek by just subtracting
  456. libCurPosition.QuadPart -= sizeof(dv);
  457. // Save column order and size info
  458. HRESULT hr = SaveColumns(pdv, pstm);
  459. if (SUCCEEDED(hr))
  460. {
  461. dv.dvSaveHeader.cbSize = sizeof(dv.dvSaveHeader);
  462. // We save the view mode to determine if the scroll positions are
  463. // still valid on restore
  464. dv.dvSaveHeader.ViewMode = _ViewMode;
  465. dv.dvSaveHeader.ptScroll.x = _ptScroll.x;
  466. dv.dvSaveHeader.ptScroll.y = _ptScroll.y;
  467. dv.dvSaveHeader.dvState.lParamSort = (LONG)_lParamSort;
  468. dv.dvSaveHeader.dvState.iDirection = _iDirection;
  469. dv.dvSaveHeader.dvState.iLastColumnClick = _iLastColumnClick;
  470. // dvSaveHeaderEx.cbColOffset holds the true offset.
  471. // Win95 gets confused when cbColOffset points to the new
  472. // format. Zeroing this out tells Win95 to use default widths
  473. // (after uninstall of ie40).
  474. //
  475. // dv.dvSaveHeader.cbColOffset = 0;
  476. dv.dvSaveHeaderEx.dwSignature = IE4HEADER_SIGNATURE;
  477. dv.dvSaveHeaderEx.cbSize = sizeof(dv.dvSaveHeaderEx);
  478. dv.dvSaveHeaderEx.wVersion = IE4HEADER_VERSION;
  479. ULARGE_INTEGER libPosPosition;
  480. // Save the Position Information
  481. dlibMove.QuadPart = 0;
  482. pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libPosPosition);
  483. dv.dvSaveHeaderEx.cbColOffset = sizeof(dv);
  484. dv.dvSaveHeader.cbPosOffset = (USHORT)(libPosPosition.QuadPart - libCurPosition.QuadPart);
  485. // Save potision info, currently stream is positioned immediately after column info
  486. hr = SavePositionBlob(pdv, pstm);
  487. if (SUCCEEDED(hr))
  488. {
  489. ULARGE_INTEGER libEndPosition;
  490. // Win95 expects cbPosOffset to be at the end of the stream --
  491. // don't change it's value and never store anything after
  492. // the position information.
  493. // Calculate size of total information saved.
  494. // This is needed when we read the stream.
  495. dlibMove.QuadPart = 0;
  496. if (SUCCEEDED(pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libEndPosition)))
  497. {
  498. dv.dvSaveHeaderEx.cbStreamSize = (DWORD)(libEndPosition.QuadPart - libCurPosition.QuadPart);
  499. }
  500. // Now save the header information
  501. dlibMove.QuadPart = libCurPosition.QuadPart;
  502. pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  503. hr = pstm->Write(&dv, sizeof(dv), &ulWrite);
  504. if (FAILED(hr) || ulWrite != sizeof(dv))
  505. {
  506. SetSize(libCurPosition, pstm);
  507. hr = S_OK;
  508. }
  509. // Make sure we save all information written so far
  510. libCurPosition.QuadPart += dv.dvSaveHeaderEx.cbStreamSize;
  511. }
  512. }
  513. return hr;
  514. }
  515. HRESULT CViewState::SaveToPropertyBag(CDefView* pdv, IPropertyBag* ppb)
  516. {
  517. // Get the current info.
  518. Sync(pdv, FALSE);
  519. SHPropertyBag_WriteDWORD(ppb, VS_PROPSTR_MODE, _ViewMode);
  520. SHPropertyBag_WritePOINTSScreenRes(ppb, VS_PROPSTR_SCROLL, &_ptScroll);
  521. SHPropertyBag_WriteDWORD(ppb, VS_PROPSTR_SORT, static_cast<DWORD>(_lParamSort));
  522. SHPropertyBag_WriteInt(ppb, VS_PROPSTR_SORTDIR, _iDirection);
  523. SHPropertyBag_WriteInt(ppb, VS_PROPSTR_COL, _iLastColumnClick);
  524. IStream* pstm = SHCreateMemStream(NULL, 0);
  525. if (pstm)
  526. {
  527. if (S_OK == SaveColumns(pdv, pstm))
  528. {
  529. SHPropertyBag_WriteStream(ppb, VS_PROPSTR_COLINFO, pstm);
  530. }
  531. else
  532. {
  533. SHPropertyBag_Delete(ppb, VS_PROPSTR_COLINFO);
  534. }
  535. pstm->Release();
  536. }
  537. pstm = SHCreateMemStream(NULL, 0);
  538. if (pstm)
  539. {
  540. if (S_OK == SavePositionBlob(pdv, pstm))
  541. {
  542. SHPropertyBag_WriteStreamScreenRes(ppb, VS_PROPSTR_ITEMPOS, pstm);
  543. }
  544. else
  545. {
  546. SHPropertyBag_DeleteScreenRes(ppb, VS_PROPSTR_ITEMPOS);
  547. }
  548. pstm->Release();
  549. }
  550. return S_OK;
  551. }
  552. HRESULT CViewState::LoadFromPropertyBag(CDefView* pdv, IPropertyBag* ppb)
  553. {
  554. SHPropertyBag_ReadDWORDDef(ppb, VS_PROPSTR_MODE, &_ViewMode, FVM_ICON);
  555. SHPropertyBag_ReadDWORDDef(ppb, VS_PROPSTR_SORT, reinterpret_cast<DWORD*>(&_lParamSort), 0);
  556. SHPropertyBag_ReadIntDef(ppb, VS_PROPSTR_SORTDIR, &_iDirection, 1);
  557. SHPropertyBag_ReadIntDef(ppb, VS_PROPSTR_COL, &_iLastColumnClick, -1);
  558. if (FAILED(SHPropertyBag_ReadPOINTSScreenRes(ppb, VS_PROPSTR_SCROLL, &_ptScroll)))
  559. {
  560. _ptScroll.x = _ptScroll.y = 0;
  561. }
  562. IStream* pstm;
  563. if (SUCCEEDED(SHPropertyBag_ReadStream(ppb, VS_PROPSTR_COLINFO, &pstm)))
  564. {
  565. LoadColumns(pdv, pstm);
  566. pstm->Release();
  567. }
  568. if (SUCCEEDED(SHPropertyBag_ReadStreamScreenRes(ppb, VS_PROPSTR_ITEMPOS, &pstm)))
  569. {
  570. LoadPositionBlob(pdv, _GetStreamSize(pstm), pstm);
  571. pstm->Release();
  572. }
  573. return S_OK;
  574. }
  575. HDSA DSA_CreateFromStream(DWORD cbSize, int cItems, IStream* pstm)
  576. {
  577. HDSA hdsa = DSA_Create(cbSize, cItems);
  578. if (hdsa)
  579. {
  580. BYTE* pb = (BYTE*)LocalAlloc(LPTR, cbSize);
  581. if (pb)
  582. {
  583. BOOL fFailedToRead = FALSE;
  584. ULONG cbRead;
  585. while (cItems--)
  586. {
  587. if (SUCCEEDED(pstm->Read(pb, cbSize, &cbRead) && cbRead == cbSize))
  588. {
  589. DSA_AppendItem(hdsa, pb);
  590. }
  591. else
  592. {
  593. fFailedToRead = TRUE;
  594. }
  595. }
  596. LocalFree(pb);
  597. if (fFailedToRead)
  598. {
  599. // The stream is probrably corrupt.
  600. DSA_Destroy(hdsa);
  601. hdsa = NULL;
  602. }
  603. }
  604. }
  605. return hdsa;
  606. }
  607. // When Loading from a View Callback provided stream.
  608. HRESULT CViewState::LoadColumns(CDefView* pdv, IStream* pstm)
  609. {
  610. // Read the extended View state header
  611. HRESULT hr;
  612. ULONG cbRead;
  613. VIEWSTATEHEADER vsh;
  614. ULARGE_INTEGER libStartPos;
  615. LARGE_INTEGER dlibMove = {0};
  616. // Store off the current stream pointer. If we are called directly, this is probrably Zero,
  617. // However this method gets called from ::Load, so this it definitly not zero in that case.
  618. pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libStartPos);
  619. // The VSH struct has many "Substructs" indicating the version of ths struct we are reading.
  620. // There is probrably a more efficient mechanism of version discovery, but this is easiest to read and understand.
  621. hr = pstm->Read(&vsh.Version1, sizeof(vsh.Version1), &cbRead);
  622. if (SUCCEEDED(hr) &&
  623. sizeof(vsh.Version1) == cbRead && // Fail if we didn't read enough
  624. VIEWSTATEHEADER_SIGNATURE == vsh.Version1.dwSignature) // Fail if the signature is bogus
  625. {
  626. if (vsh.Version1.uVersion >= VIEWSTATEHEADER_VERSION_1)
  627. {
  628. if (vsh.Version1.uCols > 0)
  629. {
  630. // Load the Column Ordering
  631. dlibMove.QuadPart = libStartPos.QuadPart + vsh.Version1.uOffsetColOrder;
  632. pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  633. if (_hdsaColumnOrder)
  634. DSA_Destroy(_hdsaColumnOrder);
  635. _hdsaColumnOrder = DSA_CreateFromStream(sizeof(int), vsh.Version1.uCols, pstm);
  636. // Load the Column Widths
  637. dlibMove.QuadPart = libStartPos.QuadPart + vsh.Version1.uOffsetWidths;
  638. pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  639. if (_hdsaColumnWidths)
  640. DSA_Destroy(_hdsaColumnWidths);
  641. _hdsaColumnWidths = DSA_CreateFromStream(sizeof(USHORT), vsh.Version1.uCols, pstm);
  642. }
  643. if (vsh.Version1.uVersion >= VIEWSTATEHEADER_VERSION_2 &&
  644. vsh.Version1.uCols > 0)
  645. {
  646. DWORD dwRead;
  647. // Seek to read the rest of the header
  648. dlibMove.QuadPart = libStartPos.QuadPart + sizeof(vsh.Version1);
  649. pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  650. hr = pstm->Read(&vsh.Version2, sizeof(vsh.Version2), &cbRead);
  651. if (SUCCEEDED(hr) &&
  652. sizeof(vsh.Version2) == cbRead &&
  653. vsh.Version2.uOffsetColStates)
  654. {
  655. // Load the Column States
  656. dlibMove.QuadPart = libStartPos.QuadPart + vsh.Version2.uOffsetColStates;
  657. pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  658. // This one is funky: There is a terminating sentinal....
  659. if (_hdsaColumnStates)
  660. DSA_Destroy(_hdsaColumnStates);
  661. _hdsaColumnStates = DSA_Create(sizeof(DWORD), 5);
  662. if (_hdsaColumnStates)
  663. {
  664. do
  665. {
  666. if (SUCCEEDED(pstm->Read(&dwRead, sizeof(DWORD), &cbRead)) &&
  667. cbRead == sizeof(DWORD) &&
  668. dwRead != 0xFFFFFFFF)
  669. {
  670. DSA_AppendItem(_hdsaColumnStates, &dwRead);
  671. }
  672. else
  673. {
  674. break;
  675. }
  676. }
  677. while (dwRead != 0xFFFFFFFF);
  678. }
  679. }
  680. }
  681. if (vsh.Version1.uVersion >= VIEWSTATEHEADER_VERSION_3)
  682. {
  683. // Seek to read the rest of the header
  684. dlibMove.QuadPart = libStartPos.QuadPart + sizeof(vsh.Version1) + sizeof(vsh.Version2);
  685. pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  686. hr = pstm->Read(&vsh.Version3, sizeof(vsh.Version3), &cbRead);
  687. if (SUCCEEDED(hr) &&
  688. sizeof(vsh.Version3) == cbRead &&
  689. vsh.Version3.uOffsetGroup)
  690. {
  691. GROUP_PERSIST gp;
  692. dlibMove.QuadPart = libStartPos.QuadPart + vsh.Version3.uOffsetGroup;
  693. pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  694. hr = pstm->Read(&gp, sizeof(gp), &cbRead);
  695. if (SUCCEEDED(hr) &&
  696. sizeof(gp) == cbRead)
  697. {
  698. _guidGroupID = gp.guidGroupID;
  699. _scidDetails = gp.scidDetails;
  700. }
  701. }
  702. _fFirstViewed = FALSE;
  703. }
  704. /////////////////////////////////////////////////////////////////////////////////////
  705. // ***** NEW Data *****
  706. // 1) Add a version to the VIEWSTATEHEADER
  707. // 2) Add a version to VIEWSTATEHEADER_VERSION_*
  708. // 3) Check that version here
  709. /////////////////////////////////////////////////////////////////////////////////////
  710. }
  711. }
  712. return hr;
  713. }
  714. HRESULT CViewState::SaveColumns(CDefView* pdv, IStream* pstm)
  715. {
  716. HRESULT hr;
  717. USHORT uOffset;
  718. VIEWSTATEHEADER vsh = {0};
  719. ULARGE_INTEGER libStartPos = {0};
  720. LARGE_INTEGER dlibMove = {0};
  721. // No point in persisting, if there aren't any columns around.
  722. // this is true for folders that are just opened and closed
  723. if (!pdv->_psd && !pdv->_pshf2 && !pdv->HasCB())
  724. {
  725. return S_FALSE;
  726. }
  727. // First, we persist a known bad quantity, just in case we wax the stream
  728. pstm->Seek(g_li0, STREAM_SEEK_CUR, &libStartPos);
  729. hr = pstm->Write(&vsh, sizeof(vsh), NULL);
  730. if (SUCCEEDED(hr))
  731. {
  732. vsh.Version1.dwSignature = VIEWSTATEHEADER_SIGNATURE;
  733. vsh.Version1.uVersion = VIEWSTATEHEADER_VERSION_CURRENT;
  734. vsh.Version1.uCols = _hdsaColumnOrder? (UINT) DSA_GetItemCount(_hdsaColumnOrder) : 0;
  735. uOffset = sizeof(VIEWSTATEHEADER);
  736. // No point in persisting if we don't have any columns
  737. if (vsh.Version1.uCols)
  738. {
  739. // Note- dependent on DSA storing data internally as byte-packed.
  740. if (_hdsaColumnOrder)
  741. {
  742. vsh.Version1.uOffsetColOrder = uOffset;
  743. uOffset += (USHORT)(sizeof(UINT) * DSA_GetItemCount(_hdsaColumnOrder));
  744. hr = pstm->Write(DSA_GetItemPtr(_hdsaColumnOrder, 0), sizeof(UINT) * DSA_GetItemCount(_hdsaColumnOrder), NULL);
  745. }
  746. if (_hdsaColumnWidths && SUCCEEDED(hr))
  747. {
  748. vsh.Version1.uOffsetWidths = uOffset;
  749. uOffset += (USHORT)(sizeof(USHORT) * DSA_GetItemCount(_hdsaColumnWidths));
  750. hr = pstm->Write(DSA_GetItemPtr(_hdsaColumnWidths, 0), sizeof(USHORT) * DSA_GetItemCount(_hdsaColumnWidths), NULL);
  751. }
  752. if (_hdsaColumnStates && SUCCEEDED(hr))
  753. {
  754. vsh.Version2.uOffsetColStates = uOffset;
  755. uOffset += (USHORT)(sizeof(DWORD) * DSA_GetItemCount(_hdsaColumnStates));
  756. pstm->Write(DSA_GetItemPtr(_hdsaColumnStates, 0), sizeof(DWORD) * DSA_GetItemCount(_hdsaColumnStates), NULL);
  757. }
  758. }
  759. if (SUCCEEDED(hr))
  760. {
  761. GROUP_PERSIST gp = {0};
  762. vsh.Version3.uOffsetGroup = uOffset;
  763. uOffset += sizeof(GROUP_PERSIST);
  764. if (pdv->_fGroupView)
  765. {
  766. gp.guidGroupID = _guidGroupID;
  767. gp.scidDetails = _scidDetails;
  768. }
  769. hr = pstm->Write(&gp, sizeof(gp), NULL);
  770. }
  771. /////////////////////////////////////////////////////////////////////////////////////
  772. // ***** NEW Data *****
  773. // 1) Add a version to the VIEWSTATEHEADER
  774. // 2) Add a version to VIEWSTATEHEADER_VERSION_*
  775. // 3) Add a "Loader" for your value
  776. // 4) Set the offset to uOffset.
  777. // 5) Write your data.
  778. // 6) Update the running total of dwOffset.
  779. /////////////////////////////////////////////////////////////////////////////////////
  780. dlibMove.QuadPart = libStartPos.QuadPart;
  781. // Store off the current position
  782. pstm->Seek(g_li0, STREAM_SEEK_CUR, &libStartPos);
  783. // Move to the beginning
  784. pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  785. // Write out the correct header
  786. hr = pstm->Write(&vsh, sizeof(vsh), NULL);
  787. if (SUCCEEDED(hr))
  788. {
  789. // Reset the current pos
  790. dlibMove.QuadPart = libStartPos.QuadPart;
  791. pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  792. }
  793. }
  794. return hr;
  795. }
  796. BOOL CViewState::AppendColumn(UINT uCol, USHORT uWidth, INT uOrder)
  797. {
  798. if (_hdsaColumnOrder == NULL ||
  799. _hdsaColumnWidths == NULL)
  800. {
  801. return FALSE;
  802. }
  803. // Slide every index above this one up
  804. for (INT u = 0; u < DSA_GetItemCount(_hdsaColumnOrder); u++)
  805. {
  806. UINT *p = (UINT *) DSA_GetItemPtr(_hdsaColumnOrder, u);
  807. if (!p)
  808. break; // safety...
  809. if (*p >= uCol)
  810. (*p)++;
  811. }
  812. DSA_AppendItem(_hdsaColumnWidths, &uWidth);
  813. DSA_AppendItem(_hdsaColumnOrder, &uOrder);
  814. // maybe we should store column ordering as absolute numbers
  815. return TRUE;
  816. }
  817. BOOL CViewState::RemoveColumn(UINT uCol)
  818. {
  819. if (_hdsaColumnWidths == NULL ||
  820. _hdsaColumnWidths == NULL)
  821. {
  822. return FALSE;
  823. }
  824. if ((int)uCol >= DSA_GetItemCount(_hdsaColumnWidths))
  825. return FALSE;
  826. // Slide every index above this one down
  827. for (INT u = 0; u < DSA_GetItemCount(_hdsaColumnOrder); u++)
  828. {
  829. UINT *p = (UINT *) DSA_GetItemPtr(_hdsaColumnOrder, u);
  830. if (!p)
  831. break; // safety...
  832. if (*p > uCol)
  833. (*p)--;
  834. }
  835. DSA_DeleteItem(_hdsaColumnWidths, uCol);
  836. DSA_DeleteItem(_hdsaColumnOrder, uCol);
  837. return TRUE;
  838. }
  839. UINT CViewState::GetColumnWidth(UINT uCol, UINT uDefWid)
  840. {
  841. if (!_hdsaColumnWidths)
  842. return uDefWid;
  843. USHORT uWidth = 0;
  844. if (uCol < (UINT) DSA_GetItemCount(_hdsaColumnWidths))
  845. {
  846. DSA_GetItem(_hdsaColumnWidths, uCol, &uWidth);
  847. }
  848. return uWidth ? uWidth : uDefWid; // disallow zero width columns
  849. }
  850. BOOL CViewState::SyncColumnOrder(CDefView* pdv, BOOL fSetListViewState)
  851. {
  852. UINT cCols = pdv->_GetHeaderCount();
  853. if (fSetListViewState)
  854. {
  855. if (!_hdsaColumnOrder)
  856. return FALSE;
  857. if (cCols != (UINT) DSA_GetItemCount(_hdsaColumnOrder))
  858. {
  859. // this is a normal case if a folder is opened and there is no saved state. no need to spew.
  860. return TRUE;
  861. }
  862. UINT *pCols = (UINT *)LocalAlloc(LPTR, cCols * sizeof(*pCols));
  863. if (pCols)
  864. {
  865. for (UINT u = 0; u < cCols; u++)
  866. {
  867. DSA_GetItem(_hdsaColumnOrder, u, pCols + u);
  868. }
  869. ListView_SetColumnOrderArray(pdv->_hwndListview, cCols, pCols);
  870. LocalFree(pCols);
  871. }
  872. }
  873. else
  874. {
  875. BOOL bDefaultOrder = TRUE;
  876. if (cCols)
  877. {
  878. if (!_hdsaColumnOrder)
  879. _hdsaColumnOrder = DSA_Create(sizeof(UINT), 6);
  880. if (_hdsaColumnOrder)
  881. {
  882. UINT *pCols = (UINT *)LocalAlloc(LPTR, cCols * sizeof(*pCols));
  883. if (pCols)
  884. {
  885. ListView_GetColumnOrderArray(pdv->_hwndListview, cCols, pCols);
  886. DSA_DeleteAllItems(_hdsaColumnOrder);
  887. for (UINT u = 0; u < cCols; u++)
  888. {
  889. DSA_AppendItem(_hdsaColumnOrder, &pCols[u]);
  890. if (pCols[u] != u)
  891. {
  892. bDefaultOrder = FALSE;
  893. }
  894. }
  895. LocalFree(pCols);
  896. }
  897. }
  898. }
  899. return bDefaultOrder;
  900. }
  901. return TRUE;
  902. }
  903. BOOL CViewState::SyncColumnWidths(CDefView* pdv, BOOL fSetListViewState)
  904. {
  905. UINT cCols = pdv->_GetHeaderCount();
  906. if (fSetListViewState)
  907. {
  908. return FALSE;
  909. }
  910. else
  911. {
  912. USHORT us;
  913. LV_COLUMN lvc;
  914. BOOL bOk = TRUE;
  915. if (!cCols)
  916. return TRUE;
  917. HDSA dsaNewWidths = DSA_Create(sizeof(USHORT), cCols);
  918. if (!dsaNewWidths)
  919. return TRUE;
  920. for (UINT u = 0; u < cCols && bOk; ++u)
  921. {
  922. lvc.mask = LVCF_WIDTH;
  923. bOk = ListView_GetColumn(pdv->_hwndListview, u, &lvc);
  924. us = (USHORT) lvc.cx; // make sure its a short
  925. DSA_AppendItem(dsaNewWidths, &us);
  926. // TraceMsg(TF_DEFVIEW, " saving col %d width of %d", u, us);
  927. }
  928. if (bOk)
  929. {
  930. if (_hdsaColumnWidths)
  931. DSA_Destroy(_hdsaColumnWidths);
  932. _hdsaColumnWidths = dsaNewWidths;
  933. }
  934. else
  935. DSA_Destroy(dsaNewWidths);
  936. return !bOk;
  937. }
  938. }
  939. BOOL CViewState::SyncColumnStates(CDefView* pdv, BOOL fSetListViewstate)
  940. {
  941. if (fSetListViewstate)
  942. {
  943. return FALSE;
  944. }
  945. else
  946. {
  947. // Save off Column States
  948. if (_hdsaColumnStates)
  949. {
  950. DSA_Destroy(_hdsaColumnStates);
  951. _hdsaColumnStates = NULL;
  952. }
  953. UINT cCol = GetColumnCount();
  954. if (cCol)
  955. {
  956. DWORD i;
  957. _hdsaColumnStates = DSA_Create(sizeof(DWORD), 5);
  958. if (_hdsaColumnStates)
  959. {
  960. for (i = 0; i < cCol; i++)
  961. {
  962. if (pdv->_IsDetailsColumn(i))
  963. DSA_AppendItem(_hdsaColumnStates, &i);
  964. }
  965. i = 0xFFFFFFFF; // Terminating Sentinal
  966. DSA_AppendItem(_hdsaColumnStates,&i);
  967. }
  968. }
  969. }
  970. return TRUE;
  971. }
  972. // Syncronizes ListView with the current View State.
  973. // TRUE means take the view state object and set it into the listview.
  974. HRESULT CViewState::Sync(CDefView* pdv, BOOL fSetListViewState)
  975. {
  976. SyncColumnWidths(pdv, fSetListViewState);
  977. SyncColumnOrder(pdv, fSetListViewState);
  978. SyncColumnStates(pdv, fSetListViewState);
  979. if (fSetListViewState)
  980. {
  981. // Only do this the first time.
  982. if (pdv->_pcat == NULL)
  983. {
  984. if (_fFirstViewed)
  985. {
  986. // See if the desktop.ini specifies one
  987. pdv->_LoadCategory(&_guidGroupID);
  988. if (IsEqualGUID(_guidGroupID, GUID_NULL))
  989. {
  990. ICategoryProvider* pcp;
  991. if (SUCCEEDED(pdv->_pshf->CreateViewObject(NULL, IID_PPV_ARG(ICategoryProvider, &pcp))))
  992. {
  993. pcp->GetDefaultCategory(&_guidGroupID, &_scidDetails);
  994. pcp->Release();
  995. }
  996. }
  997. }
  998. if (!IsEqualGUID(_guidGroupID, GUID_NULL) || !IsEqualGUID(_scidDetails.fmtid, GUID_NULL))
  999. pdv->_CategorizeOnGUID(&_guidGroupID, &_scidDetails);
  1000. }
  1001. // this is only needed to sort the items who's positions are not known
  1002. // it would be nice to optimize this case and only sort then
  1003. pdv->_Sort();
  1004. SyncPositions(pdv);
  1005. }
  1006. else
  1007. {
  1008. // Take what Listview has, and save it to me.
  1009. _ViewMode = pdv->_fs.ViewMode;
  1010. _ptScroll.x = (SHORT) GetScrollPos(pdv->_hwndListview, SB_HORZ);
  1011. _ptScroll.y = (SHORT) GetScrollPos(pdv->_hwndListview, SB_VERT);
  1012. }
  1013. return S_OK;
  1014. }