[SHELL32][SHLWAPI][BROWSEUI][EXPLORER] Save folder view state (#7127)

Saves/restores the Listview icon mode, columns and sort info per-folder.
This commit is contained in:
Whindmar Saksit 2024-07-19 14:40:20 +02:00 committed by GitHub
parent fa95a96e9b
commit 802dc9714b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 365 additions and 48 deletions

View File

@ -660,8 +660,18 @@ public:
return S_OK;
}
void SaveState()
{
if (SHRestricted(REST_NOSAVESET))
return;
SendMessage(m_DesktopWnd, WM_PROGMAN_SAVESTATE, 0, 0);
}
LRESULT DoExitWindows()
{
SaveState();
/* Display the ReactOS Shutdown Dialog */
ExitWindowsDialog(m_hWnd);
@ -983,6 +993,7 @@ public:
DisplayRunFileDlg();
break;
case TRAYCMD_LOGOFF_DIALOG:
SaveState();
LogoffWindowsDialog(m_hWnd); // FIXME: Maybe handle it in a similar way as DoExitWindows?
break;
case TRAYCMD_CASCADE:
@ -2629,6 +2640,13 @@ ChangePos:
return 0;
}
LRESULT OnEndSession(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if (wParam)
SaveState();
return 0;
}
LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if (m_Theme)
@ -3581,6 +3599,7 @@ HandleTrayContextMenu:
MESSAGE_HANDLER(WM_SIZE, OnSize)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_ENDSESSION, OnEndSession)
MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
MESSAGE_HANDLER(WM_COMMAND, OnCommand)
MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)

View File

