|
|
#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; }
|