/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /// Covers sw/source/uibase/uiview/ fixes. class SwUibaseUiviewTest : public SwModelTestBase { public: SwUibaseUiviewTest() : SwModelTestBase(u"/sw/qa/uibase/uiview/data/"_ustr) { } }; CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest, testUpdateAllObjectReplacements) { // Make a temporary copy of the test document createTempCopy(u"updateall-objectreplacements.odt"); /* 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 xFactory(comphelper::getProcessServiceFactory()); // Load the copy uno::Reference xInterface = xFactory->createInstance(u"com.sun.star.frame.Desktop"_ustr); uno::Reference xComponentLoader(xInterface, uno::UNO_QUERY); uno::Sequence aLoadArgs{ comphelper::makePropertyValue(u"Hidden"_ustr, true) }; mxComponent = xComponentLoader->loadComponentFromURL(maTempFile.GetURL(), u"_default"_ustr, 0, aLoadArgs); // Perform the .uno:UpdateAll call and save xInterface = xFactory->createInstance(u"com.sun.star.frame.DispatchHelper"_ustr); uno::Reference xDispatchHelper(xInterface, uno::UNO_QUERY); uno::Reference xModel(mxComponent, uno::UNO_QUERY); uno::Reference xDispatchProvider( xModel->getCurrentController()->getFrame(), uno::UNO_QUERY); uno::Sequence aNoArgs; xDispatchHelper->executeDispatch(xDispatchProvider, u".uno:UpdateAll"_ustr, OUString(), 0, aNoArgs); uno::Reference xStorable(mxComponent, uno::UNO_QUERY); xStorable->storeSelf(aNoArgs); // Check the contents of the updated copy and verify that ObjectReplacements are there uno::Reference xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(xFactory), maTempFile.GetURL()); CPPUNIT_ASSERT(xNameAccess->hasByName(u"ObjectReplacements/Components"_ustr)); CPPUNIT_ASSERT(xNameAccess->hasByName(u"ObjectReplacements/Components_1"_ustr)); } CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest, testUpdateReplacementNosetting) { // Load a copy of the document in hidden mode. OUString aSourceURL = createFileURL(u"update-replacement-nosetting.odt"); CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, osl::File::copy(aSourceURL, maTempFile.GetURL())); loadWithParams(maTempFile.GetURL(), { comphelper::makePropertyValue(u"Hidden"_ustr, true) }); // Update "everything" (including object replacements) and save it. dispatchCommand(mxComponent, u".uno:UpdateAll"_ustr, {}); uno::Reference xStorable(mxComponent, uno::UNO_QUERY); xStorable->storeSelf({}); // Check the contents of the updated copy. uno::Reference xContext = comphelper::getProcessComponentContext(); uno::Reference 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(u"ObjectReplacements/Components"_ustr)); } CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest, testKeepRatio) { // Given a document with a custom KeepRatio: createSwDoc("keep-ratio.fodt"); // Then make sure we read the custom value: SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); const SwViewOption* pViewOption = pWrtShell->GetViewOptions(); comphelper::ScopeGuard g([pWrtShell, pViewOption] { SwViewOption aViewOption(*pViewOption); aViewOption.SetKeepRatio(false); SwModule::get()->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: save(u"writer8"_ustr); xmlDocUniquePtr pXmlDoc = parseExport(u"settings.xml"_ustr); assertXPathContent(pXmlDoc, "//config:config-item[@config:name='KeepRatio']", u"true"); } namespace { /// Interception implementation that disables .uno:Zoom on Image1, but not on Image2. struct ImageInterceptor : public cppu::WeakImplHelper { uno::Reference m_xSelectionSupplier; uno::Reference m_xMaster; uno::Reference m_xSlave; int m_nEnabled = 0; int m_nDisabled = 0; public: ImageInterceptor(const uno::Reference& xComponent); // XDispatchProviderInterceptor uno::Reference SAL_CALL getMasterDispatchProvider() override; uno::Reference SAL_CALL getSlaveDispatchProvider() override; void SAL_CALL setMasterDispatchProvider( const uno::Reference& xNewSupplier) override; void SAL_CALL setSlaveDispatchProvider(const uno::Reference& xNewSupplier) override; // XDispatchProvider uno::Reference SAL_CALL queryDispatch(const util::URL& rURL, const OUString& rTargetFrameName, sal_Int32 SearchFlags) override; uno::Sequence> SAL_CALL queryDispatches(const uno::Sequence& rRequests) override; }; } ImageInterceptor::ImageInterceptor(const uno::Reference& xComponent) { uno::Reference xModel(xComponent, uno::UNO_QUERY); CPPUNIT_ASSERT(xModel.is()); m_xSelectionSupplier.set(xModel->getCurrentController(), uno::UNO_QUERY); CPPUNIT_ASSERT(m_xSelectionSupplier.is()); } uno::Reference ImageInterceptor::getMasterDispatchProvider() { return m_xMaster; } uno::Reference ImageInterceptor::getSlaveDispatchProvider() { return m_xSlave; } void ImageInterceptor::setMasterDispatchProvider( const uno::Reference& xNewSupplier) { m_xMaster = xNewSupplier; } void ImageInterceptor::setSlaveDispatchProvider( const uno::Reference& xNewSupplier) { m_xSlave = xNewSupplier; } uno::Reference 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 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> ImageInterceptor::queryDispatches(const uno::Sequence& /*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: createSwDoc(); SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); uno::Reference xMSF(mxComponent, uno::UNO_QUERY); uno::Reference xTextDocument(mxComponent, uno::UNO_QUERY); uno::Reference xText = xTextDocument->getText(); uno::Reference xCursor = xText->createTextCursor(); for (int i = 0; i < 2; ++i) { uno::Reference xTextGraphic( xMSF->createInstance(u"com.sun.star.text.TextGraphicObject"_ustr), uno::UNO_QUERY); xTextGraphic->setPropertyValue(u"AnchorType"_ustr, uno::Any(text::TextContentAnchorType_AS_CHARACTER)); xTextGraphic->setPropertyValue(u"Size"_ustr, uno::Any(awt::Size(5000, 5000))); uno::Reference xTextContent(xTextGraphic, uno::UNO_QUERY); xText->insertTextContent(xCursor, xTextContent, false); } pWrtShell->SttEndDoc(/*bStt=*/false); uno::Reference xModel(mxComponent, uno::UNO_QUERY); uno::Reference 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: selectShape(1); // 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: selectShape(2); // 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): selectShape(1); // 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_TEST_FIXTURE(SwUibaseUiviewTest, testPrintPreview) { // Given a normal Writer view, in half-destroyed state, similar to what // SfxViewFrame::SwitchToViewShell_Impl() does in practice: createSwDoc(); SwDocShell* pDocShell = getSwDocShell(); SwView* pView = pDocShell->GetView(); FmFormShell* pFormShell = pView->GetFormShell(); pView->SetFormShell(reinterpret_cast(-1)); pView->SetDying(); // When selecting a shell, similar to what happens the doc size changes: // Then make sure we don't crash: pView->SelectShell(); // Restore the state and shut down. pView->SetFormShell(pFormShell); } CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest, TestTdf152839_Formtext) { createSwDoc("tdf152839_formtext.rtf"); xmlDocUniquePtr pXmlDoc = parseLayoutDump(); sal_Int32 nHeight = getXPath(pXmlDoc, "/root/page[1]/body/tab[1]/row[2]/cell[1]/txt/infos/bounds", "height") .toInt32(); CPPUNIT_ASSERT_EQUAL(sal_Int32(723), nHeight); } CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest, testEditInReadonly) { createSwDoc("editinsection.odt"); SwDocShell* pDocShell = getSwDocShell(); SwView* pView = pDocShell->GetView(); pView->GetViewFrame().GetDispatcher()->Execute(SID_EDITDOC, SfxCallMode::SYNCHRON); uno::Reference xModel(mxComponent, uno::UNO_QUERY); uno::Reference xTextDocument(mxComponent, uno::UNO_QUERY); uno::Reference xParaCursor(xTextDocument->getText()->createTextCursor(), uno::UNO_QUERY); uno::Reference xSelSupplier(xModel->getCurrentController(), uno::UNO_QUERY_THROW); xSelSupplier->select(css::uno::Any(xParaCursor)); std::unique_ptr pItem; SfxItemState eState = pView->GetViewFrame().GetBindings().QueryState(FN_INSERT_TABLE, pItem); //status disabled in read only content CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, eState); //move cursor to section xParaCursor->gotoNextParagraph(false); xSelSupplier->select(css::uno::Any(xParaCursor)); eState = pView->GetViewFrame().GetBindings().QueryState(FN_INSERT_TABLE, pItem); //status default in editable section CPPUNIT_ASSERT_EQUAL(SfxItemState::DEFAULT, eState); } CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */