diff options
Diffstat (limited to 'sw/qa/core/unocore/unocore.cxx')
-rw-r--r-- | sw/qa/core/unocore/unocore.cxx | 1029 |
1 files changed, 1016 insertions, 13 deletions
diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx index 62c3f439c4e5..3e52b12a363c 100644 --- a/sw/qa/core/unocore/unocore.cxx +++ b/sw/qa/core/unocore/unocore.cxx @@ -9,30 +9,47 @@ #include <swmodeltestbase.hxx> +#include <com/sun/star/text/BibliographyDataType.hpp> #include <com/sun/star/text/XTextAppend.hpp> #include <com/sun/star/text/XTextFrame.hpp> +#include <com/sun/star/text/XTextViewCursorSupplier.hpp> +#include <com/sun/star/text/XDependentTextField.hpp> +#include <com/sun/star/document/XDocumentInsertable.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/text/XTextViewCursorSupplier.hpp> +#include <com/sun/star/text/XPageCursor.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <vcl/errinf.hxx> +#include <editeng/wghtitem.hxx> #include <wrtsh.hxx> #include <unotextrange.hxx> #include <unotxdoc.hxx> #include <docsh.hxx> +#include <ndtxt.hxx> +#include <textlinebreak.hxx> +#include <textcontentcontrol.hxx> +#include <frmmgr.hxx> +#include <fmtcntnt.hxx> using namespace ::com::sun::star; -namespace -{ -constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/core/unocore/data/"; -} - /// Covers sw/source/core/unocore/ fixes. class SwCoreUnocoreTest : public SwModelTestBase { +public: + SwCoreUnocoreTest() + : SwModelTestBase("/sw/qa/core/unocore/data/") + { + } }; CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testTdf119081) { // Load a doc with a nested table in it. - load(DATA_DIRECTORY, "tdf119081.odt"); + createSwDoc("tdf119081.odt"); SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); SwDocShell* pDocShell = pTextDoc->GetDocShell(); @@ -41,14 +58,14 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testTdf119081) // Enter outer A1. pWrtShell->Down(/*bSelect=*/false, /*nCount=*/3); // Enter inner A1. - pWrtShell->Right(CRSR_SKIP_CELLS, /*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false, + pWrtShell->Right(SwCursorSkipMode::Cells, /*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false, /*bVisual=*/true); // Enter outer B1. pWrtShell->Down(/*bSelect=*/false, /*nCount=*/2); SwDoc* pDoc = pDocShell->GetDoc(); SwPaM& rCursor = pWrtShell->GetCurrentShellCursor(); - uno::Reference<text::XTextRange> xInsertPosition + rtl::Reference<SwXTextRange> xInsertPosition = SwXTextRange::CreateXTextRange(*pDoc, *rCursor.GetPoint(), nullptr); uno::Reference<text::XTextAppend> xTextAppend(xInsertPosition->getText(), uno::UNO_QUERY); // Without the accompanying fix in place, this test would have failed with: @@ -56,21 +73,47 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testTdf119081) xTextAppend->insertTextPortion("x", {}, xInsertPosition); // Verify that the string is indeed inserted. - pWrtShell->Left(CRSR_SKIP_CELLS, /*bSelect=*/true, /*nCount=*/1, /*bBasicCall=*/false, + pWrtShell->Left(SwCursorSkipMode::Cells, /*bSelect=*/true, /*nCount=*/1, /*bBasicCall=*/false, /*bVisual=*/true); CPPUNIT_ASSERT_EQUAL(OUString("x"), pWrtShell->GetCurrentShellCursor().GetText()); } +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, selectTextRange) +{ + createSwDoc(); + uno::Reference<text::XTextDocument> const xTD(mxComponent, uno::UNO_QUERY_THROW); + uno::Reference<text::XText> const xText(xTD->getText()); + uno::Reference<text::XTextCursor> const xCursor(xText->createTextCursor()); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(false); + xCursor->gotoEnd(true); + CPPUNIT_ASSERT_EQUAL(OUString("test"), xCursor->getString()); + uno::Reference<lang::XMultiServiceFactory> const xMSF(mxComponent, uno::UNO_QUERY_THROW); + uno::Reference<text::XTextSection> const xSection( + xMSF->createInstance("com.sun.star.text.TextSection"), uno::UNO_QUERY_THROW); + xText->insertTextContent(xCursor, xSection, true); + uno::Reference<text::XTextRange> const xAnchor(xSection->getAnchor()); + uno::Reference<view::XSelectionSupplier> const xView(xTD->getCurrentController(), + uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("test"), xAnchor->getString()); + CPPUNIT_ASSERT(xView->select(uno::Any(xAnchor))); + uno::Reference<container::XIndexAccess> xSel; + CPPUNIT_ASSERT(xView->getSelection() >>= xSel); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSel->getCount()); + uno::Reference<text::XTextRange> xSelRange; + CPPUNIT_ASSERT(xSel->getByIndex(0) >>= xSelRange); + CPPUNIT_ASSERT_EQUAL(OUString("test"), xSelRange->getString()); +} + CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, flyAtParaAnchor) { - mxComponent = loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument"); + createSwDoc(); uno::Reference<lang::XMultiServiceFactory> const xMSF(mxComponent, uno::UNO_QUERY_THROW); uno::Reference<text::XTextDocument> const xTD(mxComponent, uno::UNO_QUERY_THROW); uno::Reference<text::XTextFrame> const xTextFrame( xMSF->createInstance("com.sun.star.text.TextFrame"), uno::UNO_QUERY_THROW); uno::Reference<beans::XPropertySet> const xFrameProps(xTextFrame, uno::UNO_QUERY_THROW); - xFrameProps->setPropertyValue("AnchorType", - uno::makeAny(text::TextContentAnchorType_AT_PARAGRAPH)); + xFrameProps->setPropertyValue("AnchorType", uno::Any(text::TextContentAnchorType_AT_PARAGRAPH)); auto const xText = xTD->getText(); auto const xTextCursor = xText->createTextCursor(); CPPUNIT_ASSERT(xTextCursor.is()); @@ -78,10 +121,970 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, flyAtParaAnchor) auto const xAnchor = xTextFrame->getAnchor(); uno::Reference<text::XTextContent> const xFieldmark( xMSF->createInstance("com.sun.star.text.Fieldmark"), uno::UNO_QUERY_THROW); - // this crashed because the anchor didn't have SwIndex + // this crashed because the anchor didn't have SwContentIndex xText->insertTextContent(xAnchor, xFieldmark, false); } +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testRtlGutter) +{ + createSwDoc(); + uno::Reference<beans::XPropertySet> xPageStyle(getStyles("PageStyles")->getByName("Standard"), + uno::UNO_QUERY); + // Without the accompanying fix in place, this test would have failed with: + // - Unknown property: RtlGutter + auto bRtlGutter = getProperty<bool>(xPageStyle, "RtlGutter"); + CPPUNIT_ASSERT(!bRtlGutter); + xPageStyle->setPropertyValue("RtlGutter", uno::Any(true)); + bRtlGutter = getProperty<bool>(xPageStyle, "RtlGutter"); + CPPUNIT_ASSERT(bRtlGutter); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testBiblioLocalCopy) +{ + // Given an empty document: + createSwDoc(); + + // When setting the LocalURL of a biblio field: + uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xField( + xFactory->createInstance("com.sun.star.text.TextField.Bibliography"), uno::UNO_QUERY); + uno::Sequence<beans::PropertyValue> aFields = { + comphelper::makePropertyValue("BibiliographicType", text::BibliographyDataType::WWW), + comphelper::makePropertyValue("Identifier", OUString("ARJ00")), + comphelper::makePropertyValue("Author", OUString("Me")), + comphelper::makePropertyValue("Title", OUString("mytitle")), + comphelper::makePropertyValue("Year", OUString("2020")), + comphelper::makePropertyValue("URL", OUString("http://www.example.com/test.pdf")), + comphelper::makePropertyValue("LocalURL", OUString("file:///home/me/test.pdf")), + }; + xField->setPropertyValue("Fields", uno::Any(aFields)); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + uno::Reference<text::XTextContent> xContent(xField, uno::UNO_QUERY); + xText->insertTextContent(xCursor, xContent, /*bAbsorb=*/false); + + // Then make sure we get that LocalURL back: + comphelper::SequenceAsHashMap aMap(xField->getPropertyValue("Fields")); + // Without the accompanying fix in place, this test would have failed, there was no LocalURL key + // in the map. + CPPUNIT_ASSERT(aMap.find("LocalURL") != aMap.end()); + auto aActual = aMap["LocalURL"].get<OUString>(); + CPPUNIT_ASSERT_EQUAL(OUString("file:///home/me/test.pdf"), aActual); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testLinkedStyles) +{ + // Given an empty document: + createSwDoc(); + + // When defining a linked style for a para style: + uno::Reference<container::XNameAccess> xParaStyles = getStyles("ParagraphStyles"); + uno::Reference<beans::XPropertySet> xParaStyle(xParaStyles->getByName("Caption"), + uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString(), getProperty<OUString>(xParaStyle, "LinkStyle")); + xParaStyle->setPropertyValue("LinkStyle", uno::Any(OUString("Emphasis"))); + // Then make sure we get the linked char style back: + CPPUNIT_ASSERT_EQUAL(OUString("Emphasis"), getProperty<OUString>(xParaStyle, "LinkStyle")); + + // When defining a linked style for a char style: + uno::Reference<container::XNameAccess> xCharStyles = getStyles("CharacterStyles"); + uno::Reference<beans::XPropertySet> xCharStyle(xCharStyles->getByName("Emphasis"), + uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString(), getProperty<OUString>(xCharStyle, "LinkStyle")); + xCharStyle->setPropertyValue("LinkStyle", uno::Any(OUString("Caption"))); + // Then make sure we get the linked para style back: + CPPUNIT_ASSERT_EQUAL(OUString("Caption"), getProperty<OUString>(xCharStyle, "LinkStyle")); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testViewCursorTextFrame) +{ + // Given a document with a graphic and holding a reference to that graphic frame: + createSwDoc(); + uno::Sequence<beans::PropertyValue> aInsertArgs + = { comphelper::makePropertyValue("FileName", createFileURL(u"graphic.png")) }; + dispatchCommand(mxComponent, ".uno:InsertGraphic", aInsertArgs); + uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier( + xModel->getCurrentController(), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xViewCursor(xTextViewCursorSupplier->getViewCursor(), + uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xFrame; + xViewCursor->getPropertyValue("TextFrame") >>= xFrame; + + // When saving to ODT, then make sure the store doesn't fail: + uno::Reference<frame::XStorable> xStorable(xModel, uno::UNO_QUERY); + uno::Sequence<beans::PropertyValue> aStoreArgs + = { comphelper::makePropertyValue("FilterName", OUString("writer8")) }; + // Without the accompanying fix in place, this test would have failed with: + // uno.RuntimeException: "SwXParagraph: disposed or invalid ..." + xStorable->storeToURL(maTempFile.GetURL(), aStoreArgs); +} + +/// Fails the test if an error popup would be presented. +static void BasicDisplayErrorHandler(const OUString& /*rErr*/, const OUString& /*rAction*/) +{ + CPPUNIT_ASSERT(false); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testBrokenEmbeddedObject) +{ + // Given a document with a broken embedded object (the XML markup is not well-formed): + createSwDoc("broken-embedded-object.odt"); + uno::Reference<text::XTextEmbeddedObjectsSupplier> xSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XIndexAccess> xObjects(xSupplier->getEmbeddedObjects(), + uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xObject(xObjects->getByIndex(0), uno::UNO_QUERY); + uno::Reference<lang::XServiceInfo> xEmbeddedObject; + // Get the property first, which initializes Draw, which would overwrite our error handler. + xObject->getPropertyValue("EmbeddedObject") >>= xEmbeddedObject; + ErrorRegistry::RegisterDisplay(&BasicDisplayErrorHandler); + + // When trying to load that embedded object: + xObject->getPropertyValue("EmbeddedObject") >>= xEmbeddedObject; + + // Then make sure we get a non-empty reference and an error popup it not shown: + CPPUNIT_ASSERT(xEmbeddedObject.is()); + // Without the accompanying fix in place, we got this reference, but first an error popup was + // shown to the user. + CPPUNIT_ASSERT( + xEmbeddedObject->supportsService("com.sun.star.comp.embed.OCommonEmbeddedObject")); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testLineBreakInsert) +{ + // Given an empty document: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + + // When inserting a line-break with properties: + uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextContent> xLineBreak( + xMSF->createInstance("com.sun.star.text.LineBreak"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xLineBreakProps(xLineBreak, uno::UNO_QUERY); + auto eClear = static_cast<sal_Int16>(SwLineBreakClear::ALL); + xLineBreakProps->setPropertyValue("Clear", uno::Any(eClear)); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + xText->insertTextContent(xCursor, xLineBreak, /*bAbsorb=*/false); + + // Then make sure that both the line break and its matching text attribute is inserted: + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + SwNodeOffset nIndex = pWrtShell->GetCursor()->GetPointNode().GetIndex(); + SwTextNode* pTextNode = pDoc->GetNodes()[nIndex]->GetTextNode(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: "\n" (newline) + // - Actual : "" (empty string) + // i.e. SwXLineBreak::attach() did not insert the newline + its text attribute. + CPPUNIT_ASSERT_EQUAL(OUString("\n"), pTextNode->GetText()); + SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_LINEBREAK); + CPPUNIT_ASSERT(pAttr); + auto pTextLineBreak = static_cast<SwTextLineBreak*>(pAttr); + auto& rFormatLineBreak = static_cast<SwFormatLineBreak&>(pTextLineBreak->GetAttr()); + CPPUNIT_ASSERT_EQUAL(SwLineBreakClear::ALL, rFormatLineBreak.GetValue()); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testLineBreakTextPortionEnum) +{ + // Given a document with a clearing break: + createSwDoc(); + uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextContent> xLineBreak( + xMSF->createInstance("com.sun.star.text.LineBreak"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xLineBreakProps(xLineBreak, uno::UNO_QUERY); + auto eClear = static_cast<sal_Int16>(SwLineBreakClear::ALL); + xLineBreakProps->setPropertyValue("Clear", uno::Any(eClear)); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + xText->insertTextContent(xCursor, xLineBreak, /*bAbsorb=*/false); + + // When enumerating the text portions of the only paragraph in the document: + uno::Reference<css::text::XTextRange> xTextPortion = getRun(getParagraph(1), 1); + + // Then make sure that the text portion type is correct + the clear type can be read: + auto aPortionType = getProperty<OUString>(xTextPortion, "TextPortionType"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: LineBreak + // - Actual : Text + // i.e. a line break with properties was part of the normal Text portion, making it impossible + // to get those properties. + CPPUNIT_ASSERT_EQUAL(OUString("LineBreak"), aPortionType); + xLineBreak = getProperty<uno::Reference<text::XTextContent>>(xTextPortion, "LineBreak"); + eClear = getProperty<sal_Int16>(xLineBreak, "Clear"); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(SwLineBreakClear::ALL), eClear); +} + +CPPUNIT_TEST_FIXTURE(SwModelTestBase, testUserFieldTooltip) +{ + // Given a document with a user field: + createSwDoc(); + uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XDependentTextField> xField( + xFactory->createInstance("com.sun.star.text.TextField.User"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xMaster( + xFactory->createInstance("com.sun.star.text.FieldMaster.User"), uno::UNO_QUERY); + xMaster->setPropertyValue("Name", uno::Any(OUString("a_user_field"))); + xField->attachTextFieldMaster(xMaster); + xField->getTextFieldMaster()->setPropertyValue("Content", uno::Any(OUString("42"))); + uno::Reference<text::XTextDocument> xDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xDocument->getText(); + xText->insertTextContent(xText->createTextCursor(), xField, /*bAbsorb=*/false); + uno::Reference<beans::XPropertySet> xFieldProps(xField, uno::UNO_QUERY); + + // When setting a tooltip on the field: + OUString aExpected("first line\nsecond line"); + xFieldProps->setPropertyValue("Title", uno::Any(aExpected)); + + // Then make sure that the tooltip we read back matches the one previously specified: + // Without the accompanying fix in place, this test would have failed with: + // - the property is of unexpected type or void: Title + // i.e. reading of the tooltip was broken. + CPPUNIT_ASSERT_EQUAL(aExpected, getProperty<OUString>(xFieldProps, "Title")); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlInsert) +{ + // Given an empty document: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + + // When inserting a content control around one or more text portions: + 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(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + // Set a custom property on the content control: + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("ShowingPlaceHolder", uno::Any(true)); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // Then make sure that the text attribute is inserted: + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + SwNodeOffset nIndex = pWrtShell->GetCursor()->GetPointNode().GetIndex(); + SwTextNode* pTextNode = pDoc->GetNodes()[nIndex]->GetTextNode(); + SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); + // Without the accompanying fix in place, this test would have failed, as the + // SwXContentControl::attach() implementation was missing. + CPPUNIT_ASSERT(pAttr); + // Also verify that the custom property was set: + auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); + auto& rFormatContentControl + = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr()); + std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); + CPPUNIT_ASSERT(pContentControl->GetShowingPlaceHolder()); + + // Also verify that setText() and getText() works: + uno::Reference<text::XText> xContentControlText(xContentControl, uno::UNO_QUERY); + xContentControlText->setString("new"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: new + // - Actual : + // i.e. getString() always returned an empty string. + CPPUNIT_ASSERT_EQUAL(OUString("new"), xContentControlText->getString()); +} + +CPPUNIT_TEST_FIXTURE(SwModelTestBase, testImageTooltip) +{ + // Given a document with an image and a hyperlink on it: + createSwDoc(); + uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + uno::Reference<text::XTextContent> xImage( + xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY); + xText->insertTextContent(xCursor, xImage, /*bAbsorb=*/false); + uno::Reference<beans::XPropertySet> xImageProps(xImage, uno::UNO_QUERY); + xImageProps->setPropertyValue("HyperLinkURL", uno::Any(OUString("http://www.example.com"))); + + // When setting a tooltip on the image: + OUString aExpected("first line\nsecond line"); + xImageProps->setPropertyValue("Tooltip", uno::Any(aExpected)); + + // Then make sure that the tooltip we read back matches the one previously specified: + // Without the accompanying fix in place, this test would have failed with: + // An uncaught exception of type com.sun.star.beans.UnknownPropertyException + // i.e. reading/writing of the tooltip was broken. + CPPUNIT_ASSERT_EQUAL(aExpected, getProperty<OUString>(xImageProps, "Tooltip")); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlTextPortionEnum) +{ + // Given a document with a content control around one or more text portions: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + 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(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // When enumerating the text portions of the only paragraph in the document: + uno::Reference<css::text::XTextRange> xTextPortion = getRun(getParagraph(1), 1); + + // Then make sure that the text portion type is correct + the content can be read: + auto aPortionType = getProperty<OUString>(xTextPortion, "TextPortionType"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: ContentControl + // - Actual : Text + // i.e. the content control text attribute was ignored. + CPPUNIT_ASSERT_EQUAL(OUString("ContentControl"), aPortionType); + xContentControl + = getProperty<uno::Reference<text::XTextContent>>(xTextPortion, "ContentControl"); + uno::Reference<text::XTextRange> xContentControlRange(xContentControl, uno::UNO_QUERY); + xText = xContentControlRange->getText(); + uno::Reference<container::XEnumerationAccess> xContentEnumAccess(xText, uno::UNO_QUERY); + uno::Reference<container::XEnumeration> xContentEnum = xContentEnumAccess->createEnumeration(); + uno::Reference<text::XTextRange> xContent(xContentEnum->nextElement(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("test"), xContent->getString()); + + // Also test the generated layout: + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwFieldPortion"_ostr, "expand"_ostr, ""); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: PortionType::ContentControl + // - Actual : PortionType::Text + // i.e. SwContentControl generated a plain text portion, not a dedicated content control + // portion. + assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion"_ostr, "type"_ostr, + "PortionType::ContentControl"); + assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion"_ostr, "portion"_ostr, + "test*"); + + // Also test the doc model, make sure that there is a dummy character at the start and end, so + // the user can explicitly decide if they want to expand the content control or not: + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + OUString aText = pWrtShell->GetCursor()->GetPointNode().GetTextNode()->GetText(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: ^Atest^A + // - Actual : ^Atest + // i.e. there was no dummy character at the end. + CPPUNIT_ASSERT_EQUAL(OUString("\x0001test\x0001"), aText); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlCheckbox) +{ + // Given an empty document: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + + // When inserting a checkbox content control: + 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(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + // Without the accompanying fix in place, this test would have failed with: + // An uncaught exception of type com.sun.star.beans.UnknownPropertyException + xContentControlProps->setPropertyValue("Checkbox", uno::Any(true)); + xContentControlProps->setPropertyValue("Checked", uno::Any(true)); + xContentControlProps->setPropertyValue("CheckedState", uno::Any(u"☒"_ustr)); + xContentControlProps->setPropertyValue("UncheckedState", uno::Any(u"☐"_ustr)); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // Then make sure that the specified properties are set: + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode(); + SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); + auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); + auto& rFormatContentControl + = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr()); + std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); + CPPUNIT_ASSERT(pContentControl->GetCheckbox()); + CPPUNIT_ASSERT(pContentControl->GetChecked()); + CPPUNIT_ASSERT_EQUAL(u"☒"_ustr, pContentControl->GetCheckedState()); + CPPUNIT_ASSERT_EQUAL(u"☐"_ustr, pContentControl->GetUncheckedState()); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlDropdown) +{ + // Given an empty document: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + + // When inserting a dropdown content control: + 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(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + { + uno::Sequence<beans::PropertyValues> aListItems = { + { + comphelper::makePropertyValue("DisplayText", uno::Any(OUString("red"))), + comphelper::makePropertyValue("Value", uno::Any(OUString("R"))), + }, + { + comphelper::makePropertyValue("DisplayText", uno::Any(OUString("green"))), + comphelper::makePropertyValue("Value", uno::Any(OUString("G"))), + }, + { + comphelper::makePropertyValue("DisplayText", uno::Any(OUString("blue"))), + comphelper::makePropertyValue("Value", uno::Any(OUString("B"))), + }, + }; + // Without the accompanying fix in place, this test would have failed with: + // An uncaught exception of type com.sun.star.beans.UnknownPropertyException + xContentControlProps->setPropertyValue("ListItems", uno::Any(aListItems)); + } + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // Then make sure that the specified properties are set: + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode(); + SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); + auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); + auto& rFormatContentControl + = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr()); + std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); + std::vector<SwContentControlListItem> aListItems = pContentControl->GetListItems(); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aListItems.size()); + CPPUNIT_ASSERT_EQUAL(OUString("red"), aListItems[0].m_aDisplayText); + CPPUNIT_ASSERT_EQUAL(OUString("R"), aListItems[0].m_aValue); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testInsertFileInContentControlException) +{ + // Given a document with a content control: + createSwDoc(); + 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(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // Reject inserting a document inside the content control: + xCursor->goLeft(1, false); + OUString aURL(createFileURL(u"tdf119081.odt")); + uno::Reference<document::XDocumentInsertable> xInsertable(xCursor, uno::UNO_QUERY); + CPPUNIT_ASSERT_THROW(xInsertable->insertDocumentFromURL(aURL, {}), uno::RuntimeException); + + // Accept inserting a document outside the content control: + xCursor->goRight(1, false); + xInsertable->insertDocumentFromURL(aURL, {}); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlPicture) +{ + // Given an empty document: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + + // When inserting a picture content control: + 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(); + uno::Reference<beans::XPropertySet> xTextGraphic( + xMSF->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY); + xTextGraphic->setPropertyValue("AnchorType", + uno::Any(text::TextContentAnchorType_AS_CHARACTER)); + uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY); + xText->insertTextContent(xCursor, xTextContent, false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + // Without the accompanying fix in place, this test would have failed with: + // An uncaught exception of type com.sun.star.beans.UnknownPropertyException + xContentControlProps->setPropertyValue("Picture", uno::Any(true)); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // Then make sure that the specified properties are set: + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode(); + SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); + auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); + auto& rFormatContentControl + = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr()); + std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); + CPPUNIT_ASSERT(pContentControl->GetPicture()); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlDate) +{ + // Given an empty document: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + + // When inserting a date content control: + 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(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + // Without the accompanying fix in place, this test would have failed with: + // An uncaught exception of type com.sun.star.beans.UnknownPropertyException + xContentControlProps->setPropertyValue("Date", uno::Any(true)); + xContentControlProps->setPropertyValue("DateFormat", uno::Any(OUString("M/d/yyyy"))); + xContentControlProps->setPropertyValue("DateLanguage", uno::Any(OUString("en-US"))); + xContentControlProps->setPropertyValue("CurrentDate", + uno::Any(OUString("2022-05-25T00:00:00Z"))); + xContentControlProps->setPropertyValue("PlaceholderDocPart", + uno::Any(OUString("DefaultPlaceholder_-1854013437"))); + xContentControlProps->setPropertyValue( + "DataBindingPrefixMappings", + uno::Any(OUString("xmlns:ns0='http://schemas.microsoft.com/vsto/samples' "))); + xContentControlProps->setPropertyValue( + "DataBindingXpath", + uno::Any(OUString("/ns0:employees[1]/ns0:employee[1]/ns0:hireDate[1]"))); + xContentControlProps->setPropertyValue( + "DataBindingStoreItemID", uno::Any(OUString("{241A8A02-7FFD-488D-8827-63FBE74E8BC9}"))); + xContentControlProps->setPropertyValue("Color", uno::Any(OUString("008000"))); + xContentControlProps->setPropertyValue("Alias", uno::Any(OUString("myalias"))); + xContentControlProps->setPropertyValue("Tag", uno::Any(OUString("mytag"))); + xContentControlProps->setPropertyValue("Id", uno::Any(static_cast<sal_Int32>(123))); + xContentControlProps->setPropertyValue("TabIndex", uno::Any(sal_uInt32(1))); + xContentControlProps->setPropertyValue("Lock", uno::Any(OUString("sdtContentLocked"))); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // Then make sure that the specified properties are set: + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode(); + SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); + auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); + auto& rFormatContentControl + = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr()); + std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); + CPPUNIT_ASSERT(pContentControl->GetDate()); + CPPUNIT_ASSERT_EQUAL(OUString("M/d/yyyy"), pContentControl->GetDateFormat()); + CPPUNIT_ASSERT_EQUAL(OUString("en-US"), pContentControl->GetDateLanguage()); + CPPUNIT_ASSERT_EQUAL(OUString("2022-05-25T00:00:00Z"), pContentControl->GetCurrentDate()); + CPPUNIT_ASSERT_EQUAL(OUString("DefaultPlaceholder_-1854013437"), + pContentControl->GetPlaceholderDocPart()); + CPPUNIT_ASSERT_EQUAL(OUString("xmlns:ns0='http://schemas.microsoft.com/vsto/samples' "), + pContentControl->GetDataBindingPrefixMappings()); + CPPUNIT_ASSERT_EQUAL(OUString("/ns0:employees[1]/ns0:employee[1]/ns0:hireDate[1]"), + pContentControl->GetDataBindingXpath()); + CPPUNIT_ASSERT_EQUAL(OUString("{241A8A02-7FFD-488D-8827-63FBE74E8BC9}"), + pContentControl->GetDataBindingStoreItemID()); + CPPUNIT_ASSERT_EQUAL(OUString("008000"), pContentControl->GetColor()); + CPPUNIT_ASSERT_EQUAL(OUString("myalias"), pContentControl->GetAlias()); + CPPUNIT_ASSERT_EQUAL(OUString("mytag"), pContentControl->GetTag()); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(123), pContentControl->GetId()); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(1), pContentControl->GetTabIndex()); + CPPUNIT_ASSERT_EQUAL(OUString("sdtContentLocked"), pContentControl->GetLock()); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlPlainText) +{ + // Given an empty document: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + + // When inserting a plain text content control around a text portion: + 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(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("PlainText", uno::Any(true)); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // Then make sure that the text attribute is inserted: + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + SwNodeOffset nIndex = pWrtShell->GetCursor()->GetPointNode().GetIndex(); + SwTextNode* pTextNode = pDoc->GetNodes()[nIndex]->GetTextNode(); + SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); + CPPUNIT_ASSERT(pAttr); + // Also verify that the type if plain text: + auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); + auto& rFormatContentControl + = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr()); + std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); + CPPUNIT_ASSERT(pContentControl->GetPlainText()); + + // Now check if the char index range 2-4 is extended to 0-6 when we apply formatting: + pWrtShell->SttEndDoc(/*bStt=*/true); + // Select "es" from "<dummy>test<dummy>". + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2, /*bBasicCall=*/false); + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 2, /*bBasicCall=*/false); + SfxItemSetFixed<RES_CHRATR_WEIGHT, RES_CHRATR_WEIGHT> aSet(pWrtShell->GetAttrPool()); + SvxWeightItem aItem(WEIGHT_BOLD, RES_CHRATR_WEIGHT); + aSet.Put(aItem); + pWrtShell->SetAttrSet(aSet); + pAttr = pTextNode->GetTextAttrAt(2, RES_TXTATR_AUTOFMT); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 0 + // - Actual : 2 + // i.e. the plain text content control now had 3 portions (<dummy>t<b>es</b>t<dummy>), instead + // of one (<b><dummy>test<dummy></b>). + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), pAttr->GetStart()); + CPPUNIT_ASSERT(pAttr->End()); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(6), *pAttr->End()); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlComboBox) +{ + // Given an empty document: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + + // When inserting a combobox content control: + 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(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + { + uno::Sequence<beans::PropertyValues> aListItems = { + { + comphelper::makePropertyValue("DisplayText", uno::Any(OUString("red"))), + comphelper::makePropertyValue("Value", uno::Any(OUString("R"))), + }, + { + comphelper::makePropertyValue("DisplayText", uno::Any(OUString("green"))), + comphelper::makePropertyValue("Value", uno::Any(OUString("G"))), + }, + { + comphelper::makePropertyValue("DisplayText", uno::Any(OUString("blue"))), + comphelper::makePropertyValue("Value", uno::Any(OUString("B"))), + }, + }; + xContentControlProps->setPropertyValue("ListItems", uno::Any(aListItems)); + // Without the accompanying fix in place, this test would have failed with: + // An uncaught exception of type com.sun.star.beans.UnknownPropertyException + xContentControlProps->setPropertyValue("ComboBox", uno::Any(true)); + } + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // Then make sure that the specified properties are set: + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode(); + SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); + auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); + auto& rFormatContentControl + = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr()); + std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); + std::vector<SwContentControlListItem> aListItems = pContentControl->GetListItems(); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aListItems.size()); + CPPUNIT_ASSERT_EQUAL(OUString("red"), aListItems[0].m_aDisplayText); + CPPUNIT_ASSERT_EQUAL(OUString("R"), aListItems[0].m_aValue); + CPPUNIT_ASSERT(pContentControl->GetComboBox()); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControls) +{ + // Given an empty document: + createSwDoc(); + auto pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get()); + uno::Reference<container::XIndexAccess> xContentControls = pXTextDocument->getContentControls(); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), xContentControls->getCount()); + + // When inserting content controls: + 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(); + // First tag1. + xText->insertString(xCursor, "test1", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + { + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("Tag", uno::Any(OUString("tag1"))); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + } + xCursor->gotoStart(/*bExpand=*/false); + // Then tag2 before tag1. + xText->insertString(xCursor, "test2", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->goRight(5, /*bExpand=*/true); + { + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("Tag", uno::Any(OUString("tag2"))); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + } + + // Then make sure that XContentControls contains the items in a correct order: + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), xContentControls->getCount()); + uno::Reference<beans::XPropertySet> xContentControl; + xContentControls->getByIndex(0) >>= xContentControl; + OUString aTag; + xContentControl->getPropertyValue("Tag") >>= aTag; + // Without the accompanying fix in place, this test would have failed with: + // - Expected: tag2 + // - Actual : tag1 + // i.e. the order of the items was sorted by insert time, not by their doc model position. + CPPUNIT_ASSERT_EQUAL(OUString("tag2"), aTag); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testParagraphMarkerODFExport) +{ + // Given a document with a red numbering portion, from the paragraph marker's format: + createSwDoc("paragraph-marker.docx"); + + // When saving that as ODT + reload: + saveAndReload("writer8"); + + // Then make sure that it still has the correct color: + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 00ff0000 (COL_LIGHTRED) + // - Actual : ffffffff (COL_AUTO) + // i.e. the custom "red" color was lost as RES_PARATR_LIST_AUTOFMT was not serialized to ODT. + CPPUNIT_ASSERT_EQUAL( + OUString("00ff0000"), + getXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwFieldPortion/SwFont"_ostr, "color"_ostr)); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testParagraphMarkerFormattedRun) +{ + // Given a document with a bold run and non-bold paragraph marker: + createSwDoc("paragraph-marker-formatted-run.docx"); + + // When saving that as ODT + reload: + saveAndReload("writer8"); + + // Then make sure that the numbering portion is still non-bold, matching Word: + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: normal + // - Actual : bold + // i.e. the numbering portion was bold, while its weight should be normal. + CPPUNIT_ASSERT_EQUAL(OUString("normal"), + getXPath(pXmlDoc, + "//SwParaPortion/SwLineLayout/SwFieldPortion/SwFont"_ostr, + "weight"_ostr)); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testFlySplit) +{ + // Given a document with a fly frame: + createSwDoc(); + SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); + SwFlyFrameAttrMgr aMgr(true, pWrtShell, Frmmgr_Type::TEXT, nullptr); + RndStdIds eAnchor = RndStdIds::FLY_AT_PARA; + aMgr.InsertFlyFrame(eAnchor, aMgr.GetPos(), aMgr.GetSize()); + uno::Reference<text::XTextFramesSupplier> xDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xFrame(xDocument->getTextFrames()->getByName("Frame1"), + uno::UNO_QUERY); + bool bIsSplitAllowed{}; + // Without the accompanying fix in place, this test would have failed with: + // An uncaught exception of type com.sun.star.beans.UnknownPropertyException + // - Unknown property: IsSplitAllowed + // i.e. the property was missing. + xFrame->getPropertyValue("IsSplitAllowed") >>= bIsSplitAllowed; + CPPUNIT_ASSERT(!bIsSplitAllowed); + + // When marking it as IsSplitAllowed=true: + xFrame->setPropertyValue("IsSplitAllowed", uno::Any(true)); + + // Then make sure that IsSplitAllowed is true when asking back: + xFrame->getPropertyValue("IsSplitAllowed") >>= bIsSplitAllowed; + CPPUNIT_ASSERT(bIsSplitAllowed); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testConvertToTextFrame) +{ + // Given a document with 2 non-interesting frames, an inner frame and an outer frame: + createSwDoc("floattable-outer-nonsplit-inner.docx"); + + // When checking the anchor of the inner frame: + SwDoc* pDoc = getSwDoc(); + const sw::FrameFormats<sw::SpzFrameFormat*>& rFrames = *pDoc->GetSpzFrameFormats(); + sw::SpzFrameFormat* pFrame3 = rFrames.FindFrameFormatByName("Frame3"); + SwNodeIndex aFrame3Anchor = pFrame3->GetAnchor().GetContentAnchor()->nNode; + + // Then make sure it's anchored in the outer frame's last content node: + sw::SpzFrameFormat* pFrame4 = rFrames.FindFrameFormatByName("Frame4"); + SwPaM aPaM(*pFrame4->GetContent().GetContentIdx()->GetNode().EndOfSectionNode()); + aPaM.Move(fnMoveBackward, GoInContent); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: SwNodeIndex (node 27) + // - Actual : SwNodeIndex (node 49) + // i.e. Frame3 was anchored much later, in the body text, not in Frame4. + CPPUNIT_ASSERT_EQUAL(aPaM.GetPoint()->nNode, aFrame3Anchor); +} + +namespace +{ +/// This selection listener calls XTextRange::getString() on a selection change, which triggered +/// a new selection change event by accident, resulting infinite recursion and crash +struct SelectionChangeListener : public cppu::WeakImplHelper<view::XSelectionChangeListener> +{ +public: + SelectionChangeListener(); + // view::XSelectionChangeListener + void SAL_CALL selectionChanged(const lang::EventObject& rEvent) override; + + // lang::XEventListener + void SAL_CALL disposing(const lang::EventObject& rSource) override; +}; +} + +SelectionChangeListener::SelectionChangeListener() {} + +void SelectionChangeListener::selectionChanged(const lang::EventObject& rEvent) +{ + uno::Reference<view::XSelectionSupplier> xSelectionSupplier(rEvent.Source, uno::UNO_QUERY); + css::uno::Reference<css::container::XIndexAccess> xSelection(xSelectionSupplier->getSelection(), + css::uno::UNO_QUERY_THROW); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSelection->getCount()); + css::uno::Reference<css::text::XTextRange> xTextRange(xSelection->getByIndex(0), + css::uno::UNO_QUERY_THROW); + CPPUNIT_ASSERT(xTextRange->getString().startsWith("test")); +} + +void SelectionChangeListener::disposing(const lang::EventObject& /*rSource*/) {} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testTdf155951) +{ + createSwDoc(); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + + uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY); + uno::Reference<view::XSelectionSupplier> xController(xModel->getCurrentController(), + uno::UNO_QUERY); + xController->addSelectionChangeListener(new SelectionChangeListener()); + + // This crashed here because of infinite recursion + dispatchCommand(mxComponent, ".uno:WordLeftSel", {}); + + // this needs to wait for dispatching (trigger also a second selection change) + xText->insertString(xCursor, "test", /*bAbsorb=*/false); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testCollectFrameAtNodeWithLayout) +{ + // Given a document with a floating table on 2 pages, with a calculated layout: + createSwDoc("floattable-split.docx"); + calcLayout(); + + // When saving to ODT: + save("writer8"); + + // Then make sure the output is valid and hasa 1 <draw:frame>: + // Without the accompanying fix in place, this test would have failed with: + // Error: uncompleted content model. + // i.e. the output was not valid, the second <draw:frame> has an empty <table:table> as a child + // element. + xmlDocUniquePtr pXmlDoc = parseExport("content.xml"); + // Also make sure that we don't have multiple <draw:frame> elements in the first place. + assertXPath(pXmlDoc, "//draw:frame"_ostr, 1); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testTdf149555) +{ + createSwDoc("tdf149555.docx"); + + uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier( + xModel->getCurrentController(), uno::UNO_QUERY); + uno::Reference<text::XPageCursor> xCursor(xTextViewCursorSupplier->getViewCursor(), + uno::UNO_QUERY); + + xCursor->jumpToFirstPage(); + OUString sPageStyleName = getProperty<OUString>(xCursor, "PageStyleName"); + uno::Reference<text::XText> xHeaderText = getProperty<uno::Reference<text::XText>>( + getStyles("PageStyles")->getByName(sPageStyleName), "HeaderText"); + CPPUNIT_ASSERT_EQUAL(OUString("HEADER 1"), xHeaderText->getString()); + + // Without the fix in place, this test would have failed with + // - Expected: HEADER 2 + // - Actual: HEADER 1 + xCursor->jumpToPage(2); + sPageStyleName = getProperty<OUString>(xCursor, "PageStyleName"); + xHeaderText = getProperty<uno::Reference<text::XText>>( + getStyles("PageStyles")->getByName(sPageStyleName), "HeaderText"); + CPPUNIT_ASSERT_EQUAL(OUString("HEADER 2"), xHeaderText->getString()); + + xCursor->jumpToPage(3); + sPageStyleName = getProperty<OUString>(xCursor, "PageStyleName"); + xHeaderText = getProperty<uno::Reference<text::XText>>( + getStyles("PageStyles")->getByName(sPageStyleName), "HeaderText"); + CPPUNIT_ASSERT_EQUAL(OUString("HEADER 2"), xHeaderText->getString()); + + xCursor->jumpToPage(4); + sPageStyleName = getProperty<OUString>(xCursor, "PageStyleName"); + xHeaderText = getProperty<uno::Reference<text::XText>>( + getStyles("PageStyles")->getByName(sPageStyleName), "HeaderText"); + CPPUNIT_ASSERT_EQUAL(OUString("HEADER 2"), xHeaderText->getString()); +} + +// just care that it doesn't crash/assert +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testTdf108272Crash) +{ + createSwDoc("tdf108272-1-minimal.docx"); +} + +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testWrapTextAtFlyStart) +{ + // Given a document with a fly frame: + createSwDoc(); + SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); + SwFlyFrameAttrMgr aMgr(true, pWrtShell, Frmmgr_Type::TEXT, nullptr); + RndStdIds eAnchor = RndStdIds::FLY_AT_PARA; + aMgr.InsertFlyFrame(eAnchor, aMgr.GetPos(), aMgr.GetSize()); + uno::Reference<text::XTextFramesSupplier> xDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xFrame(xDocument->getTextFrames()->getByName("Frame1"), + uno::UNO_QUERY); + bool bWrapTextAtFlyStart{}; + // Without the accompanying fix in place, this test would have failed with: + // An uncaught exception of type com.sun.star.beans.UnknownPropertyException + // - Unknown property: WrapTextAtFlyStart + // i.e. the property was missing. + xFrame->getPropertyValue("WrapTextAtFlyStart") >>= bWrapTextAtFlyStart; + CPPUNIT_ASSERT(!bWrapTextAtFlyStart); + + // When marking it as WrapTextAtFlyStart=true: + xFrame->setPropertyValue("WrapTextAtFlyStart", uno::Any(true)); + + // Then make sure that WrapTextAtFlyStart is true when asking back: + xFrame->getPropertyValue("WrapTextAtFlyStart") >>= bWrapTextAtFlyStart; + CPPUNIT_ASSERT(bWrapTextAtFlyStart); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |