tdf#149907: sc: Add UItest
[LibreOffice.git] / sw / qa / extras / unowriter / unowriter.cxx
blobfee61f62b819ba16b32a01ac2385f8b1f1dae2f9
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/awt/FontSlant.hpp>
13 #include <com/sun/star/datatransfer/XTransferableSupplier.hpp>
14 #include <com/sun/star/datatransfer/XTransferableTextSupplier.hpp>
15 #include <com/sun/star/frame/XDispatch.hpp>
16 #include <com/sun/star/frame/XDispatchProviderInterception.hpp>
17 #include <com/sun/star/frame/XDispatchProviderInterceptor.hpp>
18 #include <com/sun/star/table/XCellRange.hpp>
19 #include <com/sun/star/text/TextContentAnchorType.hpp>
20 #include <com/sun/star/text/AutoTextContainer.hpp>
21 #include <com/sun/star/text/VertOrientation.hpp>
22 #include <com/sun/star/text/XAutoTextGroup.hpp>
23 #include <com/sun/star/text/XTextPortionAppend.hpp>
24 #include <com/sun/star/text/XTextContentAppend.hpp>
25 #include <com/sun/star/text/XTextRangeCompare.hpp>
26 #include <com/sun/star/text/XPasteListener.hpp>
27 #include <com/sun/star/rdf/URI.hpp>
28 #include <com/sun/star/rdf/URIs.hpp>
29 #include <com/sun/star/awt/XDevice.hpp>
30 #include <com/sun/star/awt/XToolkit.hpp>
31 #include <com/sun/star/graphic/XGraphic.hpp>
32 #include <com/sun/star/style/LineSpacing.hpp>
33 #include <com/sun/star/view/XSelectionSupplier.hpp>
34 #include <com/sun/star/text/XTextDocument.hpp>
35 #include <com/sun/star/container/XNameContainer.hpp>
36 #include <com/sun/star/view/XRenderable.hpp>
37 #include <com/sun/star/text/XBookmarksSupplier.hpp>
38 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
39 #include <com/sun/star/text/XTextTable.hpp>
40 #include <com/sun/star/text/XPageCursor.hpp>
42 #include <comphelper/propertyvalue.hxx>
43 #include <tools/UnitConversion.hxx>
44 #include <toolkit/helper/vclunohelper.hxx>
45 #include <vcl/graphicfilter.hxx>
46 #include <comphelper/sequenceashashmap.hxx>
47 #include <comphelper/processfactory.hxx>
48 #include <comphelper/propertysequence.hxx>
49 #include <comphelper/compbase.hxx>
51 #include <wrtsh.hxx>
52 #include <ndtxt.hxx>
53 #include <swdtflvr.hxx>
54 #include <view.hxx>
55 #include <PostItMgr.hxx>
56 #include <postithelper.hxx>
57 #include <AnnotationWin.hxx>
58 #include <flyfrm.hxx>
59 #include <fmtanchr.hxx>
60 #include <unotxdoc.hxx>
61 #include <docsh.hxx>
63 using namespace ::com::sun::star;
65 namespace
67 /// Listener implementation for testPasteListener.
68 class PasteListener : public cppu::WeakImplHelper<text::XPasteListener>
70 OUString m_aString;
71 uno::Reference<text::XTextContent> m_xTextGraphicObject;
73 public:
74 void SAL_CALL notifyPasteEvent(const uno::Sequence<beans::PropertyValue>& rEvent) override;
76 OUString& GetString();
77 uno::Reference<text::XTextContent>& GetTextGraphicObject();
80 void PasteListener::notifyPasteEvent(const uno::Sequence<beans::PropertyValue>& rEvent)
82 comphelper::SequenceAsHashMap aMap(rEvent);
83 auto it = aMap.find(u"TextRange"_ustr);
84 if (it != aMap.end())
86 auto xTextRange = it->second.get<uno::Reference<text::XTextRange>>();
87 if (xTextRange.is())
88 m_aString = xTextRange->getString();
89 return;
92 it = aMap.find(u"TextGraphicObject"_ustr);
93 if (it != aMap.end())
95 auto xTextGraphicObject = it->second.get<uno::Reference<text::XTextContent>>();
96 if (xTextGraphicObject.is())
97 m_xTextGraphicObject = xTextGraphicObject;
101 OUString& PasteListener::GetString() { return m_aString; }
103 uno::Reference<text::XTextContent>& PasteListener::GetTextGraphicObject()
105 return m_xTextGraphicObject;
108 /// Test to assert UNO API call results of Writer.
109 class SwUnoWriter : public SwModelTestBase
111 public:
112 SwUnoWriter()
113 : SwModelTestBase(u"/sw/qa/extras/unowriter/data/"_ustr, u"writer8"_ustr)
118 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testDefaultCharStyle)
120 // Create a new document, type a character, set its char style to Emphasis
121 // and assert the style was set.
122 createSwDoc();
124 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
125 uno::Reference<text::XSimpleText> xBodyText = xTextDocument->getText();
126 xBodyText->insertString(xBodyText->getStart(), u"x"_ustr, false);
128 uno::Reference<text::XTextCursor> xCursor(xBodyText->createTextCursor());
129 xCursor->goLeft(1, true);
131 uno::Reference<beans::XPropertySet> xCursorProps(xCursor, uno::UNO_QUERY);
132 xCursorProps->setPropertyValue(u"CharStyleName"_ustr, uno::Any(u"Emphasis"_ustr));
133 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_ITALIC,
134 getProperty<awt::FontSlant>(xCursorProps, u"CharPosture"_ustr));
136 // Now reset the char style and assert that the font slant is back to none.
137 // This resulted in a lang.IllegalArgumentException, Standard was not
138 // mapped to 'Default Style'.
139 xCursorProps->setPropertyValue(u"CharStyleName"_ustr, uno::Any(u"Standard"_ustr));
140 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE,
141 getProperty<awt::FontSlant>(xCursorProps, u"CharPosture"_ustr));
144 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testInsertStringExpandsHints)
146 createSwDoc();
147 uno::Reference<text::XTextDocument> const xTextDocument(mxComponent, uno::UNO_QUERY);
148 uno::Reference<text::XText> const xText(xTextDocument->getText());
149 uno::Reference<text::XTextCursor> const xCursor(xText->createTextCursor());
150 uno::Reference<beans::XPropertySet> const xProps(xCursor, uno::UNO_QUERY);
152 xText->insertString(xCursor, u"ab"_ustr, false);
153 xCursor->gotoStart(false);
154 xCursor->goRight(1, true);
155 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE,
156 getProperty<awt::FontSlant>(xProps, u"CharPosture"_ustr));
157 xProps->setPropertyValue(u"CharPosture"_ustr, uno::Any(awt::FontSlant_ITALIC));
158 xCursor->collapseToEnd();
159 xText->insertString(xCursor, u"x"_ustr, false);
160 xCursor->goLeft(1, true);
161 CPPUNIT_ASSERT_EQUAL(u"x"_ustr, xCursor->getString());
162 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_ITALIC,
163 getProperty<awt::FontSlant>(xProps, u"CharPosture"_ustr));
166 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testInsertTextPortionNotExpandsHints)
168 createSwDoc();
169 uno::Reference<text::XTextDocument> const xTextDocument(mxComponent, uno::UNO_QUERY);
170 uno::Reference<text::XText> const xText(xTextDocument->getText());
171 uno::Reference<text::XTextPortionAppend> const xTextA(xText, uno::UNO_QUERY);
172 uno::Reference<text::XTextCursor> const xCursor(xText->createTextCursor());
173 uno::Reference<beans::XPropertySet> const xProps(xCursor, uno::UNO_QUERY);
175 xText->insertString(xCursor, u"ab"_ustr, false);
176 xCursor->gotoStart(false);
177 xCursor->goRight(1, true);
178 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE,
179 getProperty<awt::FontSlant>(xProps, u"CharPosture"_ustr));
180 xProps->setPropertyValue(u"CharPosture"_ustr, uno::Any(awt::FontSlant_ITALIC));
181 xCursor->collapseToEnd();
182 xTextA->insertTextPortion(u"x"_ustr, uno::Sequence<beans::PropertyValue>(), xCursor);
183 xCursor->goLeft(1, true);
184 CPPUNIT_ASSERT_EQUAL(u"x"_ustr, xCursor->getString());
185 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE,
186 getProperty<awt::FontSlant>(xProps, u"CharPosture"_ustr));
189 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testInsertTextContentExpandsHints)
191 createSwDoc();
192 uno::Reference<text::XTextDocument> const xTextDocument(mxComponent, uno::UNO_QUERY);
193 uno::Reference<lang::XMultiServiceFactory> const xFactory(mxComponent, uno::UNO_QUERY);
194 uno::Reference<text::XText> const xText(xTextDocument->getText());
195 uno::Reference<text::XTextCursor> const xCursor(xText->createTextCursor());
196 uno::Reference<beans::XPropertySet> const xProps(xCursor, uno::UNO_QUERY);
198 xText->insertString(xCursor, u"ab"_ustr, false);
199 xCursor->gotoStart(false);
200 xCursor->goRight(1, true);
201 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE,
202 getProperty<awt::FontSlant>(xProps, u"CharPosture"_ustr));
203 xProps->setPropertyValue(u"CharPosture"_ustr, uno::Any(awt::FontSlant_ITALIC));
204 xCursor->collapseToEnd();
205 uno::Reference<text::XTextContent> const xContent(
206 xFactory->createInstance(u"com.sun.star.text.Footnote"_ustr), uno::UNO_QUERY);
207 xText->insertTextContent(xCursor, xContent, false);
208 xCursor->goLeft(1, true);
209 CPPUNIT_ASSERT_EQUAL(u"1"_ustr, xCursor->getString());
210 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_ITALIC,
211 getProperty<awt::FontSlant>(xProps, u"CharPosture"_ustr));
214 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testInsertTextContentWithPropertiesNotExpandsHints)
216 createSwDoc();
217 uno::Reference<text::XTextDocument> const xTextDocument(mxComponent, uno::UNO_QUERY);
218 uno::Reference<lang::XMultiServiceFactory> const xFactory(mxComponent, uno::UNO_QUERY);
219 uno::Reference<text::XText> const xText(xTextDocument->getText());
220 uno::Reference<text::XTextContentAppend> const xTextA(xText, uno::UNO_QUERY);
221 uno::Reference<text::XTextCursor> const xCursor(xText->createTextCursor());
222 uno::Reference<beans::XPropertySet> const xProps(xCursor, uno::UNO_QUERY);
224 xText->insertString(xCursor, u"ab"_ustr, false);
225 xCursor->gotoStart(false);
226 xCursor->goRight(1, true);
227 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE,
228 getProperty<awt::FontSlant>(xProps, u"CharPosture"_ustr));
229 xProps->setPropertyValue(u"CharPosture"_ustr, uno::Any(awt::FontSlant_ITALIC));
230 xCursor->collapseToEnd();
231 uno::Reference<text::XTextContent> const xContent(
232 xFactory->createInstance(u"com.sun.star.text.Footnote"_ustr), uno::UNO_QUERY);
233 xTextA->insertTextContentWithProperties(xContent, uno::Sequence<beans::PropertyValue>(),
234 xCursor);
235 xCursor->goLeft(1, true);
236 CPPUNIT_ASSERT_EQUAL(u"1"_ustr, xCursor->getString());
237 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE,
238 getProperty<awt::FontSlant>(xProps, u"CharPosture"_ustr));
241 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testGraphicDescriptorURL)
243 createSwDoc();
245 // Create a graphic object, but don't insert it yet.
246 uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
247 uno::Reference<beans::XPropertySet> xTextGraphic(
248 xFactory->createInstance(u"com.sun.star.text.TextGraphicObject"_ustr), uno::UNO_QUERY);
250 // Set a URL on it.
251 xTextGraphic->setPropertyValue(u"GraphicURL"_ustr, uno::Any(createFileURL(u"test.jpg")));
252 xTextGraphic->setPropertyValue(u"AnchorType"_ustr,
253 uno::Any(text::TextContentAnchorType_AT_CHARACTER));
255 // Insert it.
256 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
257 uno::Reference<text::XText> xBodyText = xTextDocument->getText();
258 uno::Reference<text::XTextCursor> xCursor(xBodyText->createTextCursor());
259 uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY);
260 xBodyText->insertTextContent(xCursor, xTextContent, false);
262 // This failed, the graphic object had no graphic.
263 auto xGraphic = getProperty<uno::Reference<graphic::XGraphic>>(getShape(1), u"Graphic"_ustr);
264 CPPUNIT_ASSERT(xGraphic.is());
267 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testGraphicDescriptorURLBitmap)
269 createSwDoc();
271 // Load a bitmap into the bitmap table.
272 uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
273 uno::Reference<container::XNameContainer> xBitmaps(
274 xFactory->createInstance(u"com.sun.star.drawing.BitmapTable"_ustr), uno::UNO_QUERY);
275 xBitmaps->insertByName(u"test"_ustr, uno::Any(createFileURL(u"test.jpg")));
277 // Create a graphic.
278 uno::Reference<beans::XPropertySet> xTextGraphic(
279 xFactory->createInstance(u"com.sun.star.text.TextGraphicObject"_ustr), uno::UNO_QUERY);
280 xTextGraphic->setPropertyValue(u"GraphicURL"_ustr, xBitmaps->getByName(u"test"_ustr));
281 xTextGraphic->setPropertyValue(u"AnchorType"_ustr,
282 uno::Any(text::TextContentAnchorType_AT_CHARACTER));
284 // Insert it.
285 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
286 uno::Reference<text::XText> xBodyText = xTextDocument->getText();
287 uno::Reference<text::XTextCursor> xCursor(xBodyText->createTextCursor());
288 uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY);
289 xBodyText->insertTextContent(xCursor, xTextContent, false);
291 // This failed: setting GraphicURL to the result of getByName() did not
292 // work anymore.
293 auto xGraphic = getProperty<uno::Reference<graphic::XGraphic>>(getShape(1), u"Graphic"_ustr);
294 CPPUNIT_ASSERT(xGraphic.is());
297 bool ensureAutoTextExistsByTitle(const uno::Reference<text::XAutoTextGroup>& autoTextGroup,
298 std::u16string_view autoTextName)
300 const uno::Sequence<OUString> aTitles(autoTextGroup->getTitles());
301 for (const auto& rTitle : aTitles)
303 if (rTitle == autoTextName)
304 return true;
306 return false;
309 bool ensureAutoTextExistsByName(const uno::Reference<text::XAutoTextGroup>& autoTextGroup,
310 std::u16string_view autoTextName)
312 const uno::Sequence<OUString> aTitles(autoTextGroup->getElementNames());
313 for (const auto& rTitle : aTitles)
315 if (rTitle == autoTextName)
316 return true;
318 return false;
321 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testXAutoTextGroup)
323 createSwDoc("xautotextgroup.odt");
324 uno::Reference<text::XAutoTextContainer> xAutoTextContainer
325 = text::AutoTextContainer::create(comphelper::getProcessComponentContext());
327 uno::Reference<text::XTextRange> xTextRange = getRun(getParagraph(1), 1);
329 static constexpr OUString sGroupName = u"TestGroup*1"_ustr;
330 static constexpr OUString sTextName = u"TEST"_ustr;
331 static constexpr OUString sTextNameNew = u"TESTRENAMED"_ustr;
332 static constexpr OUString sTextTitle = u"Test Auto Text"_ustr;
333 static constexpr OUString sTextTitleNew = u"Test Auto Text Renamed"_ustr;
335 // Create new temporary group
336 uno::Reference<text::XAutoTextGroup> xAutoTextGroup
337 = xAutoTextContainer->insertNewByName(sGroupName);
338 CPPUNIT_ASSERT_MESSAGE("AutoTextGroup was not found!", xAutoTextGroup.is());
340 // Insert new element and ensure it exists
341 uno::Reference<text::XAutoTextEntry> xAutoTextEntry
342 = xAutoTextGroup->insertNewByName(sTextName, sTextTitle, xTextRange);
343 CPPUNIT_ASSERT_MESSAGE("AutoText was not inserted!", xAutoTextEntry.is());
344 CPPUNIT_ASSERT_MESSAGE("Can't find newly created AutoText by title!",
345 ensureAutoTextExistsByTitle(xAutoTextGroup, sTextTitle));
346 CPPUNIT_ASSERT_MESSAGE("Can't find newly created AutoText by name!",
347 ensureAutoTextExistsByName(xAutoTextGroup, sTextName));
349 // Insert once again the same should throw an exception
350 CPPUNIT_ASSERT_THROW_MESSAGE("We expect an exception on insertion of same AutoText",
351 xAutoTextGroup->insertNewByName(sTextName, sTextTitle, xTextRange),
352 container::ElementExistException);
354 // Rename it & ensure everything is ok
355 xAutoTextGroup->renameByName(sTextName, sTextNameNew, sTextTitleNew);
356 CPPUNIT_ASSERT_MESSAGE("Can't find renamed AutoText by title!",
357 ensureAutoTextExistsByTitle(xAutoTextGroup, sTextTitleNew));
358 CPPUNIT_ASSERT_MESSAGE("Can't find renamed AutoText by name!",
359 ensureAutoTextExistsByName(xAutoTextGroup, sTextNameNew));
360 // Not found by old names
361 CPPUNIT_ASSERT_MESSAGE("Found AutoText by old title!",
362 !ensureAutoTextExistsByTitle(xAutoTextGroup, sTextTitle));
363 CPPUNIT_ASSERT_MESSAGE("Found AutoText by old name!",
364 !ensureAutoTextExistsByName(xAutoTextGroup, sTextName));
366 // Rename not existing should throw an exception
367 CPPUNIT_ASSERT_THROW_MESSAGE(
368 "We expect an exception on renaming not-existing AutoText",
369 xAutoTextGroup->renameByName(sTextName, sTextNameNew, sTextTitleNew),
370 container::ElementExistException);
372 // Remove it and ensure it does not exist
373 xAutoTextGroup->removeByName(sTextNameNew);
374 CPPUNIT_ASSERT_MESSAGE("AutoText was not removed!",
375 !ensureAutoTextExistsByTitle(xAutoTextGroup, sTextTitleNew));
376 CPPUNIT_ASSERT_MESSAGE("AutoText was not removed!",
377 !ensureAutoTextExistsByName(xAutoTextGroup, sTextNameNew));
379 // Remove non-existing element should throw an exception
380 CPPUNIT_ASSERT_THROW_MESSAGE("We expect an exception on removing not-existing AutoText",
381 xAutoTextGroup->removeByName(sTextName),
382 container::NoSuchElementException);
384 // Remove our temporary group
385 xAutoTextContainer->removeByName(sGroupName);
388 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSectionAnchorCopyTableAtStart)
390 // this contains a section that starts with a table
391 createSwDoc("tdf134250.fodt");
393 uno::Reference<text::XTextTablesSupplier> const xTextTablesSupplier(mxComponent,
394 uno::UNO_QUERY);
395 uno::Reference<container::XIndexAccess> const xTables(xTextTablesSupplier->getTextTables(),
396 uno::UNO_QUERY);
397 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
399 uno::Reference<text::XTextSectionsSupplier> const xTextSectionsSupplier(mxComponent,
400 uno::UNO_QUERY);
401 uno::Reference<container::XIndexAccess> const xSections(
402 xTextSectionsSupplier->getTextSections(), uno::UNO_QUERY);
404 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
406 uno::Reference<text::XTextContent> const xSection(xSections->getByIndex(0), uno::UNO_QUERY);
407 uno::Reference<text::XTextRange> const xAnchor(xSection->getAnchor());
408 CPPUNIT_ASSERT_EQUAL(u"foo" SAL_NEWLINE_STRING "bar"_ustr, xAnchor->getString());
410 // copy the content of the section to a clipboard document
411 uno::Reference<datatransfer::XTransferableSupplier> const xTS(
412 uno::Reference<frame::XModel>(mxComponent, uno::UNO_QUERY_THROW)->getCurrentController(),
413 uno::UNO_QUERY);
414 uno::Reference<datatransfer::XTransferableTextSupplier> const xTTS(xTS, uno::UNO_QUERY);
415 uno::Reference<datatransfer::XTransferable> const xTransferable(
416 xTTS->getTransferableForTextRange(xAnchor));
418 // check this doesn't throw
419 CPPUNIT_ASSERT(xAnchor->getText().is());
420 CPPUNIT_ASSERT(xAnchor->getStart().is());
421 CPPUNIT_ASSERT(xAnchor->getEnd().is());
423 // replace section content
424 xAnchor->setString(u"quux"_ustr);
426 // table in section was deleted, but not section itself
427 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xTables->getCount());
428 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
429 CPPUNIT_ASSERT_EQUAL(u"\""
430 "quux" /*SAL_NEWLINE_STRING*/ "\""_ustr,
431 OUString("\"" + xAnchor->getString() + "\""));
433 // now paste it
434 uno::Reference<text::XTextViewCursorSupplier> const xTVCS(xTS, uno::UNO_QUERY);
435 uno::Reference<text::XTextViewCursor> const xCursor(xTVCS->getViewCursor());
436 xCursor->gotoEnd(false);
437 xTS->insertTransferable(xTransferable);
439 // table in section was pasted, but not section itself
440 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
441 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
442 xCursor->gotoStart(true);
443 CPPUNIT_ASSERT_EQUAL(u"quux" SAL_NEWLINE_STRING "foo" SAL_NEWLINE_STRING "bar"_ustr,
444 xCursor->getString());
447 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSectionAnchorCopyTableAtEnd)
449 // this contains a section that ends with a table (plus another section)
450 createSwDoc("tdf134252.fodt");
452 uno::Reference<text::XTextTablesSupplier> const xTextTablesSupplier(mxComponent,
453 uno::UNO_QUERY);
454 uno::Reference<container::XIndexAccess> const xTables(xTextTablesSupplier->getTextTables(),
455 uno::UNO_QUERY);
456 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
458 uno::Reference<text::XTextSectionsSupplier> const xTextSectionsSupplier(mxComponent,
459 uno::UNO_QUERY);
460 uno::Reference<container::XIndexAccess> const xSections(
461 xTextSectionsSupplier->getTextSections(), uno::UNO_QUERY);
463 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount());
465 uno::Reference<text::XTextContent> const xSection(xSections->getByIndex(0), uno::UNO_QUERY);
466 uno::Reference<text::XTextRange> const xAnchor(xSection->getAnchor());
467 CPPUNIT_ASSERT_EQUAL(u"bar" SAL_NEWLINE_STRING "baz" SAL_NEWLINE_STRING ""_ustr,
468 xAnchor->getString());
470 // copy the content of the section to a clipboard document
471 uno::Reference<datatransfer::XTransferableSupplier> const xTS(
472 uno::Reference<frame::XModel>(mxComponent, uno::UNO_QUERY_THROW)->getCurrentController(),
473 uno::UNO_QUERY);
474 uno::Reference<datatransfer::XTransferableTextSupplier> const xTTS(xTS, uno::UNO_QUERY);
475 uno::Reference<datatransfer::XTransferable> const xTransferable(
476 xTTS->getTransferableForTextRange(xAnchor));
478 // check this doesn't throw
479 CPPUNIT_ASSERT(xAnchor->getText().is());
480 CPPUNIT_ASSERT(xAnchor->getStart().is());
481 CPPUNIT_ASSERT(xAnchor->getEnd().is());
483 // replace section content
484 xAnchor->setString(u"quux"_ustr);
486 // table in section was deleted, but not section itself
487 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xTables->getCount());
488 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount());
489 CPPUNIT_ASSERT_EQUAL(u"\""
490 "quux" /*SAL_NEWLINE_STRING*/ "\""_ustr,
491 OUString("\"" + xAnchor->getString() + "\""));
493 // now paste it
494 uno::Reference<text::XTextViewCursorSupplier> const xTVCS(xTS, uno::UNO_QUERY);
495 uno::Reference<text::XTextViewCursor> const xCursor(xTVCS->getViewCursor());
496 xCursor->gotoEnd(false);
497 xTS->insertTransferable(xTransferable);
499 // table in section was pasted, but not section itself
500 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
501 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount());
502 // note: this selects the 2nd section because it calls StartOfSection()
503 // not SttEndDoc() like it should?
504 xCursor->gotoStart(true);
505 CPPUNIT_ASSERT_EQUAL(u"foobar" SAL_NEWLINE_STRING "baz" SAL_NEWLINE_STRING ""_ustr,
506 xCursor->getString());
509 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSectionAnchorCopyTable)
511 // this contains a section that ends with a table (plus another section)
512 createSwDoc("tdf134252_onlytable_protected.fodt");
514 uno::Reference<text::XTextTablesSupplier> const xTextTablesSupplier(mxComponent,
515 uno::UNO_QUERY);
516 uno::Reference<container::XIndexAccess> const xTables(xTextTablesSupplier->getTextTables(),
517 uno::UNO_QUERY);
518 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
520 uno::Reference<text::XTextSectionsSupplier> const xTextSectionsSupplier(mxComponent,
521 uno::UNO_QUERY);
522 uno::Reference<container::XIndexAccess> const xSections(
523 xTextSectionsSupplier->getTextSections(), uno::UNO_QUERY);
525 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
527 uno::Reference<text::XTextContent> const xSection(xSections->getByIndex(0), uno::UNO_QUERY);
528 uno::Reference<text::XTextRange> const xAnchor(xSection->getAnchor());
529 CPPUNIT_ASSERT_EQUAL(u"baz" SAL_NEWLINE_STRING ""_ustr, xAnchor->getString());
531 // copy the content of the section to a clipboard document
532 uno::Reference<datatransfer::XTransferableSupplier> const xTS(
533 uno::Reference<frame::XModel>(mxComponent, uno::UNO_QUERY_THROW)->getCurrentController(),
534 uno::UNO_QUERY);
535 uno::Reference<datatransfer::XTransferableTextSupplier> const xTTS(xTS, uno::UNO_QUERY);
536 uno::Reference<datatransfer::XTransferable> const xTransferable(
537 xTTS->getTransferableForTextRange(xAnchor));
539 // check this doesn't throw
540 CPPUNIT_ASSERT(xAnchor->getText().is());
541 CPPUNIT_ASSERT(xAnchor->getStart().is());
542 CPPUNIT_ASSERT(xAnchor->getEnd().is());
544 // replace section content
545 xAnchor->setString(u"quux"_ustr);
547 // table in section was deleted, but not section itself
548 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xTables->getCount());
549 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
550 CPPUNIT_ASSERT_EQUAL(u"\""
551 "quux" /*SAL_NEWLINE_STRING*/ "\""_ustr,
552 OUString("\"" + xAnchor->getString() + "\""));
554 // now paste it
555 uno::Reference<text::XTextViewCursorSupplier> const xTVCS(xTS, uno::UNO_QUERY);
556 uno::Reference<text::XTextViewCursor> const xCursor(xTVCS->getViewCursor());
557 xCursor->gotoEnd(false);
558 xTS->insertTransferable(xTransferable);
560 // table in section was pasted, but not section itself
561 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
562 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
563 xCursor->gotoStart(true);
564 CPPUNIT_ASSERT_EQUAL(u"quux" SAL_NEWLINE_STRING "foo" SAL_NEWLINE_STRING
565 "baz" SAL_NEWLINE_STRING ""_ustr,
566 xCursor->getString());
569 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSectionAnchorProperties)
571 createSwDoc("section-table.fodt");
573 uno::Reference<text::XTextSectionsSupplier> const xTextSectionsSupplier(mxComponent,
574 uno::UNO_QUERY);
575 uno::Reference<container::XIndexAccess> const xSections(
576 xTextSectionsSupplier->getTextSections(), uno::UNO_QUERY);
577 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
579 uno::Reference<text::XTextContent> const xSection(xSections->getByIndex(0), uno::UNO_QUERY);
580 uno::Reference<text::XTextRange> const xAnchor(xSection->getAnchor());
581 uno::Reference<beans::XPropertySet> const xAnchorProp(xAnchor, uno::UNO_QUERY);
583 // the problem was that the property set didn't work
584 auto xSecFromProp = getProperty<uno::Reference<text::XTextContent>>(xAnchorProp, "TextSection");
585 CPPUNIT_ASSERT_EQUAL(xSection, xSecFromProp);
587 xAnchorProp->setPropertyValue("CharHeight", uno::Any(float(64)));
588 CPPUNIT_ASSERT_EQUAL(float(64), getProperty<float>(xAnchorProp, "CharHeight"));
589 uno::Reference<beans::XPropertyState> const xAnchorState(xAnchor, uno::UNO_QUERY);
590 // TODO: why does this return DEFAULT_VALUE instead of DIRECT_VALUE?
591 CPPUNIT_ASSERT_EQUAL(beans::PropertyState_DEFAULT_VALUE,
592 xAnchorState->getPropertyState("CharHeight"));
593 CPPUNIT_ASSERT_EQUAL(beans::PropertyState_DEFAULT_VALUE,
594 xAnchorState->getPropertyStates({ "CharHeight" })[0]);
595 CPPUNIT_ASSERT_EQUAL(float(12), xAnchorState->getPropertyDefault("CharHeight").get<float>());
596 xAnchorState->setPropertyToDefault("CharHeight");
597 CPPUNIT_ASSERT_EQUAL(float(12), getProperty<float>(xAnchorProp, "CharHeight"));
600 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTextRangeInTable)
602 createSwDoc("bookmarkintable.fodt");
604 uno::Reference<text::XBookmarksSupplier> const xBS(mxComponent, uno::UNO_QUERY);
605 uno::Reference<container::XNameAccess> const xMarks(xBS->getBookmarks());
606 uno::Reference<text::XTextContent> const xMark(xMarks->getByName(u"Bookmark 1"_ustr),
607 uno::UNO_QUERY);
608 uno::Reference<container::XEnumerationAccess> const xAnchor(xMark->getAnchor(), uno::UNO_QUERY);
609 uno::Reference<container::XEnumeration> const xEnum(xAnchor->createEnumeration());
610 uno::Reference<lang::XServiceInfo> const xPara(xEnum->nextElement(), uno::UNO_QUERY);
611 // not the top-level table!
612 CPPUNIT_ASSERT(!xPara->supportsService(u"com.sun.star.text.TextTable"_ustr));
613 CPPUNIT_ASSERT(!xEnum->hasMoreElements());
614 uno::Reference<container::XEnumerationAccess> const xParaEA(xPara, uno::UNO_QUERY);
615 uno::Reference<container::XEnumeration> const xPortions(xParaEA->createEnumeration());
616 uno::Reference<beans::XPropertySet> const xP1(xPortions->nextElement(), uno::UNO_QUERY);
617 CPPUNIT_ASSERT_EQUAL(u"Bookmark"_ustr, getProperty<OUString>(xP1, u"TextPortionType"_ustr));
618 uno::Reference<beans::XPropertySet> const xP2(xPortions->nextElement(), uno::UNO_QUERY);
619 CPPUNIT_ASSERT_EQUAL(u"Text"_ustr, getProperty<OUString>(xP2, u"TextPortionType"_ustr));
620 uno::Reference<text::XTextRange> const xP2R(xP2, uno::UNO_QUERY);
621 CPPUNIT_ASSERT_EQUAL(u"foo"_ustr, xP2R->getString());
622 uno::Reference<beans::XPropertySet> const xP3(xPortions->nextElement(), uno::UNO_QUERY);
623 CPPUNIT_ASSERT_EQUAL(u"Bookmark"_ustr, getProperty<OUString>(xP3, u"TextPortionType"_ustr));
624 CPPUNIT_ASSERT(!xPortions->hasMoreElements());
627 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testXURI)
629 uno::Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
631 // createKnown()
632 uno::Reference<rdf::XURI> xURIcreateKnown(
633 rdf::URI::createKnown(xContext, rdf::URIs::ODF_PREFIX), uno::UNO_SET_THROW);
634 CPPUNIT_ASSERT(xURIcreateKnown.is());
635 CPPUNIT_ASSERT_EQUAL(u"http://docs.oasis-open.org/ns/office/1.2/meta/odf#"_ustr,
636 xURIcreateKnown->getNamespace());
637 CPPUNIT_ASSERT_EQUAL(u"prefix"_ustr, xURIcreateKnown->getLocalName());
638 CPPUNIT_ASSERT_EQUAL(u"http://docs.oasis-open.org/ns/office/1.2/meta/odf#prefix"_ustr,
639 xURIcreateKnown->getStringValue());
641 // createKnown() with invalid constant
642 CPPUNIT_ASSERT_THROW_MESSAGE("We expect an exception on invalid constant",
643 rdf::URI::createKnown(xContext, 12345),
644 lang::IllegalArgumentException);
646 // create()
647 uno::Reference<rdf::XURI> xURIcreate(
648 rdf::URI::create(xContext, u"http://example.com/url#somedata"_ustr), uno::UNO_SET_THROW);
649 CPPUNIT_ASSERT_EQUAL(u"http://example.com/url#"_ustr, xURIcreate->getNamespace());
650 CPPUNIT_ASSERT_EQUAL(u"somedata"_ustr, xURIcreate->getLocalName());
651 CPPUNIT_ASSERT_EQUAL(u"http://example.com/url#somedata"_ustr, xURIcreate->getStringValue());
653 // create() without local name split with "/"
654 uno::Reference<rdf::XURI> xURIcreate2(
655 rdf::URI::create(xContext, u"http://example.com/url"_ustr), uno::UNO_SET_THROW);
656 CPPUNIT_ASSERT_EQUAL(u"http://example.com/"_ustr, xURIcreate2->getNamespace());
657 CPPUNIT_ASSERT_EQUAL(u"url"_ustr, xURIcreate2->getLocalName());
658 CPPUNIT_ASSERT_EQUAL(u"http://example.com/url"_ustr, xURIcreate2->getStringValue());
660 // create() without prefix
661 uno::Reference<rdf::XURI> xURIcreate3(rdf::URI::create(xContext, u"#somedata"_ustr),
662 uno::UNO_SET_THROW);
663 CPPUNIT_ASSERT_EQUAL(u"#"_ustr, xURIcreate3->getNamespace());
664 CPPUNIT_ASSERT_EQUAL(u"somedata"_ustr, xURIcreate3->getLocalName());
665 CPPUNIT_ASSERT_EQUAL(u"#somedata"_ustr, xURIcreate3->getStringValue());
667 // create() with invalid URI
668 CPPUNIT_ASSERT_THROW_MESSAGE("We expect an exception on invalid URI",
669 rdf::URI::create(xContext, u"some junk and not URI"_ustr),
670 lang::IllegalArgumentException);
672 // createNS()
673 uno::Reference<rdf::XURI> xURIcreateNS(
674 rdf::URI::createNS(xContext, u"http://example.com/url#"_ustr, u"somedata"_ustr),
675 uno::UNO_SET_THROW);
676 CPPUNIT_ASSERT_EQUAL(u"http://example.com/url#"_ustr, xURIcreateNS->getNamespace());
677 CPPUNIT_ASSERT_EQUAL(u"somedata"_ustr, xURIcreateNS->getLocalName());
678 CPPUNIT_ASSERT_EQUAL(u"http://example.com/url#somedata"_ustr, xURIcreateNS->getStringValue());
680 // TODO: What's going on here? Is such usecase valid?
681 uno::Reference<rdf::XURI> xURIcreateNS2(
682 rdf::URI::createNS(xContext, u"http://example.com/url"_ustr, u"somedata"_ustr),
683 uno::UNO_SET_THROW);
684 CPPUNIT_ASSERT_EQUAL(u"http://example.com/"_ustr, xURIcreateNS2->getNamespace());
685 CPPUNIT_ASSERT_EQUAL(u"urlsomedata"_ustr, xURIcreateNS2->getLocalName());
686 CPPUNIT_ASSERT_EQUAL(u"http://example.com/urlsomedata"_ustr, xURIcreateNS2->getStringValue());
688 // createNS() some invalid cases
689 CPPUNIT_ASSERT_THROW_MESSAGE("We expect an exception on invalid URI",
690 rdf::URI::createNS(xContext, u"bla"_ustr, u"bla"_ustr),
691 lang::IllegalArgumentException);
693 CPPUNIT_ASSERT_THROW_MESSAGE("We expect an exception on invalid URI",
694 rdf::URI::createNS(xContext, OUString(), OUString()),
695 lang::IllegalArgumentException);
698 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSetPagePrintSettings)
700 // Create an empty new document with a single char
701 createSwDoc();
703 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
704 uno::Reference<text::XSimpleText> xBodyText = xTextDocument->getText();
705 xBodyText->insertString(xBodyText->getStart(), u"x"_ustr, false);
707 uno::Reference<text::XPagePrintable> xPagePrintable(mxComponent, uno::UNO_QUERY);
709 // set some stuff, try to get it back
710 uno::Sequence<beans::PropertyValue> aProps{
711 comphelper::makePropertyValue(u"PageColumns"_ustr, sal_Int16(2)),
712 comphelper::makePropertyValue(u"IsLandscape"_ustr, true)
715 xPagePrintable->setPagePrintSettings(aProps);
716 const comphelper::SequenceAsHashMap aMap(xPagePrintable->getPagePrintSettings());
718 CPPUNIT_ASSERT_EQUAL(sal_Int16(2), aMap.getValue(u"PageColumns"_ustr).get<short>());
719 CPPUNIT_ASSERT_EQUAL(true, aMap.getValue(u"IsLandscape"_ustr).get<bool>());
722 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testDeleteFlyAtCharAtStart)
724 createSwDoc();
725 SwWrtShell* const pWrtShell(getSwDocShell()->GetWrtShell());
726 SwDoc* const pDoc(pWrtShell->GetDoc());
728 // insert some text
729 IDocumentContentOperations& rIDCO(pDoc->getIDocumentContentOperations());
730 rIDCO.InsertString(*pWrtShell->GetCursor(), u"foo bar baz"_ustr);
732 // insert fly anchored at start of body text
733 pWrtShell->ClearMark();
734 pWrtShell->SttEndDoc(true);
735 SfxItemSet frameSet(pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1>);
736 SfxItemSet grfSet(pDoc->GetAttrPool(), svl::Items<RES_GRFATR_BEGIN, RES_GRFATR_END - 1>);
737 SwFormatAnchor anchor(RndStdIds::FLY_AT_CHAR);
738 frameSet.Put(anchor);
739 Graphic grf;
740 CPPUNIT_ASSERT(rIDCO.InsertGraphic(*pWrtShell->GetCursor(), OUString(), OUString(), &grf,
741 &frameSet, &grfSet, nullptr));
743 // check fly
744 CPPUNIT_ASSERT_EQUAL(1, getShapes());
745 uno::Reference<text::XTextContent> const xShape(getShape(1), uno::UNO_QUERY);
746 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
747 // anchored at start of body text?
748 uno::Reference<text::XText> const xText(xTextDocument->getText());
749 uno::Reference<text::XTextRangeCompare> const xTextRC(xText, uno::UNO_QUERY);
750 CPPUNIT_ASSERT_EQUAL(sal_Int16(0),
751 xTextRC->compareRegionStarts(xText->getStart(), xShape->getAnchor()));
753 // delete 1st character
754 uno::Reference<text::XTextCursor> const xCursor(xText->createTextCursor());
755 xCursor->goRight(1, true);
756 xCursor->setString(u""_ustr);
758 // there is exactly one fly
759 CPPUNIT_ASSERT_EQUAL(1, getShapes());
761 // select entire body text
762 xCursor->gotoStart(true);
763 xCursor->gotoEnd(true);
764 xCursor->setString(u""_ustr);
766 // there is no fly
767 CPPUNIT_ASSERT_EQUAL(0, getShapes());
770 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSelectionInTableEnum)
772 createSwDoc("selection-in-table-enum.odt");
773 // Select the A1 cell's text.
774 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
775 CPPUNIT_ASSERT(pWrtShell);
776 pWrtShell->Down(/*bSelect=*/false);
777 pWrtShell->EndPara(/*bSelect=*/true);
778 CPPUNIT_ASSERT_EQUAL(u"A1"_ustr,
779 pWrtShell->GetCursor()->GetPointNode().GetTextNode()->GetText());
781 // Access the selection.
782 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
783 CPPUNIT_ASSERT(xModel.is());
784 uno::Reference<container::XIndexAccess> xSelections(xModel->getCurrentSelection(),
785 uno::UNO_QUERY);
786 CPPUNIT_ASSERT(xSelections.is());
787 uno::Reference<text::XTextRange> xSelection(xSelections->getByIndex(0), uno::UNO_QUERY);
788 CPPUNIT_ASSERT(xSelection.is());
790 // Enumerate paragraphs in the selection.
791 uno::Reference<container::XEnumerationAccess> xCursor(
792 xSelection->getText()->createTextCursorByRange(xSelection), uno::UNO_QUERY);
793 CPPUNIT_ASSERT(xCursor.is());
794 uno::Reference<container::XEnumeration> xEnum = xCursor->createEnumeration();
795 xEnum->nextElement();
796 // Without the accompanying fix in place, this test would have failed: i.e.
797 // the enumeration contained a second paragraph, even if the cell has only
798 // one paragraph.
799 CPPUNIT_ASSERT(!xEnum->hasMoreElements());
802 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSelectionInTableEnumEnd)
804 createSwDoc("selection-in-table-enum.odt");
805 // Select from "Before" till the table end.
806 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
807 CPPUNIT_ASSERT(pWrtShell);
808 pWrtShell->Down(/*bSelect=*/true);
810 // Access the selection.
811 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
812 CPPUNIT_ASSERT(xModel.is());
813 uno::Reference<container::XIndexAccess> xSelections(xModel->getCurrentSelection(),
814 uno::UNO_QUERY);
815 CPPUNIT_ASSERT(xSelections.is());
816 uno::Reference<text::XTextRange> xSelection(xSelections->getByIndex(0), uno::UNO_QUERY);
817 CPPUNIT_ASSERT(xSelection.is());
818 CPPUNIT_ASSERT_EQUAL(u"Before" SAL_NEWLINE_STRING "A1" SAL_NEWLINE_STRING
819 "B1" SAL_NEWLINE_STRING "C2" SAL_NEWLINE_STRING "A2" SAL_NEWLINE_STRING
820 "B2" SAL_NEWLINE_STRING "C2" SAL_NEWLINE_STRING ""_ustr,
821 xSelection->getString());
823 // Enumerate paragraphs in the selection.
824 uno::Reference<container::XEnumerationAccess> xCursor(
825 xSelection->getText()->createTextCursorByRange(xSelection), uno::UNO_QUERY);
826 CPPUNIT_ASSERT(xCursor.is());
827 uno::Reference<container::XEnumeration> xEnum = xCursor->createEnumeration();
828 // Before.
829 xEnum->nextElement();
830 // Table.
831 xEnum->nextElement();
832 // Without the accompanying fix in place, this test would have failed: i.e.
833 // the enumeration contained the paragraph after the table, but no part of
834 // that paragraph was part of the selection.
835 CPPUNIT_ASSERT(!xEnum->hasMoreElements());
838 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTdf62603)
840 // Unit test for tdf#62603
841 // Test to see if font style is retained when performing find/replace on strings
842 // containing mixed font styles/sizes
843 createSwDoc();
844 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
845 uno::Reference<text::XText> xText = xTextDocument->getText();
846 uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
847 uno::Reference<beans::XPropertySet> xCursorProps(xCursor, uno::UNO_QUERY);
849 // Set up test by inserting strings with different font style/sizes
850 // Inserts 1st string containing quotation marks (") with no font style
851 xText->insertString(xCursor, "\"", false);
852 xCursor->gotoStart(true); // selects full string
853 CPPUNIT_ASSERT_EQUAL(OUString("\""), xCursor->getString());
854 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE,
855 getProperty<awt::FontSlant>(xCursorProps, "CharPosture"));
856 xCursor->collapseToEnd();
858 // Inserts 2nd string 'test' with italic font style
859 xCursorProps->setPropertyValue("CharPosture", uno::Any(awt::FontSlant_ITALIC));
860 xText->insertString(xCursor, "test", false);
861 xCursor->goLeft(4, true); // selects 2nd string
862 CPPUNIT_ASSERT_EQUAL(OUString("test"), xCursor->getString());
863 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_ITALIC,
864 getProperty<awt::FontSlant>(xCursorProps, "CharPosture"));
865 xCursor->collapseToEnd();
867 // Insert 3rd string '? ' with 28 pt font height
868 xCursorProps->setPropertyValue("CharPosture", uno::Any(awt::FontSlant_NONE)); // no font style
869 xCursorProps->setPropertyValue("CharHeight", uno::Any(float(28.0)));
870 xText->insertString(xCursor, "? ", false);
871 xCursor->goLeft(2, true); // selects 3rd string
872 CPPUNIT_ASSERT_EQUAL(float(28.0), getProperty<float>(xCursorProps, "CharHeight"));
873 xCursor->collapseToEnd();
875 // Insert 4th string 'who' with default 12 pt font height
876 xCursorProps->setPropertyValue("CharHeight", uno::Any(float(12.0)));
877 xText->insertString(xCursor, "who", false);
878 xCursor->goLeft(3, true); // selects 4rd string
879 CPPUNIT_ASSERT_EQUAL(float(12.0), getProperty<float>(xCursorProps, "CharHeight"));
880 xCursor->collapseToEnd();
882 // Asserts that full string is properly inserted as: '"test? who'
883 CPPUNIT_ASSERT_EQUAL(1, getParagraphs());
884 CPPUNIT_ASSERT_EQUAL(OUString("\"test? who"), getParagraph(1)->getString());
886 uno::Reference<util::XReplaceable> xReplace(mxComponent, uno::UNO_QUERY);
887 uno::Reference<util::XReplaceDescriptor> xReplaceDesc(xReplace->createReplaceDescriptor());
889 // Searches for "t and replaces with "gu
890 // Note: Search string contains both no font style and italic font style
891 xReplaceDesc->setSearchString("\"t");
892 xReplaceDesc->setReplaceString("\"gu");
893 xReplace->replaceAll(xReplaceDesc);
895 // Search/replace adds extra space between ? and w
896 // Note: Search string contains both 28 pt and 12 pt font sizes
897 xReplaceDesc->setSearchString("? w");
898 xReplaceDesc->setReplaceString("? w");
899 xReplace->replaceAll(xReplaceDesc);
901 // Asserts that '"test? who' is replaced with '"guest? who'
902 CPPUNIT_ASSERT_EQUAL(OUString("\"guest? who"), getParagraph(1)->getString());
904 // Asserts no font style is on double quote mark (")
905 CPPUNIT_ASSERT_EQUAL(
906 awt::FontSlant_NONE,
907 getProperty<awt::FontSlant>(getRun(getParagraph(1), 1, u"\""_ustr), "CharPosture"));
909 // Asserts font style for 'guest' is italic
910 // Without the test, 'g' and 'u' in 'guest' will change to no font style
911 // - Expected: 2 // ITALIC
912 // - Actual: 0 // NONE
913 CPPUNIT_ASSERT_EQUAL(
914 awt::FontSlant_ITALIC,
915 getProperty<awt::FontSlant>(getRun(getParagraph(1), 2, u"guest"_ustr), "CharPosture"));
917 // Asserts font size is 28 pt
918 CPPUNIT_ASSERT_EQUAL(float(28.0),
919 getProperty<float>(getRun(getParagraph(1), 3, u"? "_ustr), "CharHeight"));
921 // Asserts font size is 12 pt
922 // Without the test, the space ' ' and 'w' will change to 28 pt font size
923 // - Expected: 12
924 // - Actual: 28
925 CPPUNIT_ASSERT_EQUAL(
926 float(12.0), getProperty<float>(getRun(getParagraph(1), 4, u" who"_ustr), "CharHeight"));
929 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testRenderablePagePosition)
931 createSwDoc("renderable-page-position.odt");
932 // Make sure that the document has 2 pages.
934 uno::Any aSelection(mxComponent);
936 uno::Reference<awt::XToolkit> xToolkit = VCLUnoHelper::CreateToolkit();
937 uno::Reference<awt::XDevice> xDevice(xToolkit->createScreenCompatibleDevice(32, 32));
939 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
940 uno::Reference<frame::XController> xController = xModel->getCurrentController();
942 beans::PropertyValues aRenderOptions = {
943 comphelper::makePropertyValue(u"IsPrinter"_ustr, true),
944 comphelper::makePropertyValue(u"RenderDevice"_ustr, xDevice),
945 comphelper::makePropertyValue(u"View"_ustr, xController),
946 comphelper::makePropertyValue(u"RenderToGraphic"_ustr, true),
949 uno::Reference<view::XRenderable> xRenderable(mxComponent, uno::UNO_QUERY);
950 sal_Int32 nPages = xRenderable->getRendererCount(aSelection, aRenderOptions);
951 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), nPages);
953 // Make sure that the first page has some offset.
954 comphelper::SequenceAsHashMap aRenderer1(
955 xRenderable->getRenderer(0, aSelection, aRenderOptions));
956 // Without the accompanying fix in place, this test would have failed: i.e.
957 // there was no PagePos key in this map.
958 awt::Point aPosition1 = aRenderer1[u"PagePos"_ustr].get<awt::Point>();
959 CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), aPosition1.X);
960 CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), aPosition1.Y);
962 // Make sure that the second page is below the first one.
963 comphelper::SequenceAsHashMap aRenderer2(
964 xRenderable->getRenderer(1, aSelection, aRenderOptions));
965 awt::Point aPosition2 = aRenderer2[u"PagePos"_ustr].get<awt::Point>();
966 CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), aPosition2.X);
967 CPPUNIT_ASSERT_GREATER(aPosition1.Y, aPosition2.Y);
970 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testPasteListener)
972 createSwDoc();
974 // Insert initial string.
975 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
976 uno::Reference<text::XSimpleText> xBodyText = xTextDocument->getText();
977 xBodyText->insertString(xBodyText->getStart(), u"ABCDEF"_ustr, false);
979 // Add paste listener.
980 uno::Reference<text::XPasteBroadcaster> xBroadcaster(mxComponent, uno::UNO_QUERY);
981 uno::Reference<text::XPasteListener> xListener(new PasteListener);
982 auto pListener = static_cast<PasteListener*>(xListener.get());
983 xBroadcaster->addPasteEventListener(xListener);
985 // Cut "DE" and then paste it.
986 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
987 CPPUNIT_ASSERT(pWrtShell);
988 pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 3, /*bBasicCall=*/false);
989 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 2, /*bBasicCall=*/false);
990 rtl::Reference<SwTransferable> pTransfer = new SwTransferable(*pWrtShell);
991 pTransfer->Cut();
992 TransferableDataHelper aHelper(pTransfer);
993 SwTransferable::Paste(*pWrtShell, aHelper);
994 // Without working listener registration in place, this test would have
995 // failed with 'Expected: DE; Actual:', i.e. the paste listener was not
996 // invoked.
997 CPPUNIT_ASSERT_EQUAL(u"DE"_ustr, pListener->GetString());
999 // Make sure that paste did not overwrite anything.
1000 CPPUNIT_ASSERT_EQUAL(u"ABCDEF"_ustr, xBodyText->getString());
1002 // Paste again, this time overwriting "BC".
1003 pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 4, /*bBasicCall=*/false);
1004 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 2, /*bBasicCall=*/false);
1005 pListener->GetString().clear();
1006 SwTransferable::Paste(*pWrtShell, aHelper);
1007 CPPUNIT_ASSERT_EQUAL(u"DE"_ustr, pListener->GetString());
1009 // Make sure that paste overwrote "BC".
1010 CPPUNIT_ASSERT_EQUAL(u"ADEDEF"_ustr, xBodyText->getString());
1012 // Test image paste.
1013 SwView& rView = pWrtShell->GetView();
1014 rView.InsertGraphic(createFileURL(u"test.jpg"), OUString(), /*bAsLink=*/false,
1015 &GraphicFilter::GetGraphicFilter());
1017 // Test that the pasted image is anchored as-char.
1018 SwFlyFrame* pFly = pWrtShell->GetSelectedFlyFrame();
1019 CPPUNIT_ASSERT(pFly);
1020 SwFrameFormat* pFlyFormat = pFly->GetFormat();
1021 CPPUNIT_ASSERT(pFlyFormat);
1022 RndStdIds eFlyAnchor = pFlyFormat->GetAnchor().GetAnchorId();
1023 // Without the working image listener in place, this test would have
1024 // failed, eFlyAnchor was FLY_AT_PARA.
1025 CPPUNIT_ASSERT_EQUAL(RndStdIds::FLY_AT_CHAR, eFlyAnchor);
1027 pTransfer->Cut();
1028 pListener->GetString().clear();
1029 SwTransferable::Paste(*pWrtShell, aHelper);
1030 // Without the working image listener in place, this test would have
1031 // failed, the listener was not invoked in case of a graphic paste.
1032 CPPUNIT_ASSERT(pListener->GetTextGraphicObject().is());
1033 CPPUNIT_ASSERT(pListener->GetString().isEmpty());
1035 // Deregister paste listener, make sure it's not invoked.
1036 xBroadcaster->removePasteEventListener(xListener);
1037 pListener->GetString().clear();
1038 SwTransferable::Paste(*pWrtShell, aHelper);
1039 CPPUNIT_ASSERT(pListener->GetString().isEmpty());
1042 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testImageCommentAtChar)
1044 // Load a document with an at-char image in it (and a comment on the image).
1045 createSwDoc("image-comment-at-char.odt");
1046 SwDoc* pDoc = getSwDoc();
1048 // Verify that we have an annotation mark (comment with a text range) in the document.
1049 // Without the accompanying fix in place, this test would have failed, as comments lost their
1050 // ranges on load when their range only covered the placeholder character of the comment (which
1051 // is also the anchor position of the image).
1052 IDocumentMarkAccess* pMarks = pDoc->getIDocumentMarkAccess();
1053 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pMarks->getAnnotationMarksCount());
1055 uno::Reference<text::XTextRange> xPara = getParagraph(1);
1056 CPPUNIT_ASSERT_EQUAL(u"Text"_ustr,
1057 getProperty<OUString>(getRun(xPara, 1), u"TextPortionType"_ustr));
1058 // Without the accompanying fix in place, this test would have failed with 'Expected:
1059 // Annotation; Actual: Frame', i.e. the comment-start portion was after the commented image.
1060 CPPUNIT_ASSERT_EQUAL(u"Annotation"_ustr,
1061 getProperty<OUString>(getRun(xPara, 2), u"TextPortionType"_ustr));
1062 CPPUNIT_ASSERT_EQUAL(u"Frame"_ustr,
1063 getProperty<OUString>(getRun(xPara, 3), u"TextPortionType"_ustr));
1064 CPPUNIT_ASSERT_EQUAL(u"AnnotationEnd"_ustr,
1065 getProperty<OUString>(getRun(xPara, 4), u"TextPortionType"_ustr));
1066 CPPUNIT_ASSERT_EQUAL(u"Text"_ustr,
1067 getProperty<OUString>(getRun(xPara, 5), u"TextPortionType"_ustr));
1069 // Without the accompanying fix in place, this test would have failed with 'Expected:
1070 // 5892; Actual: 1738', i.e. the anchor pos was between the "aaa" and "bbb" portions, not at the
1071 // center of the page (horizontally) where the image is. On macOS, though, with the fix in
1072 // place the actual value consistently is even greater with 6283 now instead of 5892, for
1073 // whatever reason.
1074 SwView* pView = getSwDocShell()->GetView();
1075 SwPostItMgr* pPostItMgr = pView->GetPostItMgr();
1076 for (const auto& pItem : *pPostItMgr)
1078 const SwRect& rAnchor = pItem->mpPostIt->GetAnchorRect();
1079 CPPUNIT_ASSERT_GREATEREQUAL(static_cast<tools::Long>(5892), rAnchor.Left());
1083 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testChapterNumberingCharStyle)
1085 createSwDoc();
1087 uno::Reference<lang::XMultiServiceFactory> xDoc(mxComponent, uno::UNO_QUERY);
1088 uno::Reference<beans::XPropertySet> xStyle(
1089 xDoc->createInstance(u"com.sun.star.style.CharacterStyle"_ustr), uno::UNO_QUERY);
1090 uno::Reference<container::XNamed> xStyleN(xStyle, uno::UNO_QUERY);
1091 xStyle->setPropertyValue(u"CharColor"_ustr, uno::Any(COL_LIGHTRED));
1092 uno::Reference<style::XStyleFamiliesSupplier> xSFS(mxComponent, uno::UNO_QUERY);
1093 uno::Reference<container::XNameContainer> xStyles(
1094 xSFS->getStyleFamilies()->getByName(u"CharacterStyles"_ustr), uno::UNO_QUERY);
1095 xStyles->insertByName(u"red"_ustr, uno::Any(xStyle));
1097 uno::Reference<text::XChapterNumberingSupplier> xCNS(mxComponent, uno::UNO_QUERY);
1098 uno::Reference<container::XIndexReplace> xOutline(xCNS->getChapterNumberingRules());
1100 comphelper::SequenceAsHashMap hashMap(xOutline->getByIndex(0));
1101 hashMap[u"CharStyleName"_ustr] <<= u"red"_ustr;
1102 uno::Sequence<beans::PropertyValue> props;
1103 hashMap >> props;
1104 xOutline->replaceByIndex(0, uno::Any(props));
1106 // now rename the style
1107 xStyleN->setName(u"reddishred"_ustr);
1109 comphelper::SequenceAsHashMap hashMap(xOutline->getByIndex(0));
1111 // tdf#137810 this failed, was old value "red"
1112 CPPUNIT_ASSERT_EQUAL(u"reddishred"_ustr, hashMap[u"CharStyleName"_ustr].get<OUString>());
1116 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testViewCursorPageStyle)
1118 // Load a document with 2 pages, but a single paragraph.
1119 createSwDoc("view-cursor-page-style.fodt");
1120 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
1121 CPPUNIT_ASSERT(xModel.is());
1122 uno::Reference<text::XTextViewCursorSupplier> xController(xModel->getCurrentController(),
1123 uno::UNO_QUERY);
1124 CPPUNIT_ASSERT(xController.is());
1125 uno::Reference<text::XPageCursor> xViewCursor(xController->getViewCursor(), uno::UNO_QUERY);
1126 CPPUNIT_ASSERT(xViewCursor.is());
1128 // Go to the first page, which has an explicit page style.
1129 xViewCursor->jumpToPage(1);
1130 OUString aActualPageStyleName = getProperty<OUString>(xViewCursor, u"PageStyleName"_ustr);
1131 CPPUNIT_ASSERT_EQUAL(u"First Page"_ustr, aActualPageStyleName);
1133 // Go to the second page, which is still the first paragraph, but the page style is different,
1134 // as the explicit 'First Page' page style has a next style defined (Standard).
1135 xViewCursor->jumpToPage(2);
1136 aActualPageStyleName = getProperty<OUString>(xViewCursor, u"PageStyleName"_ustr);
1137 // Without the accompanying fix in place, this test would have failed with:
1138 // - Expected: Standard
1139 // - Actual : First Page
1140 // i.e. the cursor position was determined only based on the node index, ignoring the content
1141 // index.
1142 CPPUNIT_ASSERT_EQUAL(u"Standard"_ustr, aActualPageStyleName);
1145 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testXTextCursor_setPropertyValues)
1147 // Create a new document, type a character, pass a set of property/value pairs consisting of one
1148 // unknown property and CharStyleName, assert that it threw UnknownPropertyException (actually
1149 // wrapped into WrappedTargetException), and assert the style was set, not discarded.
1150 createSwDoc();
1152 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
1153 uno::Reference<text::XSimpleText> xBodyText = xTextDocument->getText();
1154 xBodyText->insertString(xBodyText->getStart(), u"x"_ustr, false);
1156 uno::Reference<text::XTextCursor> xCursor(xBodyText->createTextCursor());
1157 xCursor->goLeft(1, true);
1159 uno::Reference<beans::XMultiPropertySet> xCursorProps(xCursor, uno::UNO_QUERY);
1160 uno::Sequence<OUString> aPropNames = { u"OneUnknownProperty"_ustr, u"CharStyleName"_ustr };
1161 uno::Sequence<uno::Any> aPropValues = { uno::Any(), uno::Any(u"Emphasis"_ustr) };
1162 CPPUNIT_ASSERT_THROW(xCursorProps->setPropertyValues(aPropNames, aPropValues),
1163 lang::WrappedTargetException);
1164 CPPUNIT_ASSERT_EQUAL(u"Emphasis"_ustr,
1165 getProperty<OUString>(xCursorProps, u"CharStyleName"_ustr));
1168 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testShapeAllowOverlap)
1170 // Test the AllowOverlap frame/shape property.
1172 // Create a new document and insert a rectangle.
1173 createSwDoc();
1174 uno::Reference<lang::XMultiServiceFactory> xDocument(mxComponent, uno::UNO_QUERY);
1175 awt::Point aPoint(1000, 1000);
1176 awt::Size aSize(10000, 10000);
1177 uno::Reference<drawing::XShape> xShape(
1178 xDocument->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY);
1179 xShape->setPosition(aPoint);
1180 xShape->setSize(aSize);
1181 uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xDocument, uno::UNO_QUERY);
1182 xDrawPageSupplier->getDrawPage()->add(xShape);
1184 // The property is on by default, turn it off & verify.
1185 uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY);
1186 xShapeProperties->setPropertyValue(u"AllowOverlap"_ustr, uno::Any(false));
1187 CPPUNIT_ASSERT(!getProperty<bool>(xShapeProperties, u"AllowOverlap"_ustr));
1189 // Turn it back to on & verify.
1190 xShapeProperties->setPropertyValue(u"AllowOverlap"_ustr, uno::Any(true));
1191 CPPUNIT_ASSERT(getProperty<bool>(xShapeProperties, u"AllowOverlap"_ustr));
1194 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTextConvertToTableLineSpacing)
1196 // Load a document which has a table with a single cell.
1197 // The cell has both a table style and a paragraph style, with different line spacing
1198 // heights.
1199 createSwDoc("table-line-spacing.docx");
1200 uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
1201 uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(),
1202 uno::UNO_QUERY);
1203 uno::Reference<text::XTextTable> xTable(xTables->getByIndex(0), uno::UNO_QUERY);
1204 uno::Reference<table::XCell> xCell = xTable->getCellByName(u"A1"_ustr);
1205 uno::Reference<text::XText> xCellText(xCell, uno::UNO_QUERY);
1206 uno::Reference<text::XTextRange> xParagraph = getParagraphOfText(1, xCellText);
1207 style::LineSpacing aLineSpacing
1208 = getProperty<style::LineSpacing>(xParagraph, u"ParaLineSpacing"_ustr);
1209 // Make sure that we take the line spacing from the paragraph style, not from the table style.
1210 // Without the accompanying fix in place, this test would have failed with:
1211 // - Expected: 388
1212 // - Actual : 635
1213 // I.e. the 360 twips line spacing was taken from the table style, not the 220 twips one from
1214 // the paragraph style.
1215 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(convertTwipToMm100(220)), aLineSpacing.Height);
1218 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testMultiSelect)
1220 // Create a new document and add a text with several repeated sequences.
1221 createSwDoc();
1222 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, css::uno::UNO_QUERY_THROW);
1223 auto xSimpleText = xTextDocument->getText();
1224 xSimpleText->insertString(xSimpleText->getStart(), u"Abc aBc abC"_ustr, false);
1226 // Create a search descriptor and find all occurrences of search string
1227 css::uno::Reference<css::util::XSearchable> xSearchable(mxComponent, css::uno::UNO_QUERY_THROW);
1228 auto xSearchDescriptor = xSearchable->createSearchDescriptor();
1229 xSearchDescriptor->setPropertyValue(u"SearchStyles"_ustr, css::uno::Any(false));
1230 xSearchDescriptor->setPropertyValue(u"SearchCaseSensitive"_ustr, css::uno::Any(false));
1231 xSearchDescriptor->setPropertyValue(u"SearchBackwards"_ustr, css::uno::Any(true));
1232 xSearchDescriptor->setPropertyValue(u"SearchRegularExpression"_ustr, css::uno::Any(false));
1233 xSearchDescriptor->setSearchString(u"abc"_ustr);
1234 auto xSearchResult = xSearchable->findAll(xSearchDescriptor);
1236 // Select them all
1237 auto xController = xTextDocument->getCurrentController();
1238 css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier(
1239 xController, css::uno::UNO_QUERY_THROW);
1240 xSelectionSupplier->select(css::uno::Any(xSearchResult));
1241 css::uno::Reference<css::container::XIndexAccess> xSelection(xSelectionSupplier->getSelection(),
1242 css::uno::UNO_QUERY_THROW);
1243 // Now check that they all are selected in the reverse order ("SearchBackwards").
1244 CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xSelection->getCount());
1245 css::uno::Reference<css::text::XTextRange> xTextRange(xSelection->getByIndex(0),
1246 css::uno::UNO_QUERY_THROW);
1247 // For #0, result was empty (cursor was put before the last occurrence without selection)
1248 CPPUNIT_ASSERT_EQUAL(u"abC"_ustr, xTextRange->getString());
1249 xTextRange.set(xSelection->getByIndex(1), css::uno::UNO_QUERY_THROW);
1250 CPPUNIT_ASSERT_EQUAL(u"aBc"_ustr, xTextRange->getString());
1251 xTextRange.set(xSelection->getByIndex(2), css::uno::UNO_QUERY_THROW);
1252 CPPUNIT_ASSERT_EQUAL(u"Abc"_ustr, xTextRange->getString());
1255 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTransparentText)
1257 // Test the CharTransparence text portion property.
1259 // Create a new document.
1260 createSwDoc();
1262 // Set a custom transparency.
1263 uno::Reference<beans::XPropertySet> xParagraph(getParagraph(1), uno::UNO_QUERY);
1264 sal_Int16 nExpected = 42;
1265 xParagraph->setPropertyValue(u"CharTransparence"_ustr, uno::Any(nExpected));
1267 // Get the transparency & verify.
1268 CPPUNIT_ASSERT_EQUAL(nExpected, getProperty<sal_Int16>(xParagraph, u"CharTransparence"_ustr));
1271 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTdf129839)
1273 // Create a new document and add a table
1274 createSwDoc();
1275 css::uno::Reference<css::text::XTextDocument> xTextDocument(mxComponent,
1276 css::uno::UNO_QUERY_THROW);
1277 css::uno::Reference<css::lang::XMultiServiceFactory> xFac(xTextDocument,
1278 css::uno::UNO_QUERY_THROW);
1279 css::uno::Reference<css::text::XTextTable> xTable(
1280 xFac->createInstance(u"com.sun.star.text.TextTable"_ustr), css::uno::UNO_QUERY_THROW);
1281 xTable->initialize(4, 4);
1282 auto xSimpleText = xTextDocument->getText();
1283 xSimpleText->insertTextContent(xSimpleText->createTextCursor(), xTable, true);
1284 css::uno::Reference<css::table::XCellRange> xTableCellRange(xTable, css::uno::UNO_QUERY_THROW);
1285 // Get instance of SwXCellRange
1286 css::uno::Reference<css::beans::XPropertySet> xCellRange(
1287 xTableCellRange->getCellRangeByPosition(0, 0, 1, 1), css::uno::UNO_QUERY_THROW);
1288 // Test retrieval of VertOrient property - this crashed
1289 css::uno::Any aOrient = xCellRange->getPropertyValue(u"VertOrient"_ustr);
1290 CPPUNIT_ASSERT_EQUAL(css::uno::Any(css::text::VertOrientation::NONE), aOrient);
1293 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTdf129841)
1295 // Create a new document and add a table
1296 createSwDoc();
1297 css::uno::Reference<css::text::XTextDocument> xTextDocument(mxComponent,
1298 css::uno::UNO_QUERY_THROW);
1299 css::uno::Reference<css::lang::XMultiServiceFactory> xFac(xTextDocument,
1300 css::uno::UNO_QUERY_THROW);
1301 css::uno::Reference<css::text::XTextTable> xTable(
1302 xFac->createInstance(u"com.sun.star.text.TextTable"_ustr), css::uno::UNO_QUERY_THROW);
1303 xTable->initialize(4, 4);
1304 auto xSimpleText = xTextDocument->getText();
1305 xSimpleText->insertTextContent(xSimpleText->createTextCursor(), xTable, true);
1306 // Get SwXTextTableCursor
1307 css::uno::Reference<css::beans::XPropertySet> xTableCursor(
1308 xTable->createCursorByCellName(u"A1"_ustr), css::uno::UNO_QUERY_THROW);
1309 css::uno::Reference<css::table::XCellRange> xTableCellRange(xTable, css::uno::UNO_QUERY_THROW);
1310 // Get SwXCellRange for the same cell
1311 css::uno::Reference<css::beans::XPropertySet> xCellRange(
1312 xTableCellRange->getCellRangeByName(u"A1:A1"_ustr), css::uno::UNO_QUERY_THROW);
1313 static constexpr OUString sBackColor = u"BackColor"_ustr;
1314 // Apply background color to table cursor, and read background color from cell range
1315 css::uno::Any aRefColor(COL_LIGHTRED);
1316 xTableCursor->setPropertyValue(sBackColor, aRefColor);
1317 css::uno::Any aColor = xCellRange->getPropertyValue(sBackColor);
1318 // This failed
1319 CPPUNIT_ASSERT_EQUAL(aRefColor, aColor);
1320 // Now the other way round
1321 aRefColor <<= COL_LIGHTGREEN;
1322 xCellRange->setPropertyValue(sBackColor, aRefColor);
1323 aColor = xTableCursor->getPropertyValue(sBackColor);
1324 CPPUNIT_ASSERT_EQUAL(aRefColor, aColor);
1327 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTdf141525)
1329 // Unit test for tdf#141525:
1330 // Checks if "Line with Arrow/Circle" is inserted with correct end points
1331 createSwDoc();
1333 // Insert "Line with Arrow/Circle" shape with CTRL key
1334 uno::Sequence<beans::PropertyValue> aArgs(
1335 comphelper::InitPropertySequence({ { "KeyModifier", uno::Any(KEY_MOD1) } }));
1336 dispatchCommand(mxComponent, u".uno:LineArrowCircle"_ustr, aArgs);
1338 // Asserts line shape has been inserted into the doc
1339 CPPUNIT_ASSERT_EQUAL(1, getShapes());
1340 CPPUNIT_ASSERT_EQUAL(u"com.sun.star.drawing.LineShape"_ustr, getShape(1)->getShapeType());
1342 // Asserts end of line has a circle
1343 // Without the test, "Line Starts with Arrow" is inserted
1344 // i.e. the circle is missing from the line end point
1345 // - Expected: "Circle"
1346 // - Actual: ""
1347 CPPUNIT_ASSERT_EQUAL(u"Circle"_ustr, getProperty<OUString>(getShape(1), u"LineEndName"_ustr));
1348 // Asserts start of line has an arrow
1349 CPPUNIT_ASSERT_EQUAL(u"Arrow"_ustr, getProperty<OUString>(getShape(1), u"LineStartName"_ustr));
1352 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTdf160278)
1354 createSwDoc();
1355 auto xTextDocument(mxComponent.queryThrow<css::text::XTextDocument>());
1356 auto xText(xTextDocument->getText());
1357 xText->setString(u"123"_ustr);
1358 CPPUNIT_ASSERT_EQUAL(u"123"_ustr, xText->getString());
1359 auto xCursor = xText->createTextCursorByRange(xText->getEnd());
1360 xCursor->goLeft(1, true);
1361 CPPUNIT_ASSERT_EQUAL(u"3"_ustr, xCursor->getString());
1362 // Insert an SMP character U+1f702 (so it's two UTF-16 code units, 0xd83d 0xdf02):
1363 xCursor->setString(u"🜂"_ustr);
1364 // Without the fix, the replacement would expand the cursor one too many characters to the left,
1365 // and the cursor text would become "2🜂", failing the next test:
1366 CPPUNIT_ASSERT_EQUAL(u"🜂"_ustr, xCursor->getString());
1367 xCursor->setString(u"test"_ustr);
1368 CPPUNIT_ASSERT_EQUAL(u"test"_ustr, xCursor->getString());
1369 // This test would fail, too; the text would be "1test":
1370 CPPUNIT_ASSERT_EQUAL(u"12test"_ustr, xText->getString());
1373 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTdf161035)
1375 // Given a paragraph with a bookmark:
1376 createSwDoc("tdf161035.fodt");
1377 auto xModel = mxComponent.queryThrow<frame::XModel>();
1379 // Create a text view cursor in the paragraph.
1380 auto xController = xModel->getCurrentController().queryThrow<text::XTextViewCursorSupplier>();
1381 auto xViewCursor = xController->getViewCursor();
1382 CPPUNIT_ASSERT(xViewCursor);
1383 auto xText = xViewCursor->getText();
1384 CPPUNIT_ASSERT(xText);
1385 // Create a text cursor from the text view cursor, and move it to the end of the paragraph
1386 auto xTextCursor = xText->createTextCursorByRange(xViewCursor);
1387 CPPUNIT_ASSERT(xTextCursor);
1388 xTextCursor->gotoEnd(false);
1389 // Get the first paragraph portion from the text cursor
1390 auto xParaEnum = xTextCursor.queryThrow<container::XEnumerationAccess>()->createEnumeration();
1391 CPPUNIT_ASSERT(xParaEnum);
1392 auto xPara = xParaEnum->nextElement().queryThrow<container::XEnumerationAccess>();
1393 // Try to enumerate text portions. Without the fix, it would fail an assertion in debug builds,
1394 // and hang in release builds, because the paragraph portion started after the bookmark, and
1395 // so the bookmark wasn't processed (expectedly):
1396 auto xRunEnum = xPara->createEnumeration();
1397 CPPUNIT_ASSERT(!xRunEnum->hasMoreElements()); // Empty enumeration for empty selection
1400 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTdf162480)
1402 createSwDoc();
1404 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence({
1405 { "Name", uno::Any(createFileURL(u"textboxInColumn2.fodt")) },
1408 // Inserting a document with text box attached in a table's second column must not crash
1409 dispatchCommand(mxComponent, u".uno:InsertDoc"_ustr, aPropertyValues);
1411 auto xTextBox = getShape(1).queryThrow<css::text::XTextContent>();
1412 auto xTable = getParagraphOrTable(2).queryThrow<css::text::XTextTable>();
1413 auto xAnchorRange = xTextBox->getAnchor();
1414 auto xCellText = xTable->getCellByName("B1").queryThrow<css::text::XText>();
1415 CPPUNIT_ASSERT_EQUAL(xCellText, xAnchorRange->getText());
1418 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTdf164885)
1420 class LocalDispatch : public comphelper::WeakImplHelper<css::frame::XDispatch>
1422 public:
1423 LocalDispatch() = default;
1425 void SAL_CALL dispatch(const css::util::URL& URL,
1426 const css::uno::Sequence<css::beans::PropertyValue>&) override
1428 sLastCommand = URL.Complete;
1430 void SAL_CALL addStatusListener(const css::uno::Reference<css::frame::XStatusListener>&,
1431 const css::util::URL&) override
1433 // empty
1435 void SAL_CALL removeStatusListener(const css::uno::Reference<css::frame::XStatusListener>&,
1436 const css::util::URL&) override
1438 // empty
1441 OUString sLastCommand;
1444 class LocalInterceptor
1445 : public comphelper::WeakImplHelper<css::frame::XDispatchProviderInterceptor>
1447 public:
1448 LocalInterceptor() = default;
1450 // XDispatchProvider
1451 css::uno::Reference<css::frame::XDispatch>
1452 SAL_CALL queryDispatch(const css::util::URL& URL, const OUString& TargetFrameName,
1453 sal_Int32 SearchFlags) override
1455 if (URL.Complete == ".uno:Open")
1456 return pDispatch;
1457 if (m_slave)
1458 return m_slave->queryDispatch(URL, TargetFrameName, SearchFlags);
1459 return {};
1461 css::uno::Sequence<css::uno::Reference<css::frame::XDispatch>> SAL_CALL
1462 queryDispatches(const css::uno::Sequence<css::frame::DispatchDescriptor>&) override
1464 return {};
1467 // XDispatchProviderInterceptor
1468 css::uno::Reference<css::frame::XDispatchProvider>
1469 SAL_CALL getSlaveDispatchProvider() override
1471 return m_slave;
1473 void SAL_CALL setSlaveDispatchProvider(
1474 const css::uno::Reference<css::frame::XDispatchProvider>& val) override
1476 m_slave = val;
1478 css::uno::Reference<css::frame::XDispatchProvider>
1479 SAL_CALL getMasterDispatchProvider() override
1481 return m_master;
1483 void SAL_CALL setMasterDispatchProvider(
1484 const css::uno::Reference<css::frame::XDispatchProvider>& val) override
1486 m_master = val;
1489 rtl::Reference<LocalDispatch> pDispatch{ new LocalDispatch };
1491 private:
1492 css::uno::Reference<css::frame::XDispatchProvider> m_master;
1493 css::uno::Reference<css::frame::XDispatchProvider> m_slave;
1496 // Given a document with a hyperlink
1497 createSwDoc("hyperlink.fodt");
1498 auto controller(mxComponent.queryThrow<frame::XModel>()->getCurrentController());
1499 auto xProvider(controller->getFrame().queryThrow<css::frame::XDispatchProviderInterception>());
1501 rtl::Reference<LocalInterceptor> interceptor(new LocalInterceptor);
1502 xProvider->registerDispatchProviderInterceptor(interceptor);
1504 auto xCursor = controller.queryThrow<text::XTextViewCursorSupplier>()->getViewCursor();
1505 xCursor->goRight(5, false); // put cursor inside the hyperlink
1507 // Initiate "open hyperlink"
1508 dispatchCommand(mxComponent, u".uno:OpenHyperlinkOnCursor"_ustr, {});
1510 xProvider->releaseDispatchProviderInterceptor(interceptor);
1512 // Without the fix, this failed with
1513 // - Expected: .uno:Open
1514 // - Actual :
1515 // because the interception didn't happen
1516 CPPUNIT_ASSERT_EQUAL(u".uno:Open"_ustr, interceptor->pDispatch->sLastCommand);
1519 } // end of anonymous namespace
1520 CPPUNIT_PLUGIN_IMPLEMENT();
1522 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */