windows-nt/Source/XPSP1/NT/shell/shell32/viewstate.cpp
2020-09-26 16:20:57 +08:00

1214 lines
37 KiB
C++

#include "shellprv.h"
#include "defviewp.h"
#include "ViewState.h"
CViewState::CViewState()
{
ASSERT(_lParamSort == NULL);
ASSERT(_iDirection == 0);
ASSERT(_iLastColumnClick == 0);
ASSERT(_ViewMode == 0);
ASSERT(_ptScroll.x == 0 && _ptScroll.y == 0);
ASSERT(_guidGroupID == GUID_NULL);
ASSERT(_scidDetails.fmtid == GUID_NULL);
ASSERT(_scidDetails.pid == 0);
ASSERT(_hdsaColumnOrder == NULL);
ASSERT(_hdsaColumnWidths == NULL);
ASSERT(_hdsaColumnStates == NULL);
ASSERT(_hdpaItemPos == NULL);
ASSERT(_pbPositionData == NULL);
_iDirection = 1;
_fFirstViewed = TRUE; // Assume this is the first time we are looking at a folder.
}
CViewState::~CViewState()
{
if (_hdsaColumnOrder)
DSA_Destroy(_hdsaColumnOrder);
if (_hdsaColumnWidths)
DSA_Destroy(_hdsaColumnWidths);
if (_hdsaColumnStates)
DSA_Destroy(_hdsaColumnStates);
if (_hdsaColumns)
DSA_Destroy(_hdsaColumns);
ClearPositionData();
LocalFree(_pbPositionData); // accepts NULL
}
// When initializing a new DefView, see if we can
// propogate information from the previous one.
void CViewState::InitFromPreviousView(IUnknown* pPrevView)
{
CDefView *pdsvPrev;
if (SUCCEEDED(pPrevView->QueryInterface(IID_PPV_ARG(CDefView, &pdsvPrev))))
{
// preserve stuff like sort order
_lParamSort = pdsvPrev->_vs._lParamSort;
_iDirection = pdsvPrev->_vs._iDirection;
_iLastColumnClick = pdsvPrev->_vs._iLastColumnClick;
pdsvPrev->Release();
}
}
void CViewState::InitFromHeader(DVSAVEHEADER_COMBO* pdv)
{
_lParamSort = pdv->dvSaveHeader.dvState.lParamSort;
_iDirection = pdv->dvSaveHeader.dvState.iDirection;
// Patch this up. I guess at one time we persisted this wrong.
if (_iDirection == 0)
_iDirection = 1;
_iLastColumnClick = pdv->dvSaveHeader.dvState.iLastColumnClick;
_ViewMode = pdv->dvSaveHeader.ViewMode;
_ptScroll = pdv->dvSaveHeader.ptScroll;
}
void CViewState::GetDefaults(CDefView* pdv, LPARAM* plParamSort, int* piDirection, int* piLastColumnClick)
{
SHELLSTATE ss;
SHGetSetSettings(&ss, SSF_SORTCOLUMNS, FALSE);
if (plParamSort)
*plParamSort = ss.lParamSort;
if (piDirection)
*piDirection = ss.iSortDirection ? ss.iSortDirection : 1;
if (piLastColumnClick)
*piLastColumnClick = -1;
pdv->CallCB(SFVM_GETSORTDEFAULTS, (LPARAM)piDirection, (WPARAM)plParamSort);
}
void CViewState::InitWithDefaults(CDefView* pdv)
{
GetDefaults(pdv, &_lParamSort, &_iDirection, &_iLastColumnClick);
}
int CALLBACK CViewState::_SavedItemCompare(void *p1, void *p2, LPARAM lParam)
{
CDefView *pdv = reinterpret_cast<CDefView*>(lParam);
UNALIGNED VIEWSTATE_POSITION *pdvi1 = (UNALIGNED VIEWSTATE_POSITION *)p1;
UNALIGNED VIEWSTATE_POSITION *pdvi2 = (UNALIGNED VIEWSTATE_POSITION *)p2;
// manually terminate these pidls because they are packed together
// in the save buffer
LPITEMIDLIST pFakeEnd1 = _ILNext(&pdvi1->idl);
USHORT uSave1 = pFakeEnd1->mkid.cb;
pFakeEnd1->mkid.cb = 0;
LPITEMIDLIST pFakeEnd2 = _ILNext(&pdvi2->idl);
USHORT uSave2 = pFakeEnd2->mkid.cb;
pFakeEnd2->mkid.cb = 0;
int nCmp = pdv->_Compare(&pdvi1->idl, &pdvi2->idl, reinterpret_cast<LPARAM>(pdv));
pFakeEnd2->mkid.cb = uSave2;
pFakeEnd1->mkid.cb = uSave1;
return nCmp;
}
BOOL CViewState::SyncPositions(CDefView* pdv)
{
if (_ViewMode != pdv->_fs.ViewMode)
{
return FALSE;
}
if (_hdpaItemPos == NULL || DPA_GetPtrCount(_hdpaItemPos) == 0)
{
return FALSE;
}
if (DPA_Sort(_hdpaItemPos, _SavedItemCompare, (LPARAM)pdv))
{
UNALIGNED VIEWSTATE_POSITION * UNALIGNED * ppDVItem = (UNALIGNED VIEWSTATE_POSITION * UNALIGNED *)DPA_GetPtrPtr(_hdpaItemPos);
UNALIGNED VIEWSTATE_POSITION * UNALIGNED *ppEndDVItems = ppDVItem + DPA_GetPtrCount(_hdpaItemPos);
// Turn off auto-arrange and snap-to-grid if it's on at the mo.
DWORD dwStyle = GetWindowStyle(pdv->_hwndListview);
if (dwStyle & LVS_AUTOARRANGE)
SetWindowLong(pdv->_hwndListview, GWL_STYLE, dwStyle & ~LVS_AUTOARRANGE);
DWORD dwLVExStyle = ListView_GetExtendedListViewStyle(pdv->_hwndListview);
if (dwLVExStyle & LVS_EX_SNAPTOGRID)
ListView_SetExtendedListViewStyle(pdv->_hwndListview, dwLVExStyle & ~LVS_EX_SNAPTOGRID);
HDSA hdsaPositionlessItems = NULL;
int iCount = ListView_GetItemCount(pdv->_hwndListview);
for (int i = 0; i < iCount; i++)
{
LPCITEMIDLIST pidl = pdv->_GetPIDL(i);
// need to check for pidl because this could be on a background
// thread and an fsnotify could be coming through to blow it away
for ( ; pidl ; )
{
int nCmp;
if (ppDVItem < ppEndDVItems)
{
// We terminate the IDList manually after saving
// the needed information. Note we will not GP fault
// since we added sizeof(ITEMIDLIST) onto the Alloc
LPITEMIDLIST pFakeEnd = _ILNext(&(*ppDVItem)->idl);
USHORT uSave = pFakeEnd->mkid.cb;
pFakeEnd->mkid.cb = 0;
nCmp = pdv->_Compare(&((*ppDVItem)->idl), (void *)pidl, (LPARAM)pdv);
pFakeEnd->mkid.cb = uSave;
}
else
{
// do this by default. this prevents overlap of icons
//
// i.e. if we've run out of saved positions information,
// we need to just loop through and set all remaining items
// to position 0x7FFFFFFFF so that when it's really shown,
// the listview will pick a new (unoccupied) spot.
// breaking out now would leave it were the _Sort
// put it, but another item with saved state info could
// have come and be placed on top of it.
nCmp = 1;
}
if (nCmp > 0)
{
// We did not find the item
// reset it's position to be recomputed
if (NULL == hdsaPositionlessItems)
hdsaPositionlessItems = DSA_Create(sizeof(int), 16);
if (hdsaPositionlessItems)
DSA_AppendItem(hdsaPositionlessItems, (void*)&i);
break;
}
else if (nCmp == 0) // They are equal
{
UNALIGNED VIEWSTATE_POSITION * pDVItem = *ppDVItem;
pdv->_SetItemPosition(i, pDVItem->pt.x, pDVItem->pt.y);
ppDVItem++; // move on to the next
break;
}
ppDVItem++; // move to the next
}
}
if (hdsaPositionlessItems)
{
for (i = 0; i < DSA_GetItemCount(hdsaPositionlessItems); i++)
{
int* pIndex = (int*)DSA_GetItemPtr(hdsaPositionlessItems, i);
pdv->_SetItemPosition(*pIndex, 0x7FFFFFFF, 0x7FFFFFFF);
}
DSA_Destroy(hdsaPositionlessItems);
}
// Turn auto-arrange and snap to grid back on if needed...
if (dwLVExStyle & LVS_EX_SNAPTOGRID)
ListView_SetExtendedListViewStyle(pdv->_hwndListview, dwLVExStyle);
if (dwStyle & LVS_AUTOARRANGE)
SetWindowLong(pdv->_hwndListview, GWL_STYLE, dwStyle);
}
return TRUE;
}
void CViewState::LoadPositionBlob(CDefView* pdv, DWORD cbSizeofStream, IStream* pstm)
{
// Allocate a blob of memory to hold the position info.
if (_pbPositionData)
LocalFree(_pbPositionData);
_pbPositionData = (BYTE*)LocalAlloc(LPTR, cbSizeofStream);
if (_pbPositionData == NULL)
return;
// Read into that blob.
if (SUCCEEDED(pstm->Read(_pbPositionData, cbSizeofStream, NULL)))
{
// Walk the blob, and append to the DPA.
UNALIGNED VIEWSTATE_POSITION *pDVItem = (UNALIGNED VIEWSTATE_POSITION *)(_pbPositionData);
UNALIGNED VIEWSTATE_POSITION *pDVEnd = (UNALIGNED VIEWSTATE_POSITION *)(_pbPositionData + cbSizeofStream - sizeof(VIEWSTATE_POSITION));
ClearPositionData(); // destroy _hdpaItemPos
// Grow every 16 items
_hdpaItemPos = DPA_Create(16);
if (_hdpaItemPos)
{
for ( ; ; pDVItem = (UNALIGNED VIEWSTATE_POSITION *)_ILNext(&pDVItem->idl))
{
if (pDVItem > pDVEnd)
{
break; // Invalid list
}
// End normally when we reach a NULL IDList
if (pDVItem->idl.mkid.cb == 0)
{
break;
}
if (DPA_AppendPtr(_hdpaItemPos, pDVItem) < 0)
{
break;
}
}
}
}
}
HRESULT CViewState::SavePositionBlob(CDefView* pdv, IStream* pstm)
{
HRESULT hr = S_FALSE; // success, but did nothing
if (pdv->_fUserPositionedItems && pdv->_IsPositionedView())
{
VIEWSTATE_POSITION dvitem = {0};
int iCount = ListView_GetItemCount(pdv->_hwndListview);
for (int i = 0; SUCCEEDED(hr) && (i < iCount); i++)
{
ListView_GetItemPosition(pdv->_hwndListview, i, &dvitem.pt);
hr = pstm->Write(&dvitem.pt, sizeof(dvitem.pt), NULL);
if (SUCCEEDED(hr))
{
LPCITEMIDLIST pidl = pdv->_GetPIDL(i);
if (pidl)
hr = pstm->Write(pidl, pidl->mkid.cb, NULL);
else
hr = E_FAIL;
}
}
if (SUCCEEDED(hr))
{
// Terminate the list with a NULL IDList
dvitem.idl.mkid.cb = 0;
hr = pstm->Write(&dvitem, sizeof(dvitem), NULL);
}
}
return hr;
}
void CViewState::ClearPositionData()
{
if (_hdpaItemPos)
{
DPA_Destroy(_hdpaItemPos);
_hdpaItemPos = NULL;
}
}
UINT CViewState::GetColumnCount()
{
if (!_hdsaColumns)
return 0;
return DSA_GetItemCount(_hdsaColumns);
}
DWORD CViewState::GetColumnState(UINT uCol)
{
if (_hdsaColumns && (uCol < GetColumnCount()))
{
COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, uCol);
return pci->csFlags;
}
return 0;
}
DWORD CViewState::GetTransientColumnState(UINT uCol)
{
if (_hdsaColumns && (uCol < GetColumnCount()))
{
COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, uCol);
return pci->tsFlags;
}
return 0;
}
void CViewState::SetColumnState(UINT uCol, DWORD dwMask, DWORD dwNewBits)
{
if (_hdsaColumns && uCol < GetColumnCount())
{
COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, uCol);
pci->csFlags = (pci->csFlags & ~dwMask) | (dwNewBits & dwMask);
}
}
void CViewState::SetTransientColumnState(UINT uCol, DWORD dwMask, DWORD dwNewBits)
{
if (_hdsaColumns && uCol < GetColumnCount())
{
COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, uCol);
pci->tsFlags = (pci->tsFlags & ~dwMask) | (dwNewBits & dwMask);
}
}
LPTSTR CViewState::GetColumnName(UINT uCol)
{
if (_hdsaColumns && (uCol < GetColumnCount()))
{
COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, uCol);
return pci->szName;
}
return NULL;
}
UINT CViewState::GetColumnCharCount(UINT uCol)
{
if (_hdsaColumns && (uCol < GetColumnCount()))
{
COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, uCol);
return pci->cChars;
}
return 0;
}
int CViewState::GetColumnFormat(UINT uCol)
{
if (_hdsaColumns && (uCol < GetColumnCount()))
{
COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, uCol);
return pci->fmt;
}
return 0;
}
HRESULT CViewState::InitializeColumns(CDefView* pdv)
{
if (_hdsaColumns != NULL)
return S_OK;
_hdsaColumns = DSA_Create(sizeof(COL_INFO), 6);
if (!_hdsaColumns)
return E_OUTOFMEMORY;
for (UINT iReal = 0; ; iReal++)
{
DETAILSINFO di = {0};
di.fmt = LVCFMT_LEFT;
di.cxChar = 20;
di.str.uType = (UINT)-1;
if (SUCCEEDED(pdv->_GetDetailsHelper(iReal, &di)))
{
COL_INFO ci = {0};
StrRetToBuf(&di.str, NULL, ci.szName, ARRAYSIZE(ci.szName));
ci.cChars = di.cxChar;
ci.csFlags = pdv->_DefaultColumnState(iReal);
ci.fmt = di.fmt;
DSA_AppendItem(_hdsaColumns, &ci);
}
else
break;
}
// Set up saved column state only if the saved state
// contains information other than "nothing".
if (_hdsaColumnStates)
{
UINT cStates = DSA_GetItemCount(_hdsaColumnStates);
if (cStates > 0)
{
// 99/02/05 vtan: If there is a saved column state then
// clear all the column "on" states to "off" and only
// display what columns are specified. Start at 1 so
// that name is always on.
for (iReal = 1; iReal < GetColumnCount(); iReal++)
{
COL_INFO* pci = (COL_INFO*)DSA_GetItemPtr(_hdsaColumns, iReal);
pci->csFlags &= ~SHCOLSTATE_ONBYDEFAULT;
}
for (UINT i = 0; i < cStates; i++)
{
DWORD dw;
DSA_GetItem(_hdsaColumnStates, i, &dw);
SetColumnState(dw, SHCOLSTATE_ONBYDEFAULT, SHCOLSTATE_ONBYDEFAULT);
}
}
}
return S_OK;
}
// When Loading or Saving from the View State Stream
HRESULT CViewState::LoadFromStream(CDefView* pdv, IStream* pstm)
{
ULONG cbRead;
DVSAVEHEADER_COMBO dv;
ULARGE_INTEGER libStartPos;
LARGE_INTEGER dlibMove = {0};
pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libStartPos);
// See what format the persisted view is in:
HRESULT hr = pstm->Read(&dv, sizeof(dv), &cbRead);
if (SUCCEEDED(hr) &&
sizeof(DVSAVEHEADER_COMBO) == cbRead &&
dv.dvSaveHeader.cbSize == sizeof(WIN95HEADER) &&
dv.dvSaveHeader.cbColOffset == 0 &&
dv.dvSaveHeaderEx.dwSignature == IE4HEADER_SIGNATURE &&
dv.dvSaveHeaderEx.cbSize >= sizeof(IE4HEADER))
{
InitFromHeader(&dv);
if (dv.dvSaveHeaderEx.wVersion < IE4HEADER_VERSION)
{
// We used to store szExtended in here -- not any more
dv.dvSaveHeaderEx.dwUnused = 0;
}
if (dv.dvSaveHeaderEx.cbColOffset >= sizeof(dv))
{
dlibMove.QuadPart = libStartPos.QuadPart + dv.dvSaveHeaderEx.cbColOffset;
hr = pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
if (SUCCEEDED(hr))
{
hr = LoadColumns(pdv, pstm);
}
}
if (SUCCEEDED(hr))
{
dlibMove.QuadPart = libStartPos.QuadPart + dv.dvSaveHeader.cbPosOffset;
hr = pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
if (SUCCEEDED(hr))
{
LoadPositionBlob(pdv, dv.dvSaveHeaderEx.cbStreamSize, pstm);
}
}
}
return S_OK;
}
void SetSize(ULARGE_INTEGER libCurPosition, IStream* pstm)
{
LARGE_INTEGER dlibMove;
dlibMove.QuadPart = libCurPosition.QuadPart;
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
pstm->SetSize(libCurPosition);
}
DWORD CViewState::_GetStreamSize(IStream* pstm)
{
DWORD dwRet = 0;
ULARGE_INTEGER uli;
if (SUCCEEDED(IStream_Size(pstm, &uli)))
{
if (0 == uli.HighPart)
{
dwRet = uli.LowPart;
}
}
return dwRet;
}
HRESULT CViewState::SaveToStream(CDefView* pdv, IStream* pstm)
{
ULONG ulWrite;
DVSAVEHEADER_COMBO dv = {0};
LARGE_INTEGER dlibMove = {0};
ULARGE_INTEGER libCurPosition;
// Get the current info.
Sync(pdv, FALSE);
// Position the stream right after the headers, and save the starting
// position at the same time
dlibMove.QuadPart = sizeof(dv);
pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libCurPosition);
// Avoid 2 calls to seek by just subtracting
libCurPosition.QuadPart -= sizeof(dv);
// Save column order and size info
HRESULT hr = SaveColumns(pdv, pstm);
if (SUCCEEDED(hr))
{
dv.dvSaveHeader.cbSize = sizeof(dv.dvSaveHeader);
// We save the view mode to determine if the scroll positions are
// still valid on restore
dv.dvSaveHeader.ViewMode = _ViewMode;
dv.dvSaveHeader.ptScroll.x = _ptScroll.x;
dv.dvSaveHeader.ptScroll.y = _ptScroll.y;
dv.dvSaveHeader.dvState.lParamSort = (LONG)_lParamSort;
dv.dvSaveHeader.dvState.iDirection = _iDirection;
dv.dvSaveHeader.dvState.iLastColumnClick = _iLastColumnClick;
// dvSaveHeaderEx.cbColOffset holds the true offset.
// Win95 gets confused when cbColOffset points to the new
// format. Zeroing this out tells Win95 to use default widths
// (after uninstall of ie40).
//
// dv.dvSaveHeader.cbColOffset = 0;
dv.dvSaveHeaderEx.dwSignature = IE4HEADER_SIGNATURE;
dv.dvSaveHeaderEx.cbSize = sizeof(dv.dvSaveHeaderEx);
dv.dvSaveHeaderEx.wVersion = IE4HEADER_VERSION;
ULARGE_INTEGER libPosPosition;
// Save the Position Information
dlibMove.QuadPart = 0;
pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libPosPosition);
dv.dvSaveHeaderEx.cbColOffset = sizeof(dv);
dv.dvSaveHeader.cbPosOffset = (USHORT)(libPosPosition.QuadPart - libCurPosition.QuadPart);
// Save potision info, currently stream is positioned immediately after column info
hr = SavePositionBlob(pdv, pstm);
if (SUCCEEDED(hr))
{
ULARGE_INTEGER libEndPosition;
// Win95 expects cbPosOffset to be at the end of the stream --
// don't change it's value and never store anything after
// the position information.
// Calculate size of total information saved.
// This is needed when we read the stream.
dlibMove.QuadPart = 0;
if (SUCCEEDED(pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libEndPosition)))
{
dv.dvSaveHeaderEx.cbStreamSize = (DWORD)(libEndPosition.QuadPart - libCurPosition.QuadPart);
}
// Now save the header information
dlibMove.QuadPart = libCurPosition.QuadPart;
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
hr = pstm->Write(&dv, sizeof(dv), &ulWrite);
if (FAILED(hr) || ulWrite != sizeof(dv))
{
SetSize(libCurPosition, pstm);
hr = S_OK;
}
// Make sure we save all information written so far
libCurPosition.QuadPart += dv.dvSaveHeaderEx.cbStreamSize;
}
}
return hr;
}
HRESULT CViewState::SaveToPropertyBag(CDefView* pdv, IPropertyBag* ppb)
{
// Get the current info.
Sync(pdv, FALSE);
SHPropertyBag_WriteDWORD(ppb, VS_PROPSTR_MODE, _ViewMode);
SHPropertyBag_WritePOINTSScreenRes(ppb, VS_PROPSTR_SCROLL, &_ptScroll);
SHPropertyBag_WriteDWORD(ppb, VS_PROPSTR_SORT, static_cast<DWORD>(_lParamSort));
SHPropertyBag_WriteInt(ppb, VS_PROPSTR_SORTDIR, _iDirection);
SHPropertyBag_WriteInt(ppb, VS_PROPSTR_COL, _iLastColumnClick);
IStream* pstm = SHCreateMemStream(NULL, 0);
if (pstm)
{
if (S_OK == SaveColumns(pdv, pstm))
{
SHPropertyBag_WriteStream(ppb, VS_PROPSTR_COLINFO, pstm);
}
else
{
SHPropertyBag_Delete(ppb, VS_PROPSTR_COLINFO);
}
pstm->Release();
}
pstm = SHCreateMemStream(NULL, 0);
if (pstm)
{
if (S_OK == SavePositionBlob(pdv, pstm))
{
SHPropertyBag_WriteStreamScreenRes(ppb, VS_PROPSTR_ITEMPOS, pstm);
}
else
{
SHPropertyBag_DeleteScreenRes(ppb, VS_PROPSTR_ITEMPOS);
}
pstm->Release();
}
return S_OK;
}
HRESULT CViewState::LoadFromPropertyBag(CDefView* pdv, IPropertyBag* ppb)
{
SHPropertyBag_ReadDWORDDef(ppb, VS_PROPSTR_MODE, &_ViewMode, FVM_ICON);
SHPropertyBag_ReadDWORDDef(ppb, VS_PROPSTR_SORT, reinterpret_cast<DWORD*>(&_lParamSort), 0);
SHPropertyBag_ReadIntDef(ppb, VS_PROPSTR_SORTDIR, &_iDirection, 1);
SHPropertyBag_ReadIntDef(ppb, VS_PROPSTR_COL, &_iLastColumnClick, -1);
if (FAILED(SHPropertyBag_ReadPOINTSScreenRes(ppb, VS_PROPSTR_SCROLL, &_ptScroll)))
{
_ptScroll.x = _ptScroll.y = 0;
}
IStream* pstm;
if (SUCCEEDED(SHPropertyBag_ReadStream(ppb, VS_PROPSTR_COLINFO, &pstm)))
{
LoadColumns(pdv, pstm);
pstm->Release();
}
if (SUCCEEDED(SHPropertyBag_ReadStreamScreenRes(ppb, VS_PROPSTR_ITEMPOS, &pstm)))
{
LoadPositionBlob(pdv, _GetStreamSize(pstm), pstm);
pstm->Release();
}
return S_OK;
}
HDSA DSA_CreateFromStream(DWORD cbSize, int cItems, IStream* pstm)
{
HDSA hdsa = DSA_Create(cbSize, cItems);
if (hdsa)
{
BYTE* pb = (BYTE*)LocalAlloc(LPTR, cbSize);
if (pb)
{
BOOL fFailedToRead = FALSE;
ULONG cbRead;
while (cItems--)
{
if (SUCCEEDED(pstm->Read(pb, cbSize, &cbRead) && cbRead == cbSize))
{
DSA_AppendItem(hdsa, pb);
}
else
{
fFailedToRead = TRUE;
}
}
LocalFree(pb);
if (fFailedToRead)
{
// The stream is probrably corrupt.
DSA_Destroy(hdsa);
hdsa = NULL;
}
}
}
return hdsa;
}
// When Loading from a View Callback provided stream.
HRESULT CViewState::LoadColumns(CDefView* pdv, IStream* pstm)
{
// Read the extended View state header
HRESULT hr;
ULONG cbRead;
VIEWSTATEHEADER vsh;
ULARGE_INTEGER libStartPos;
LARGE_INTEGER dlibMove = {0};
// Store off the current stream pointer. If we are called directly, this is probrably Zero,
// However this method gets called from ::Load, so this it definitly not zero in that case.
pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libStartPos);
// The VSH struct has many "Substructs" indicating the version of ths struct we are reading.
// There is probrably a more efficient mechanism of version discovery, but this is easiest to read and understand.
hr = pstm->Read(&vsh.Version1, sizeof(vsh.Version1), &cbRead);
if (SUCCEEDED(hr) &&
sizeof(vsh.Version1) == cbRead && // Fail if we didn't read enough
VIEWSTATEHEADER_SIGNATURE == vsh.Version1.dwSignature) // Fail if the signature is bogus
{
if (vsh.Version1.uVersion >= VIEWSTATEHEADER_VERSION_1)
{
if (vsh.Version1.uCols > 0)
{
// Load the Column Ordering
dlibMove.QuadPart = libStartPos.QuadPart + vsh.Version1.uOffsetColOrder;
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
if (_hdsaColumnOrder)
DSA_Destroy(_hdsaColumnOrder);
_hdsaColumnOrder = DSA_CreateFromStream(sizeof(int), vsh.Version1.uCols, pstm);
// Load the Column Widths
dlibMove.QuadPart = libStartPos.QuadPart + vsh.Version1.uOffsetWidths;
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
if (_hdsaColumnWidths)
DSA_Destroy(_hdsaColumnWidths);
_hdsaColumnWidths = DSA_CreateFromStream(sizeof(USHORT), vsh.Version1.uCols, pstm);
}
if (vsh.Version1.uVersion >= VIEWSTATEHEADER_VERSION_2 &&
vsh.Version1.uCols > 0)
{
DWORD dwRead;
// Seek to read the rest of the header
dlibMove.QuadPart = libStartPos.QuadPart + sizeof(vsh.Version1);
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
hr = pstm->Read(&vsh.Version2, sizeof(vsh.Version2), &cbRead);
if (SUCCEEDED(hr) &&
sizeof(vsh.Version2) == cbRead &&
vsh.Version2.uOffsetColStates)
{
// Load the Column States
dlibMove.QuadPart = libStartPos.QuadPart + vsh.Version2.uOffsetColStates;
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
// This one is funky: There is a terminating sentinal....
if (_hdsaColumnStates)
DSA_Destroy(_hdsaColumnStates);
_hdsaColumnStates = DSA_Create(sizeof(DWORD), 5);
if (_hdsaColumnStates)
{
do
{
if (SUCCEEDED(pstm->Read(&dwRead, sizeof(DWORD), &cbRead)) &&
cbRead == sizeof(DWORD) &&
dwRead != 0xFFFFFFFF)
{
DSA_AppendItem(_hdsaColumnStates, &dwRead);
}
else
{
break;
}
}
while (dwRead != 0xFFFFFFFF);
}
}
}
if (vsh.Version1.uVersion >= VIEWSTATEHEADER_VERSION_3)
{
// Seek to read the rest of the header
dlibMove.QuadPart = libStartPos.QuadPart + sizeof(vsh.Version1) + sizeof(vsh.Version2);
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
hr = pstm->Read(&vsh.Version3, sizeof(vsh.Version3), &cbRead);
if (SUCCEEDED(hr) &&
sizeof(vsh.Version3) == cbRead &&
vsh.Version3.uOffsetGroup)
{
GROUP_PERSIST gp;
dlibMove.QuadPart = libStartPos.QuadPart + vsh.Version3.uOffsetGroup;
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
hr = pstm->Read(&gp, sizeof(gp), &cbRead);
if (SUCCEEDED(hr) &&
sizeof(gp) == cbRead)
{
_guidGroupID = gp.guidGroupID;
_scidDetails = gp.scidDetails;
}
}
_fFirstViewed = FALSE;
}
/////////////////////////////////////////////////////////////////////////////////////
// ***** NEW Data *****
// 1) Add a version to the VIEWSTATEHEADER
// 2) Add a version to VIEWSTATEHEADER_VERSION_*
// 3) Check that version here
/////////////////////////////////////////////////////////////////////////////////////
}
}
return hr;
}
HRESULT CViewState::SaveColumns(CDefView* pdv, IStream* pstm)
{
HRESULT hr;
USHORT uOffset;
VIEWSTATEHEADER vsh = {0};
ULARGE_INTEGER libStartPos = {0};
LARGE_INTEGER dlibMove = {0};
// No point in persisting, if there aren't any columns around.
// this is true for folders that are just opened and closed
if (!pdv->_psd && !pdv->_pshf2 && !pdv->HasCB())
{
return S_FALSE;
}
// First, we persist a known bad quantity, just in case we wax the stream
pstm->Seek(g_li0, STREAM_SEEK_CUR, &libStartPos);
hr = pstm->Write(&vsh, sizeof(vsh), NULL);
if (SUCCEEDED(hr))
{
vsh.Version1.dwSignature = VIEWSTATEHEADER_SIGNATURE;
vsh.Version1.uVersion = VIEWSTATEHEADER_VERSION_CURRENT;
vsh.Version1.uCols = _hdsaColumnOrder? (UINT) DSA_GetItemCount(_hdsaColumnOrder) : 0;
uOffset = sizeof(VIEWSTATEHEADER);
// No point in persisting if we don't have any columns
if (vsh.Version1.uCols)
{
// Note- dependent on DSA storing data internally as byte-packed.
if (_hdsaColumnOrder)
{
vsh.Version1.uOffsetColOrder = uOffset;
uOffset += (USHORT)(sizeof(UINT) * DSA_GetItemCount(_hdsaColumnOrder));
hr = pstm->Write(DSA_GetItemPtr(_hdsaColumnOrder, 0), sizeof(UINT) * DSA_GetItemCount(_hdsaColumnOrder), NULL);
}
if (_hdsaColumnWidths && SUCCEEDED(hr))
{
vsh.Version1.uOffsetWidths = uOffset;
uOffset += (USHORT)(sizeof(USHORT) * DSA_GetItemCount(_hdsaColumnWidths));
hr = pstm->Write(DSA_GetItemPtr(_hdsaColumnWidths, 0), sizeof(USHORT) * DSA_GetItemCount(_hdsaColumnWidths), NULL);
}
if (_hdsaColumnStates && SUCCEEDED(hr))
{
vsh.Version2.uOffsetColStates = uOffset;
uOffset += (USHORT)(sizeof(DWORD) * DSA_GetItemCount(_hdsaColumnStates));
pstm->Write(DSA_GetItemPtr(_hdsaColumnStates, 0), sizeof(DWORD) * DSA_GetItemCount(_hdsaColumnStates), NULL);
}
}
if (SUCCEEDED(hr))
{
GROUP_PERSIST gp = {0};
vsh.Version3.uOffsetGroup = uOffset;
uOffset += sizeof(GROUP_PERSIST);
if (pdv->_fGroupView)
{
gp.guidGroupID = _guidGroupID;
gp.scidDetails = _scidDetails;
}
hr = pstm->Write(&gp, sizeof(gp), NULL);
}
/////////////////////////////////////////////////////////////////////////////////////
// ***** NEW Data *****
// 1) Add a version to the VIEWSTATEHEADER
// 2) Add a version to VIEWSTATEHEADER_VERSION_*
// 3) Add a "Loader" for your value
// 4) Set the offset to uOffset.
// 5) Write your data.
// 6) Update the running total of dwOffset.
/////////////////////////////////////////////////////////////////////////////////////
dlibMove.QuadPart = libStartPos.QuadPart;
// Store off the current position
pstm->Seek(g_li0, STREAM_SEEK_CUR, &libStartPos);
// Move to the beginning
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
// Write out the correct header
hr = pstm->Write(&vsh, sizeof(vsh), NULL);
if (SUCCEEDED(hr))
{
// Reset the current pos
dlibMove.QuadPart = libStartPos.QuadPart;
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
}
}
return hr;
}
BOOL CViewState::AppendColumn(UINT uCol, USHORT uWidth, INT uOrder)
{
if (_hdsaColumnOrder == NULL ||
_hdsaColumnWidths == NULL)
{
return FALSE;
}
// Slide every index above this one up
for (INT u = 0; u < DSA_GetItemCount(_hdsaColumnOrder); u++)
{
UINT *p = (UINT *) DSA_GetItemPtr(_hdsaColumnOrder, u);
if (!p)
break; // safety...
if (*p >= uCol)
(*p)++;
}
DSA_AppendItem(_hdsaColumnWidths, &uWidth);
DSA_AppendItem(_hdsaColumnOrder, &uOrder);
// maybe we should store column ordering as absolute numbers
return TRUE;
}
BOOL CViewState::RemoveColumn(UINT uCol)
{
if (_hdsaColumnWidths == NULL ||
_hdsaColumnWidths == NULL)
{
return FALSE;
}
if ((int)uCol >= DSA_GetItemCount(_hdsaColumnWidths))
return FALSE;
// Slide every index above this one down
for (INT u = 0; u < DSA_GetItemCount(_hdsaColumnOrder); u++)
{
UINT *p = (UINT *) DSA_GetItemPtr(_hdsaColumnOrder, u);
if (!p)
break; // safety...
if (*p > uCol)
(*p)--;
}
DSA_DeleteItem(_hdsaColumnWidths, uCol);
DSA_DeleteItem(_hdsaColumnOrder, uCol);
return TRUE;
}
UINT CViewState::GetColumnWidth(UINT uCol, UINT uDefWid)
{
if (!_hdsaColumnWidths)
return uDefWid;
USHORT uWidth = 0;
if (uCol < (UINT) DSA_GetItemCount(_hdsaColumnWidths))
{
DSA_GetItem(_hdsaColumnWidths, uCol, &uWidth);
}
return uWidth ? uWidth : uDefWid; // disallow zero width columns
}
BOOL CViewState::SyncColumnOrder(CDefView* pdv, BOOL fSetListViewState)
{
UINT cCols = pdv->_GetHeaderCount();
if (fSetListViewState)
{
if (!_hdsaColumnOrder)
return FALSE;
if (cCols != (UINT) DSA_GetItemCount(_hdsaColumnOrder))
{
// this is a normal case if a folder is opened and there is no saved state. no need to spew.
return TRUE;
}
UINT *pCols = (UINT *)LocalAlloc(LPTR, cCols * sizeof(*pCols));
if (pCols)
{
for (UINT u = 0; u < cCols; u++)
{
DSA_GetItem(_hdsaColumnOrder, u, pCols + u);
}
ListView_SetColumnOrderArray(pdv->_hwndListview, cCols, pCols);
LocalFree(pCols);
}
}
else
{
BOOL bDefaultOrder = TRUE;
if (cCols)
{
if (!_hdsaColumnOrder)
_hdsaColumnOrder = DSA_Create(sizeof(UINT), 6);
if (_hdsaColumnOrder)
{
UINT *pCols = (UINT *)LocalAlloc(LPTR, cCols * sizeof(*pCols));
if (pCols)
{
ListView_GetColumnOrderArray(pdv->_hwndListview, cCols, pCols);
DSA_DeleteAllItems(_hdsaColumnOrder);
for (UINT u = 0; u < cCols; u++)
{
DSA_AppendItem(_hdsaColumnOrder, &pCols[u]);
if (pCols[u] != u)
{
bDefaultOrder = FALSE;
}
}
LocalFree(pCols);
}
}
}
return bDefaultOrder;
}
return TRUE;
}
BOOL CViewState::SyncColumnWidths(CDefView* pdv, BOOL fSetListViewState)
{
UINT cCols = pdv->_GetHeaderCount();
if (fSetListViewState)
{
return FALSE;
}
else
{
USHORT us;
LV_COLUMN lvc;
BOOL bOk = TRUE;
if (!cCols)
return TRUE;
HDSA dsaNewWidths = DSA_Create(sizeof(USHORT), cCols);
if (!dsaNewWidths)
return TRUE;
for (UINT u = 0; u < cCols && bOk; ++u)
{
lvc.mask = LVCF_WIDTH;
bOk = ListView_GetColumn(pdv->_hwndListview, u, &lvc);
us = (USHORT) lvc.cx; // make sure its a short
DSA_AppendItem(dsaNewWidths, &us);
// TraceMsg(TF_DEFVIEW, " saving col %d width of %d", u, us);
}
if (bOk)
{
if (_hdsaColumnWidths)
DSA_Destroy(_hdsaColumnWidths);
_hdsaColumnWidths = dsaNewWidths;
}
else
DSA_Destroy(dsaNewWidths);
return !bOk;
}
}
BOOL CViewState::SyncColumnStates(CDefView* pdv, BOOL fSetListViewstate)
{
if (fSetListViewstate)
{
return FALSE;
}
else
{
// Save off Column States
if (_hdsaColumnStates)
{
DSA_Destroy(_hdsaColumnStates);
_hdsaColumnStates = NULL;
}
UINT cCol = GetColumnCount();
if (cCol)
{
DWORD i;
_hdsaColumnStates = DSA_Create(sizeof(DWORD), 5);
if (_hdsaColumnStates)
{
for (i = 0; i < cCol; i++)
{
if (pdv->_IsDetailsColumn(i))
DSA_AppendItem(_hdsaColumnStates, &i);
}
i = 0xFFFFFFFF; // Terminating Sentinal
DSA_AppendItem(_hdsaColumnStates,&i);
}
}
}
return TRUE;
}
// Syncronizes ListView with the current View State.
// TRUE means take the view state object and set it into the listview.
HRESULT CViewState::Sync(CDefView* pdv, BOOL fSetListViewState)
{
SyncColumnWidths(pdv, fSetListViewState);
SyncColumnOrder(pdv, fSetListViewState);
SyncColumnStates(pdv, fSetListViewState);
if (fSetListViewState)
{
// Only do this the first time.
if (pdv->_pcat == NULL)
{
if (_fFirstViewed)
{
// See if the desktop.ini specifies one
pdv->_LoadCategory(&_guidGroupID);
if (IsEqualGUID(_guidGroupID, GUID_NULL))
{
ICategoryProvider* pcp;
if (SUCCEEDED(pdv->_pshf->CreateViewObject(NULL, IID_PPV_ARG(ICategoryProvider, &pcp))))
{
pcp->GetDefaultCategory(&_guidGroupID, &_scidDetails);
pcp->Release();
}
}
}
if (!IsEqualGUID(_guidGroupID, GUID_NULL) || !IsEqualGUID(_scidDetails.fmtid, GUID_NULL))
pdv->_CategorizeOnGUID(&_guidGroupID, &_scidDetails);
}
// this is only needed to sort the items who's positions are not known
// it would be nice to optimize this case and only sort then
pdv->_Sort();
SyncPositions(pdv);
}
else
{
// Take what Listview has, and save it to me.
_ViewMode = pdv->_fs.ViewMode;
_ptScroll.x = (SHORT) GetScrollPos(pdv->_hwndListview, SB_HORZ);
_ptScroll.y = (SHORT) GetScrollPos(pdv->_hwndListview, SB_VERT);
}
return S_OK;
}