@ -332,6 +332,7 @@ public:
HRESULT BrowseToPIDL(LPCITEMIDLIST pidl, long flags);
HRESULT BrowseToPath(IShellFolder *newShellFolder, LPCITEMIDLIST absolutePIDL,
FOLDERSETTINGS *folderSettings, long flags);
void SaveViewState();
HRESULT GetMenuBand(REFIID riid, void **shellMenu);
HRESULT GetBaseBar(bool vertical, REFIID riid, void **theBaseBar);
BOOL IsBandLoaded(const CLSID clsidBand, bool vertical, DWORD *pdwBandID);
@ -406,7 +407,7 @@ public:
// *** IServiceProvider methods ***
STDMETHOD(QueryService)(REFGUID guidService, REFIID riid, void **ppvObject) override;
// *** IShellBowserService methods ***
// *** IShellBrowserService methods ***
STDMETHOD(GetPropertyBag)(long flags, REFIID riid, void **ppvObject) override;
// *** IDispatch methods ***
@ -826,6 +827,8 @@ HRESULT CShellBrowser::ApplyBrowserDefaultFolderSettings(IShellView *pvs)
{
m_settings.Reset();
hr = CGlobalFolderSettings::ResetBrowserSettings();
if (SUCCEEDED(hr))
m_deffoldersettings.Load();
}
return hr;
}
@ -995,6 +998,13 @@ HRESULT IEGetNameAndFlags(LPITEMIDLIST pidl, SHGDNF uFlags, LPWSTR pszBuf, UINT
return IEGetNameAndFlagsEx(pidl, uFlags, 0, pszBuf, cchBuf, rgfInOut);
}
void CShellBrowser::SaveViewState()
{
// TODO: Also respect EBO_NOPERSISTVIEWSTATE?
if (gCabinetState.fSaveLocalView && fCurrentShellView && !SHRestricted(REST_NOSAVESET))
fCurrentShellView->SaveViewState();
}
HRESULT CShellBrowser::BrowseToPath(IShellFolder *newShellFolder,
LPCITEMIDLIST absolutePIDL, FOLDERSETTINGS *folderSettings, long flags)
{
@ -1029,6 +1039,7 @@ HRESULT CShellBrowser::BrowseToPath(IShellFolder *newShellFolder,
if (fCurrentShellView)
{
SaveViewState();
fCurrentShellView->UIActivate(SVUIA_DEACTIVATE);
}
@ -2204,6 +2215,7 @@ HRESULT STDMETHODCALLTYPE CShellBrowser::Exec(const GUID *pguidCmdGroup, DWORD n
switch (nCmdID)
{
case DVCMDID_RESET_DEFAULTFOLDER_SETTINGS:
ApplyBrowserDefaultFolderSettings(NULL);
IUnknown_Exec(fCurrentShellView, CGID_DefView, nCmdID, OLECMDEXECOPT_DODEFAULT, NULL, NULL);
break;
}
@ -3615,6 +3627,7 @@ LRESULT CShellBrowser::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &
{
fToolbarProxy.Destroy();
SaveViewState();
fCurrentShellView->DestroyViewWindow();
fCurrentShellView->UIActivate(SVUIA_DEACTIVATE);

View File

@ -26,7 +26,6 @@
/*
TODO:
- Load/Save the view state from/into the stream provided by the ShellBrowser unless FWF_NOBROWSERVIEWSTATE is set.
- When editing starts on item, set edit text to for editing value.
- Fix shell view to handle view mode popup exec.
- The background context menu should have a pidl just like foreground menus. This
@ -60,8 +59,18 @@ enum {
typedef struct
{
BOOL bIsAscending;
INT8 Direction;
bool bLoadedFromViewState;
bool bColumnIsFolderColumn;
UINT8 Reserved; // Unused
INT ListColumn;
enum { UNSPECIFIEDCOLUMN = -1 };
void Reset()
{
*(UINT*)this = 0;
ListColumn = UNSPECIFIEDCOLUMN;
}
} LISTVIEW_SORT_INFO, *LPLISTVIEW_SORT_INFO;
#define SHV_CHANGE_NOTIFY (WM_USER + 0x1111)
@ -71,6 +80,25 @@ typedef struct
// to call TrackPopupMenu and let it use the 0 value as an indication that the menu was canceled
#define CONTEXT_MENU_BASE_ID 1
struct PERSISTCOLUMNS
{
enum { MAXCOUNT = 100 };
static const UINT SIG = ('R' << 0) | ('O' << 8) | ('S' << 16) | (('c') << 24);
UINT Signature;
UINT Count;
UINT Columns[MAXCOUNT];
};
struct PERSISTCLASSICVIEWSTATE
{
static const UINT SIG = ('R' << 0) | ('O' << 8) | ('S' << 16) | (('c' ^ 'v' ^ 's') << 24);
UINT Signature;
WORD SortColId;
INT8 SortDir;
static const UINT VALIDFWF = FWF_AUTOARRANGE | FWF_SNAPTOGRID | FWF_NOGROUPING; // Note: The desktop applies FWF_NOICONS when appropriate
FOLDERSETTINGS FolderSettings;
};
static UINT
GetContextMenuFlags(IShellBrowser *pSB, SFGAOF sfgao)
{
@ -215,6 +243,7 @@ private:
UINT m_cidl;
PCUITEMID_CHILD *m_apidl;
PIDLIST_ABSOLUTE m_pidlParent;
HDPA m_LoadColumnsList;
HDPA m_ListToFolderColMap;
LISTVIEW_SORT_INFO m_sortInfo;
ULONG m_hNotify; // Change notification handle
@ -274,8 +303,8 @@ public:
HRESULT MapListColumnToFolderColumn(UINT ListCol);
HRESULT GetDetailsByFolderColumn(PCUITEMID_CHILD pidl, UINT FoldCol, SHELLDETAILS &sd);
HRESULT GetDetailsByListColumn(PCUITEMID_CHILD pidl, UINT ListCol, SHELLDETAILS &sd);
HRESULT LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert);
HRESULT LoadColumns(UINT *pColList = NULL, UINT ColListCount = 0);
HRESULT LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert, UINT ForceWidth = 0);
HRESULT LoadColumns(SIZE_T *pColList = NULL, UINT ColListCount = 0);
void ColumnListChanged();
PCUITEMID_CHILD _PidlByItem(int i);
PCUITEMID_CHILD _PidlByItem(LVITEM& lvItem);
@ -301,6 +330,11 @@ public:
HRESULT drag_notify_subitem(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
HRESULT InvokeContextMenuCommand(CComPtr<IContextMenu>& pCM, LPCSTR lpVerb, POINT* pt = NULL);
LRESULT OnExplorerCommand(UINT uCommand, BOOL bUseSelection);
FOLDERVIEWMODE GetDefaultViewMode();
HRESULT GetDefaultViewStream(DWORD Stgm, IStream **ppStream);
HRESULT LoadViewState();
HRESULT SaveViewState(IStream *pStream);
void UpdateFolderViewFlags();
// *** IOleWindow methods ***
STDMETHOD(GetWindow)(HWND *lphwnd) override;
@ -529,6 +563,7 @@ CDefView::CDefView() :
m_cidl(0),
m_apidl(NULL),
m_pidlParent(NULL),
m_LoadColumnsList(NULL),
m_hNotify(0),
m_hAccel(NULL),
m_dwAspects(0),
@ -541,13 +576,13 @@ CDefView::CDefView() :
m_Destroyed(FALSE)
{
ZeroMemory(&m_FolderSettings, sizeof(m_FolderSettings));
ZeroMemory(&m_sortInfo, sizeof(m_sortInfo));
ZeroMemory(&m_ptLastMousePos, sizeof(m_ptLastMousePos));
ZeroMemory(&m_Category, sizeof(m_Category));
m_viewinfo_data.clrText = GetSysColor(COLOR_WINDOWTEXT);
m_viewinfo_data.clrTextBack = GetSysColor(COLOR_WINDOW);
m_viewinfo_data.hbmBack = NULL;
m_sortInfo.Reset();
m_ListToFolderColMap = DPA_Create(0);
m_hMyComputerIcon = LoadIconW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_COMPUTER_DESKTOP));
}
@ -570,6 +605,7 @@ CDefView::~CDefView()
}
SHFree(m_apidl);
DPA_Destroy(m_LoadColumnsList);
DPA_Destroy(m_ListToFolderColMap);
}
@ -757,20 +793,21 @@ BOOL CDefView::CreateList()
TRACE("%p\n", this);
dwStyle = WS_TABSTOP | WS_VISIBLE | WS_CHILDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
LVS_SHAREIMAGELISTS | LVS_EDITLABELS | LVS_AUTOARRANGE; // FIXME: Why is LVS_AUTOARRANGE here?
dwExStyle = WS_EX_CLIENTEDGE;
ListExStyle = 0;
LVS_SHAREIMAGELISTS | LVS_EDITLABELS | LVS_AUTOARRANGE; // FIXME: Remove LVS_AUTOARRANGE when the view is able to save ItemPos
dwExStyle = (m_FolderSettings.fFlags & FWF_NOCLIENTEDGE) ? 0 : WS_EX_CLIENTEDGE;
ListExStyle = LVS_EX_INFOTIP | LVS_EX_LABELTIP;
if (m_FolderSettings.fFlags & FWF_DESKTOP)
{
m_FolderSettings.fFlags |= FWF_NOCLIENTEDGE | FWF_NOSCROLL;
dwStyle |= LVS_ALIGNLEFT;
// LVS_EX_REGIONAL?
}
else
{
dwStyle |= LVS_SHOWSELALWAYS; // MSDN says FWF_SHOWSELALWAYS is deprecated, always turn on for folders
dwStyle |= (m_FolderSettings.fFlags & FWF_ALIGNLEFT) ? LVS_ALIGNLEFT : LVS_ALIGNTOP;
ListExStyle = LVS_EX_DOUBLEBUFFER;
ListExStyle |= LVS_EX_DOUBLEBUFFER;
}
ViewMode = m_FolderSettings.ViewMode;
@ -839,9 +876,6 @@ BOOL CDefView::CreateList()
m_ListView.SetExtendedListViewStyle(ListExStyle);
m_sortInfo.bIsAscending = TRUE;
m_sortInfo.ListColumn = -1;
/* UpdateShellSettings(); */
return TRUE;
}
@ -916,7 +950,16 @@ BOOL CDefView::InitList()
m_ListView.SetImageList(small_icons, LVSIL_SMALL);
m_hMenuArrangeModes = CreateMenu();
LoadColumns();
SIZE_T *pColumns = m_LoadColumnsList ? (SIZE_T*)DPA_GetPtrPtr(m_LoadColumnsList) : NULL;
UINT ColumnCount = pColumns ? DPA_GetPtrCount(m_LoadColumnsList) : 0;
LoadColumns(pColumns, ColumnCount);
if (m_sortInfo.bColumnIsFolderColumn)
{
m_sortInfo.bColumnIsFolderColumn = FALSE;
HRESULT hr = MapFolderColumnToListColumn(m_sortInfo.ListColumn);
m_sortInfo.ListColumn = SUCCEEDED(hr) ? hr : 0;
}
return TRUE;
}
@ -998,7 +1041,7 @@ HRESULT CDefView::GetDetailsByListColumn(PCUITEMID_CHILD pidl, UINT ListCol, SHE
return hr;
}
HRESULT CDefView::LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert)
HRESULT CDefView::LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert, UINT ForceWidth)
{
WCHAR buf[MAX_PATH];
SHELLDETAILS sd;
@ -1020,7 +1063,7 @@ HRESULT CDefView::LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert)
lvc.mask = LVCF_TEXT | LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM;
lvc.pszText = buf;
lvc.fmt = sd.fmt;
lvc.cx = sd.cxChar * chavewidth; // FIXME: DPI?
lvc.cx = ForceWidth ? ForceWidth : (sd.cxChar * chavewidth); // FIXME: DPI?
lvc.iSubItem = FoldCol; // Used by MapFolderColumnToListColumn & MapListColumnToFolderColumn
if ((int)ListCol == -1)
{
@ -1035,11 +1078,11 @@ HRESULT CDefView::LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert)
return S_OK;
}
HRESULT CDefView::LoadColumns(UINT *pColList, UINT ColListCount)
HRESULT CDefView::LoadColumns(SIZE_T *pColList, UINT ColListCount)
{
HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
UINT newColCount = 0, oldColCount = Header_GetItemCount(hWndHdr);
UINT foldCol, i;
UINT width = 0, foldCol, i;
HRESULT hr = S_FALSE;
m_ListView.SetRedraw(FALSE);
@ -1052,7 +1095,8 @@ HRESULT CDefView::LoadColumns(UINT *pColList, UINT ColListCount)
{
if (i >= ColListCount)
break;
foldCol = pColList[i++];
width = HIWORD(pColList[i]);
foldCol = LOWORD(pColList[i++]);
}
SHCOLSTATEF state = 0;
@ -1074,7 +1118,7 @@ HRESULT CDefView::LoadColumns(UINT *pColList, UINT ColListCount)
bool insert = newColCount >= oldColCount;
UINT listCol = insert ? -1 : newColCount;
hr = LoadColumn(foldCol, listCol, insert);
hr = LoadColumn(foldCol, listCol, insert, width);
if (FAILED(hr))
{
if (!pColList)
@ -1091,6 +1135,11 @@ HRESULT CDefView::LoadColumns(UINT *pColList, UINT ColListCount)
m_ListView.SetRedraw(TRUE);
ColumnListChanged();
assert(SUCCEEDED(MapFolderColumnToListColumn(0))); // We don't allow turning off the Name column
if (m_LoadColumnsList)
{
DPA_Destroy(m_LoadColumnsList);
m_LoadColumnsList = NULL;
}
return hr;
}
@ -1170,9 +1219,7 @@ INT CALLBACK CDefView::ListViewCompareItems(LPARAM lParam1, LPARAM lParam2, LPAR
return 0;
SHORT nDiff = HRESULT_CODE(hres);
if (!pThis->m_sortInfo.bIsAscending)
nDiff = -nDiff;
return nDiff;
return nDiff * pThis->m_sortInfo.Direction;
}
BOOL CDefView::_Sort(int Col)
@ -1180,6 +1227,7 @@ BOOL CDefView::_Sort(int Col)
HWND hHeader;
HDITEM hColumn;
int prevCol = m_sortInfo.ListColumn;
m_sortInfo.bLoadedFromViewState = FALSE;
// FIXME: Is this correct? Who sets this style?
// And if it is set, should it also block sorting using the menu?
@ -1197,11 +1245,13 @@ BOOL CDefView::_Sort(int Col)
}
if (prevCol == Col)
m_sortInfo.bIsAscending = !m_sortInfo.bIsAscending;
m_sortInfo.Direction *= -1;
else
m_sortInfo.bIsAscending = TRUE;
m_sortInfo.Direction = 0;
m_sortInfo.ListColumn = Col;
}
if (!m_sortInfo.Direction)
m_sortInfo.Direction += 1;
/* If the sorting column changed, remove the sorting style from the old column */
if (prevCol != -1 && prevCol != m_sortInfo.ListColumn)
@ -1215,11 +1265,12 @@ BOOL CDefView::_Sort(int Col)
/* Set the sorting style on the new column */
hColumn.mask = HDI_FORMAT;
Header_GetItem(hHeader, m_sortInfo.ListColumn, &hColumn);
hColumn.fmt &= (m_sortInfo.bIsAscending ? ~HDF_SORTDOWN : ~HDF_SORTUP );
hColumn.fmt |= (m_sortInfo.bIsAscending ? HDF_SORTUP : HDF_SORTDOWN);
hColumn.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
hColumn.fmt |= (m_sortInfo.Direction > 0 ? HDF_SORTUP : HDF_SORTDOWN);
Header_SetItem(hHeader, m_sortInfo.ListColumn, &hColumn);
/* Sort the list, using the current values of ListColumn and bIsAscending */
ASSERT(m_sortInfo.Direction == 1 || m_sortInfo.Direction == -1);
return m_ListView.SortItems(ListViewCompareItems, this);
}
@ -1453,9 +1504,9 @@ HRESULT CDefView::FillList(BOOL IsRefreshCommand)
/* sort the array */
int sortCol = -1;
if (!IsRefreshCommand) // Are we loading for the first time?
if (!IsRefreshCommand && !m_sortInfo.bLoadedFromViewState) // Are we loading for the first time?
{
m_sortInfo.bIsAscending = TRUE;
m_sortInfo.Direction = 0;
sortCol = 0; // In case the folder does not know/care
if (m_pSF2Parent)
{
@ -3073,11 +3124,190 @@ HRESULT WINAPI CDefView::AddPropertySheetPages(DWORD dwReserved, LPFNADDPROPSHEE
return S_OK;
}
static HRESULT Read(IStream *pS, LPVOID buffer, ULONG cb)
{
ULONG read;
HRESULT hr = pS->Read(buffer, cb, &read);
return FAILED(hr) ? hr : (cb == read ? S_OK : HResultFromWin32(ERROR_MORE_DATA));
}
static DWORD ReadDWORD(IPropertyBag *pPB, LPCWSTR name, DWORD def)
{
DWORD value;
HRESULT hr = SHPropertyBag_ReadDWORD(pPB, name, &value);
return SUCCEEDED(hr) ? value : def;
}
HRESULT CDefView::GetDefaultViewStream(DWORD Stgm, IStream **ppStream)
{
CLSID clsid;
HRESULT hr = IUnknown_GetClassID(m_pSFParent, &clsid);
if (SUCCEEDED(hr))
{
WCHAR path[MAX_PATH], name[39];
wsprintfW(path, L"%s\\%s", REGSTR_PATH_EXPLORER, L"Streams\\Default");
StringFromGUID2(clsid, name, 39);
*ppStream = SHOpenRegStream2W(HKEY_CURRENT_USER, path, name, Stgm);
hr = *ppStream ? S_OK : E_FAIL;
}
return hr;
}
static HRESULT LoadColumnsStream(PERSISTCOLUMNS &cols, IStream *pS)
{
HRESULT hr = Read(pS, &cols, FIELD_OFFSET(PERSISTCOLUMNS, Columns));
if (FAILED(hr))
return hr;
if (cols.Signature != PERSISTCOLUMNS::SIG || cols.Count > cols.MAXCOUNT)
return HResultFromWin32(ERROR_INVALID_DATA);
return Read(pS, &cols.Columns, sizeof(*cols.Columns) * cols.Count);
}
HRESULT CDefView::LoadViewState()
{
PERSISTCLASSICVIEWSTATE cvs;
PERSISTCOLUMNS cols;
CComPtr<IStream> pS;
CComPtr<IPropertyBag> pPB;
bool fallback = false;
HRESULT hrColumns = E_FAIL;
HRESULT hr = IUnknown_QueryServicePropertyBag(m_pShellBrowser, SHGVSPB_FOLDER, IID_PPV_ARG(IPropertyBag, &pPB));
if (SUCCEEDED(hr))
{
DWORD data;
if (FAILED(hr = SHPropertyBag_ReadDWORD(pPB, L"Mode", &data)))
goto loadfallback;
cvs.FolderSettings.ViewMode = data;
cvs.FolderSettings.fFlags = ReadDWORD(pPB, L"FFlags", FWF_NOGROUPING);
data = ReadDWORD(pPB, L"Sort", ~0ul);
cvs.SortColId = data != ~0ul ? (WORD)data : LISTVIEW_SORT_INFO::UNSPECIFIEDCOLUMN;
cvs.SortDir = (INT8)ReadDWORD(pPB, L"SortDir", 1);
if (SUCCEEDED(hrColumns = SHPropertyBag_ReadStream(pPB, L"ColInfo", &pS)))
hrColumns = LoadColumnsStream(cols, pS);
}
else
{
if (FAILED(hr = (m_pShellBrowser ? m_pShellBrowser->GetViewStateStream(STGM_READ, &pS) : E_UNEXPECTED)))
{
loadfallback:
hr = GetDefaultViewStream(STGM_READ, &pS);
fallback = true;
}
if (FAILED(hr) || FAILED(hr = Read(pS, &cvs, sizeof(cvs))))
return hr;
if (cvs.Signature != cvs.SIG)
return HResultFromWin32(ERROR_INVALID_DATA);
hrColumns = LoadColumnsStream(cols, pS);
}
m_FolderSettings.ViewMode = cvs.FolderSettings.ViewMode;
m_FolderSettings.fFlags &= ~cvs.VALIDFWF;
m_FolderSettings.fFlags |= cvs.FolderSettings.fFlags & cvs.VALIDFWF;
if (SUCCEEDED(hrColumns))
{
BOOL failed = FALSE;
if ((m_LoadColumnsList = DPA_Create(cols.Count)) != NULL)
{
for (UINT i = 0; i < cols.Count; ++i)
{
failed |= !DPA_SetPtr(m_LoadColumnsList, i, UlongToPtr(cols.Columns[i]));
}
}
if (failed || !cols.Count)
{
DPA_Destroy(m_LoadColumnsList);
m_LoadColumnsList = NULL;
}
}
m_sortInfo.bLoadedFromViewState = !fallback && m_LoadColumnsList && cvs.SortColId != LISTVIEW_SORT_INFO::UNSPECIFIEDCOLUMN;
m_sortInfo.bColumnIsFolderColumn = TRUE;
m_sortInfo.Direction = cvs.SortDir > 0 ? 1 : -1;
m_sortInfo.ListColumn = cvs.SortColId;
return hr;
}
HRESULT CDefView::SaveViewState(IStream *pStream)
{
int sortcol = MapListColumnToFolderColumn(m_sortInfo.ListColumn);
PERSISTCLASSICVIEWSTATE cvs;
cvs.SortColId = sortcol >= 0 ? (WORD)sortcol : 0;
cvs.SortDir = m_sortInfo.Direction;
PERSISTCOLUMNS cols;
cols.Signature = PERSISTCOLUMNS::SIG;
cols.Count = 0;
LVCOLUMN lvc;
lvc.mask = LVCF_WIDTH | LVCF_SUBITEM;
for (UINT i = 0, j = 0; i < PERSISTCOLUMNS::MAXCOUNT && ListView_GetColumn(m_ListView, j, &lvc); ++j)
{
HRESULT hr = MapListColumnToFolderColumn(lvc.iSubItem);
if (SUCCEEDED(hr))
{
cols.Columns[i] = MAKELONG(hr, lvc.cx);
cols.Count = ++i;
}
}
UINT cbColumns = FIELD_OFFSET(PERSISTCOLUMNS, Columns) + (sizeof(*cols.Columns) * cols.Count);
UpdateFolderViewFlags();
IPropertyBag *pPB;
HRESULT hr = S_OK;
if (pStream)
{
pStream->AddRef();
goto stream;
}
hr = IUnknown_QueryServicePropertyBag(m_pShellBrowser, SHGVSPB_FOLDER, IID_PPV_ARG(IPropertyBag, &pPB));
if (SUCCEEDED(hr))
{
UINT uViewMode;
GetCurrentViewMode(&uViewMode);
hr = SHPropertyBag_WriteDWORD(pPB, L"Mode", uViewMode);
SHPropertyBag_WriteDWORD(pPB, L"FFlags", m_FolderSettings.fFlags);
SHPropertyBag_WriteDWORD(pPB, L"Sort", cvs.SortColId);
SHPropertyBag_WriteDWORD(pPB, L"SortDir", cvs.SortDir);
pStream = cols.Count ? SHCreateMemStream((LPBYTE)&cols, cbColumns) : NULL;
if (!pStream || FAILED(SHPropertyBag_WriteStream(pPB, L"ColInfo", pStream)))
SHPropertyBag_Delete(pPB, L"ColInfo");
#if 0 // TODO
WCHAR name[MAX_PATH];
memcpy(name, L"ItemPos", sizeof(L"ItemPos"));
if (SHGetPerScreenResName(name + 7, _countof(name) - 7, 0))
{
if (GetAutoArrange() == S_FALSE)
// TODO: Save listview item positions
else
SHPropertyBag_Delete(pPB, name);
}
#endif
pPB->Release();
}
else if (SUCCEEDED(hr = (m_pShellBrowser ? m_pShellBrowser->GetViewStateStream(STGM_WRITE, &pStream) : E_UNEXPECTED)))
{
stream:
ULONG written;
cvs.Signature = cvs.SIG;
cvs.FolderSettings = m_FolderSettings;
hr = pStream->Write(&cvs, sizeof(cvs), &written);
if (SUCCEEDED(hr))
hr = pStream->Write(&cols, cbColumns, &written);
}
if (pStream)
pStream->Release();
return hr;
}
HRESULT WINAPI CDefView::SaveViewState()
{
FIXME("(%p) stub\n", this);
if (!(m_FolderSettings.fFlags & FWF_NOBROWSERVIEWSTATE))
return SaveViewState(NULL);
return S_FALSE;
}
return S_OK;
#define UPDATEFOLDERVIEWFLAGS(bits, bit, set) ( (bits) = ((bits) & ~(bit)) | ((set) ? (bit) : 0) )
void CDefView::UpdateFolderViewFlags()
{
UPDATEFOLDERVIEWFLAGS(m_FolderSettings.fFlags, FWF_AUTOARRANGE, GetAutoArrange() == S_OK);
UPDATEFOLDERVIEWFLAGS(m_FolderSettings.fFlags, FWF_SNAPTOGRID, _GetSnapToGrid() == S_OK);
UPDATEFOLDERVIEWFLAGS(m_FolderSettings.fFlags, FWF_NOGROUPING, !ListView_IsGroupViewEnabled(m_ListView.m_hWnd));
}
HRESULT WINAPI CDefView::SelectItem(PCUITEMID_CHILD pidl, UINT uFlags)
@ -3186,6 +3416,15 @@ HRESULT WINAPI CDefView::GetItemObject(UINT uItem, REFIID riid, LPVOID *ppvOut)
return hr;
}
FOLDERVIEWMODE CDefView::GetDefaultViewMode()
{
FOLDERVIEWMODE mode = ((m_FolderSettings.fFlags & FWF_DESKTOP) || !IsOS(OS_SERVERADMINUI)) ? FVM_ICON : FVM_DETAILS;
FOLDERVIEWMODE temp;
if (SUCCEEDED(_DoFolderViewCB(SFVM_DEFVIEWMODE, 0, (LPARAM)&temp)) && temp >= FVM_FIRST && temp <= FVM_LAST)
mode = temp;
return mode;
}
HRESULT STDMETHODCALLTYPE CDefView::GetCurrentViewMode(UINT *pViewMode)
{
TRACE("(%p)->(%p), stub\n", this, pViewMode);
@ -3422,6 +3661,8 @@ HRESULT STDMETHODCALLTYPE CDefView::CreateViewWindow2(LPSV2CVW2_PARAMS view_para
HRESULT STDMETHODCALLTYPE CDefView::CreateViewWindow3(IShellBrowser *psb, IShellView *psvPrevious, SV3CVW3_FLAGS view_flags, FOLDERFLAGS mask, FOLDERFLAGS flags, FOLDERVIEWMODE mode, const SHELLVIEWID *view_id, const RECT *prcView, HWND *hwnd)
{
OLEMENUGROUPWIDTHS omw = { { 0, 0, 0, 0, 0, 0 } };
const UINT SUPPORTED_SV3CVW3 = SV3CVW3_FORCEVIEWMODE | SV3CVW3_FORCEFOLDERFLAGS;
const UINT IGNORE_FWF = FWF_OWNERDATA; // FIXME: Support this
*hwnd = NULL;
@ -3433,13 +3674,16 @@ HRESULT STDMETHODCALLTYPE CDefView::CreateViewWindow3(IShellBrowser *psb, IShell
if (psb == NULL || m_hWnd)
return E_UNEXPECTED;
if (view_flags != SV3CVW3_DEFAULT)
FIXME("unsupported view flags 0x%08x\n", view_flags);
if (view_flags & ~SUPPORTED_SV3CVW3)
FIXME("unsupported view flags 0x%08x\n", view_flags & ~SUPPORTED_SV3CVW3);
if (mode == FVM_AUTO)
mode = GetDefaultViewMode();
/* Set up the member variables */
m_pShellBrowser = psb;
m_FolderSettings.ViewMode = mode;
m_FolderSettings.fFlags = mask & flags;
m_FolderSettings.fFlags = (mask & flags) & ~IGNORE_FWF;
if (view_id)
{
@ -3460,6 +3704,7 @@ HRESULT STDMETHODCALLTYPE CDefView::CreateViewWindow3(IShellBrowser *psb, IShell
else
FIXME("Ignoring unrecognized VID %s\n", debugstr_guid(view_id));
}
const UINT requestedViewMode = m_FolderSettings.ViewMode;
/* Get our parent window */
m_pShellBrowser->GetWindow(&m_hWndParent);
@ -3471,6 +3716,12 @@ HRESULT STDMETHODCALLTYPE CDefView::CreateViewWindow3(IShellBrowser *psb, IShell
TRACE("-- CommDlgBrowser\n");
}
LoadViewState();
if (view_flags & SV3CVW3_FORCEVIEWMODE)
m_FolderSettings.ViewMode = requestedViewMode;
if (view_flags & SV3CVW3_FORCEFOLDERFLAGS)
m_FolderSettings.fFlags = (mask & flags) & ~IGNORE_FWF;
RECT rcView = *prcView;
Create(m_hWndParent, rcView, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP, 0, 0U);
if (m_hWnd == NULL)
@ -3800,17 +4051,20 @@ HRESULT WINAPI CDefView::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCm
if (IsEqualIID(*pguidCmdGroup, CGID_DefView))
{
CComPtr<IStream> pStream;
WCHAR SubKey[MAX_PATH];
switch (nCmdID)
{
case DVCMDID_SET_DEFAULTFOLDER_SETTINGS:
SHDeleteKey(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\ShellNoRoam\\Bags");
FIXME("Save current as default\n");
if (SUCCEEDED(GetDefaultViewStream(STGM_WRITE, &pStream)))
SaveViewState(pStream);
break;
case DVCMDID_RESET_DEFAULTFOLDER_SETTINGS:
wsprintfW(SubKey, L"%s\\%s", REGSTR_PATH_EXPLORER, L"Streams\\Default");
SHDeleteKey(HKEY_CURRENT_USER, SubKey);
SHDeleteKey(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\ShellNoRoam\\Bags");
m_FolderSettings.fFlags |= FWF_NOBROWSERVIEWSTATE; // Don't let this folder save itself
break;
}
}

View File

@ -528,11 +528,11 @@ CFSFolder::~CFSFolder()
}
static const shvheader GenericSFHeader[] = {
{IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
{IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 18},
{IDS_SHV_COLUMN_SIZE, SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
{IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
{IDS_SHV_COLUMN_MODIFIED, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
{IDS_SHV_COLUMN_ATTRIBUTES, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
{IDS_SHV_COLUMN_ATTRIBUTES, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 8},
{IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR | SHCOLSTATE_SLOW, LVCFMT_LEFT, 10}, // We don't currently support comments but CRegFolder does
};

View File

@ -37,6 +37,7 @@ class CDesktopBrowser :
public CWindowImpl<CDesktopBrowser, CWindow, CFrameWinTraits>,
public CComObjectRootEx<CComMultiThreadModelNoCS>,
public IShellBrowser,
public IShellBrowserService,
public IServiceProvider
{
private:
@ -76,6 +77,9 @@ public:
STDMETHOD(OnViewWindowActive)(struct IShellView *ppshv) override;
STDMETHOD(SetToolbarItems)(LPTBBUTTON lpButtons, UINT nButtons, UINT uFlags) override;
// *** IShellBrowserService methods ***
STDMETHOD(GetPropertyBag)(long flags, REFIID riid, void **ppv) override;
// *** IBrowserService2 methods (fake for now) ***
inline void SetTopBrowser() const {}
@ -93,6 +97,7 @@ public:
LRESULT OnGetChangeNotifyServer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnDeviceChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnShowOptionsDlg(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnSaveState(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
DECLARE_WND_CLASS_EX(szProgmanClassName, CS_DBLCLKS, COLOR_DESKTOP)
@ -108,11 +113,13 @@ BEGIN_MSG_MAP(CBaseBar)
MESSAGE_HANDLER(WM_DESKTOP_GET_CNOTIFY_SERVER, OnGetChangeNotifyServer)
MESSAGE_HANDLER(WM_DEVICECHANGE, OnDeviceChange)
MESSAGE_HANDLER(WM_PROGMAN_OPENSHELLSETTINGS, OnShowOptionsDlg)
MESSAGE_HANDLER(WM_PROGMAN_SAVESTATE, OnSaveState)
END_MSG_MAP()
BEGIN_COM_MAP(CDesktopBrowser)
COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
COM_INTERFACE_ENTRY_IID(IID_IShellBrowser, IShellBrowser)
COM_INTERFACE_ENTRY_IID(IID_IShellBrowserService, IShellBrowserService)
COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider)
END_COM_MAP()
};
@ -228,10 +235,12 @@ HRESULT CDesktopBrowser::Initialize(IShellDesktopTray *ShellDesk)
if (FAILED_UNEXPECTEDLY(hRet))
return hRet;
BOOL fHideIcons = SHELL_GetSetting(SSF_HIDEICONS, fHideIcons);
FOLDERSETTINGS fs;
RECT rcShellView = {0,0,0,0};
fs.ViewMode = FVM_ICON;
fs.fFlags = FWF_DESKTOP | FWF_NOCLIENTEDGE | FWF_NOSCROLL | FWF_TRANSPARENT;
fs.fFlags = FWF_DESKTOP | FWF_NOCLIENTEDGE | FWF_NOSCROLL | FWF_TRANSPARENT |
FWF_AUTOARRANGE | (fHideIcons ? FWF_NOICONS : 0);
hRet = m_ShellView->CreateViewWindow(NULL, &fs, (IShellBrowser *)this, &rcShellView, &m_hWndShellView);
if (FAILED_UNEXPECTEDLY(hRet))
return hRet;
@ -349,9 +358,15 @@ HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetToolbarItems(LPTBBUTTON lpButtons,
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetPropertyBag(long flags, REFIID riid, void **ppv)
{
ITEMIDLIST deskpidl = {};
return SHGetViewStatePropertyBag(&deskpidl, L"Desktop", flags | SHGVSPB_ROAM, riid, ppv);
}
HRESULT STDMETHODCALLTYPE CDesktopBrowser::QueryService(REFGUID guidService, REFIID riid, PVOID *ppv)
{
/* FIXME - handle guidService */
/* FIXME - handle guidService (SID_STopLevelBrowser for IShellBrowserService etc) */
return QueryInterface(riid, ppv);
}
@ -517,6 +532,13 @@ LRESULT CDesktopBrowser::OnShowOptionsDlg(UINT uMsg, WPARAM wParam, LPARAM lPara
return 0;
}
LRESULT CDesktopBrowser::OnSaveState(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
if (m_ShellView && !SHRestricted(REST_NOSAVESET))
m_ShellView->SaveViewState();
return 0;
}
HRESULT CDesktopBrowser_CreateInstance(IShellDesktopTray *Tray, REFIID riid, void **ppv)
{
return ShellObjectCreatorInit<CDesktopBrowser, IShellDesktopTray*>(Tray, riid, ppv);

View File

@ -1802,6 +1802,7 @@ BOOL WINAPI ReadCabinetState(CABINETSTATE *cs, int length)
{
HKEY hkey = 0;
DWORD type, r;
C_ASSERT(sizeof(*cs) == FIELD_OFFSET(CABINETSTATE, fMenuEnumFilter) + sizeof(UINT));
TRACE("%p %d\n", cs, length);
@ -1822,6 +1823,10 @@ BOOL WINAPI ReadCabinetState(CABINETSTATE *cs, int length)
if ( (r != ERROR_SUCCESS) || (cs->cLength < sizeof(*cs)) ||
(cs->cLength != length) )
{
SHELLSTATE shellstate;
shellstate.fWin95Classic = FALSE;
SHGetSetSettings(&shellstate, SSF_WIN95CLASSIC, FALSE);
TRACE("Initializing shell cabinet settings\n");
memset(cs, 0, sizeof(*cs));
cs->cLength = sizeof(*cs);
@ -1831,11 +1836,11 @@ BOOL WINAPI ReadCabinetState(CABINETSTATE *cs, int length)
cs->fNotShell = FALSE;
cs->fSimpleDefault = TRUE;
cs->fDontShowDescBar = FALSE;
cs->fNewWindowMode = FALSE;
cs->fNewWindowMode = shellstate.fWin95Classic;
cs->fShowCompColor = FALSE;
cs->fDontPrettyNames = FALSE;
cs->fAdminsCreateCommonGroups = TRUE;
cs->fMenuEnumFilter = 96;
cs->fMenuEnumFilter = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
}
return TRUE;

View File

@ -1463,7 +1463,7 @@ CViewStatePropertyBag::_GetMRUSlots(
return hr;
hr = pMruList->QueryPidl(pidl, cSlots, puSlots, pcSlots);
if (hr == S_OK && MODE_CAN_WRITE(dwMode))
if (hr == S_OK || MODE_CAN_WRITE(dwMode)) // FIXME: HACK! (Without this, a new pidl can never be saved)
hr = pMruList->UsePidl(pidl, puSlots);
else if (cSlots == 1)
hr = E_FAIL;
@ -1917,13 +1917,9 @@ SHGetViewStatePropertyBag(
::LeaveCriticalSection(&g_csBagCacheLock);
return hr;
}
g_pCachedBag.Attach(pBag);
hr = g_pCachedBag->QueryInterface(riid, ppv);
g_pCachedBag = pBag;
::LeaveCriticalSection(&g_csBagCacheLock);
return hr;
return pBag->QueryInterface(riid, ppv);
}
EXTERN_C VOID FreeViewStatePropertyBagCache(VOID)

View File

@ -204,6 +204,13 @@ SHCreatePropertyBagOnProfileSection(
_In_ REFIID riid,
_Out_ void **ppvObj);
EXTERN_C HRESULT WINAPI
IUnknown_QueryServicePropertyBag(
_In_ IUnknown *punk,
_In_ long flags,
_In_ REFIID riid,
_Outptr_ void **ppvObj);
HWND WINAPI SHCreateWorkerWindowA(WNDPROC wndProc, HWND hWndParent, DWORD dwExStyle,
DWORD dwStyle, HMENU hMenu, LONG_PTR wnd_extra);

View File

@ -58,6 +58,7 @@ typedef struct _TRAYNOTIFYDATAW
* ProgMan messages
*/
#define WM_PROGMAN_OPENSHELLSETTINGS (WM_USER + 22) /* wParam specifies the dialog (and tab page) */
#define WM_PROGMAN_SAVESTATE (WM_USER + 77)
/****************************************************************************
* IDList Functions