[BROWSEUI] Save/Restore ShellBrowser bar states (#7035)
authorWhindmar Saksit <whindsaks@proton.me>
Fri, 21 Jun 2024 17:17:13 +0000 (19:17 +0200)
committerGitHub <noreply@github.com>
Fri, 21 Jun 2024 17:17:13 +0000 (19:17 +0200)
Save/Restore the state of the ShellBrowser toolbar/addressbar/statusbar.

Windows shares the state of the Go button and the locked state between Explorer and Internet Explorer but the bar states are not shared.

Notes:
- Seems to fix CORE-17236.
- The stream layout does not match Windows so it uses a different name. The toolbar customize dialog needs to be fixed before it makes sense trying to save the toolbar state and the layout of other bands.

CORE-17236

dll/win32/browseui/internettoolbar.cpp
dll/win32/browseui/internettoolbar.h
dll/win32/browseui/resource.h
dll/win32/browseui/settings.cpp
dll/win32/browseui/shellbrowser.cpp
sdk/include/reactos/browseui_undoc.h

index 5d16b05..f094e9a 100644 (file)
@@ -75,6 +75,19 @@ TODO:
 
 extern HRESULT WINAPI SHBindToFolder(LPCITEMIDLIST path, IShellFolder **newFolder);
 
+struct ITBARSTATE
+{
+    static const UINT SIG = ('R' << 0) | ('O' << 8) | ('S' << 16) | (('i' ^ 't' ^ 'b') << 24);
+    UINT cbSize;
+    UINT Signature; // Note: Windows has something else here (12 bytes)
+    UINT StdToolbar : 1;
+    UINT Addressbar : 1;
+    UINT Linksbar : 1;
+    UINT Throbber : 1; // toastytech.com/files/throboff.html
+    UINT Menubar : 1; // ..\Explorer\Advanced\AlwaysShowMenus for NT6?
+    // Note: Windows 8/10 stores the Ribbon state in ..\Explorer\Ribbon
+};
+
 HRESULT IUnknown_RelayWinEvent(IUnknown * punk, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
 {
     CComPtr<IWinEventHandler> menuWinEventHandler;
@@ -614,6 +627,7 @@ CInternetToolbar::CInternetToolbar()
     fToolbarWindow = NULL;
     fAdviseCookie = 0;
     pSettings = NULL;
+    fIgnoreChanges = FALSE;
 }
 
 CInternetToolbar::~CInternetToolbar()
@@ -893,6 +907,7 @@ HRESULT STDMETHODCALLTYPE CInternetToolbar::ShowDW(BOOL fShow)
             return hResult;
     }
 
+#if 0 // Why should showing the IDockingWindow change all bands? Related to CORE-17236
     if (fMenuBar)
     {
         hResult = IUnknown_ShowDW(fMenuBar, fShow);
@@ -918,6 +933,7 @@ HRESULT STDMETHODCALLTYPE CInternetToolbar::ShowDW(BOOL fShow)
         if (FAILED_UNEXPECTEDLY(hResult))
             return hResult;
     }
+#endif
     return S_OK;
 }
 
@@ -1001,6 +1017,14 @@ HRESULT STDMETHODCALLTYPE CInternetToolbar::GetClassID(CLSID *pClassID)
     return S_OK;
 }
 
+HRESULT CInternetToolbar::SetDirty()
+{
+    if (fIgnoreChanges)
+        return S_OK;
+    IUnknown_Exec(fSite, CGID_ShellBrowser, IDM_NOTIFYITBARDIRTY, 0, NULL, NULL);
+    return S_OK;
+}
+
 HRESULT STDMETHODCALLTYPE CInternetToolbar::IsDirty()
 {
     return E_NOTIMPL;
@@ -1008,12 +1032,35 @@ HRESULT STDMETHODCALLTYPE CInternetToolbar::IsDirty()
 
 HRESULT STDMETHODCALLTYPE CInternetToolbar::Load(IStream *pStm)
 {
-    return E_NOTIMPL;
+    fIgnoreChanges = TRUE;
+    HRESULT hr = InitNew();
+    ITBARSTATE state;
+    if (SUCCEEDED(hr))
+    {
+        hr = S_FALSE;
+        ULONG cb = sizeof(state);
+        if (pStm->Read(&state, cb, &cb) == S_OK && state.Signature == state.SIG)
+        {
+            SetBandVisibility(ITBBID_MENUBAND, state.Menubar);
+            SetBandVisibility(ITBBID_TOOLSBAND, state.StdToolbar);
+            SetBandVisibility(ITBBID_ADDRESSBAND, state.Addressbar);
+            //SetBandVisibility(ITBBID_?, state.Linksbar);
+            //SetBandVisibility(ITBBID_?, state.Throbber);
+            hr = S_OK;
+        }
+    }
+    fIgnoreChanges = FALSE;
+    return hr;
 }
 
 HRESULT STDMETHODCALLTYPE CInternetToolbar::Save(IStream *pStm, BOOL fClearDirty)
 {
-    return E_NOTIMPL;
+    ITBARSTATE state = { sizeof(state), state.SIG };
+    state.Menubar = IsBandVisible(ITBBID_MENUBAND) == S_OK;
+    state.StdToolbar = IsBandVisible(ITBBID_TOOLSBAND) == S_OK;
+    state.Addressbar = IsBandVisible(ITBBID_ADDRESSBAND) == S_OK;
+    state.Linksbar = FALSE;
+    return pStm->Write(&state, sizeof(state), NULL);
 }
 
 HRESULT STDMETHODCALLTYPE CInternetToolbar::GetSizeMax(ULARGE_INTEGER *pcbSize)
@@ -1080,24 +1127,34 @@ HRESULT CInternetToolbar::IsBandVisible(int BandID)
     return (bandInfo.fStyle & RBBS_HIDDEN) ? S_FALSE : S_OK;
 }
 
-HRESULT CInternetToolbar::ToggleBandVisibility(int BandID)
+HRESULT CInternetToolbar::SetBandVisibility(int BandID, int Show)
 {
     int index = (int)SendMessage(fMainReBar, RB_IDTOINDEX, BandID, 0);
+    REBARBANDINFOW bandInfo = {sizeof(REBARBANDINFOW), RBBIM_STYLE | RBBIM_CHILD};
+    if (!SendMessage(fMainReBar, RB_GETBANDINFOW, index, (LPARAM)&bandInfo))
+        return E_FAIL;
 
-    REBARBANDINFOW bandInfo = {sizeof(REBARBANDINFOW), RBBIM_STYLE};
-    SendMessage(fMainReBar, RB_GETBANDINFOW, index, (LPARAM)&bandInfo);
-
-    if (bandInfo.fStyle & RBBS_HIDDEN)
+    if (Show < 0)
+        bandInfo.fStyle ^= RBBS_HIDDEN; // Toggle
+    else if (Show)
         bandInfo.fStyle &= ~RBBS_HIDDEN;
     else
         bandInfo.fStyle |= RBBS_HIDDEN;
 
+    bandInfo.fMask &= ~RBBIM_CHILD;
+    ::ShowWindow(bandInfo.hwndChild, (bandInfo.fStyle & RBBS_HIDDEN) ? SW_HIDE : SW_SHOW); // CORE-17236
     SendMessage(fMainReBar, RB_SETBANDINFOW, index, (LPARAM)&bandInfo);
 
     ReserveBorderSpace(0);
+    SetDirty();
     return S_OK;
 }
 
+HRESULT CInternetToolbar::ToggleBandVisibility(int BandID)
+{
+    return SetBandVisibility(BandID, -1);
+}
+
 HRESULT STDMETHODCALLTYPE CInternetToolbar::QueryStatus(const GUID *pguidCmdGroup,
     ULONG cCmds, OLECMD prgCmds[  ], OLECMDTEXT *pCmdText)
 {
@@ -1685,7 +1742,6 @@ LRESULT CInternetToolbar::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam,
     if (hitTestInfo.iBand == -1)
         return 0;
 
-    pSettings->Load();
     rebarBandInfo.cbSize = sizeof(rebarBandInfo);
     rebarBandInfo.fMask = RBBIM_ID;
     SendMessage(fMainReBar, RB_GETBANDINFOW, hitTestInfo.iBand, (LPARAM)&rebarBandInfo);
@@ -1930,3 +1986,19 @@ LRESULT CInternetToolbar::OnSettingsChange(UINT uMsg, WPARAM wParam, LPARAM lPar
 
     return 0;
 }
+
+HRESULT CInternetToolbar::GetStream(UINT StreamFor, DWORD Stgm, IStream **ppS)
+{
+    WCHAR path[MAX_PATH];
+    LPCWSTR subkey = NULL;
+    switch (StreamFor)
+    {
+    case ITBARSTREAM_SHELLBROWSER: subkey = L"ShellBrowser"; break;
+    case ITBARSTREAM_WEBBROWSER: subkey = L"WebBrowser"; break;
+    case ITBARSTREAM_EXPLORER: subkey = L"Explorer"; break;
+    default: return E_INVALIDARG;
+    }
+    wsprintfW(path, L"Software\\Microsoft\\Internet Explorer\\Toolbar\\%s", subkey);
+    *ppS = SHOpenRegStream2W(HKEY_CURRENT_USER, path, L"RosITBarLayout", Stgm); // ROS prefix until we figure out the correct format
+    return *ppS ? S_OK : E_FAIL;
+}
index 997b0a1..873788c 100644 (file)
 
 #pragma once
 
+#define ITBARSTREAM_SHELLBROWSER 0
+#define ITBARSTREAM_WEBBROWSER 1
+#define ITBARSTREAM_EXPLORER 2
+
 static const int gSearchCommandID = 1003;
 static const int gFoldersCommandID = 1004;
 static const int gMoveToCommandID = FCIDM_SHVIEW_MOVETO;
@@ -93,6 +97,7 @@ public:
     POINT                                   fStartPosition;
     LONG                                    fStartHeight;
     ShellSettings                           *pSettings;
+    BOOL                                    fIgnoreChanges;
 public:
     CInternetToolbar();
     virtual ~CInternetToolbar();
@@ -104,9 +109,13 @@ public:
     HRESULT CommandStateChanged(bool newValue, int commandID);
     HRESULT CreateAndInitBandProxy();
     HRESULT IsBandVisible(int BandID);
+    HRESULT SetBandVisibility(int BandID, int Show);
     HRESULT ToggleBandVisibility(int BandID);
     HRESULT SetState(const GUID *pguidCmdGroup, long commandID, OLECMD* pcmd);
     void RefreshLockedToolbarState();
+    HRESULT SetDirty();
+
+    static HRESULT GetStream(UINT StreamFor, DWORD Stgm, IStream **ppS);
 
 public:
     // *** IInputObject specific methods ***
index 9620a89..fecbd49 100644 (file)
@@ -73,6 +73,7 @@
 
 /* Random id for band close button, feel free to change it */
 #define IDM_BASEBAR_CLOSE                0xA200
+#define IDM_NOTIFYITBARDIRTY             0xA239 /* Arbitrary id */
 
 /* User-installed explorer band IDs according to API Monitor traces */
 #define IDM_EXPLORERBAND_BEGINCUSTOM     0xA240
index 0c0cf45..89fb28f 100644 (file)
@@ -35,6 +35,7 @@ void ShellSettings::Load()
 
 void CabinetStateSettings::Load()
 {
+    this->cLength = sizeof(CABINETSTATE);
     ReadCabinetState(this, this->cLength);
 
     /* Overrides */
index 38ab15e..b6af28f 100644 (file)
@@ -351,8 +351,10 @@ public:
     HRESULT UpdateUpState();
     void UpdateGotoMenu(HMENU theMenu);
     void UpdateViewMenu(HMENU theMenu);
+    HRESULT IsInternetToolbarBandShown(UINT ITId);
     void RefreshCabinetState();
     void UpdateWindowTitle();
+    void SaveITBarLayout();
 
 /*    // *** IDockingWindowFrame methods ***
     STDMETHOD(AddToolbar)(IUnknown *punkSrc, LPCWSTR pwszItem, DWORD dwAddFlags) override;
@@ -771,19 +773,11 @@ HRESULT CShellBrowser::Initialize()
     if (FAILED_UNEXPECTEDLY(hResult))
         return hResult;
 
-    // TODO: create settingsStream from registry entry
-    //if (settingsStream.p)
-    //{
-    //    hResult = persistStreamInit->Load(settingsStream);
-    //    if (FAILED_UNEXPECTEDLY(hResult))
-    //        return hResult;
-    //}
-    //else
-    {
-        hResult = persistStreamInit->InitNew();
-        if (FAILED_UNEXPECTEDLY(hResult))
-            return hResult;
-    }
+    CComPtr<IStream> pITBarStream;
+    hResult = CInternetToolbar::GetStream(ITBARSTREAM_EXPLORER, STGM_READ, &pITBarStream);
+    hResult = SUCCEEDED(hResult) ? persistStreamInit->Load(pITBarStream) : persistStreamInit->InitNew();
+    if (FAILED_UNEXPECTEDLY(hResult))
+        return hResult;
 
     hResult = IUnknown_ShowDW(clientBar, TRUE);
     if (FAILED_UNEXPECTEDLY(hResult))
@@ -2136,6 +2130,9 @@ HRESULT STDMETHODCALLTYPE CShellBrowser::Exec(const GUID *pguidCmdGroup, DWORD n
         {
             case 40994:
                 return NavigateToParent();
+            case IDM_NOTIFYITBARDIRTY:
+                SaveITBarLayout();
+                break;
         }
     }
     else if (IsEqualIID(*pguidCmdGroup, CGID_IExplorerToolbar))
@@ -2383,7 +2380,7 @@ HRESULT STDMETHODCALLTYPE CShellBrowser::QueryService(REFGUID guidService, REFII
         return this->QueryInterface(riid, ppvObject);
     if (IsEqualIID(guidService, SID_SProxyBrowser))
         return this->QueryInterface(riid, ppvObject);
-    if (IsEqualIID(guidService, SID_IExplorerToolbar))
+    if (IsEqualIID(guidService, SID_IExplorerToolbar) && fClientBars[BIInternetToolbar].clientBar.p)
         return fClientBars[BIInternetToolbar].clientBar->QueryInterface(riid, ppvObject);
     if (IsEqualIID(riid, IID_IShellBrowser))
         return this->QueryInterface(riid, ppvObject);
@@ -2495,6 +2492,9 @@ HRESULT STDMETHODCALLTYPE CShellBrowser::ShowControlWindow(UINT id, BOOL fShow)
             return S_OK;
         case FCW_TREE:
             return Exec(&CGID_Explorer, SBCMDID_EXPLORERBARFOLDERS, 0, NULL, NULL);
+        case FCW_ADDRESSBAR:
+            return IUnknown_Exec(fClientBars[BIInternetToolbar].clientBar,
+                                 CGID_PrivCITCommands, ITID_ADDRESSBANDSHOWN, 0, NULL, NULL);
     }
     return E_NOTIMPL;
 }
@@ -2515,6 +2515,10 @@ HRESULT STDMETHODCALLTYPE CShellBrowser::IsControlWindowShown(UINT id, BOOL *pfS
             shown = cmd.cmdf & OLECMDF_LATCHED;
             break;
         }
+        case FCW_ADDRESSBAR:
+            hr = IsInternetToolbarBandShown(ITID_ADDRESSBANDSHOWN);
+            shown = hr == S_OK;
+            break;
         default:
             hr = E_NOTIMPL;
     }
@@ -2526,6 +2530,14 @@ HRESULT STDMETHODCALLTYPE CShellBrowser::IsControlWindowShown(UINT id, BOOL *pfS
     return SUCCEEDED(hr) ? (shown ? S_OK : S_FALSE) : hr;
 }
 
+HRESULT CShellBrowser::IsInternetToolbarBandShown(UINT ITId)
+{
+    OLECMD cmd = { ITId };
+    HRESULT hr = IUnknown_QueryStatus(fClientBars[BIInternetToolbar].clientBar,
+                                      CGID_PrivCITCommands, 1, &cmd, NULL);
+    return SUCCEEDED(hr) ? (cmd.cmdf & OLECMDF_LATCHED) ? S_OK : S_FALSE : hr;
+}
+
 HRESULT STDMETHODCALLTYPE CShellBrowser::IEGetDisplayName(LPCITEMIDLIST pidl, LPWSTR pwszName, UINT uFlags)
 {
     return E_NOTIMPL;
@@ -3986,3 +3998,25 @@ void CShellBrowser::UpdateWindowTitle()
     if (SUCCEEDED(IEGetNameAndFlags(fCurrentDirectoryPIDL, flags, title, _countof(title), NULL)))
         SetWindowText(title);
 }
+
+void CShellBrowser::SaveITBarLayout()
+{
+    if (!gCabinetState.fSaveLocalView)
+        return;
+#if 0 // If CDesktopBrowser aggregates us, skip saving
+    FOLDERSETTINGS fs;
+    if (fCurrentShellView && SUCCEEDED(fCurrentShellView->GetCurrentInfo(&fs)) && (fs.fFlags & FWF_DESKTOP))
+        return;
+#endif
+
+    CComPtr<IPersistStreamInit> pPSI;
+    CComPtr<IStream> pITBarStream;
+    if (!fClientBars[BIInternetToolbar].clientBar.p)
+        return;
+    HRESULT hr = fClientBars[BIInternetToolbar].clientBar->QueryInterface(IID_PPV_ARG(IPersistStreamInit, &pPSI));
+    if (FAILED(hr))
+        return;
+    if (FAILED(hr = CInternetToolbar::GetStream(ITBARSTREAM_EXPLORER, STGM_WRITE, &pITBarStream)))
+        return;
+    pPSI->Save(pITBarStream, TRUE);
+}
index 3104a4d..fc210f0 100644 (file)
@@ -25,6 +25,8 @@
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+#define FCW_ADDRESSBAR 9 // GetControlWindow/IsControlWindowShown
+
 // Name is IETHREADPARAM according to symbols / mangled function names
 #ifdef _WIN64
 typedef struct IEThreadParamBlock