tdf#158198 sw: prevent xBookmark.getAnchor().setString("") from deleting
[LibreOffice.git] / sw / qa / core / unocore / unocore.cxx
blobca373915ab05f2b5e766e49e047523bc1f6f7455
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
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>
26 #include <wrtsh.hxx>
27 #include <unotextrange.hxx>
28 #include <unotxdoc.hxx>
29 #include <docsh.hxx>
30 #include <ndtxt.hxx>
31 #include <textlinebreak.hxx>
32 #include <textcontentcontrol.hxx>
33 #include <frmmgr.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
42 public:
43 SwCoreUnocoreTest()
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();
56 // Enter outer A1.
57 pWrtShell->Down(/*bSelect=*/false, /*nCount=*/3);
58 // Enter inner A1.
59 pWrtShell->Right(SwCursorSkipMode::Cells, /*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false,
60 /*bVisual=*/true);
61 // Enter outer B1.
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,
75 /*bVisual=*/true);
76 CPPUNIT_ASSERT_EQUAL(u"x"_ustr, pWrtShell->GetCurrentShellCursor().GetText());
79 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, selectTextRange)
81 createSwDoc();
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(),
95 uno::UNO_QUERY);
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)
108 createSwDoc();
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)
129 createSwDoc();
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:
144 createSwDoc();
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
169 // in the map.
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:
178 createSwDoc();
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),
183 uno::UNO_QUERY);
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),
192 uno::UNO_QUERY);
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:
202 createSwDoc();
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(),
210 uno::UNO_QUERY);
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(),
235 uno::UNO_QUERY);
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.
249 CPPUNIT_ASSERT(
250 xEmbeddedObject->supportsService(u"com.sun.star.comp.embed.OCommonEmbeddedObject"_ustr));
253 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testLineBreakInsert)
255 // Given an empty document:
256 createSwDoc();
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:
290 createSwDoc();
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
309 // - Actual : Text
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:
321 createSwDoc();
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:
349 createSwDoc();
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:
386 // - Expected: new
387 // - Actual :
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:
395 createSwDoc();
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:
420 createSwDoc();
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
439 // - Actual : Text
440 // i.e. the content control text attribute was ignored.
441 CPPUNIT_ASSERT_EQUAL(u"ContentControl"_ustr, aPortionType);
442 xContentControl
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
458 // portion.
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
469 // - Actual : ^Atest
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:
477 createSwDoc();
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:
515 createSwDoc();
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:
566 createSwDoc();
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:
592 createSwDoc();
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:
629 createSwDoc();
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:
698 createSwDoc();
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);
735 aSet.Put(aItem);
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:
739 // - Expected: 0
740 // - Actual : 2
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:
751 createSwDoc();
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:
804 createSwDoc();
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();
813 // First tag1.
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;
841 OUString aTag;
842 xContentControl->getPropertyValue(u"Tag"_ustr) >>= aTag;
843 // Without the accompanying fix in place, this test would have failed with:
844 // - Expected: tag2
845 // - Actual : tag1
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",
865 u"00ff0000");
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
880 // - Actual : bold
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:
888 createSwDoc();
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);
934 namespace
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>
940 public:
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)
967 createSwDoc();
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(),
975 uno::UNO_QUERY);
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");
989 calcLayout();
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
998 // element.
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(),
1012 uno::UNO_QUERY);
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:
1051 createSwDoc();
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.
1078 createSwDoc();
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"),
1083 uno::UNO_QUERY);
1084 uno::Reference<beans::XPropertySet> xHeaderText(xPageStyle->getPropertyValue("HeaderText"),
1085 uno::UNO_QUERY);
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: */