From 802dc9714b459ce8c1283909658ec46b30e878a6 Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Fri, 19 Jul 2024 14:40:20 +0200 Subject: [PATCH] [SHELL32][SHLWAPI][BROWSEUI][EXPLORER] Save folder view state (#7127) Saves/restores the Listview icon mode, columns and sort info per-folder. --- base/shell/explorer/traywnd.cpp | 19 ++ dll/win32/browseui/shellbrowser.cpp | 15 +- dll/win32/shell32/CDefView.cpp | 322 ++++++++++++++++-- dll/win32/shell32/folders/CFSFolder.cpp | 4 +- .../shell32/shelldesktop/CDesktopBrowser.cpp | 26 +- dll/win32/shell32/wine/shellord.c | 9 +- dll/win32/shlwapi/propbag.cpp | 10 +- sdk/include/reactos/shlwapi_undoc.h | 7 + sdk/include/reactos/undocshell.h | 1 + 9 files changed, 365 insertions(+), 48 deletions(-) diff --git a/base/shell/explorer/traywnd.cpp b/base/shell/explorer/traywnd.cpp index ac9b6bab0fa..21420cd7ddc 100644 --- a/base/shell/explorer/traywnd.cpp +++ b/base/shell/explorer/traywnd.cpp @@ -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) diff --git a/dll/win32/browseui/shellbrowser.cpp b/dll/win32/browseui/shellbrowser.cpp index 63b80b327c3..c697884e5be 100644 --- a/dll/win32/browseui/shellbrowser.cpp +++ b/dll/win32/browseui/shellbrowser.cpp @@ -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); diff --git a/dll/win32/shell32/CDefView.cpp b/dll/win32/shell32/CDefView.cpp index 71f7c56b198..d666366716c 100644 --- a/dll/win32/shell32/CDefView.cpp +++ b/dll/win32/shell32/CDefView.cpp @@ -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& 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 pS; + CComPtr 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 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; } } diff --git a/dll/win32/shell32/folders/CFSFolder.cpp b/dll/win32/shell32/folders/CFSFolder.cpp index 53f61bd1e17..8b444f05635 100644 --- a/dll/win32/shell32/folders/CFSFolder.cpp +++ b/dll/win32/shell32/folders/CFSFolder.cpp @@ -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 }; diff --git a/dll/win32/shell32/shelldesktop/CDesktopBrowser.cpp b/dll/win32/shell32/shelldesktop/CDesktopBrowser.cpp index 453dd573573..11c65fb9819 100644 --- a/dll/win32/shell32/shelldesktop/CDesktopBrowser.cpp +++ b/dll/win32/shell32/shelldesktop/CDesktopBrowser.cpp @@ -37,6 +37,7 @@ class CDesktopBrowser : public CWindowImpl, public CComObjectRootEx, 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(Tray, riid, ppv); diff --git a/dll/win32/shell32/wine/shellord.c b/dll/win32/shell32/wine/shellord.c index cd819f0a52e..2300a7aab88 100644 --- a/dll/win32/shell32/wine/shellord.c +++ b/dll/win32/shell32/wine/shellord.c @@ -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; diff --git a/dll/win32/shlwapi/propbag.cpp b/dll/win32/shlwapi/propbag.cpp index 98d2bc45926..b28f4fd40bf 100644 --- a/dll/win32/shlwapi/propbag.cpp +++ b/dll/win32/shlwapi/propbag.cpp @@ -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) diff --git a/sdk/include/reactos/shlwapi_undoc.h b/sdk/include/reactos/shlwapi_undoc.h index ab6af45d82c..921361a947f 100644 --- a/sdk/include/reactos/shlwapi_undoc.h +++ b/sdk/include/reactos/shlwapi_undoc.h @@ -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); diff --git a/sdk/include/reactos/undocshell.h b/sdk/include/reactos/undocshell.h index 40cc2841595..84b0f54e206 100644 --- a/sdk/include/reactos/undocshell.h +++ b/sdk/include/reactos/undocshell.h @@ -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