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/XPageCursor.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>
35 #include <strings.hrc>
37 using namespace ::com::sun::star
;
39 /// Covers sw/source/core/unocore/ fixes.
40 class SwCoreUnocoreTest
: public SwModelTestBase
44 : SwModelTestBase(u
"/sw/qa/core/unocore/data/"_ustr
)
49 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testTdf119081
)
51 // Load a doc with a nested table in it.
52 createSwDoc("tdf119081.odt");
53 SwDocShell
* pDocShell
= getSwDocShell();
54 SwWrtShell
* pWrtShell
= pDocShell
->GetWrtShell();
57 pWrtShell
->Down(/*bSelect=*/false, /*nCount=*/3);
59 pWrtShell
->Right(SwCursorSkipMode::Cells
, /*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false,
62 pWrtShell
->Down(/*bSelect=*/false, /*nCount=*/2);
64 SwDoc
* pDoc
= pDocShell
->GetDoc();
65 SwPaM
& rCursor
= pWrtShell
->GetCurrentShellCursor();
66 rtl::Reference
<SwXTextRange
> xInsertPosition
67 = SwXTextRange::CreateXTextRange(*pDoc
, *rCursor
.GetPoint(), nullptr);
68 uno::Reference
<text::XTextAppend
> xTextAppend(xInsertPosition
->getText(), uno::UNO_QUERY
);
69 // Without the accompanying fix in place, this test would have failed with:
70 // An uncaught exception of type com.sun.star.uno.RuntimeException
71 xTextAppend
->insertTextPortion(u
"x"_ustr
, {}, xInsertPosition
);
73 // Verify that the string is indeed inserted.
74 pWrtShell
->Left(SwCursorSkipMode::Cells
, /*bSelect=*/true, /*nCount=*/1, /*bBasicCall=*/false,
76 CPPUNIT_ASSERT_EQUAL(u
"x"_ustr
, pWrtShell
->GetCurrentShellCursor().GetText());
79 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, selectTextRange
)
82 uno::Reference
<text::XTextDocument
> const xTD(mxComponent
, uno::UNO_QUERY_THROW
);
83 uno::Reference
<text::XText
> const xText(xTD
->getText());
84 uno::Reference
<text::XTextCursor
> const xCursor(xText
->createTextCursor());
85 xText
->insertString(xCursor
, u
"test"_ustr
, /*bAbsorb=*/false);
86 xCursor
->gotoStart(false);
87 xCursor
->gotoEnd(true);
88 CPPUNIT_ASSERT_EQUAL(u
"test"_ustr
, xCursor
->getString());
89 uno::Reference
<lang::XMultiServiceFactory
> const xMSF(mxComponent
, uno::UNO_QUERY_THROW
);
90 uno::Reference
<text::XTextSection
> const xSection(
91 xMSF
->createInstance(u
"com.sun.star.text.TextSection"_ustr
), uno::UNO_QUERY_THROW
);
92 xText
->insertTextContent(xCursor
, xSection
, true);
93 uno::Reference
<text::XTextRange
> const xAnchor(xSection
->getAnchor());
94 uno::Reference
<view::XSelectionSupplier
> const xView(xTD
->getCurrentController(),
96 CPPUNIT_ASSERT_EQUAL(u
"test"_ustr
, xAnchor
->getString());
97 CPPUNIT_ASSERT(xView
->select(uno::Any(xAnchor
)));
98 uno::Reference
<container::XIndexAccess
> xSel
;
99 CPPUNIT_ASSERT(xView
->getSelection() >>= xSel
);
100 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSel
->getCount());
101 uno::Reference
<text::XTextRange
> xSelRange
;
102 CPPUNIT_ASSERT(xSel
->getByIndex(0) >>= xSelRange
);
103 CPPUNIT_ASSERT_EQUAL(u
"test"_ustr
, xSelRange
->getString());
106 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, flyAtParaAnchor
)
109 uno::Reference
<lang::XMultiServiceFactory
> const xMSF(mxComponent
, uno::UNO_QUERY_THROW
);
110 uno::Reference
<text::XTextDocument
> const xTD(mxComponent
, uno::UNO_QUERY_THROW
);
111 uno::Reference
<text::XTextFrame
> const xTextFrame(
112 xMSF
->createInstance(u
"com.sun.star.text.TextFrame"_ustr
), uno::UNO_QUERY_THROW
);
113 uno::Reference
<beans::XPropertySet
> const xFrameProps(xTextFrame
, uno::UNO_QUERY_THROW
);
114 xFrameProps
->setPropertyValue(u
"AnchorType"_ustr
,
115 uno::Any(text::TextContentAnchorType_AT_PARAGRAPH
));
116 auto const xText
= xTD
->getText();
117 auto const xTextCursor
= xText
->createTextCursor();
118 CPPUNIT_ASSERT(xTextCursor
.is());
119 xText
->insertTextContent(xTextCursor
, xTextFrame
, false);
120 auto const xAnchor
= xTextFrame
->getAnchor();
121 uno::Reference
<text::XTextContent
> const xFieldmark(
122 xMSF
->createInstance(u
"com.sun.star.text.Fieldmark"_ustr
), uno::UNO_QUERY_THROW
);
123 // this crashed because the anchor didn't have SwContentIndex
124 xText
->insertTextContent(xAnchor
, xFieldmark
, false);
127 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testRtlGutter
)
130 uno::Reference
<beans::XPropertySet
> xPageStyle(
131 getStyles(u
"PageStyles"_ustr
)->getByName(u
"Standard"_ustr
), uno::UNO_QUERY
);
132 // Without the accompanying fix in place, this test would have failed with:
133 // - Unknown property: RtlGutter
134 auto bRtlGutter
= getProperty
<bool>(xPageStyle
, u
"RtlGutter"_ustr
);
135 CPPUNIT_ASSERT(!bRtlGutter
);
136 xPageStyle
->setPropertyValue(u
"RtlGutter"_ustr
, uno::Any(true));
137 bRtlGutter
= getProperty
<bool>(xPageStyle
, u
"RtlGutter"_ustr
);
138 CPPUNIT_ASSERT(bRtlGutter
);
141 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testBiblioLocalCopy
)
143 // Given an empty document:
146 // When setting the LocalURL of a biblio field:
147 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
148 uno::Reference
<beans::XPropertySet
> xField(
149 xFactory
->createInstance(u
"com.sun.star.text.TextField.Bibliography"_ustr
), uno::UNO_QUERY
);
150 uno::Sequence
<beans::PropertyValue
> aFields
= {
151 comphelper::makePropertyValue(u
"BibiliographicType"_ustr
, text::BibliographyDataType::WWW
),
152 comphelper::makePropertyValue(u
"Identifier"_ustr
, u
"ARJ00"_ustr
),
153 comphelper::makePropertyValue(u
"Author"_ustr
, u
"Me"_ustr
),
154 comphelper::makePropertyValue(u
"Title"_ustr
, u
"mytitle"_ustr
),
155 comphelper::makePropertyValue(u
"Year"_ustr
, u
"2020"_ustr
),
156 comphelper::makePropertyValue(u
"URL"_ustr
, u
"http://www.example.com/test.pdf"_ustr
),
157 comphelper::makePropertyValue(u
"LocalURL"_ustr
, u
"file:///home/me/test.pdf"_ustr
),
159 xField
->setPropertyValue(u
"Fields"_ustr
, uno::Any(aFields
));
160 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
161 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
162 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
163 uno::Reference
<text::XTextContent
> xContent(xField
, uno::UNO_QUERY
);
164 xText
->insertTextContent(xCursor
, xContent
, /*bAbsorb=*/false);
166 // Then make sure we get that LocalURL back:
167 comphelper::SequenceAsHashMap
aMap(xField
->getPropertyValue(u
"Fields"_ustr
));
168 // Without the accompanying fix in place, this test would have failed, there was no LocalURL key
170 CPPUNIT_ASSERT(aMap
.contains(u
"LocalURL"_ustr
));
171 auto aActual
= aMap
[u
"LocalURL"_ustr
].get
<OUString
>();
172 CPPUNIT_ASSERT_EQUAL(u
"file:///home/me/test.pdf"_ustr
, aActual
);
175 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testLinkedStyles
)
177 // Given an empty document:
180 // When defining a linked style for a para style:
181 uno::Reference
<container::XNameAccess
> xParaStyles
= getStyles(u
"ParagraphStyles"_ustr
);
182 uno::Reference
<beans::XPropertySet
> xParaStyle(xParaStyles
->getByName(u
"Caption"_ustr
),
184 CPPUNIT_ASSERT_EQUAL(OUString(), getProperty
<OUString
>(xParaStyle
, u
"LinkStyle"_ustr
));
185 xParaStyle
->setPropertyValue(u
"LinkStyle"_ustr
, uno::Any(u
"Emphasis"_ustr
));
186 // Then make sure we get the linked char style back:
187 CPPUNIT_ASSERT_EQUAL(u
"Emphasis"_ustr
, getProperty
<OUString
>(xParaStyle
, u
"LinkStyle"_ustr
));
189 // When defining a linked style for a char style:
190 uno::Reference
<container::XNameAccess
> xCharStyles
= getStyles(u
"CharacterStyles"_ustr
);
191 uno::Reference
<beans::XPropertySet
> xCharStyle(xCharStyles
->getByName(u
"Emphasis"_ustr
),
193 CPPUNIT_ASSERT_EQUAL(OUString(), getProperty
<OUString
>(xCharStyle
, u
"LinkStyle"_ustr
));
194 xCharStyle
->setPropertyValue(u
"LinkStyle"_ustr
, uno::Any(u
"Caption"_ustr
));
195 // Then make sure we get the linked para style back:
196 CPPUNIT_ASSERT_EQUAL(u
"Caption"_ustr
, getProperty
<OUString
>(xCharStyle
, u
"LinkStyle"_ustr
));
199 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testViewCursorTextFrame
)
201 // Given a document with a graphic and holding a reference to that graphic frame:
203 uno::Sequence
<beans::PropertyValue
> aInsertArgs
204 = { comphelper::makePropertyValue(u
"FileName"_ustr
, createFileURL(u
"graphic.png")) };
205 dispatchCommand(mxComponent
, u
".uno:InsertGraphic"_ustr
, aInsertArgs
);
206 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
207 uno::Reference
<text::XTextViewCursorSupplier
> xTextViewCursorSupplier(
208 xModel
->getCurrentController(), uno::UNO_QUERY
);
209 uno::Reference
<beans::XPropertySet
> xViewCursor(xTextViewCursorSupplier
->getViewCursor(),
211 uno::Reference
<beans::XPropertySet
> xFrame
;
212 xViewCursor
->getPropertyValue(u
"TextFrame"_ustr
) >>= xFrame
;
214 // When saving to ODT, then make sure the store doesn't fail:
215 uno::Reference
<frame::XStorable
> xStorable(xModel
, uno::UNO_QUERY
);
216 uno::Sequence
<beans::PropertyValue
> aStoreArgs
217 = { comphelper::makePropertyValue(u
"FilterName"_ustr
, u
"writer8"_ustr
) };
218 // Without the accompanying fix in place, this test would have failed with:
219 // uno.RuntimeException: "SwXParagraph: disposed or invalid ..."
220 xStorable
->storeToURL(maTempFile
.GetURL(), aStoreArgs
);
223 /// Fails the test if an error popup would be presented.
224 static void BasicDisplayErrorHandler(const OUString
& /*rErr*/, const OUString
& /*rAction*/)
226 CPPUNIT_ASSERT(false);
229 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testBrokenEmbeddedObject
)
231 // Given a document with a broken embedded object (the XML markup is not well-formed):
232 createSwDoc("broken-embedded-object.odt");
233 uno::Reference
<text::XTextEmbeddedObjectsSupplier
> xSupplier(mxComponent
, uno::UNO_QUERY
);
234 uno::Reference
<container::XIndexAccess
> xObjects(xSupplier
->getEmbeddedObjects(),
236 uno::Reference
<beans::XPropertySet
> xObject(xObjects
->getByIndex(0), uno::UNO_QUERY
);
237 uno::Reference
<lang::XServiceInfo
> xEmbeddedObject
;
238 // Get the property first, which initializes Draw, which would overwrite our error handler.
239 xObject
->getPropertyValue(u
"EmbeddedObject"_ustr
) >>= xEmbeddedObject
;
240 ErrorRegistry::RegisterDisplay(&BasicDisplayErrorHandler
);
242 // When trying to load that embedded object:
243 xObject
->getPropertyValue(u
"EmbeddedObject"_ustr
) >>= xEmbeddedObject
;
245 // Then make sure we get a non-empty reference and an error popup it not shown:
246 CPPUNIT_ASSERT(xEmbeddedObject
.is());
247 // Without the accompanying fix in place, we got this reference, but first an error popup was
248 // shown to the user.
250 xEmbeddedObject
->supportsService(u
"com.sun.star.comp.embed.OCommonEmbeddedObject"_ustr
));
253 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testLineBreakInsert
)
255 // Given an empty document:
257 SwDoc
* pDoc
= getSwDoc();
259 // When inserting a line-break with properties:
260 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
261 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
262 uno::Reference
<text::XTextContent
> xLineBreak(
263 xMSF
->createInstance(u
"com.sun.star.text.LineBreak"_ustr
), uno::UNO_QUERY
);
264 uno::Reference
<beans::XPropertySet
> xLineBreakProps(xLineBreak
, uno::UNO_QUERY
);
265 auto eClear
= static_cast<sal_Int16
>(SwLineBreakClear::ALL
);
266 xLineBreakProps
->setPropertyValue(u
"Clear"_ustr
, uno::Any(eClear
));
267 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
268 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
269 xText
->insertTextContent(xCursor
, xLineBreak
, /*bAbsorb=*/false);
271 // Then make sure that both the line break and its matching text attribute is inserted:
272 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
273 SwNodeOffset nIndex
= pWrtShell
->GetCursor()->GetPointNode().GetIndex();
274 SwTextNode
* pTextNode
= pDoc
->GetNodes()[nIndex
]->GetTextNode();
275 // Without the accompanying fix in place, this test would have failed with:
276 // - Expected: "\n" (newline)
277 // - Actual : "" (empty string)
278 // i.e. SwXLineBreak::attach() did not insert the newline + its text attribute.
279 CPPUNIT_ASSERT_EQUAL(u
"\n"_ustr
, pTextNode
->GetText());
280 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_LINEBREAK
);
281 CPPUNIT_ASSERT(pAttr
);
282 auto pTextLineBreak
= static_cast<SwTextLineBreak
*>(pAttr
);
283 auto& rFormatLineBreak
= static_cast<SwFormatLineBreak
&>(pTextLineBreak
->GetAttr());
284 CPPUNIT_ASSERT_EQUAL(SwLineBreakClear::ALL
, rFormatLineBreak
.GetValue());
287 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testLineBreakTextPortionEnum
)
289 // Given a document with a clearing break:
291 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
292 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
293 uno::Reference
<text::XTextContent
> xLineBreak(
294 xMSF
->createInstance(u
"com.sun.star.text.LineBreak"_ustr
), uno::UNO_QUERY
);
295 uno::Reference
<beans::XPropertySet
> xLineBreakProps(xLineBreak
, uno::UNO_QUERY
);
296 auto eClear
= static_cast<sal_Int16
>(SwLineBreakClear::ALL
);
297 xLineBreakProps
->setPropertyValue(u
"Clear"_ustr
, uno::Any(eClear
));
298 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
299 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
300 xText
->insertTextContent(xCursor
, xLineBreak
, /*bAbsorb=*/false);
302 // When enumerating the text portions of the only paragraph in the document:
303 uno::Reference
<css::text::XTextRange
> xTextPortion
= getRun(getParagraph(1), 1);
305 // Then make sure that the text portion type is correct + the clear type can be read:
306 auto aPortionType
= getProperty
<OUString
>(xTextPortion
, u
"TextPortionType"_ustr
);
307 // Without the accompanying fix in place, this test would have failed with:
308 // - Expected: LineBreak
310 // i.e. a line break with properties was part of the normal Text portion, making it impossible
311 // to get those properties.
312 CPPUNIT_ASSERT_EQUAL(u
"LineBreak"_ustr
, aPortionType
);
313 xLineBreak
= getProperty
<uno::Reference
<text::XTextContent
>>(xTextPortion
, u
"LineBreak"_ustr
);
314 eClear
= getProperty
<sal_Int16
>(xLineBreak
, u
"Clear"_ustr
);
315 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16
>(SwLineBreakClear::ALL
), eClear
);
318 CPPUNIT_TEST_FIXTURE(SwModelTestBase
, testUserFieldTooltip
)
320 // Given a document with a user field:
322 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
323 uno::Reference
<text::XDependentTextField
> xField(
324 xFactory
->createInstance(u
"com.sun.star.text.TextField.User"_ustr
), uno::UNO_QUERY
);
325 uno::Reference
<beans::XPropertySet
> xMaster(
326 xFactory
->createInstance(u
"com.sun.star.text.FieldMaster.User"_ustr
), uno::UNO_QUERY
);
327 xMaster
->setPropertyValue(u
"Name"_ustr
, uno::Any(u
"a_user_field"_ustr
));
328 xField
->attachTextFieldMaster(xMaster
);
329 xField
->getTextFieldMaster()->setPropertyValue(u
"Content"_ustr
, uno::Any(u
"42"_ustr
));
330 uno::Reference
<text::XTextDocument
> xDocument(mxComponent
, uno::UNO_QUERY
);
331 uno::Reference
<text::XText
> xText
= xDocument
->getText();
332 xText
->insertTextContent(xText
->createTextCursor(), xField
, /*bAbsorb=*/false);
333 uno::Reference
<beans::XPropertySet
> xFieldProps(xField
, uno::UNO_QUERY
);
335 // When setting a tooltip on the field:
336 OUString
aExpected(u
"first line\nsecond line"_ustr
);
337 xFieldProps
->setPropertyValue(u
"Title"_ustr
, uno::Any(aExpected
));
339 // Then make sure that the tooltip we read back matches the one previously specified:
340 // Without the accompanying fix in place, this test would have failed with:
341 // - the property is of unexpected type or void: Title
342 // i.e. reading of the tooltip was broken.
343 CPPUNIT_ASSERT_EQUAL(aExpected
, getProperty
<OUString
>(xFieldProps
, u
"Title"_ustr
));
346 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlInsert
)
348 // Given an empty document:
350 SwDoc
* pDoc
= getSwDoc();
352 // When inserting a content control around one or more text portions:
353 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
354 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
355 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
356 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
357 xText
->insertString(xCursor
, u
"test"_ustr
, /*bAbsorb=*/false);
358 xCursor
->gotoStart(/*bExpand=*/false);
359 xCursor
->gotoEnd(/*bExpand=*/true);
360 uno::Reference
<text::XTextContent
> xContentControl(
361 xMSF
->createInstance(u
"com.sun.star.text.ContentControl"_ustr
), uno::UNO_QUERY
);
362 // Set a custom property on the content control:
363 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
364 xContentControlProps
->setPropertyValue(u
"ShowingPlaceHolder"_ustr
, uno::Any(true));
365 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
367 // Then make sure that the text attribute is inserted:
368 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
369 SwNodeOffset nIndex
= pWrtShell
->GetCursor()->GetPointNode().GetIndex();
370 SwTextNode
* pTextNode
= pDoc
->GetNodes()[nIndex
]->GetTextNode();
371 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
372 // Without the accompanying fix in place, this test would have failed, as the
373 // SwXContentControl::attach() implementation was missing.
374 CPPUNIT_ASSERT(pAttr
);
375 // Also verify that the custom property was set:
376 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
377 auto& rFormatContentControl
378 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
379 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
380 CPPUNIT_ASSERT(pContentControl
->GetShowingPlaceHolder());
382 // Also verify that setText() and getText() works:
383 uno::Reference
<text::XText
> xContentControlText(xContentControl
, uno::UNO_QUERY
);
384 xContentControlText
->setString(u
"new"_ustr
);
385 // Without the accompanying fix in place, this test would have failed with:
388 // i.e. getString() always returned an empty string.
389 CPPUNIT_ASSERT_EQUAL(u
"new"_ustr
, xContentControlText
->getString());
392 CPPUNIT_TEST_FIXTURE(SwModelTestBase
, testImageTooltip
)
394 // Given a document with an image and a hyperlink on it:
396 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
397 uno::Reference
<text::XTextDocument
> xDocument(mxComponent
, uno::UNO_QUERY
);
398 uno::Reference
<text::XText
> xText
= xDocument
->getText();
399 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
400 uno::Reference
<text::XTextContent
> xImage(
401 xFactory
->createInstance(u
"com.sun.star.text.TextGraphicObject"_ustr
), uno::UNO_QUERY
);
402 xText
->insertTextContent(xCursor
, xImage
, /*bAbsorb=*/false);
403 uno::Reference
<beans::XPropertySet
> xImageProps(xImage
, uno::UNO_QUERY
);
404 xImageProps
->setPropertyValue(u
"HyperLinkURL"_ustr
, uno::Any(u
"http://www.example.com"_ustr
));
406 // When setting a tooltip on the image:
407 OUString
aExpected(u
"first line\nsecond line"_ustr
);
408 xImageProps
->setPropertyValue(u
"Tooltip"_ustr
, uno::Any(aExpected
));
410 // Then make sure that the tooltip we read back matches the one previously specified:
411 // Without the accompanying fix in place, this test would have failed with:
412 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
413 // i.e. reading/writing of the tooltip was broken.
414 CPPUNIT_ASSERT_EQUAL(aExpected
, getProperty
<OUString
>(xImageProps
, u
"Tooltip"_ustr
));
417 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlTextPortionEnum
)
419 // Given a document with a content control around one or more text portions:
421 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
422 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
423 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
424 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
425 xText
->insertString(xCursor
, u
"test"_ustr
, /*bAbsorb=*/false);
426 xCursor
->gotoStart(/*bExpand=*/false);
427 xCursor
->gotoEnd(/*bExpand=*/true);
428 uno::Reference
<text::XTextContent
> xContentControl(
429 xMSF
->createInstance(u
"com.sun.star.text.ContentControl"_ustr
), uno::UNO_QUERY
);
430 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
432 // When enumerating the text portions of the only paragraph in the document:
433 uno::Reference
<css::text::XTextRange
> xTextPortion
= getRun(getParagraph(1), 1);
435 // Then make sure that the text portion type is correct + the content can be read:
436 auto aPortionType
= getProperty
<OUString
>(xTextPortion
, u
"TextPortionType"_ustr
);
437 // Without the accompanying fix in place, this test would have failed with:
438 // - Expected: ContentControl
440 // i.e. the content control text attribute was ignored.
441 CPPUNIT_ASSERT_EQUAL(u
"ContentControl"_ustr
, aPortionType
);
443 = getProperty
<uno::Reference
<text::XTextContent
>>(xTextPortion
, u
"ContentControl"_ustr
);
444 uno::Reference
<text::XTextRange
> xContentControlRange(xContentControl
, uno::UNO_QUERY
);
445 xText
= xContentControlRange
->getText();
446 uno::Reference
<container::XEnumerationAccess
> xContentEnumAccess(xText
, uno::UNO_QUERY
);
447 uno::Reference
<container::XEnumeration
> xContentEnum
= xContentEnumAccess
->createEnumeration();
448 uno::Reference
<text::XTextRange
> xContent(xContentEnum
->nextElement(), uno::UNO_QUERY
);
449 CPPUNIT_ASSERT_EQUAL(u
"test"_ustr
, xContent
->getString());
451 // Also test the generated layout:
452 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
453 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u
"");
454 // Without the accompanying fix in place, this test would have failed with:
455 // - Expected: PortionType::ContentControl
456 // - Actual : PortionType::Text
457 // i.e. SwContentControl generated a plain text portion, not a dedicated content control
459 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout/SwLinePortion", "type",
460 u
"PortionType::ContentControl");
461 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout/SwLinePortion", "portion", u
"test*");
463 // Also test the doc model, make sure that there is a dummy character at the start and end, so
464 // the user can explicitly decide if they want to expand the content control or not:
465 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
466 OUString aText
= pWrtShell
->GetCursor()->GetPointNode().GetTextNode()->GetText();
467 // Without the accompanying fix in place, this test would have failed with:
468 // - Expected: ^Atest^A
470 // i.e. there was no dummy character at the end.
471 CPPUNIT_ASSERT_EQUAL(u
"\x0001test\x0001"_ustr
, aText
);
474 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlCheckbox
)
476 // Given an empty document:
479 // When inserting a checkbox content control:
480 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
481 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
482 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
483 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
484 xText
->insertString(xCursor
, u
"test"_ustr
, /*bAbsorb=*/false);
485 xCursor
->gotoStart(/*bExpand=*/false);
486 xCursor
->gotoEnd(/*bExpand=*/true);
487 uno::Reference
<text::XTextContent
> xContentControl(
488 xMSF
->createInstance(u
"com.sun.star.text.ContentControl"_ustr
), uno::UNO_QUERY
);
489 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
490 // Without the accompanying fix in place, this test would have failed with:
491 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
492 xContentControlProps
->setPropertyValue(u
"Checkbox"_ustr
, uno::Any(true));
493 xContentControlProps
->setPropertyValue(u
"Checked"_ustr
, uno::Any(true));
494 xContentControlProps
->setPropertyValue(u
"CheckedState"_ustr
, uno::Any(u
"☒"_ustr
));
495 xContentControlProps
->setPropertyValue(u
"UncheckedState"_ustr
, uno::Any(u
"☐"_ustr
));
496 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
498 // Then make sure that the specified properties are set:
499 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
500 SwTextNode
* pTextNode
= pWrtShell
->GetCursor()->GetPointNode().GetTextNode();
501 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
502 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
503 auto& rFormatContentControl
504 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
505 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
506 CPPUNIT_ASSERT(pContentControl
->GetCheckbox());
507 CPPUNIT_ASSERT(pContentControl
->GetChecked());
508 CPPUNIT_ASSERT_EQUAL(u
"☒"_ustr
, pContentControl
->GetCheckedState());
509 CPPUNIT_ASSERT_EQUAL(u
"☐"_ustr
, pContentControl
->GetUncheckedState());
512 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlDropdown
)
514 // Given an empty document:
517 // When inserting a dropdown content control:
518 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
519 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
520 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
521 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
522 xText
->insertString(xCursor
, u
"test"_ustr
, /*bAbsorb=*/false);
523 xCursor
->gotoStart(/*bExpand=*/false);
524 xCursor
->gotoEnd(/*bExpand=*/true);
525 uno::Reference
<text::XTextContent
> xContentControl(
526 xMSF
->createInstance(u
"com.sun.star.text.ContentControl"_ustr
), uno::UNO_QUERY
);
527 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
529 uno::Sequence
<beans::PropertyValues
> aListItems
= {
531 comphelper::makePropertyValue(u
"DisplayText"_ustr
, uno::Any(u
"red"_ustr
)),
532 comphelper::makePropertyValue(u
"Value"_ustr
, uno::Any(u
"R"_ustr
)),
535 comphelper::makePropertyValue(u
"DisplayText"_ustr
, uno::Any(u
"green"_ustr
)),
536 comphelper::makePropertyValue(u
"Value"_ustr
, uno::Any(u
"G"_ustr
)),
539 comphelper::makePropertyValue(u
"DisplayText"_ustr
, uno::Any(u
"blue"_ustr
)),
540 comphelper::makePropertyValue(u
"Value"_ustr
, uno::Any(u
"B"_ustr
)),
543 // Without the accompanying fix in place, this test would have failed with:
544 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
545 xContentControlProps
->setPropertyValue(u
"ListItems"_ustr
, uno::Any(aListItems
));
547 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
549 // Then make sure that the specified properties are set:
550 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
551 SwTextNode
* pTextNode
= pWrtShell
->GetCursor()->GetPointNode().GetTextNode();
552 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
553 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
554 auto& rFormatContentControl
555 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
556 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
557 std::vector
<SwContentControlListItem
> aListItems
= pContentControl
->GetListItems();
558 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aListItems
.size());
559 CPPUNIT_ASSERT_EQUAL(u
"red"_ustr
, aListItems
[0].m_aDisplayText
);
560 CPPUNIT_ASSERT_EQUAL(u
"R"_ustr
, aListItems
[0].m_aValue
);
563 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testInsertFileInContentControlException
)
565 // Given a document with a content control:
567 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
568 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
569 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
570 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
571 xText
->insertString(xCursor
, u
"test"_ustr
, /*bAbsorb=*/false);
572 xCursor
->gotoStart(/*bExpand=*/false);
573 xCursor
->gotoEnd(/*bExpand=*/true);
574 uno::Reference
<text::XTextContent
> xContentControl(
575 xMSF
->createInstance(u
"com.sun.star.text.ContentControl"_ustr
), uno::UNO_QUERY
);
576 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
578 // Reject inserting a document inside the content control:
579 xCursor
->goLeft(1, false);
580 OUString
aURL(createFileURL(u
"tdf119081.odt"));
581 uno::Reference
<document::XDocumentInsertable
> xInsertable(xCursor
, uno::UNO_QUERY
);
582 CPPUNIT_ASSERT_THROW(xInsertable
->insertDocumentFromURL(aURL
, {}), uno::RuntimeException
);
584 // Accept inserting a document outside the content control:
585 xCursor
->goRight(1, false);
586 xInsertable
->insertDocumentFromURL(aURL
, {});
589 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlPicture
)
591 // Given an empty document:
594 // When inserting a picture content control:
595 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
596 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
597 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
598 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
599 uno::Reference
<beans::XPropertySet
> xTextGraphic(
600 xMSF
->createInstance(u
"com.sun.star.text.TextGraphicObject"_ustr
), uno::UNO_QUERY
);
601 xTextGraphic
->setPropertyValue(u
"AnchorType"_ustr
,
602 uno::Any(text::TextContentAnchorType_AS_CHARACTER
));
603 uno::Reference
<text::XTextContent
> xTextContent(xTextGraphic
, uno::UNO_QUERY
);
604 xText
->insertTextContent(xCursor
, xTextContent
, false);
605 xCursor
->gotoStart(/*bExpand=*/false);
606 xCursor
->gotoEnd(/*bExpand=*/true);
607 uno::Reference
<text::XTextContent
> xContentControl(
608 xMSF
->createInstance(u
"com.sun.star.text.ContentControl"_ustr
), uno::UNO_QUERY
);
609 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
610 // Without the accompanying fix in place, this test would have failed with:
611 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
612 xContentControlProps
->setPropertyValue(u
"Picture"_ustr
, uno::Any(true));
613 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
615 // Then make sure that the specified properties are set:
616 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
617 SwTextNode
* pTextNode
= pWrtShell
->GetCursor()->GetPointNode().GetTextNode();
618 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
619 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
620 auto& rFormatContentControl
621 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
622 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
623 CPPUNIT_ASSERT(pContentControl
->GetPicture());
626 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlDate
)
628 // Given an empty document:
631 // When inserting a date content control:
632 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
633 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
634 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
635 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
636 xText
->insertString(xCursor
, u
"test"_ustr
, /*bAbsorb=*/false);
637 xCursor
->gotoStart(/*bExpand=*/false);
638 xCursor
->gotoEnd(/*bExpand=*/true);
639 uno::Reference
<text::XTextContent
> xContentControl(
640 xMSF
->createInstance(u
"com.sun.star.text.ContentControl"_ustr
), uno::UNO_QUERY
);
641 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
642 // Without the accompanying fix in place, this test would have failed with:
643 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
644 xContentControlProps
->setPropertyValue(u
"Date"_ustr
, uno::Any(true));
645 xContentControlProps
->setPropertyValue(u
"DateFormat"_ustr
, uno::Any(u
"M/d/yyyy"_ustr
));
646 xContentControlProps
->setPropertyValue(u
"DateLanguage"_ustr
, uno::Any(u
"en-US"_ustr
));
647 xContentControlProps
->setPropertyValue(u
"CurrentDate"_ustr
,
648 uno::Any(u
"2022-05-25T00:00:00Z"_ustr
));
649 xContentControlProps
->setPropertyValue(u
"PlaceholderDocPart"_ustr
,
650 uno::Any(u
"DefaultPlaceholder_-1854013437"_ustr
));
651 xContentControlProps
->setPropertyValue(
652 u
"DataBindingPrefixMappings"_ustr
,
653 uno::Any(u
"xmlns:ns0='http://schemas.microsoft.com/vsto/samples' "_ustr
));
654 xContentControlProps
->setPropertyValue(
655 u
"DataBindingXpath"_ustr
,
656 uno::Any(u
"/ns0:employees[1]/ns0:employee[1]/ns0:hireDate[1]"_ustr
));
657 xContentControlProps
->setPropertyValue(
658 u
"DataBindingStoreItemID"_ustr
, uno::Any(u
"{241A8A02-7FFD-488D-8827-63FBE74E8BC9}"_ustr
));
659 xContentControlProps
->setPropertyValue(u
"Color"_ustr
, uno::Any(u
"008000"_ustr
));
660 xContentControlProps
->setPropertyValue(u
"Alias"_ustr
, uno::Any(u
"myalias"_ustr
));
661 xContentControlProps
->setPropertyValue(u
"Tag"_ustr
, uno::Any(u
"mytag"_ustr
));
662 xContentControlProps
->setPropertyValue(u
"Id"_ustr
, uno::Any(static_cast<sal_Int32
>(123)));
663 xContentControlProps
->setPropertyValue(u
"TabIndex"_ustr
, uno::Any(sal_uInt32(1)));
664 xContentControlProps
->setPropertyValue(u
"Lock"_ustr
, uno::Any(u
"sdtContentLocked"_ustr
));
665 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
667 // Then make sure that the specified properties are set:
668 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
669 SwTextNode
* pTextNode
= pWrtShell
->GetCursor()->GetPointNode().GetTextNode();
670 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
671 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
672 auto& rFormatContentControl
673 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
674 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
675 CPPUNIT_ASSERT(pContentControl
->GetDate());
676 CPPUNIT_ASSERT_EQUAL(u
"M/d/yyyy"_ustr
, pContentControl
->GetDateFormat());
677 CPPUNIT_ASSERT_EQUAL(u
"en-US"_ustr
, pContentControl
->GetDateLanguage());
678 CPPUNIT_ASSERT_EQUAL(u
"2022-05-25T00:00:00Z"_ustr
, pContentControl
->GetCurrentDate());
679 CPPUNIT_ASSERT_EQUAL(u
"DefaultPlaceholder_-1854013437"_ustr
,
680 pContentControl
->GetPlaceholderDocPart());
681 CPPUNIT_ASSERT_EQUAL(u
"xmlns:ns0='http://schemas.microsoft.com/vsto/samples' "_ustr
,
682 pContentControl
->GetDataBindingPrefixMappings());
683 CPPUNIT_ASSERT_EQUAL(u
"/ns0:employees[1]/ns0:employee[1]/ns0:hireDate[1]"_ustr
,
684 pContentControl
->GetDataBindingXpath());
685 CPPUNIT_ASSERT_EQUAL(u
"{241A8A02-7FFD-488D-8827-63FBE74E8BC9}"_ustr
,
686 pContentControl
->GetDataBindingStoreItemID());
687 CPPUNIT_ASSERT_EQUAL(u
"008000"_ustr
, pContentControl
->GetColor());
688 CPPUNIT_ASSERT_EQUAL(u
"myalias"_ustr
, pContentControl
->GetAlias());
689 CPPUNIT_ASSERT_EQUAL(u
"mytag"_ustr
, pContentControl
->GetTag());
690 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(123), pContentControl
->GetId());
691 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32
>(1), pContentControl
->GetTabIndex());
692 CPPUNIT_ASSERT_EQUAL(u
"sdtContentLocked"_ustr
, pContentControl
->GetLock());
695 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlPlainText
)
697 // Given an empty document:
699 SwDoc
* pDoc
= getSwDoc();
701 // When inserting a plain text content control around a text portion:
702 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
703 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
704 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
705 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
706 xText
->insertString(xCursor
, u
"test"_ustr
, /*bAbsorb=*/false);
707 xCursor
->gotoStart(/*bExpand=*/false);
708 xCursor
->gotoEnd(/*bExpand=*/true);
709 uno::Reference
<text::XTextContent
> xContentControl(
710 xMSF
->createInstance(u
"com.sun.star.text.ContentControl"_ustr
), uno::UNO_QUERY
);
711 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
712 xContentControlProps
->setPropertyValue(u
"PlainText"_ustr
, uno::Any(true));
713 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
715 // Then make sure that the text attribute is inserted:
716 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
717 SwNodeOffset nIndex
= pWrtShell
->GetCursor()->GetPointNode().GetIndex();
718 SwTextNode
* pTextNode
= pDoc
->GetNodes()[nIndex
]->GetTextNode();
719 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
720 CPPUNIT_ASSERT(pAttr
);
721 // Also verify that the type if plain text:
722 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
723 auto& rFormatContentControl
724 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
725 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
726 CPPUNIT_ASSERT(pContentControl
->GetPlainText());
728 // Now check if the char index range 2-4 is extended to 0-6 when we apply formatting:
729 pWrtShell
->SttEndDoc(/*bStt=*/true);
730 // Select "es" from "<dummy>test<dummy>".
731 pWrtShell
->Right(SwCursorSkipMode::Chars
, /*bSelect=*/false, 2, /*bBasicCall=*/false);
732 pWrtShell
->Right(SwCursorSkipMode::Chars
, /*bSelect=*/true, 2, /*bBasicCall=*/false);
733 SfxItemSetFixed
<RES_CHRATR_WEIGHT
, RES_CHRATR_WEIGHT
> aSet(pWrtShell
->GetAttrPool());
734 SvxWeightItem
aItem(WEIGHT_BOLD
, RES_CHRATR_WEIGHT
);
736 pWrtShell
->SetAttrSet(aSet
);
737 pAttr
= pTextNode
->GetTextAttrAt(2, RES_TXTATR_AUTOFMT
);
738 // Without the accompanying fix in place, this test would have failed with:
741 // i.e. the plain text content control now had 3 portions (<dummy>t<b>es</b>t<dummy>), instead
742 // of one (<b><dummy>test<dummy></b>).
743 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(0), pAttr
->GetStart());
744 CPPUNIT_ASSERT(pAttr
->End());
745 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(6), *pAttr
->End());
748 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControlComboBox
)
750 // Given an empty document:
753 // When inserting a combobox content control:
754 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
755 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
756 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
757 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
758 xText
->insertString(xCursor
, u
"test"_ustr
, /*bAbsorb=*/false);
759 xCursor
->gotoStart(/*bExpand=*/false);
760 xCursor
->gotoEnd(/*bExpand=*/true);
761 uno::Reference
<text::XTextContent
> xContentControl(
762 xMSF
->createInstance(u
"com.sun.star.text.ContentControl"_ustr
), uno::UNO_QUERY
);
763 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
765 uno::Sequence
<beans::PropertyValues
> aListItems
= {
767 comphelper::makePropertyValue(u
"DisplayText"_ustr
, uno::Any(u
"red"_ustr
)),
768 comphelper::makePropertyValue(u
"Value"_ustr
, uno::Any(u
"R"_ustr
)),
771 comphelper::makePropertyValue(u
"DisplayText"_ustr
, uno::Any(u
"green"_ustr
)),
772 comphelper::makePropertyValue(u
"Value"_ustr
, uno::Any(u
"G"_ustr
)),
775 comphelper::makePropertyValue(u
"DisplayText"_ustr
, uno::Any(u
"blue"_ustr
)),
776 comphelper::makePropertyValue(u
"Value"_ustr
, uno::Any(u
"B"_ustr
)),
779 xContentControlProps
->setPropertyValue(u
"ListItems"_ustr
, uno::Any(aListItems
));
780 // Without the accompanying fix in place, this test would have failed with:
781 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
782 xContentControlProps
->setPropertyValue(u
"ComboBox"_ustr
, uno::Any(true));
784 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
786 // Then make sure that the specified properties are set:
787 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
788 SwTextNode
* pTextNode
= pWrtShell
->GetCursor()->GetPointNode().GetTextNode();
789 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
790 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
791 auto& rFormatContentControl
792 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
793 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
794 std::vector
<SwContentControlListItem
> aListItems
= pContentControl
->GetListItems();
795 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aListItems
.size());
796 CPPUNIT_ASSERT_EQUAL(u
"red"_ustr
, aListItems
[0].m_aDisplayText
);
797 CPPUNIT_ASSERT_EQUAL(u
"R"_ustr
, aListItems
[0].m_aValue
);
798 CPPUNIT_ASSERT(pContentControl
->GetComboBox());
801 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testContentControls
)
803 // Given an empty document:
805 uno::Reference
<container::XIndexAccess
> xContentControls
= getSwTextDoc()->getContentControls();
806 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(0), xContentControls
->getCount());
808 // When inserting content controls:
809 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
810 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
811 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
812 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
814 xText
->insertString(xCursor
, u
"test1"_ustr
, /*bAbsorb=*/false);
815 xCursor
->gotoStart(/*bExpand=*/false);
816 xCursor
->gotoEnd(/*bExpand=*/true);
818 uno::Reference
<text::XTextContent
> xContentControl(
819 xMSF
->createInstance(u
"com.sun.star.text.ContentControl"_ustr
), uno::UNO_QUERY
);
820 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
821 xContentControlProps
->setPropertyValue(u
"Tag"_ustr
, uno::Any(u
"tag1"_ustr
));
822 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
824 xCursor
->gotoStart(/*bExpand=*/false);
825 // Then tag2 before tag1.
826 xText
->insertString(xCursor
, u
"test2"_ustr
, /*bAbsorb=*/false);
827 xCursor
->gotoStart(/*bExpand=*/false);
828 xCursor
->goRight(5, /*bExpand=*/true);
830 uno::Reference
<text::XTextContent
> xContentControl(
831 xMSF
->createInstance(u
"com.sun.star.text.ContentControl"_ustr
), uno::UNO_QUERY
);
832 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
833 xContentControlProps
->setPropertyValue(u
"Tag"_ustr
, uno::Any(u
"tag2"_ustr
));
834 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
837 // Then make sure that XContentControls contains the items in a correct order:
838 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(2), xContentControls
->getCount());
839 uno::Reference
<beans::XPropertySet
> xContentControl
;
840 xContentControls
->getByIndex(0) >>= xContentControl
;
842 xContentControl
->getPropertyValue(u
"Tag"_ustr
) >>= aTag
;
843 // Without the accompanying fix in place, this test would have failed with:
846 // i.e. the order of the items was sorted by insert time, not by their doc model position.
847 CPPUNIT_ASSERT_EQUAL(u
"tag2"_ustr
, aTag
);
850 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testParagraphMarkerODFExport
)
852 // Given a document with a red numbering portion, from the paragraph marker's format:
853 createSwDoc("paragraph-marker.docx");
855 // When saving that as ODT + reload:
856 saveAndReload(u
"writer8"_ustr
);
858 // Then make sure that it still has the correct color:
859 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
860 // Without the accompanying fix in place, this test would have failed with:
861 // - Expected: 00ff0000 (COL_LIGHTRED)
862 // - Actual : ffffffff (COL_AUTO)
863 // i.e. the custom "red" color was lost as RES_PARATR_LIST_AUTOFMT was not serialized to ODT.
864 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout/SwFieldPortion/SwFont", "color",
868 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testParagraphMarkerFormattedRun
)
870 // Given a document with a bold run and non-bold paragraph marker:
871 createSwDoc("paragraph-marker-formatted-run.docx");
873 // When saving that as ODT + reload:
874 saveAndReload(u
"writer8"_ustr
);
876 // Then make sure that the numbering portion is still non-bold, matching Word:
877 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
878 // Without the accompanying fix in place, this test would have failed with:
879 // - Expected: normal
881 // i.e. the numbering portion was bold, while its weight should be normal.
882 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout/SwFieldPortion/SwFont", "weight", u
"normal");
885 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testFlySplit
)
887 // Given a document with a fly frame:
889 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
890 SwFlyFrameAttrMgr
aMgr(true, pWrtShell
, Frmmgr_Type::TEXT
, nullptr);
891 RndStdIds eAnchor
= RndStdIds::FLY_AT_PARA
;
892 aMgr
.InsertFlyFrame(eAnchor
, aMgr
.GetPos(), aMgr
.GetSize());
893 uno::Reference
<text::XTextFramesSupplier
> xDocument(mxComponent
, uno::UNO_QUERY
);
894 uno::Reference
<beans::XPropertySet
> xFrame(
895 xDocument
->getTextFrames()->getByName(u
"Frame1"_ustr
), uno::UNO_QUERY
);
896 bool bIsSplitAllowed
{};
897 // Without the accompanying fix in place, this test would have failed with:
898 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
899 // - Unknown property: IsSplitAllowed
900 // i.e. the property was missing.
901 xFrame
->getPropertyValue(u
"IsSplitAllowed"_ustr
) >>= bIsSplitAllowed
;
902 CPPUNIT_ASSERT(!bIsSplitAllowed
);
904 // When marking it as IsSplitAllowed=true:
905 xFrame
->setPropertyValue(u
"IsSplitAllowed"_ustr
, uno::Any(true));
907 // Then make sure that IsSplitAllowed is true when asking back:
908 xFrame
->getPropertyValue(u
"IsSplitAllowed"_ustr
) >>= bIsSplitAllowed
;
909 CPPUNIT_ASSERT(bIsSplitAllowed
);
912 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testConvertToTextFrame
)
914 // Given a document with 2 non-interesting frames, an inner frame and an outer frame:
915 createSwDoc("floattable-outer-nonsplit-inner.docx");
917 // When checking the anchor of the inner frame:
918 SwDoc
* pDoc
= getSwDoc();
919 const sw::FrameFormats
<sw::SpzFrameFormat
*>& rFrames
= *pDoc
->GetSpzFrameFormats();
920 sw::SpzFrameFormat
* pFrame3
= rFrames
.FindFrameFormatByName(u
"Frame3"_ustr
);
921 SwNodeIndex aFrame3Anchor
= pFrame3
->GetAnchor().GetContentAnchor()->nNode
;
923 // Then make sure it's anchored in the outer frame's last content node:
924 sw::SpzFrameFormat
* pFrame4
= rFrames
.FindFrameFormatByName(u
"Frame4"_ustr
);
925 SwPaM
aPaM(*pFrame4
->GetContent().GetContentIdx()->GetNode().EndOfSectionNode());
926 aPaM
.Move(fnMoveBackward
, GoInContent
);
927 // Without the accompanying fix in place, this test would have failed with:
928 // - Expected: SwNodeIndex (node 27)
929 // - Actual : SwNodeIndex (node 49)
930 // i.e. Frame3 was anchored much later, in the body text, not in Frame4.
931 CPPUNIT_ASSERT_EQUAL(aPaM
.GetPoint()->nNode
, aFrame3Anchor
);
936 /// This selection listener calls XTextRange::getString() on a selection change, which triggered
937 /// a new selection change event by accident, resulting infinite recursion and crash
938 struct SelectionChangeListener
: public cppu::WeakImplHelper
<view::XSelectionChangeListener
>
941 SelectionChangeListener();
942 // view::XSelectionChangeListener
943 void SAL_CALL
selectionChanged(const lang::EventObject
& rEvent
) override
;
945 // lang::XEventListener
946 void SAL_CALL
disposing(const lang::EventObject
& rSource
) override
;
950 SelectionChangeListener::SelectionChangeListener() {}
952 void SelectionChangeListener::selectionChanged(const lang::EventObject
& rEvent
)
954 uno::Reference
<view::XSelectionSupplier
> xSelectionSupplier(rEvent
.Source
, uno::UNO_QUERY
);
955 css::uno::Reference
<css::container::XIndexAccess
> xSelection(xSelectionSupplier
->getSelection(),
956 css::uno::UNO_QUERY_THROW
);
957 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSelection
->getCount());
958 css::uno::Reference
<css::text::XTextRange
> xTextRange(xSelection
->getByIndex(0),
959 css::uno::UNO_QUERY_THROW
);
960 CPPUNIT_ASSERT(xTextRange
->getString().startsWith("test"));
963 void SelectionChangeListener::disposing(const lang::EventObject
& /*rSource*/) {}
965 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testTdf155951
)
968 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
969 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
970 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
971 xText
->insertString(xCursor
, u
"test"_ustr
, /*bAbsorb=*/false);
973 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
974 uno::Reference
<view::XSelectionSupplier
> xController(xModel
->getCurrentController(),
976 xController
->addSelectionChangeListener(new SelectionChangeListener());
978 // This crashed here because of infinite recursion
979 dispatchCommand(mxComponent
, u
".uno:WordLeftSel"_ustr
, {});
981 // this needs to wait for dispatching (trigger also a second selection change)
982 xText
->insertString(xCursor
, u
"test"_ustr
, /*bAbsorb=*/false);
985 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testCollectFrameAtNodeWithLayout
)
987 // Given a document with a floating table on 2 pages, with a calculated layout:
988 createSwDoc("floattable-split.docx");
991 // When saving to ODT:
992 save(u
"writer8"_ustr
);
994 // Then make sure the output is valid and hasa 1 <draw:frame>:
995 // Without the accompanying fix in place, this test would have failed with:
996 // Error: uncompleted content model.
997 // i.e. the output was not valid, the second <draw:frame> has an empty <table:table> as a child
999 xmlDocUniquePtr pXmlDoc
= parseExport(u
"content.xml"_ustr
);
1000 // Also make sure that we don't have multiple <draw:frame> elements in the first place.
1001 assertXPath(pXmlDoc
, "//draw:frame", 1);
1004 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testTdf149555
)
1006 createSwDoc("tdf149555.docx");
1008 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
1009 uno::Reference
<text::XTextViewCursorSupplier
> xTextViewCursorSupplier(
1010 xModel
->getCurrentController(), uno::UNO_QUERY
);
1011 uno::Reference
<text::XPageCursor
> xCursor(xTextViewCursorSupplier
->getViewCursor(),
1014 xCursor
->jumpToFirstPage();
1015 OUString sPageStyleName
= getProperty
<OUString
>(xCursor
, u
"PageStyleName"_ustr
);
1016 uno::Reference
<text::XText
> xHeaderText
= getProperty
<uno::Reference
<text::XText
>>(
1017 getStyles(u
"PageStyles"_ustr
)->getByName(sPageStyleName
), u
"HeaderText"_ustr
);
1018 CPPUNIT_ASSERT_EQUAL(u
"HEADER 1"_ustr
, xHeaderText
->getString());
1020 // Without the fix in place, this test would have failed with
1021 // - Expected: HEADER 2
1022 // - Actual: HEADER 1
1023 xCursor
->jumpToPage(2);
1024 sPageStyleName
= getProperty
<OUString
>(xCursor
, u
"PageStyleName"_ustr
);
1025 xHeaderText
= getProperty
<uno::Reference
<text::XText
>>(
1026 getStyles(u
"PageStyles"_ustr
)->getByName(sPageStyleName
), u
"HeaderText"_ustr
);
1027 CPPUNIT_ASSERT_EQUAL(u
"HEADER 2"_ustr
, xHeaderText
->getString());
1029 xCursor
->jumpToPage(3);
1030 sPageStyleName
= getProperty
<OUString
>(xCursor
, u
"PageStyleName"_ustr
);
1031 xHeaderText
= getProperty
<uno::Reference
<text::XText
>>(
1032 getStyles(u
"PageStyles"_ustr
)->getByName(sPageStyleName
), u
"HeaderText"_ustr
);
1033 CPPUNIT_ASSERT_EQUAL(u
"HEADER 2"_ustr
, xHeaderText
->getString());
1035 xCursor
->jumpToPage(4);
1036 sPageStyleName
= getProperty
<OUString
>(xCursor
, u
"PageStyleName"_ustr
);
1037 xHeaderText
= getProperty
<uno::Reference
<text::XText
>>(
1038 getStyles(u
"PageStyles"_ustr
)->getByName(sPageStyleName
), u
"HeaderText"_ustr
);
1039 CPPUNIT_ASSERT_EQUAL(u
"HEADER 2"_ustr
, xHeaderText
->getString());
1042 // just care that it doesn't crash/assert
1043 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testTdf108272Crash
)
1045 createSwDoc("tdf108272-1-minimal.docx");
1048 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testWrapTextAtFlyStart
)
1050 // Given a document with a fly frame:
1052 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
1053 SwFlyFrameAttrMgr
aMgr(true, pWrtShell
, Frmmgr_Type::TEXT
, nullptr);
1054 RndStdIds eAnchor
= RndStdIds::FLY_AT_PARA
;
1055 aMgr
.InsertFlyFrame(eAnchor
, aMgr
.GetPos(), aMgr
.GetSize());
1056 uno::Reference
<text::XTextFramesSupplier
> xDocument(mxComponent
, uno::UNO_QUERY
);
1057 uno::Reference
<beans::XPropertySet
> xFrame(
1058 xDocument
->getTextFrames()->getByName(u
"Frame1"_ustr
), uno::UNO_QUERY
);
1059 bool bWrapTextAtFlyStart
{};
1060 // Without the accompanying fix in place, this test would have failed with:
1061 // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
1062 // - Unknown property: WrapTextAtFlyStart
1063 // i.e. the property was missing.
1064 xFrame
->getPropertyValue(u
"WrapTextAtFlyStart"_ustr
) >>= bWrapTextAtFlyStart
;
1065 CPPUNIT_ASSERT(!bWrapTextAtFlyStart
);
1067 // When marking it as WrapTextAtFlyStart=true:
1068 xFrame
->setPropertyValue(u
"WrapTextAtFlyStart"_ustr
, uno::Any(true));
1070 // Then make sure that WrapTextAtFlyStart is true when asking back:
1071 xFrame
->getPropertyValue(u
"WrapTextAtFlyStart"_ustr
) >>= bWrapTextAtFlyStart
;
1072 CPPUNIT_ASSERT(bWrapTextAtFlyStart
);
1075 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest
, testEmptyHeader
)
1077 // Empty header: IsContentEmpty is true.
1079 uno::Sequence
<beans::PropertyValue
> aInsertArgs
1080 = { comphelper::makePropertyValue(u
"PageStyle"_ustr
, SwResId(STR_POOLPAGE_STANDARD
)) };
1081 dispatchCommand(mxComponent
, u
".uno:InsertPageHeader"_ustr
, aInsertArgs
);
1082 uno::Reference
<beans::XPropertySet
> xPageStyle(getStyles("PageStyles")->getByName("Standard"),
1084 uno::Reference
<beans::XPropertySet
> xHeaderText(xPageStyle
->getPropertyValue("HeaderText"),
1086 bool bIsContentEmpty
= false;
1087 xHeaderText
->getPropertyValue("IsContentEmpty") >>= bIsContentEmpty
;
1088 CPPUNIT_ASSERT(bIsContentEmpty
);
1090 // Header has 1 paragraph with text: IsContentEmpty is false.
1091 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
1092 pWrtShell
->Insert("text");
1093 bIsContentEmpty
= true;
1094 xHeaderText
->getPropertyValue("IsContentEmpty") >>= bIsContentEmpty
;
1095 CPPUNIT_ASSERT(!bIsContentEmpty
);
1097 // Header has 2 paragraphs: IsContentEmpty is false.
1098 pWrtShell
->SelAll();
1099 pWrtShell
->DelRight();
1100 pWrtShell
->SplitNode();
1101 bIsContentEmpty
= true;
1102 xHeaderText
->getPropertyValue("IsContentEmpty") >>= bIsContentEmpty
;
1103 CPPUNIT_ASSERT(!bIsContentEmpty
);
1105 // Header has an anchored object: IsContentEmpty is false.
1106 pWrtShell
->SelAll();
1107 pWrtShell
->DelRight();
1108 SwFlyFrameAttrMgr
aMgr(true, pWrtShell
, Frmmgr_Type::TEXT
, nullptr);
1109 RndStdIds eAnchor
= RndStdIds::FLY_AT_PARA
;
1110 aMgr
.InsertFlyFrame(eAnchor
, aMgr
.GetPos(), aMgr
.GetSize());
1111 bIsContentEmpty
= true;
1112 xHeaderText
->getPropertyValue("IsContentEmpty") >>= bIsContentEmpty
;
1113 CPPUNIT_ASSERT(!bIsContentEmpty
);
1116 CPPUNIT_PLUGIN_IMPLEMENT();
1118 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */