From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001
From: Daniel Baumann <daniel.baumann@progress-linux.org>
Date: Sun, 7 Apr 2024 11:06:44 +0200
Subject: Adding upstream version 4:7.4.7.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
---
 sw/qa/uibase/uiview/data/keep-ratio.fodt           |  21 ++
 .../uiview/data/update-replacement-nosetting.odt   | Bin 0 -> 40982 bytes
 .../uiview/data/updateall-objectreplacements.odt   | Bin 0 -> 35630 bytes
 sw/qa/uibase/uiview/uiview.cxx                     | 317 +++++++++++++++++++++
 4 files changed, 338 insertions(+)
 create mode 100644 sw/qa/uibase/uiview/data/keep-ratio.fodt
 create mode 100644 sw/qa/uibase/uiview/data/update-replacement-nosetting.odt
 create mode 100644 sw/qa/uibase/uiview/data/updateall-objectreplacements.odt
 create mode 100644 sw/qa/uibase/uiview/uiview.cxx

(limited to 'sw/qa/uibase/uiview')

diff --git a/sw/qa/uibase/uiview/data/keep-ratio.fodt b/sw/qa/uibase/uiview/data/keep-ratio.fodt
new file mode 100644
index 000000000..7cfffbec5
--- /dev/null
+++ b/sw/qa/uibase/uiview/data/keep-ratio.fodt
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<office:document xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" office:mimetype="application/vnd.oasis.opendocument.text">
+  <office:settings>
+    <config:config-item-set config:name="ooo:view-settings">
+      <config:config-item-map-indexed config:name="Views">
+        <config:config-item-map-entry>
+          <config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
+          <config:config-item config:name="VisibleTop" config:type="long">0</config:config-item>
+          <config:config-item config:name="VisibleRight" config:type="long">40190</config:config-item>
+          <config:config-item config:name="VisibleBottom" config:type="long">22728</config:config-item>
+          <config:config-item config:name="KeepRatio" config:type="boolean">true</config:config-item>
+        </config:config-item-map-entry>
+      </config:config-item-map-indexed>
+    </config:config-item-set>
+  </office:settings>
+  <office:body>
+    <office:text>
+      <text:p/>
+    </office:text>
+  </office:body>
+</office:document>
diff --git a/sw/qa/uibase/uiview/data/update-replacement-nosetting.odt b/sw/qa/uibase/uiview/data/update-replacement-nosetting.odt
new file mode 100644
index 000000000..055c3d1a2
Binary files /dev/null and b/sw/qa/uibase/uiview/data/update-replacement-nosetting.odt differ
diff --git a/sw/qa/uibase/uiview/data/updateall-objectreplacements.odt b/sw/qa/uibase/uiview/data/updateall-objectreplacements.odt
new file mode 100644
index 000000000..35decf73f
Binary files /dev/null and b/sw/qa/uibase/uiview/data/updateall-objectreplacements.odt differ
diff --git a/sw/qa/uibase/uiview/uiview.cxx b/sw/qa/uibase/uiview/uiview.cxx
new file mode 100644
index 000000000..6b608e0ae
--- /dev/null
+++ b/sw/qa/uibase/uiview/uiview.cxx
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <swmodeltestbase.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <osl/file.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <vcl/scheduler.hxx>
+
+#include <com/sun/star/frame/XDispatchHelper.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XComponentLoader.hpp>
+#include <com/sun/star/frame/XStorable2.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <unotxdoc.hxx>
+#include <docsh.hxx>
+#include <wrtsh.hxx>
+#include <swmodule.hxx>
+#include <view.hxx>
+
+constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/uibase/uiview/data/";
+
+/// Covers sw/source/uibase/uiview/ fixes.
+class SwUibaseUiviewTest : public SwModelTestBase
+{
+};
+
+CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest, testUpdateAllObjectReplacements)
+{
+    // Make a temporary copy of the test document
+    utl::TempFile tmp;
+    tmp.EnableKillingFile();
+    OUString sTempCopy = tmp.GetURL();
+    CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None,
+                         osl::File::copy(m_directories.getURLFromSrc(DATA_DIRECTORY)
+                                             + "updateall-objectreplacements.odt",
+                                         sTempCopy));
+
+    /* BASIC code that exhibits the problem:
+
+    desktop = CreateUnoService("com.sun.star.frame.Desktop")
+    Dim props(0) as new com.sun.star.beans.PropertyValue
+    props(0).Name = "Hidden"
+    props(0).Value = true
+    component = desktop.loadComponentFromURL("file://.../test.odt", "_default", 0, props)
+    Wait 1000 ' workaround
+    dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
+    frame = component.CurrentController.Frame
+    dispatcher.executeDispatch(frame, ".uno:UpdateAll", "", 0, Array())
+    component.storeSelf(Array())
+    component.dispose()
+    */
+
+    uno::Reference<lang::XMultiServiceFactory> xFactory(comphelper::getProcessServiceFactory());
+
+    // Load the copy
+    uno::Reference<uno::XInterface> xInterface
+        = xFactory->createInstance("com.sun.star.frame.Desktop");
+    uno::Reference<frame::XComponentLoader> xComponentLoader(xInterface, uno::UNO_QUERY);
+    uno::Sequence<beans::PropertyValue> aLoadArgs{ comphelper::makePropertyValue("Hidden", true) };
+    mxComponent = xComponentLoader->loadComponentFromURL(sTempCopy, "_default", 0, aLoadArgs);
+
+    // Perform the .uno:UpdateAll call and save
+    xInterface = xFactory->createInstance("com.sun.star.frame.DispatchHelper");
+    uno::Reference<frame::XDispatchHelper> xDispatchHelper(xInterface, uno::UNO_QUERY);
+    uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
+    uno::Reference<frame::XDispatchProvider> xDispatchProvider(
+        xModel->getCurrentController()->getFrame(), uno::UNO_QUERY);
+    uno::Sequence<beans::PropertyValue> aNoArgs;
+    xDispatchHelper->executeDispatch(xDispatchProvider, ".uno:UpdateAll", OUString(), 0, aNoArgs);
+    uno::Reference<frame::XStorable2> xStorable(mxComponent, uno::UNO_QUERY);
+    xStorable->storeSelf(aNoArgs);
+
+    // Check the contents of the updated copy and verify that ObjectReplacements are there
+    uno::Reference<packages::zip::XZipFileAccess2> xNameAccess
+        = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(xFactory),
+                                                      sTempCopy);
+
+    CPPUNIT_ASSERT(xNameAccess->hasByName("ObjectReplacements/Components"));
+    CPPUNIT_ASSERT(xNameAccess->hasByName("ObjectReplacements/Components_1"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest, testUpdateReplacementNosetting)
+{
+    // Load a copy of the document in hidden mode.
+    OUString aSourceURL
+        = m_directories.getURLFromSrc(DATA_DIRECTORY) + "update-replacement-nosetting.odt";
+    CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, osl::File::copy(aSourceURL, maTempFile.GetURL()));
+    mxComponent = loadFromDesktop(maTempFile.GetURL(), "com.sun.star.text.TextDocument",
+                                  { comphelper::makePropertyValue("Hidden", true) });
+
+    // Update "everything" (including object replacements) and save it.
+    dispatchCommand(mxComponent, ".uno:UpdateAll", {});
+    uno::Reference<frame::XStorable2> xStorable(mxComponent, uno::UNO_QUERY);
+    xStorable->storeSelf({});
+
+    // Check the contents of the updated copy.
+    uno::Reference<uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
+    uno::Reference<packages::zip::XZipFileAccess2> xNameAccess
+        = packages::zip::ZipFileAccess::createWithURL(xContext, maTempFile.GetURL());
+
+    // Without the accompanying fix in place, this test would have failed, because the embedded
+    // object replacement image was not generated.
+    CPPUNIT_ASSERT(xNameAccess->hasByName("ObjectReplacements/Components"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest, testKeepRatio)
+{
+    // Given a document with a custom KeepRatio:
+    OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "keep-ratio.fodt";
+
+    // When loading that document:
+    mxComponent = loadFromDesktop(aURL);
+
+    // Then make sure we read the custom value:
+    auto pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+    const SwViewOption* pViewOption = pWrtShell->GetViewOptions();
+    comphelper::ScopeGuard g([pWrtShell, pViewOption] {
+        SwViewOption aViewOption(*pViewOption);
+        aViewOption.SetKeepRatio(false);
+        SW_MOD()->ApplyUsrPref(aViewOption, &pWrtShell->GetView());
+    });
+    // Without the accompanying fix in place, this test would have failed, because KeepRatio was not
+    // mapped to settings.xml
+    CPPUNIT_ASSERT(pViewOption->IsKeepRatio());
+
+    // Then export as well:
+    uno::Reference<frame::XStorable2> xStorable(mxComponent, uno::UNO_QUERY);
+    uno::Sequence<beans::PropertyValue> aStoreArgs = {
+        comphelper::makePropertyValue("FilterName", OUString("writer8")),
+    };
+    xStorable->storeToURL(maTempFile.GetURL(), aStoreArgs);
+    mbExported = true;
+    xmlDocUniquePtr pXmlDoc = parseExport("settings.xml");
+    assertXPathContent(pXmlDoc, "//config:config-item[@config:name='KeepRatio']", "true");
+}
+
+namespace
+{
+/// Interception implementation that disables .uno:Zoom on Image1, but not on Image2.
+struct ImageInterceptor : public cppu::WeakImplHelper<frame::XDispatchProviderInterceptor>
+{
+    uno::Reference<view::XSelectionSupplier> m_xSelectionSupplier;
+    uno::Reference<frame::XDispatchProvider> m_xMaster;
+    uno::Reference<frame::XDispatchProvider> m_xSlave;
+    int m_nEnabled = 0;
+    int m_nDisabled = 0;
+
+public:
+    ImageInterceptor(const uno::Reference<lang::XComponent>& xComponent);
+
+    // XDispatchProviderInterceptor
+    uno::Reference<frame::XDispatchProvider> SAL_CALL getMasterDispatchProvider() override;
+    uno::Reference<frame::XDispatchProvider> SAL_CALL getSlaveDispatchProvider() override;
+    void SAL_CALL setMasterDispatchProvider(
+        const uno::Reference<frame::XDispatchProvider>& xNewSupplier) override;
+    void SAL_CALL
+    setSlaveDispatchProvider(const uno::Reference<frame::XDispatchProvider>& xNewSupplier) override;
+
+    // XDispatchProvider
+    uno::Reference<frame::XDispatch> SAL_CALL queryDispatch(const util::URL& rURL,
+                                                            const OUString& rTargetFrameName,
+                                                            sal_Int32 SearchFlags) override;
+    uno::Sequence<uno::Reference<frame::XDispatch>> SAL_CALL
+    queryDispatches(const uno::Sequence<frame::DispatchDescriptor>& rRequests) override;
+};
+}
+
+ImageInterceptor::ImageInterceptor(const uno::Reference<lang::XComponent>& xComponent)
+{
+    uno::Reference<frame::XModel2> xModel(xComponent, uno::UNO_QUERY);
+    CPPUNIT_ASSERT(xModel.is());
+    m_xSelectionSupplier.set(xModel->getCurrentController(), uno::UNO_QUERY);
+    CPPUNIT_ASSERT(m_xSelectionSupplier.is());
+}
+
+uno::Reference<frame::XDispatchProvider> ImageInterceptor::getMasterDispatchProvider()
+{
+    return m_xMaster;
+}
+
+uno::Reference<frame::XDispatchProvider> ImageInterceptor::getSlaveDispatchProvider()
+{
+    return m_xSlave;
+}
+
+void ImageInterceptor::setMasterDispatchProvider(
+    const uno::Reference<frame::XDispatchProvider>& xNewSupplier)
+{
+    m_xMaster = xNewSupplier;
+}
+
+void ImageInterceptor::setSlaveDispatchProvider(
+    const uno::Reference<frame::XDispatchProvider>& xNewSupplier)
+{
+    m_xSlave = xNewSupplier;
+}
+
+uno::Reference<frame::XDispatch> ImageInterceptor::queryDispatch(const util::URL& rURL,
+                                                                 const OUString& rTargetFrameName,
+                                                                 sal_Int32 nSearchFlags)
+{
+    // Disable the UNO command based on the currently selected image, i.e. this can't be cached when
+    // a different image is selected. Originally this was .uno:SetBorderStyle, but let's pick a
+    // command which is active when running cppunit tests:
+    if (rURL.Complete == ".uno:Zoom")
+    {
+        uno::Reference<container::XNamed> xImage;
+        m_xSelectionSupplier->getSelection() >>= xImage;
+        if (xImage.is() && xImage->getName() == "Image1")
+        {
+            ++m_nDisabled;
+            return {};
+        }
+
+        ++m_nEnabled;
+    }
+
+    return m_xSlave->queryDispatch(rURL, rTargetFrameName, nSearchFlags);
+}
+
+uno::Sequence<uno::Reference<frame::XDispatch>>
+ImageInterceptor::queryDispatches(const uno::Sequence<frame::DispatchDescriptor>& /*rRequests*/)
+{
+    return {};
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest, testSwitchBetweenImages)
+{
+    // Given a document with 2 images, and an interceptor catching an UNO command that specific to
+    // the current selection:
+    SwDoc* pDoc = createSwDoc();
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
+    uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+    uno::Reference<text::XText> xText = xTextDocument->getText();
+    uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+    for (int i = 0; i < 2; ++i)
+    {
+        uno::Reference<beans::XPropertySet> xTextGraphic(
+            xMSF->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
+        xTextGraphic->setPropertyValue("AnchorType",
+                                       uno::Any(text::TextContentAnchorType_AS_CHARACTER));
+        xTextGraphic->setPropertyValue("Size", uno::Any(awt::Size(5000, 5000)));
+        uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY);
+        xText->insertTextContent(xCursor, xTextContent, false);
+    }
+    pWrtShell->SttEndDoc(/*bStt=*/false);
+    uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
+    uno::Reference<frame::XDispatchProviderInterception> xRegistration(
+        xModel->getCurrentController()->getFrame(), uno::UNO_QUERY);
+    rtl::Reference pInterceptor(new ImageInterceptor(mxComponent));
+
+    xRegistration->registerDispatchProviderInterceptor(pInterceptor);
+    pInterceptor->m_nEnabled = 0;
+    pInterceptor->m_nDisabled = 0;
+
+    // When selecting the first image:
+    uno::Reference<text::XTextGraphicObjectsSupplier> xGraphicObjectsSupplier(mxComponent,
+                                                                              uno::UNO_QUERY);
+    uno::Reference<container::XIndexAccess> xGraphicObjects(
+        xGraphicObjectsSupplier->getGraphicObjects(), uno::UNO_QUERY);
+    pInterceptor->m_xSelectionSupplier->select(xGraphicObjects->getByIndex(0));
+    Scheduler::ProcessEventsToIdle();
+    SwView* pView = pDoc->GetDocShell()->GetView();
+    pView->StopShellTimer();
+
+    // Then make sure the UNO command is disabled:
+    CPPUNIT_ASSERT_EQUAL(0, pInterceptor->m_nEnabled);
+    CPPUNIT_ASSERT_GREATEREQUAL(1, pInterceptor->m_nDisabled);
+
+    // Given a clean state:
+    pInterceptor->m_nEnabled = 0;
+    pInterceptor->m_nDisabled = 0;
+
+    // When selecting the second image:
+    pInterceptor->m_xSelectionSupplier->select(xGraphicObjects->getByIndex(1));
+    Scheduler::ProcessEventsToIdle();
+    pView->StopShellTimer();
+
+    // Then make sure the UNO command is enabled:
+    CPPUNIT_ASSERT_GREATEREQUAL(1, pInterceptor->m_nEnabled);
+    CPPUNIT_ASSERT_EQUAL(0, pInterceptor->m_nDisabled);
+
+    // Given a clean state:
+    pInterceptor->m_nEnabled = 0;
+    pInterceptor->m_nDisabled = 0;
+
+    // When selecting the first image, again (this time not changing the selection type):
+    pInterceptor->m_xSelectionSupplier->select(xGraphicObjects->getByIndex(0));
+    Scheduler::ProcessEventsToIdle();
+    pView->StopShellTimer();
+
+    // Then make sure the UNO command is disabled:
+    CPPUNIT_ASSERT_EQUAL(0, pInterceptor->m_nEnabled);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected greater or equal than: 1
+    // - Actual  : 0
+    // i.e. selecting the first image didn't result in a disabled UNO command.
+    CPPUNIT_ASSERT_GREATEREQUAL(1, pInterceptor->m_nDisabled);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
-- 
cgit v1.2.3