include/sfx2/bindings.hxx                    |    1 
 sc/qa/unit/tiledrendering/tiledrendering.cxx |   56 +++++++++++++++++++++++++++
 sfx2/source/control/bindings.cxx             |   28 +++++++++++++
 sfx2/source/control/unoctitm.cxx             |   21 +++++++++-
 4 files changed, 105 insertions(+), 1 deletion(-)

New commits:
commit 51d8a2ef54751403fa707816e27ddb4e7faa8231
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Jan 5 16:51:42 2024 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Jan 8 08:27:18 2024 +0100

    cool#7492 sfx2 lok: fix bad view id / statusbar string on async binding 
update
    
    With two Calc views (first is an English one, second is a German one),
    type in the English view and in parallel to that, create a cell
    selection in the German view. Sometimes the status bar update about the
    selected cells arrives in English, in the German view.
    
    The root of the problem is that SfxBindings has a timer that does the
    expensive update of all the UNO commands only after a delay, but by the
    time this timer runs, possibly the current view is no longer the German
    one, which leads to English strings in the German view.
    
    Fix the problem somewhat similar to what commit
    ee7ca8e4ea8ed93655f99e77a9e77032ac830c46 (cool#7865 sfx2 lok: fix bad
    view id on async command dispatch, 2023-12-20) did in
    SfxHintPoster::DoEvent_Impl() by restoring the original view for the
    duration of the timer job execution.
    
    The test works even in case '--with-lang=en-US de' is not used, because
    it asserts the locale, which is not downgraded to English when
    translations are not available. This required turning the payload of the
    .uno:RowColSelCount status update into JSON, which is meant to be mostly
    backwards-compatible, given that we already have a mix of plain text and
    JSON for the various UNO commands. Another trouble is that the
    SfxBindings lazy update is a timer, so processing idles won't help and
    sleeping in test code is not ideal, either. Solve that by exposing the
    timer of SfxBindings, so test code can explicitly invoke the timer
    without waiting.
    
    Change-Id: Iacf17f81c28b95ce41a0ee29ad25eb576db0d62a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/161691
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/include/sfx2/bindings.hxx b/include/sfx2/bindings.hxx
index 8cd92a24108d..2b85b4dbd065 100644
--- a/include/sfx2/bindings.hxx
+++ b/include/sfx2/bindings.hxx
@@ -188,6 +188,7 @@ public:
     SAL_DLLPRIVATE void SetRecorder_Impl( css::uno::Reference< 
css::frame::XDispatchRecorder > const & );
     SAL_DLLPRIVATE void InvalidateSlotsInMap_Impl();
     SAL_DLLPRIVATE void AddSlotToInvalidateSlotsMap_Impl( sal_uInt16 nId );
+    Timer& GetTimer();
 };
 
 #ifdef DBG_UTIL
diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx 
b/sc/qa/unit/tiledrendering/tiledrendering.cxx
index 5548dc06fbdf..25d1258c21d3 100644
--- a/sc/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx
@@ -16,6 +16,7 @@
 #include <com/sun/star/frame/DispatchHelper.hpp>
 #include <com/sun/star/datatransfer/clipboard/LokClipboard.hpp>
 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
 #include <comphelper/processfactory.hxx>
 #include <comphelper/propertysequence.hxx>
 #include <comphelper/servicehelper.hxx>
@@ -26,6 +27,8 @@
 
 #include <comphelper/lok.hxx>
 #include <comphelper/propertyvalue.hxx>
+#include <comphelper/dispatchcommand.hxx>
+#include <sfx2/msgpool.hxx>
 #include <sfx2/childwin.hxx>
 #include <sfx2/lokhelper.hxx>
 #include <svx/svdpage.hxx>
@@ -437,6 +440,7 @@ public:
     OString m_sInvalidateSheetGeometry;
     OString m_aHyperlinkClicked;
     OString m_ShapeSelection;
+    std::map<std::string, boost::property_tree::ptree> m_aStateChanges;
     TestLokCallbackWrapper m_callbackWrapper;
 
     ViewCallback(bool bDeleteListenerOnDestruct=true)
@@ -581,6 +585,16 @@ public:
         {
             m_aTextSelectionResult.parseMessage(pPayload);
         }
+        break;
+        case LOK_CALLBACK_STATE_CHANGED:
+        {
+            std::stringstream aStream(pPayload);
+            boost::property_tree::ptree aTree;
+            boost::property_tree::read_json(aStream, aTree);
+            std::string aCommandName = aTree.get<std::string>("commandName");
+            m_aStateChanges[aCommandName] = aTree;
+        }
+        break;
         }
     }
 };
@@ -3184,6 +3198,48 @@ CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, 
testInvalidateForSplitPanes)
     CPPUNIT_ASSERT_MESSAGE("The cell visible in the bottom left pane should be 
redrawn", bFoundBottomLeftPane);
 }
 
+CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testStatusBarLocale)
+{
+    // Given 2 views, the second's locale is set to German:
+    createDoc("empty.ods");
+    int nView1 = SfxLokHelper::getView();
+    ViewCallback aView1;
+    SfxLokHelper::createView();
+    ViewCallback aView2;
+    SfxViewShell* pView2 = SfxViewShell::Current();
+    pView2->SetLOKLocale("de-DE");
+    {
+        SfxViewFrame& rFrame = pView2->GetViewFrame();
+        SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(&rFrame);
+        uno::Reference<util::XURLTransformer> 
xParser(util::URLTransformer::create(m_xContext));
+        util::URL aCommandURL;
+        aCommandURL.Complete = ".uno:RowColSelCount";
+        xParser->parseStrict(aCommandURL);
+        const SfxSlot* pSlot = rSlotPool.GetUnoSlot(aCommandURL.Path);
+        rFrame.GetBindings().GetDispatch(pSlot, aCommandURL, false);
+    }
+    aView2.m_aStateChanges.clear();
+
+    // When creating a cell selection in the 2nd view and processing jobs with 
the 1st view set to
+    // active:
+    comphelper::dispatchCommand(".uno:GoDownSel", {});
+    SfxLokHelper::setView(nView1);
+    pView2->GetViewFrame().GetBindings().GetTimer().Invoke();
+    // Once more to hit the pImpl->bMsgDirty = false case in 
SfxBindings::NextJob_Impl().
+    pView2->GetViewFrame().GetBindings().GetTimer().Invoke();
+
+    // Then make sure that the locale is taken into account while producing 
the state changed
+    // callback:
+    auto it = aView2.m_aStateChanges.find(".uno:RowColSelCount");
+    CPPUNIT_ASSERT(it != aView2.m_aStateChanges.end());
+    std::string aLocale = it->second.get<std::string>("locale");
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: de-DE
+    // - Actual  : en-US
+    // i.e. the 2nd view got its callback with the locale of the first view, 
which is buggy.
+    CPPUNIT_ASSERT_EQUAL(std::string("de-DE"), aLocale);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/bindings.cxx b/sfx2/source/control/bindings.cxx
index 40e44ff8b5a3..95823f8bde4d 100644
--- a/sfx2/source/control/bindings.cxx
+++ b/sfx2/source/control/bindings.cxx
@@ -50,6 +50,8 @@
 #include <sfx2/viewfrm.hxx>
 #include <sfx2/objsh.hxx>
 #include <sfx2/msgpool.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <comphelper/lok.hxx>
 
 #include <cstddef>
 #include <memory>
@@ -1212,7 +1214,28 @@ void SfxBindings::UpdateControllers_Impl
 
 IMPL_LINK( SfxBindings, NextJob, Timer *, pTimer, void )
 {
+    bool bSetView = false;
+    int nOldId = -1;
+    if (comphelper::LibreOfficeKit::isActive() && pDispatcher)
+    {
+        SfxViewFrame* pFrame = pDispatcher->GetFrame();
+        SfxViewShell* pShell = pFrame ? pFrame->GetViewShell() : nullptr;
+        int nNewId = SfxLokHelper::getView(pShell);
+        nOldId = SfxLokHelper::getView();
+        if (nNewId != -1 && nNewId != nOldId)
+        {
+            // The current view ID is not the one that belongs to this frame, 
switch to it.
+            SfxLokHelper::setView(nNewId);
+            bSetView = true;
+        }
+    }
+
     NextJob_Impl(pTimer);
+
+    if (bSetView)
+    {
+        SfxLokHelper::setView(nOldId);
+    }
 }
 
 bool SfxBindings::NextJob_Impl(Timer const * pTimer)
@@ -1771,4 +1794,9 @@ uno::Reference < frame::XDispatch > 
SfxBindings::GetDispatch( const SfxSlot* pSl
     return xRet;
 }
 
+Timer& SfxBindings::GetTimer()
+{
+    return pImpl->aAutoTimer;
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/unoctitm.cxx b/sfx2/source/control/unoctitm.cxx
index d9eb3ea78745..cd50e70231e7 100644
--- a/sfx2/source/control/unoctitm.cxx
+++ b/sfx2/source/control/unoctitm.cxx
@@ -1151,7 +1151,6 @@ static void InterceptLOKStateChangeEvent(sal_uInt16 nSID, 
SfxViewFrame* pViewFra
         }
     }
     else if (aEvent.FeatureURL.Path == "StatusDocPos" ||
-             aEvent.FeatureURL.Path == "RowColSelCount" ||
              aEvent.FeatureURL.Path == "StatusPageStyle" ||
              aEvent.FeatureURL.Path == "StateWordCount" ||
              aEvent.FeatureURL.Path == "PageStyleName" ||
@@ -1167,6 +1166,26 @@ static void InterceptLOKStateChangeEvent(sal_uInt16 
nSID, SfxViewFrame* pViewFra
             aBuffer.append(aString);
         }
     }
+    else if (aEvent.FeatureURL.Path == "RowColSelCount")
+    {
+        OUString aString;
+        if (aEvent.IsEnabled)
+        {
+            aEvent.State >>= aString;
+        }
+        boost::property_tree::ptree aTree;
+        aTree.put("commandName", aEvent.FeatureURL.Complete);
+        aTree.put("locale", 
comphelper::LibreOfficeKit::getLocale().getBcp47());
+        aTree.put("state", aString);
+        std::stringstream aStream;
+        boost::property_tree::write_json(aStream, aTree);
+        const SfxViewShell* pShell = pViewFrame->GetViewShell();
+        if (pShell)
+        {
+            pShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, 
OString(aStream.str()));
+        }
+        return;
+    }
     else if (aEvent.FeatureURL.Path == "StateTableCell")
     {
         if (aEvent.IsEnabled)

Reply via email to