1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <swmodeltestbase.hxx>
12 #include <com/sun/star/text/BibliographyDataType.hpp>
13 #include <com/sun/star/text/XTextAppend.hpp>
14 #include <com/sun/star/text/XTextFrame.hpp>
15 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
16 #include <com/sun/star/text/XDependentTextField.hpp>
17 #include <com/sun/star/document/XDocumentInsertable.hpp>
18 #include <com/sun/star/view/XSelectionSupplier.hpp>
19 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
21 #include <comphelper/propertyvalue.hxx>
22 #include <comphelper/sequenceashashmap.hxx>
23 #include <vcl/errinf.hxx>
24 #include <editeng/wghtitem.hxx>
27 #include <unotextrange.hxx>
28 #include <unotxdoc.hxx>
31 #include <textlinebreak.hxx>
32 #include <textcontentcontrol.hxx>
34 #include <fmtcntnt.hxx>
36 using namespace ::com::sun::star
;
38 /// Covers sw/source/core/unocore/ fixes.
39 class SwCoreUnocoreTest
: public SwModelTestBase
43 : SwModelTestBase("/sw/qa/core/unocore/data/")
48 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testTdf119081
)
50 // Load a doc with a nested table in it.
51 createSwDoc("tdf119081.odt");
52 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
53 CPPUNIT_ASSERT(pTextDoc
);
54 SwDocShell
* pDocShell
= pTextDoc
->GetDocShell();
55 SwWrtShell
* pWrtShell
= pDocShell
->GetWrtShell();
58 pWrtShell
->Down(/*bSelect=*/false, /*nCount=*/3);
60 pWrtShell
->Right(SwCursorSkipMode::Cells
, /*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false,
63 pWrtShell
->Down(/*bSelect=*/false, /*nCount=*/2);
65 SwDoc
* pDoc
= pDocShell
->GetDoc();
66 SwPaM
& rCursor
= pWrtShell
->GetCurrentShellCursor();
67 uno::Reference
<text::XTextRange
> xInsertPosition
68 = SwXTextRange::CreateXTextRange(*pDoc
, *rCursor
.GetPoint(), nullptr);
69 uno::Reference
<text::XTextAppend
> xTextAppend(xInsertPosition
->getText(), uno::UNO_QUERY
);
70 // Without the accompanying fix in place, this test would have failed with:
71 // An uncaught exception of type com.sun.star.uno.RuntimeException
72 xTextAppend
->insertTextPortion("x", {}, xInsertPosition
);
74 // Verify that the string is indeed inserted.
75 pWrtShell
->Left(SwCursorSkipMode::Cells
, /*bSelect=*/true, /*nCount=*/1, /*bBasicCall=*/false,
77 CPPUNIT_ASSERT_EQUAL(OUString("x"), pWrtShell
->GetCurrentShellCursor().GetText());
80 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, flyAtParaAnchor
)
83 uno::Reference
<lang::XMultiServiceFactory
> const xMSF(mxComponent
, uno::UNO_QUERY_THROW
);
84 uno::Reference
<text::XTextDocument
> const xTD(mxComponent
, uno::UNO_QUERY_THROW
);
85 uno::Reference
<text::XTextFrame
> const xTextFrame(
86 xMSF
->createInstance("com.sun.star.text.TextFrame"), uno::UNO_QUERY_THROW
);
87 uno::Reference
<beans::XPropertySet
> const xFrameProps(xTextFrame
, uno::UNO_QUERY_THROW
);
88 xFrameProps
->setPropertyValue("AnchorType", uno::Any(text::TextContentAnchorType_AT_PARAGRAPH
));
89 auto const xText
= xTD
->getText();
90 auto const xTextCursor
= xText
->createTextCursor();
91 CPPUNIT_ASSERT(xTextCursor
.is());
92 xText
->insertTextContent(xTextCursor
, xTextFrame
, false);
93 auto const xAnchor
= xTextFrame
->getAnchor();
94 uno::Reference
<text::XTextContent
> const xFieldmark(
95 xMSF
->createInstance("com.sun.star.text.Fieldmark"), uno::UNO_QUERY_THROW
);
96 // this crashed because the anchor didn't have SwContentIndex
97 xText
->insertTextContent(xAnchor
, xFieldmark
, false);
100 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testRtlGutter
)
103 uno::Reference
<beans::XPropertySet
> xPageStyle(getStyles("PageStyles")->getByName("Standard"),
105 // Without the accompanying fix in place, this test would have failed with:
106 // - Unknown property: RtlGutter
107 auto bRtlGutter
= getProperty
<bool>(xPageStyle
, "RtlGutter");
108 CPPUNIT_ASSERT(!bRtlGutter
);
109 xPageStyle
->setPropertyValue("RtlGutter", uno::Any(true));
110 bRtlGutter
= getProperty
<bool>(xPageStyle
, "RtlGutter");
111 CPPUNIT_ASSERT(bRtlGutter
);
114 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testBiblioLocalCopy
)
116 // Given an empty document:
119 // When setting the LocalURL of a biblio field:
120 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
121 uno::Reference
<beans::XPropertySet
> xField(
122 xFactory
->createInstance("com.sun.star.text.TextField.Bibliography"), uno::UNO_QUERY
);
123 uno::Sequence
<beans::PropertyValue
> aFields
= {
124 comphelper::makePropertyValue("BibiliographicType", text::BibliographyDataType::WWW
),
125 comphelper::makePropertyValue("Identifier", OUString("ARJ00")),
126 comphelper::makePropertyValue("Author", OUString("Me")),
127 comphelper::makePropertyValue("Title", OUString("mytitle")),
128 comphelper::makePropertyValue("Year", OUString("2020")),
129 comphelper::makePropertyValue("URL", OUString("http://www.example.com/test.pdf")),
130 comphelper::makePropertyValue("LocalURL", OUString("file:///home/me/test.pdf")),
132 xField
->setPropertyValue("Fields", uno::Any(aFields
));
133 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
134 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
135 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
136 uno::Reference
<text::XTextContent
> xContent(xField
, uno::UNO_QUERY
);
137 xText
->insertTextContent(xCursor
, xContent
, /*bAbsorb=*/false);
139 // Then make sure we get that LocalURL back:
140 comphelper::SequenceAsHashMap
aMap(xField
->getPropertyValue("Fields"));
141 // Without the accompanying fix in place, this test would have failed, there was no LocalURL key
143 CPPUNIT_ASSERT(aMap
.find("LocalURL") != aMap
.end());
144 auto aActual
= aMap
["LocalURL"].get
<OUString
>();
145 CPPUNIT_ASSERT_EQUAL(OUString("file:///home/me/test.pdf"), aActual
);
148 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testLinkedStyles
)
150 // Given an empty document:
153 // When defining a linked style for a para style:
154 uno::Reference
<container::XNameAccess
> xParaStyles
= getStyles("ParagraphStyles");
155 uno::Reference
<beans::XPropertySet
> xParaStyle(xParaStyles
->getByName("Caption"),
157 CPPUNIT_ASSERT_EQUAL(OUString(), getProperty
<OUString
>(xParaStyle
, "LinkStyle"));
158 xParaStyle
->setPropertyValue("LinkStyle", uno::Any(OUString("Emphasis")));
159 // Then make sure we get the linked char style back:
160 CPPUNIT_ASSERT_EQUAL(OUString("Emphasis"), getProperty
<OUString
>(xParaStyle
, "LinkStyle"));
162 // When defining a linked style for a char style:
163 uno::Reference
<container::XNameAccess
> xCharStyles
= getStyles("CharacterStyles");
164 uno::Reference
<beans::XPropertySet
> xCharStyle(xCharStyles
->getByName("Emphasis"),
166 CPPUNIT_ASSERT_EQUAL(OUString(), getProperty
<OUString
>(xCharStyle
, "LinkStyle"));
167 xCharStyle
->setPropertyValue("LinkStyle", uno::Any(OUString("Caption")));
168 // Then make sure we get the linked para style back:
169 CPPUNIT_ASSERT_EQUAL(OUString("Caption"), getProperty
<OUString
>(xCharStyle
, "LinkStyle"));
172 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testViewCursorTextFrame
)
174 // Given a document with a graphic and holding a reference to that graphic frame:
176 uno::Sequence
<beans::PropertyValue
> aInsertArgs
177 = { comphelper::makePropertyValue("FileName", createFileURL(u
"graphic.png")) };
178 dispatchCommand(mxComponent
, ".uno:InsertGraphic", aInsertArgs
);
179 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
180 uno::Reference
<text::XTextViewCursorSupplier
> xTextViewCursorSupplier(
181 xModel
->getCurrentController(), uno::UNO_QUERY
);
182 uno::Reference
<beans::XPropertySet
> xViewCursor(xTextViewCursorSupplier
->getViewCursor(),
184 uno::Reference
<beans::XPropertySet
> xFrame
;
185 xViewCursor
->getPropertyValue("TextFrame") >>= xFrame
;
187 // When saving to ODT, then make sure the store doesn't fail:
188 uno::Reference
<frame::XStorable
> xStorable(xModel
, uno::UNO_QUERY
);
189 uno::Sequence
<beans::PropertyValue
> aStoreArgs
190 = { comphelper::makePropertyValue("FilterName", OUString("writer8")) };
191 // Without the accompanying fix in place, this test would have failed with:
192 // uno.RuntimeException: "SwXParagraph: disposed or invalid ..."
193 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreArgs
);
196 /// Fails the test if an error popup would be presented.
197 static void BasicDisplayErrorHandler(const OUString
& /*rErr*/, const OUString
& /*rAction*/)
199 CPPUNIT_ASSERT(false);
202 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testBrokenEmbeddedObject
)
204 // Given a document with a broken embedded object (the XML markup is not well-formed):
205 createSwDoc("broken-embedded-object.odt");
206 uno::Reference
<text::XTextEmbeddedObjectsSupplier
> xSupplier(mxComponent
, uno::UNO_QUERY
);
207 uno::Reference
<container::XIndexAccess
> xObjects(xSupplier
->getEmbeddedObjects(),
209 uno::Reference
<beans::XPropertySet
> xObject(xObjects
->getByIndex(0), uno::UNO_QUERY
);
210 uno::Reference
<lang::XServiceInfo
> xEmbeddedObject
;
211 // Get the property first, which initializes Draw, which would overwrite our error handler.
212 xObject
->getPropertyValue("EmbeddedObject") >>= xEmbeddedObject
;
213 ErrorRegistry::RegisterDisplay(&BasicDisplayErrorHandler
);
215 // When trying to load that embedded object:
216 xObject
->getPropertyValue("EmbeddedObject") >>= xEmbeddedObject
;
218 // Then make sure we get a non-empty reference and an error popup it not shown:
219 CPPUNIT_ASSERT(xEmbeddedObject
.is());
220 // Without the accompanying fix in place, we got this reference, but first an error popup was
221 // shown to the user.
223 xEmbeddedObject
->supportsService("com.sun.star.comp.embed.OCommonEmbeddedObject"));
226 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testLineBreakInsert
)
228 // Given an empty document:
230 SwDoc
* pDoc
= getSwDoc();
232 // When inserting a line-break with properties:
233 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
234 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
235 uno::Reference
<text::XTextContent
> xLineBreak(
236 xMSF
->createInstance("com.sun.star.text.LineBreak"), uno::UNO_QUERY
);
237 uno::Reference
<beans::XPropertySet
> xLineBreakProps(xLineBreak
, uno::UNO_QUERY
);
238 auto eClear
= static_cast<sal_Int16
>(SwLineBreakClear::ALL
);
239 xLineBreakProps
->setPropertyValue("Clear", uno::Any(eClear
));
240 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
241 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
242 xText
->insertTextContent(xCursor
, xLineBreak
, /*bAbsorb=*/false);
244 // Then make sure that both the line break and its matching text attribute is inserted:
245 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
246 SwNodeOffset nIndex
= pWrtShell
->GetCursor()->GetPointNode().GetIndex();
247 SwTextNode
* pTextNode
= pDoc
->GetNodes()[nIndex
]->GetTextNode();
248 // Without the accompanying fix in place, this test would have failed with:
249 // - Expected: "\n" (newline)
250 // - Actual : "" (empty string)
251 // i.e. SwXLineBreak::attach() did not insert the newline + its text attribute.
252 CPPUNIT_ASSERT_EQUAL(OUString("\n"), pTextNode
->GetText());
253 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_LINEBREAK
);
254 CPPUNIT_ASSERT(pAttr
);
255 auto pTextLineBreak
= static_cast<SwTextLineBreak
*>(pAttr
);
256 auto& rFormatLineBreak
= static_cast<SwFormatLineBreak
&>(pTextLineBreak
->GetAttr());
257 CPPUNIT_ASSERT_EQUAL(SwLineBreakClear::ALL
, rFormatLineBreak
.GetValue());
260 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testLineBreakTextPortionEnum
)
262 // Given a document with a clearing break:
264 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
265 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
266 uno::Reference
<text::XTextContent
> xLineBreak(
267 xMSF
->createInstance("com.sun.star.text.LineBreak"), uno::UNO_QUERY
);
268 uno::Reference
<beans::XPropertySet
> xLineBreakProps(xLineBreak
, uno::UNO_QUERY
);
269 auto eClear
= static_cast<sal_Int16
>(SwLineBreakClear::ALL
);
270 xLineBreakProps
->setPropertyValue("Clear", uno::Any(eClear
));
271 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
272 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
273 xText
->insertTextContent(xCursor
, xLineBreak
, /*bAbsorb=*/false);
275 // When enumerating the text portions of the only paragraph in the document:
276 uno::Reference
<css::text::XTextRange
> xTextPortion
= getRun(getParagraph(1), 1);
278 // Then make sure that the text portion type is correct + the clear type can be read:
279 auto aPortionType
= getProperty
<OUString
>(xTextPortion
, "TextPortionType");
280 // Without the accompanying fix in place, this test would have failed with:
281 // - Expected: LineBreak
283 // i.e. a line break with properties was part of the normal Text portion, making it impossible
284 // to get those properties.
285 CPPUNIT_ASSERT_EQUAL(OUString("LineBreak"), aPortionType
);
286 xLineBreak
= getProperty
<uno::Reference
<text::XTextContent
>>(xTextPortion
, "LineBreak");
287 eClear
= getProperty
<sal_Int16
>(xLineBreak
, "Clear");
288 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16
>(SwLineBreakClear::ALL
), eClear
);
291 CPPUNIT_TEST_FIXTURE(SwModelTestBase
, testUserFieldTooltip
)
293 // Given a document with a user field:
295 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
296 uno::Reference
<text::XDependentTextField
> xField(
297 xFactory
->createInstance("com.sun.star.text.TextField.User"), uno::UNO_QUERY
);
298 uno::Reference
<beans::XPropertySet
> xMaster(
299 xFactory
->createInstance("com.sun.star.text.FieldMaster.User"), uno::UNO_QUERY
);
300 xMaster
->setPropertyValue("Name", uno::Any(OUString("a_user_field")));
301 xField
->attachTextFieldMaster(xMaster
);
302 xField
->getTextFieldMaster()->setPropertyValue("Content", uno::Any(OUString("42")));
303 uno::Reference
<text::XTextDocument
> xDocument(mxComponent
, uno::UNO_QUERY
);
304 uno::Reference
<text::XText
> xText
= xDocument
->getText();
305 xText
->insertTextContent(xText
->createTextCursor(), xField
, /*bAbsorb=*/false);
306 uno::Reference
<beans::XPropertySet
> xFieldProps(xField
, uno::UNO_QUERY
);
308 // When setting a tooltip on the field:
309 OUString
aExpected("first line\nsecond line");
310 xFieldProps
->setPropertyValue("Title", uno::Any(aExpected
));
312 // Then make sure that the tooltip we read back matches the one previously specified:
313 // Without the accompanying fix in place, this test would have failed with:
314 // - the property is of unexpected type or void: Title
315 // i.e. reading of the tooltip was broken.
316 CPPUNIT_ASSERT_EQUAL(aExpected
, getProperty
<OUString
>(xFieldProps
, "Title"));
319 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlInsert
)
321 // Given an empty document:
323 SwDoc
* pDoc
= getSwDoc();
325 // When inserting a content control around one or more text portions:
326 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
327 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
328 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
329 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
330 xText
->insertString(xCursor
, "test", /*bAbsorb=*/false);
331 xCursor
->gotoStart(/*bExpand=*/false);
332 xCursor
->gotoEnd(/*bExpand=*/true);
333 uno::Reference
<text::XTextContent
> xContentControl(
334 xMSF
->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY
);
335 // Set a custom property on the content control:
336 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
337 xContentControlProps
->setPropertyValue("ShowingPlaceHolder", uno::Any(true));
338 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
340 // Then make sure that the text attribute is inserted:
341 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
342 SwNodeOffset nIndex
= pWrtShell
->GetCursor()->GetPointNode().GetIndex();
343 SwTextNode
* pTextNode
= pDoc
->GetNodes()[nIndex
]->GetTextNode();
344 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
345 // Without the accompanying fix in place, this test would have failed, as the
346 // SwXContentControl::attach() implementation was missing.
347 CPPUNIT_ASSERT(pAttr
);
348 // Also verify that the custom property was set:
349 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
350 auto& rFormatContentControl
351 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
352 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
353 CPPUNIT_ASSERT(pContentControl
->GetShowingPlaceHolder());
355 // Also verify that setText() and getText() works:
356 uno::Reference
<text::XText
> xContentControlText(xContentControl
, uno::UNO_QUERY
);
357 xContentControlText
->setString("new");
358 // Without the accompanying fix in place, this test would have failed with:
361 // i.e. getString() always returned an empty string.
362 CPPUNIT_ASSERT_EQUAL(OUString("new"), xContentControlText
->getString());
365 CPPUNIT_TEST_FIXTURE(SwModelTestBase
, testImageTooltip
)
367 // Given a document with an image and a hyperlink on it:
369 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
370 uno::Reference
<text::XTextDocument
> xDocument(mxComponent
, uno::UNO_QUERY
);
371 uno::Reference
<text::XText
> xText
= xDocument
->getText();
372 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
373 uno::Reference
<text::XTextContent
> xImage(
374 xFactory
->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY
);
375 xText
->insertTextContent(xCursor
, xImage
, /*bAbsorb=*/false);
376 uno::Reference
<beans::XPropertySet
> xImageProps(xImage
, uno::UNO_QUERY
);
377 xImageProps
->setPropertyValue("HyperLinkURL", uno::Any(OUString("http://www.example.com")));
379 // When setting a tooltip on the image:
380 OUString
aExpected("first line\nsecond line");
381 xImageProps
->setPropertyValue("Tooltip", uno::Any(aExpected
));
383 // Then make sure that the tooltip we read back matches the one previously specified:
384 // Without the accompanying fix in place, this test would have failed with:
385 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
386 // i.e. reading/writing of the tooltip was broken.
387 CPPUNIT_ASSERT_EQUAL(aExpected
, getProperty
<OUString
>(xImageProps
, "Tooltip"));
390 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlTextPortionEnum
)
392 // Given a document with a content control around one or more text portions:
394 SwDoc
* pDoc
= getSwDoc();
395 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
396 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
397 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
398 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
399 xText
->insertString(xCursor
, "test", /*bAbsorb=*/false);
400 xCursor
->gotoStart(/*bExpand=*/false);
401 xCursor
->gotoEnd(/*bExpand=*/true);
402 uno::Reference
<text::XTextContent
> xContentControl(
403 xMSF
->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY
);
404 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
406 // When enumerating the text portions of the only paragraph in the document:
407 uno::Reference
<css::text::XTextRange
> xTextPortion
= getRun(getParagraph(1), 1);
409 // Then make sure that the text portion type is correct + the content can be read:
410 auto aPortionType
= getProperty
<OUString
>(xTextPortion
, "TextPortionType");
411 // Without the accompanying fix in place, this test would have failed with:
412 // - Expected: ContentControl
414 // i.e. the content control text attribute was ignored.
415 CPPUNIT_ASSERT_EQUAL(OUString("ContentControl"), aPortionType
);
417 = getProperty
<uno::Reference
<text::XTextContent
>>(xTextPortion
, "ContentControl");
418 uno::Reference
<text::XTextRange
> xContentControlRange(xContentControl
, uno::UNO_QUERY
);
419 xText
= xContentControlRange
->getText();
420 uno::Reference
<container::XEnumerationAccess
> xContentEnumAccess(xText
, uno::UNO_QUERY
);
421 uno::Reference
<container::XEnumeration
> xContentEnum
= xContentEnumAccess
->createEnumeration();
422 uno::Reference
<text::XTextRange
> xContent(xContentEnum
->nextElement(), uno::UNO_QUERY
);
423 CPPUNIT_ASSERT_EQUAL(OUString("test"), xContent
->getString());
425 // Also test the generated layout:
426 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
427 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout/SwFieldPortion", "expand", "");
428 // Without the accompanying fix in place, this test would have failed with:
429 // - Expected: PortionType::ContentControl
430 // - Actual : PortionType::Text
431 // i.e. SwContentControl generated a plain text portion, not a dedicated content control
433 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout/SwLinePortion", "type",
434 "PortionType::ContentControl");
435 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout/SwLinePortion", "portion", "test*");
437 // Also test the doc model, make sure that there is a dummy character at the start and end, so
438 // the user can explicitly decide if they want to expand the content control or not:
439 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
440 OUString aText
= pWrtShell
->GetCursor()->GetPointNode().GetTextNode()->GetText();
441 // Without the accompanying fix in place, this test would have failed with:
442 // - Expected: ^Atest^A
444 // i.e. there was no dummy character at the end.
445 CPPUNIT_ASSERT_EQUAL(OUString("\x0001test\x0001"), aText
);
448 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlCheckbox
)
450 // Given an empty document:
452 SwDoc
* pDoc
= getSwDoc();
454 // When inserting a checkbox content control:
455 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
456 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
457 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
458 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
459 xText
->insertString(xCursor
, "test", /*bAbsorb=*/false);
460 xCursor
->gotoStart(/*bExpand=*/false);
461 xCursor
->gotoEnd(/*bExpand=*/true);
462 uno::Reference
<text::XTextContent
> xContentControl(
463 xMSF
->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY
);
464 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
465 // Without the accompanying fix in place, this test would have failed with:
466 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
467 xContentControlProps
->setPropertyValue("Checkbox", uno::Any(true));
468 xContentControlProps
->setPropertyValue("Checked", uno::Any(true));
469 xContentControlProps
->setPropertyValue("CheckedState", uno::Any(OUString(u
"☒")));
470 xContentControlProps
->setPropertyValue("UncheckedState", uno::Any(OUString(u
"☐")));
471 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
473 // Then make sure that the specified properties are set:
474 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
475 SwTextNode
* pTextNode
= pWrtShell
->GetCursor()->GetPointNode().GetTextNode();
476 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
477 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
478 auto& rFormatContentControl
479 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
480 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
481 CPPUNIT_ASSERT(pContentControl
->GetCheckbox());
482 CPPUNIT_ASSERT(pContentControl
->GetChecked());
483 CPPUNIT_ASSERT_EQUAL(OUString(u
"☒"), pContentControl
->GetCheckedState());
484 CPPUNIT_ASSERT_EQUAL(OUString(u
"☐"), pContentControl
->GetUncheckedState());
487 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlDropdown
)
489 // Given an empty document:
491 SwDoc
* pDoc
= getSwDoc();
493 // When inserting a dropdown content control:
494 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
495 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
496 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
497 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
498 xText
->insertString(xCursor
, "test", /*bAbsorb=*/false);
499 xCursor
->gotoStart(/*bExpand=*/false);
500 xCursor
->gotoEnd(/*bExpand=*/true);
501 uno::Reference
<text::XTextContent
> xContentControl(
502 xMSF
->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY
);
503 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
505 uno::Sequence
<beans::PropertyValues
> aListItems
= {
507 comphelper::makePropertyValue("DisplayText", uno::Any(OUString("red"))),
508 comphelper::makePropertyValue("Value", uno::Any(OUString("R"))),
511 comphelper::makePropertyValue("DisplayText", uno::Any(OUString("green"))),
512 comphelper::makePropertyValue("Value", uno::Any(OUString("G"))),
515 comphelper::makePropertyValue("DisplayText", uno::Any(OUString("blue"))),
516 comphelper::makePropertyValue("Value", uno::Any(OUString("B"))),
519 // Without the accompanying fix in place, this test would have failed with:
520 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
521 xContentControlProps
->setPropertyValue("ListItems", uno::Any(aListItems
));
523 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
525 // Then make sure that the specified properties are set:
526 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
527 SwTextNode
* pTextNode
= pWrtShell
->GetCursor()->GetPointNode().GetTextNode();
528 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
529 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
530 auto& rFormatContentControl
531 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
532 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
533 std::vector
<SwContentControlListItem
> aListItems
= pContentControl
->GetListItems();
534 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aListItems
.size());
535 CPPUNIT_ASSERT_EQUAL(OUString("red"), aListItems
[0].m_aDisplayText
);
536 CPPUNIT_ASSERT_EQUAL(OUString("R"), aListItems
[0].m_aValue
);
539 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testInsertFileInContentControlException
)
541 // Given a document with a content control:
543 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
544 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
545 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
546 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
547 xText
->insertString(xCursor
, "test", /*bAbsorb=*/false);
548 xCursor
->gotoStart(/*bExpand=*/false);
549 xCursor
->gotoEnd(/*bExpand=*/true);
550 uno::Reference
<text::XTextContent
> xContentControl(
551 xMSF
->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY
);
552 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
554 // Reject inserting a document inside the content control:
555 xCursor
->goLeft(1, false);
556 OUString
aURL(createFileURL(u
"tdf119081.odt"));
557 uno::Reference
<document::XDocumentInsertable
> xInsertable(xCursor
, uno::UNO_QUERY
);
558 CPPUNIT_ASSERT_THROW(xInsertable
->insertDocumentFromURL(aURL
, {}), uno::RuntimeException
);
560 // Accept inserting a document outside the content control:
561 xCursor
->goRight(1, false);
562 xInsertable
->insertDocumentFromURL(aURL
, {});
565 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlPicture
)
567 // Given an empty document:
569 SwDoc
* pDoc
= getSwDoc();
571 // When inserting a picture content control:
572 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
573 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
574 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
575 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
576 uno::Reference
<beans::XPropertySet
> xTextGraphic(
577 xMSF
->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY
);
578 xTextGraphic
->setPropertyValue("AnchorType",
579 uno::Any(text::TextContentAnchorType_AS_CHARACTER
));
580 uno::Reference
<text::XTextContent
> xTextContent(xTextGraphic
, uno::UNO_QUERY
);
581 xText
->insertTextContent(xCursor
, xTextContent
, false);
582 xCursor
->gotoStart(/*bExpand=*/false);
583 xCursor
->gotoEnd(/*bExpand=*/true);
584 uno::Reference
<text::XTextContent
> xContentControl(
585 xMSF
->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY
);
586 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
587 // Without the accompanying fix in place, this test would have failed with:
588 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
589 xContentControlProps
->setPropertyValue("Picture", uno::Any(true));
590 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
592 // Then make sure that the specified properties are set:
593 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
594 SwTextNode
* pTextNode
= pWrtShell
->GetCursor()->GetPointNode().GetTextNode();
595 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
596 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
597 auto& rFormatContentControl
598 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
599 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
600 CPPUNIT_ASSERT(pContentControl
->GetPicture());
603 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlDate
)
605 // Given an empty document:
607 SwDoc
* pDoc
= getSwDoc();
609 // When inserting a date content control:
610 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
611 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
612 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
613 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
614 xText
->insertString(xCursor
, "test", /*bAbsorb=*/false);
615 xCursor
->gotoStart(/*bExpand=*/false);
616 xCursor
->gotoEnd(/*bExpand=*/true);
617 uno::Reference
<text::XTextContent
> xContentControl(
618 xMSF
->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY
);
619 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
620 // Without the accompanying fix in place, this test would have failed with:
621 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
622 xContentControlProps
->setPropertyValue("Date", uno::Any(true));
623 xContentControlProps
->setPropertyValue("DateFormat", uno::Any(OUString("M/d/yyyy")));
624 xContentControlProps
->setPropertyValue("DateLanguage", uno::Any(OUString("en-US")));
625 xContentControlProps
->setPropertyValue("CurrentDate",
626 uno::Any(OUString("2022-05-25T00:00:00Z")));
627 xContentControlProps
->setPropertyValue("PlaceholderDocPart",
628 uno::Any(OUString("DefaultPlaceholder_-1854013437")));
629 xContentControlProps
->setPropertyValue(
630 "DataBindingPrefixMappings",
631 uno::Any(OUString("xmlns:ns0='http://schemas.microsoft.com/vsto/samples' ")));
632 xContentControlProps
->setPropertyValue(
634 uno::Any(OUString("/ns0:employees[1]/ns0:employee[1]/ns0:hireDate[1]")));
635 xContentControlProps
->setPropertyValue(
636 "DataBindingStoreItemID", uno::Any(OUString("{241A8A02-7FFD-488D-8827-63FBE74E8BC9}")));
637 xContentControlProps
->setPropertyValue("Color", uno::Any(OUString("008000")));
638 xContentControlProps
->setPropertyValue("Alias", uno::Any(OUString("myalias")));
639 xContentControlProps
->setPropertyValue("Tag", uno::Any(OUString("mytag")));
640 xContentControlProps
->setPropertyValue("Id", uno::Any(static_cast<sal_Int32
>(123)));
641 xContentControlProps
->setPropertyValue("TabIndex", uno::Any(sal_uInt32(1)));
642 xContentControlProps
->setPropertyValue("Lock", uno::Any(OUString("sdtContentLocked")));
643 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
645 // Then make sure that the specified properties are set:
646 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
647 SwTextNode
* pTextNode
= pWrtShell
->GetCursor()->GetPointNode().GetTextNode();
648 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
649 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
650 auto& rFormatContentControl
651 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
652 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
653 CPPUNIT_ASSERT(pContentControl
->GetDate());
654 CPPUNIT_ASSERT_EQUAL(OUString("M/d/yyyy"), pContentControl
->GetDateFormat());
655 CPPUNIT_ASSERT_EQUAL(OUString("en-US"), pContentControl
->GetDateLanguage());
656 CPPUNIT_ASSERT_EQUAL(OUString("2022-05-25T00:00:00Z"), pContentControl
->GetCurrentDate());
657 CPPUNIT_ASSERT_EQUAL(OUString("DefaultPlaceholder_-1854013437"),
658 pContentControl
->GetPlaceholderDocPart());
659 CPPUNIT_ASSERT_EQUAL(OUString("xmlns:ns0='http://schemas.microsoft.com/vsto/samples' "),
660 pContentControl
->GetDataBindingPrefixMappings());
661 CPPUNIT_ASSERT_EQUAL(OUString("/ns0:employees[1]/ns0:employee[1]/ns0:hireDate[1]"),
662 pContentControl
->GetDataBindingXpath());
663 CPPUNIT_ASSERT_EQUAL(OUString("{241A8A02-7FFD-488D-8827-63FBE74E8BC9}"),
664 pContentControl
->GetDataBindingStoreItemID());
665 CPPUNIT_ASSERT_EQUAL(OUString("008000"), pContentControl
->GetColor());
666 CPPUNIT_ASSERT_EQUAL(OUString("myalias"), pContentControl
->GetAlias());
667 CPPUNIT_ASSERT_EQUAL(OUString("mytag"), pContentControl
->GetTag());
668 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(123), pContentControl
->GetId());
669 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32
>(1), pContentControl
->GetTabIndex());
670 CPPUNIT_ASSERT_EQUAL(OUString("sdtContentLocked"), pContentControl
->GetLock());
673 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlPlainText
)
675 // Given an empty document:
677 SwDoc
* pDoc
= getSwDoc();
679 // When inserting a plain text content control around a text portion:
680 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
681 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
682 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
683 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
684 xText
->insertString(xCursor
, "test", /*bAbsorb=*/false);
685 xCursor
->gotoStart(/*bExpand=*/false);
686 xCursor
->gotoEnd(/*bExpand=*/true);
687 uno::Reference
<text::XTextContent
> xContentControl(
688 xMSF
->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY
);
689 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
690 xContentControlProps
->setPropertyValue("PlainText", uno::Any(true));
691 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
693 // Then make sure that the text attribute is inserted:
694 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
695 SwNodeOffset nIndex
= pWrtShell
->GetCursor()->GetPointNode().GetIndex();
696 SwTextNode
* pTextNode
= pDoc
->GetNodes()[nIndex
]->GetTextNode();
697 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
698 CPPUNIT_ASSERT(pAttr
);
699 // Also verify that the type if plain text:
700 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
701 auto& rFormatContentControl
702 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
703 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
704 CPPUNIT_ASSERT(pContentControl
->GetPlainText());
706 // Now check if the char index range 2-4 is extended to 0-6 when we apply formatting:
707 pWrtShell
->SttEndDoc(/*bStt=*/true);
708 // Select "es" from "<dummy>test<dummy>".
709 pWrtShell
->Right(SwCursorSkipMode::Chars
, /*bSelect=*/false, 2, /*bBasicCall=*/false);
710 pWrtShell
->Right(SwCursorSkipMode::Chars
, /*bSelect=*/true, 2, /*bBasicCall=*/false);
711 SfxItemSetFixed
<RES_CHRATR_WEIGHT
, RES_CHRATR_WEIGHT
> aSet(pWrtShell
->GetAttrPool());
712 SvxWeightItem
aItem(WEIGHT_BOLD
, RES_CHRATR_WEIGHT
);
714 pWrtShell
->SetAttrSet(aSet
);
715 pAttr
= pTextNode
->GetTextAttrAt(2, RES_TXTATR_AUTOFMT
);
716 // Without the accompanying fix in place, this test would have failed with:
719 // i.e. the plain text content control now had 3 portions (<dummy>t<b>es</b>t<dummy>), instead
720 // of one (<b><dummy>test<dummy></b>).
721 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(0), pAttr
->GetStart());
722 CPPUNIT_ASSERT(pAttr
->End());
723 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(6), *pAttr
->End());
726 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlComboBox
)
728 // Given an empty document:
730 SwDoc
* pDoc
= getSwDoc();
732 // When inserting a combobox content control:
733 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
734 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
735 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
736 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
737 xText
->insertString(xCursor
, "test", /*bAbsorb=*/false);
738 xCursor
->gotoStart(/*bExpand=*/false);
739 xCursor
->gotoEnd(/*bExpand=*/true);
740 uno::Reference
<text::XTextContent
> xContentControl(
741 xMSF
->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY
);
742 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
744 uno::Sequence
<beans::PropertyValues
> aListItems
= {
746 comphelper::makePropertyValue("DisplayText", uno::Any(OUString("red"))),
747 comphelper::makePropertyValue("Value", uno::Any(OUString("R"))),
750 comphelper::makePropertyValue("DisplayText", uno::Any(OUString("green"))),
751 comphelper::makePropertyValue("Value", uno::Any(OUString("G"))),
754 comphelper::makePropertyValue("DisplayText", uno::Any(OUString("blue"))),
755 comphelper::makePropertyValue("Value", uno::Any(OUString("B"))),
758 xContentControlProps
->setPropertyValue("ListItems", uno::Any(aListItems
));
759 // Without the accompanying fix in place, this test would have failed with:
760 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
761 xContentControlProps
->setPropertyValue("ComboBox", uno::Any(true));
763 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
765 // Then make sure that the specified properties are set:
766 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
767 SwTextNode
* pTextNode
= pWrtShell
->GetCursor()->GetPointNode().GetTextNode();
768 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
769 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
770 auto& rFormatContentControl
771 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
772 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
773 std::vector
<SwContentControlListItem
> aListItems
= pContentControl
->GetListItems();
774 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aListItems
.size());
775 CPPUNIT_ASSERT_EQUAL(OUString("red"), aListItems
[0].m_aDisplayText
);
776 CPPUNIT_ASSERT_EQUAL(OUString("R"), aListItems
[0].m_aValue
);
777 CPPUNIT_ASSERT(pContentControl
->GetComboBox());
780 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControls
)
782 // Given an empty document:
784 auto pXTextDocument
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
785 uno::Reference
<container::XIndexAccess
> xContentControls
= pXTextDocument
->getContentControls();
786 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(0), xContentControls
->getCount());
788 // When inserting content controls:
789 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
790 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
791 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
792 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
794 xText
->insertString(xCursor
, "test1", /*bAbsorb=*/false);
795 xCursor
->gotoStart(/*bExpand=*/false);
796 xCursor
->gotoEnd(/*bExpand=*/true);
798 uno::Reference
<text::XTextContent
> xContentControl(
799 xMSF
->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY
);
800 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
801 xContentControlProps
->setPropertyValue("Tag", uno::Any(OUString("tag1")));
802 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
804 xCursor
->gotoStart(/*bExpand=*/false);
805 // Then tag2 before tag1.
806 xText
->insertString(xCursor
, "test2", /*bAbsorb=*/false);
807 xCursor
->gotoStart(/*bExpand=*/false);
808 xCursor
->goRight(5, /*bExpand=*/true);
810 uno::Reference
<text::XTextContent
> xContentControl(
811 xMSF
->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY
);
812 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
813 xContentControlProps
->setPropertyValue("Tag", uno::Any(OUString("tag2")));
814 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
817 // Then make sure that XContentControls contains the items in a correct order:
818 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(2), xContentControls
->getCount());
819 uno::Reference
<beans::XPropertySet
> xContentControl
;
820 xContentControls
->getByIndex(0) >>= xContentControl
;
822 xContentControl
->getPropertyValue("Tag") >>= aTag
;
823 // Without the accompanying fix in place, this test would have failed with:
826 // i.e. the order of the items was sorted by insert time, not by their doc model position.
827 CPPUNIT_ASSERT_EQUAL(OUString("tag2"), aTag
);
830 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testParagraphMarkerODFExport
)
832 // Given a document with a red numbering portion, from the paragraph marker's format:
833 createSwDoc("paragraph-marker.docx");
835 // When saving that as ODT + reload:
836 saveAndReload("writer8");
838 // Then make sure that it still has the correct color:
839 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
840 // Without the accompanying fix in place, this test would have failed with:
841 // - Expected: 00ff0000 (COL_LIGHTRED)
842 // - Actual : ffffffff (COL_AUTO)
843 // i.e. the custom "red" color was lost as RES_PARATR_LIST_AUTOFMT was not serialized to ODT.
844 CPPUNIT_ASSERT_EQUAL(
845 OUString("00ff0000"),
846 getXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout/SwFieldPortion/SwFont", "color"));
849 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testParagraphMarkerFormattedRun
)
851 // Given a document with a bold run and non-bold paragraph marker:
852 createSwDoc("paragraph-marker-formatted-run.docx");
854 // When saving that as ODT + reload:
855 saveAndReload("writer8");
857 // Then make sure that the numbering portion is still non-bold, matching Word:
858 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
859 // Without the accompanying fix in place, this test would have failed with:
860 // - Expected: normal
862 // i.e. the numbering portion was bold, while its weight should be normal.
863 CPPUNIT_ASSERT_EQUAL(
865 getXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout/SwFieldPortion/SwFont", "weight"));
868 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testFlySplit
)
870 // Given a document with a fly frame:
872 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
873 SwFlyFrameAttrMgr
aMgr(true, pWrtShell
, Frmmgr_Type::TEXT
, nullptr);
874 RndStdIds eAnchor
= RndStdIds::FLY_AT_PARA
;
875 aMgr
.InsertFlyFrame(eAnchor
, aMgr
.GetPos(), aMgr
.GetSize());
876 uno::Reference
<text::XTextFramesSupplier
> xDocument(mxComponent
, uno::UNO_QUERY
);
877 uno::Reference
<beans::XPropertySet
> xFrame(xDocument
->getTextFrames()->getByName("Frame1"),
879 bool bIsSplitAllowed
{};
880 // Without the accompanying fix in place, this test would have failed with:
881 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
882 // - Unknown property: IsSplitAllowed
883 // i.e. the property was missing.
884 xFrame
->getPropertyValue("IsSplitAllowed") >>= bIsSplitAllowed
;
885 CPPUNIT_ASSERT(!bIsSplitAllowed
);
887 // When marking it as IsSplitAllowed=true:
888 xFrame
->setPropertyValue("IsSplitAllowed", uno::Any(true));
890 // Then make sure that IsSplitAllowed is true when asking back:
891 xFrame
->getPropertyValue("IsSplitAllowed") >>= bIsSplitAllowed
;
892 CPPUNIT_ASSERT(bIsSplitAllowed
);
895 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testConvertToTextFrame
)
897 // Given a document with 2 non-interesting frames, an inner frame and an outer frame:
898 createSwDoc("floattable-outer-nonsplit-inner.docx");
900 // When checking the anchor of the inner frame:
901 SwDoc
* pDoc
= getSwDoc();
902 const sw::FrameFormats
<sw::SpzFrameFormat
*>& rFrames
= *pDoc
->GetSpzFrameFormats();
903 sw::SpzFrameFormat
* pFrame3
= rFrames
.FindFrameFormatByName("Frame3");
904 SwNodeIndex aFrame3Anchor
= pFrame3
->GetAnchor().GetContentAnchor()->nNode
;
906 // Then make sure it's anchored in the outer frame's last content node:
907 sw::SpzFrameFormat
* pFrame4
= rFrames
.FindFrameFormatByName("Frame4");
908 SwPaM
aPaM(*pFrame4
->GetContent().GetContentIdx()->GetNode().EndOfSectionNode());
909 aPaM
.Move(fnMoveBackward
, GoInContent
);
910 // Without the accompanying fix in place, this test would have failed with:
911 // - Expected: SwNodeIndex (node 27)
912 // - Actual : SwNodeIndex (node 49)
913 // i.e. Frame3 was anchored much later, in the body text, not in Frame4.
914 CPPUNIT_ASSERT_EQUAL(aPaM
.GetPoint()->nNode
, aFrame3Anchor
);
919 /// This selection listener calls XTextRange::getString() on a selection change, which triggered
920 /// a new selection change event by accident, resulting infinite recursion and crash
921 struct SelectionChangeListener
: public cppu::WeakImplHelper
<view::XSelectionChangeListener
>
924 SelectionChangeListener();
925 // view::XSelectionChangeListener
926 void SAL_CALL
selectionChanged(const lang::EventObject
& rEvent
) override
;
928 // lang::XEventListener
929 void SAL_CALL
disposing(const lang::EventObject
& rSource
) override
;
933 SelectionChangeListener::SelectionChangeListener() {}
935 void SelectionChangeListener::selectionChanged(const lang::EventObject
& rEvent
)
937 uno::Reference
<view::XSelectionSupplier
> xSelectionSupplier(rEvent
.Source
, uno::UNO_QUERY
);
938 css::uno::Reference
<css::container::XIndexAccess
> xSelection(xSelectionSupplier
->getSelection(),
939 css::uno::UNO_QUERY_THROW
);
940 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSelection
->getCount());
941 css::uno::Reference
<css::text::XTextRange
> xTextRange(xSelection
->getByIndex(0),
942 css::uno::UNO_QUERY_THROW
);
943 CPPUNIT_ASSERT(xTextRange
->getString().startsWith("test"));
946 void SelectionChangeListener::disposing(const lang::EventObject
& /*rSource*/) {}
948 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testTdf155951
)
951 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
952 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
953 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
954 xText
->insertString(xCursor
, "test", /*bAbsorb=*/false);
956 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
957 uno::Reference
<view::XSelectionSupplier
> xController(xModel
->getCurrentController(),
959 xController
->addSelectionChangeListener(new SelectionChangeListener());
961 // This crashed here because of infinite recursion
962 dispatchCommand(mxComponent
, ".uno:WordLeftSel", {});
964 // this needs to wait for dispatching (trigger also a second selection change)
965 xText
->insertString(xCursor
, "test", /*bAbsorb=*/false);
968 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testCollectFrameAtNodeWithLayout
)
970 // Given a document with a floating table on 2 pages, with a calculated layout:
971 createSwDoc("floattable-split.docx");
974 // When saving to ODT:
977 // Then make sure the output is valid and hasa 1 <draw:frame>:
978 // Without the accompanying fix in place, this test would have failed with:
979 // Error: uncompleted content model.
980 // i.e. the output was not valid, the second <draw:frame> has an empty <table:table> as a child
982 xmlDocUniquePtr pXmlDoc
= parseExport("content.xml");
983 // Also make sure that we don't have multiple <draw:frame> elements in the first place.
984 assertXPath(pXmlDoc
, "//draw:frame", 1);
987 CPPUNIT_PLUGIN_IMPLEMENT();
989 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */