android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / qa / extras / unowriter / unowriter.cxx
blob61b9b524b39960a82b223de11095f2172d4370fd
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/table/XCellRange.hpp>
16 #include <com/sun/star/text/TextContentAnchorType.hpp>
17 #include <com/sun/star/text/AutoTextContainer.hpp>
18 #include <com/sun/star/text/VertOrientation.hpp>
19 #include <com/sun/star/text/XAutoTextGroup.hpp>
20 #include <com/sun/star/text/XTextPortionAppend.hpp>
21 #include <com/sun/star/text/XTextContentAppend.hpp>
22 #include <com/sun/star/text/XTextRangeCompare.hpp>
23 #include <com/sun/star/text/XPasteListener.hpp>
24 #include <com/sun/star/rdf/URI.hpp>
25 #include <com/sun/star/rdf/URIs.hpp>
26 #include <com/sun/star/awt/XDevice.hpp>
27 #include <com/sun/star/awt/XToolkit.hpp>
28 #include <com/sun/star/graphic/XGraphic.hpp>
29 #include <com/sun/star/style/LineSpacing.hpp>
30 #include <com/sun/star/view/XSelectionSupplier.hpp>
31 #include <com/sun/star/text/XTextDocument.hpp>
32 #include <com/sun/star/container/XNameContainer.hpp>
33 #include <com/sun/star/view/XRenderable.hpp>
34 #include <com/sun/star/text/XBookmarksSupplier.hpp>
35 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
36 #include <com/sun/star/text/XTextTable.hpp>
37 #include <com/sun/star/text/XPageCursor.hpp>
39 #include <comphelper/propertyvalue.hxx>
40 #include <tools/UnitConversion.hxx>
41 #include <toolkit/helper/vclunohelper.hxx>
42 #include <vcl/graphicfilter.hxx>
43 #include <comphelper/sequenceashashmap.hxx>
44 #include <comphelper/processfactory.hxx>
46 #include <wrtsh.hxx>
47 #include <ndtxt.hxx>
48 #include <swdtflvr.hxx>
49 #include <view.hxx>
50 #include <PostItMgr.hxx>
51 #include <postithelper.hxx>
52 #include <AnnotationWin.hxx>
53 #include <flyfrm.hxx>
54 #include <fmtanchr.hxx>
55 #include <unotxdoc.hxx>
56 #include <docsh.hxx>
58 using namespace ::com::sun::star;
60 namespace
62 /// Listener implementation for testPasteListener.
63 class PasteListener : public cppu::WeakImplHelper<text::XPasteListener>
65 OUString m_aString;
66 uno::Reference<text::XTextContent> m_xTextGraphicObject;
68 public:
69 void SAL_CALL notifyPasteEvent(const uno::Sequence<beans::PropertyValue>& rEvent) override;
71 OUString& GetString();
72 uno::Reference<text::XTextContent>& GetTextGraphicObject();
75 void PasteListener::notifyPasteEvent(const uno::Sequence<beans::PropertyValue>& rEvent)
77 comphelper::SequenceAsHashMap aMap(rEvent);
78 auto it = aMap.find("TextRange");
79 if (it != aMap.end())
81 auto xTextRange = it->second.get<uno::Reference<text::XTextRange>>();
82 if (xTextRange.is())
83 m_aString = xTextRange->getString();
84 return;
87 it = aMap.find("TextGraphicObject");
88 if (it != aMap.end())
90 auto xTextGraphicObject = it->second.get<uno::Reference<text::XTextContent>>();
91 if (xTextGraphicObject.is())
92 m_xTextGraphicObject = xTextGraphicObject;
96 OUString& PasteListener::GetString() { return m_aString; }
98 uno::Reference<text::XTextContent>& PasteListener::GetTextGraphicObject()
100 return m_xTextGraphicObject;
104 /// Test to assert UNO API call results of Writer.
105 class SwUnoWriter : public SwModelTestBase
107 public:
108 SwUnoWriter()
109 : SwModelTestBase("/sw/qa/extras/unowriter/data/", "writer8")
114 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testDefaultCharStyle)
116 // Create a new document, type a character, set its char style to Emphasis
117 // and assert the style was set.
118 createSwDoc();
120 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
121 uno::Reference<text::XSimpleText> xBodyText = xTextDocument->getText();
122 xBodyText->insertString(xBodyText->getStart(), "x", false);
124 uno::Reference<text::XTextCursor> xCursor(xBodyText->createTextCursor());
125 xCursor->goLeft(1, true);
127 uno::Reference<beans::XPropertySet> xCursorProps(xCursor, uno::UNO_QUERY);
128 xCursorProps->setPropertyValue("CharStyleName", uno::Any(OUString("Emphasis")));
129 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_ITALIC,
130 getProperty<awt::FontSlant>(xCursorProps, "CharPosture"));
132 // Now reset the char style and assert that the font slant is back to none.
133 // This resulted in a lang.IllegalArgumentException, Standard was not
134 // mapped to 'Default Style'.
135 xCursorProps->setPropertyValue("CharStyleName", uno::Any(OUString("Standard")));
136 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE,
137 getProperty<awt::FontSlant>(xCursorProps, "CharPosture"));
140 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testInsertStringExpandsHints)
142 createSwDoc();
143 uno::Reference<text::XTextDocument> const xTextDocument(mxComponent, uno::UNO_QUERY);
144 uno::Reference<text::XText> const xText(xTextDocument->getText());
145 uno::Reference<text::XTextCursor> const xCursor(xText->createTextCursor());
146 uno::Reference<beans::XPropertySet> const xProps(xCursor, uno::UNO_QUERY);
148 xText->insertString(xCursor, "ab", false);
149 xCursor->gotoStart(false);
150 xCursor->goRight(1, true);
151 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE, getProperty<awt::FontSlant>(xProps, "CharPosture"));
152 xProps->setPropertyValue("CharPosture", uno::Any(awt::FontSlant_ITALIC));
153 xCursor->collapseToEnd();
154 xText->insertString(xCursor, "x", false);
155 xCursor->goLeft(1, true);
156 CPPUNIT_ASSERT_EQUAL(OUString("x"), xCursor->getString());
157 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_ITALIC, getProperty<awt::FontSlant>(xProps, "CharPosture"));
160 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testInsertTextPortionNotExpandsHints)
162 createSwDoc();
163 uno::Reference<text::XTextDocument> const xTextDocument(mxComponent, uno::UNO_QUERY);
164 uno::Reference<text::XText> const xText(xTextDocument->getText());
165 uno::Reference<text::XTextPortionAppend> const xTextA(xText, uno::UNO_QUERY);
166 uno::Reference<text::XTextCursor> const xCursor(xText->createTextCursor());
167 uno::Reference<beans::XPropertySet> const xProps(xCursor, uno::UNO_QUERY);
169 xText->insertString(xCursor, "ab", false);
170 xCursor->gotoStart(false);
171 xCursor->goRight(1, true);
172 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE, getProperty<awt::FontSlant>(xProps, "CharPosture"));
173 xProps->setPropertyValue("CharPosture", uno::Any(awt::FontSlant_ITALIC));
174 xCursor->collapseToEnd();
175 xTextA->insertTextPortion("x", uno::Sequence<beans::PropertyValue>(), xCursor);
176 xCursor->goLeft(1, true);
177 CPPUNIT_ASSERT_EQUAL(OUString("x"), xCursor->getString());
178 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE, getProperty<awt::FontSlant>(xProps, "CharPosture"));
181 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testInsertTextContentExpandsHints)
183 createSwDoc();
184 uno::Reference<text::XTextDocument> const xTextDocument(mxComponent, uno::UNO_QUERY);
185 uno::Reference<lang::XMultiServiceFactory> const xFactory(mxComponent, uno::UNO_QUERY);
186 uno::Reference<text::XText> const xText(xTextDocument->getText());
187 uno::Reference<text::XTextCursor> const xCursor(xText->createTextCursor());
188 uno::Reference<beans::XPropertySet> const xProps(xCursor, uno::UNO_QUERY);
190 xText->insertString(xCursor, "ab", false);
191 xCursor->gotoStart(false);
192 xCursor->goRight(1, true);
193 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE, getProperty<awt::FontSlant>(xProps, "CharPosture"));
194 xProps->setPropertyValue("CharPosture", uno::Any(awt::FontSlant_ITALIC));
195 xCursor->collapseToEnd();
196 uno::Reference<text::XTextContent> const xContent(
197 xFactory->createInstance("com.sun.star.text.Footnote"), uno::UNO_QUERY);
198 xText->insertTextContent(xCursor, xContent, false);
199 xCursor->goLeft(1, true);
200 CPPUNIT_ASSERT_EQUAL(OUString("1"), xCursor->getString());
201 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_ITALIC, getProperty<awt::FontSlant>(xProps, "CharPosture"));
204 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testInsertTextContentWithPropertiesNotExpandsHints)
206 createSwDoc();
207 uno::Reference<text::XTextDocument> const xTextDocument(mxComponent, uno::UNO_QUERY);
208 uno::Reference<lang::XMultiServiceFactory> const xFactory(mxComponent, uno::UNO_QUERY);
209 uno::Reference<text::XText> const xText(xTextDocument->getText());
210 uno::Reference<text::XTextContentAppend> const xTextA(xText, uno::UNO_QUERY);
211 uno::Reference<text::XTextCursor> const xCursor(xText->createTextCursor());
212 uno::Reference<beans::XPropertySet> const xProps(xCursor, uno::UNO_QUERY);
214 xText->insertString(xCursor, "ab", false);
215 xCursor->gotoStart(false);
216 xCursor->goRight(1, true);
217 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE, getProperty<awt::FontSlant>(xProps, "CharPosture"));
218 xProps->setPropertyValue("CharPosture", uno::Any(awt::FontSlant_ITALIC));
219 xCursor->collapseToEnd();
220 uno::Reference<text::XTextContent> const xContent(
221 xFactory->createInstance("com.sun.star.text.Footnote"), uno::UNO_QUERY);
222 xTextA->insertTextContentWithProperties(xContent, uno::Sequence<beans::PropertyValue>(),
223 xCursor);
224 xCursor->goLeft(1, true);
225 CPPUNIT_ASSERT_EQUAL(OUString("1"), xCursor->getString());
226 CPPUNIT_ASSERT_EQUAL(awt::FontSlant_NONE, getProperty<awt::FontSlant>(xProps, "CharPosture"));
229 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testGraphicDescriptorURL)
231 createSwDoc();
233 // Create a graphic object, but don't insert it yet.
234 uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
235 uno::Reference<beans::XPropertySet> xTextGraphic(
236 xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
238 // Set a URL on it.
239 xTextGraphic->setPropertyValue("GraphicURL", uno::Any(createFileURL(u"test.jpg")));
240 xTextGraphic->setPropertyValue("AnchorType",
241 uno::Any(text::TextContentAnchorType_AT_CHARACTER));
243 // Insert it.
244 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
245 uno::Reference<text::XText> xBodyText = xTextDocument->getText();
246 uno::Reference<text::XTextCursor> xCursor(xBodyText->createTextCursor());
247 uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY);
248 xBodyText->insertTextContent(xCursor, xTextContent, false);
250 // This failed, the graphic object had no graphic.
251 auto xGraphic = getProperty<uno::Reference<graphic::XGraphic>>(getShape(1), "Graphic");
252 CPPUNIT_ASSERT(xGraphic.is());
255 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testGraphicDescriptorURLBitmap)
257 createSwDoc();
259 // Load a bitmap into the bitmap table.
260 uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
261 uno::Reference<container::XNameContainer> xBitmaps(
262 xFactory->createInstance("com.sun.star.drawing.BitmapTable"), uno::UNO_QUERY);
263 xBitmaps->insertByName("test", uno::Any(createFileURL(u"test.jpg")));
265 // Create a graphic.
266 uno::Reference<beans::XPropertySet> xTextGraphic(
267 xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
268 xTextGraphic->setPropertyValue("GraphicURL", xBitmaps->getByName("test"));
269 xTextGraphic->setPropertyValue("AnchorType",
270 uno::Any(text::TextContentAnchorType_AT_CHARACTER));
272 // Insert it.
273 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
274 uno::Reference<text::XText> xBodyText = xTextDocument->getText();
275 uno::Reference<text::XTextCursor> xCursor(xBodyText->createTextCursor());
276 uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY);
277 xBodyText->insertTextContent(xCursor, xTextContent, false);
279 // This failed: setting GraphicURL to the result of getByName() did not
280 // work anymore.
281 auto xGraphic = getProperty<uno::Reference<graphic::XGraphic>>(getShape(1), "Graphic");
282 CPPUNIT_ASSERT(xGraphic.is());
285 static bool ensureAutoTextExistsByTitle(const uno::Reference<text::XAutoTextGroup>& autoTextGroup,
286 std::u16string_view autoTextName)
288 const uno::Sequence<OUString> aTitles(autoTextGroup->getTitles());
289 for (const auto& rTitle : aTitles)
291 if (rTitle == autoTextName)
292 return true;
294 return false;
297 static bool ensureAutoTextExistsByName(const uno::Reference<text::XAutoTextGroup>& autoTextGroup,
298 std::u16string_view autoTextName)
300 const uno::Sequence<OUString> aTitles(autoTextGroup->getElementNames());
301 for (const auto& rTitle : aTitles)
303 if (rTitle == autoTextName)
304 return true;
306 return false;
309 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testXAutoTextGroup)
311 createSwDoc("xautotextgroup.odt");
312 uno::Reference<text::XAutoTextContainer> xAutoTextContainer
313 = text::AutoTextContainer::create(comphelper::getProcessComponentContext());
315 uno::Reference<text::XTextRange> xTextRange = getRun(getParagraph(1), 1);
317 static const OUStringLiteral sGroupName = u"TestGroup*1";
318 static const OUStringLiteral sTextName = u"TEST";
319 static const OUStringLiteral sTextNameNew = u"TESTRENAMED";
320 static const OUStringLiteral sTextTitle = u"Test Auto Text";
321 static const OUStringLiteral sTextTitleNew = u"Test Auto Text Renamed";
323 // Create new temporary group
324 uno::Reference<text::XAutoTextGroup> xAutoTextGroup
325 = xAutoTextContainer->insertNewByName(sGroupName);
326 CPPUNIT_ASSERT_MESSAGE("AutoTextGroup was not found!", xAutoTextGroup.is());
328 // Insert new element and ensure it exists
329 uno::Reference<text::XAutoTextEntry> xAutoTextEntry
330 = xAutoTextGroup->insertNewByName(sTextName, sTextTitle, xTextRange);
331 CPPUNIT_ASSERT_MESSAGE("AutoText was not inserted!", xAutoTextEntry.is());
332 CPPUNIT_ASSERT_MESSAGE("Can't find newly created AutoText by title!",
333 ensureAutoTextExistsByTitle(xAutoTextGroup, sTextTitle));
334 CPPUNIT_ASSERT_MESSAGE("Can't find newly created AutoText by name!",
335 ensureAutoTextExistsByName(xAutoTextGroup, sTextName));
337 // Insert once again the same should throw an exception
338 CPPUNIT_ASSERT_THROW_MESSAGE("We expect an exception on insertion of same AutoText",
339 xAutoTextGroup->insertNewByName(sTextName, sTextTitle, xTextRange),
340 container::ElementExistException);
342 // Rename it & ensure everything is ok
343 xAutoTextGroup->renameByName(sTextName, sTextNameNew, sTextTitleNew);
344 CPPUNIT_ASSERT_MESSAGE("Can't find renamed AutoText by title!",
345 ensureAutoTextExistsByTitle(xAutoTextGroup, sTextTitleNew));
346 CPPUNIT_ASSERT_MESSAGE("Can't find renamed AutoText by name!",
347 ensureAutoTextExistsByName(xAutoTextGroup, sTextNameNew));
348 // Not found by old names
349 CPPUNIT_ASSERT_MESSAGE("Found AutoText by old title!",
350 !ensureAutoTextExistsByTitle(xAutoTextGroup, sTextTitle));
351 CPPUNIT_ASSERT_MESSAGE("Found AutoText by old name!",
352 !ensureAutoTextExistsByName(xAutoTextGroup, sTextName));
354 // Rename not existing should throw an exception
355 CPPUNIT_ASSERT_THROW_MESSAGE(
356 "We expect an exception on renaming not-existing AutoText",
357 xAutoTextGroup->renameByName(sTextName, sTextNameNew, sTextTitleNew),
358 container::ElementExistException);
360 // Remove it and ensure it does not exist
361 xAutoTextGroup->removeByName(sTextNameNew);
362 CPPUNIT_ASSERT_MESSAGE("AutoText was not removed!",
363 !ensureAutoTextExistsByTitle(xAutoTextGroup, sTextTitleNew));
364 CPPUNIT_ASSERT_MESSAGE("AutoText was not removed!",
365 !ensureAutoTextExistsByName(xAutoTextGroup, sTextNameNew));
367 // Remove non-existing element should throw an exception
368 CPPUNIT_ASSERT_THROW_MESSAGE("We expect an exception on removing not-existing AutoText",
369 xAutoTextGroup->removeByName(sTextName),
370 container::NoSuchElementException);
372 // Remove our temporary group
373 xAutoTextContainer->removeByName(sGroupName);
376 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSectionAnchorCopyTableAtStart)
378 // this contains a section that starts with a table
379 createSwDoc("tdf134250.fodt");
381 uno::Reference<text::XTextTablesSupplier> const xTextTablesSupplier(mxComponent,
382 uno::UNO_QUERY);
383 uno::Reference<container::XIndexAccess> const xTables(xTextTablesSupplier->getTextTables(),
384 uno::UNO_QUERY);
385 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
387 uno::Reference<text::XTextSectionsSupplier> const xTextSectionsSupplier(mxComponent,
388 uno::UNO_QUERY);
389 uno::Reference<container::XIndexAccess> const xSections(
390 xTextSectionsSupplier->getTextSections(), uno::UNO_QUERY);
392 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
394 uno::Reference<text::XTextContent> const xSection(xSections->getByIndex(0), uno::UNO_QUERY);
395 uno::Reference<text::XTextRange> const xAnchor(xSection->getAnchor());
396 CPPUNIT_ASSERT_EQUAL(OUString("foo" SAL_NEWLINE_STRING "bar"), xAnchor->getString());
398 // copy the content of the section to a clipboard document
399 uno::Reference<datatransfer::XTransferableSupplier> const xTS(
400 uno::Reference<frame::XModel>(mxComponent, uno::UNO_QUERY_THROW)->getCurrentController(),
401 uno::UNO_QUERY);
402 uno::Reference<datatransfer::XTransferableTextSupplier> const xTTS(xTS, uno::UNO_QUERY);
403 uno::Reference<datatransfer::XTransferable> const xTransferable(
404 xTTS->getTransferableForTextRange(xAnchor));
406 // check this doesn't throw
407 CPPUNIT_ASSERT(xAnchor->getText().is());
408 CPPUNIT_ASSERT(xAnchor->getStart().is());
409 CPPUNIT_ASSERT(xAnchor->getEnd().is());
411 // replace section content
412 xAnchor->setString("quux");
414 // table in section was deleted, but not section itself
415 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xTables->getCount());
416 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
417 CPPUNIT_ASSERT_EQUAL(OUString("\""
418 "quux" /*SAL_NEWLINE_STRING*/ "\""),
419 OUString("\"" + xAnchor->getString() + "\""));
421 // now paste it
422 uno::Reference<text::XTextViewCursorSupplier> const xTVCS(xTS, uno::UNO_QUERY);
423 uno::Reference<text::XTextViewCursor> const xCursor(xTVCS->getViewCursor());
424 xCursor->gotoEnd(false);
425 xTS->insertTransferable(xTransferable);
427 // table in section was pasted, but not section itself
428 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
429 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
430 xCursor->gotoStart(true);
431 CPPUNIT_ASSERT_EQUAL(OUString("quux" SAL_NEWLINE_STRING "foo" SAL_NEWLINE_STRING "bar"),
432 xCursor->getString());
435 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSectionAnchorCopyTableAtEnd)
437 // this contains a section that ends with a table (plus another section)
438 createSwDoc("tdf134252.fodt");
440 uno::Reference<text::XTextTablesSupplier> const xTextTablesSupplier(mxComponent,
441 uno::UNO_QUERY);
442 uno::Reference<container::XIndexAccess> const xTables(xTextTablesSupplier->getTextTables(),
443 uno::UNO_QUERY);
444 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
446 uno::Reference<text::XTextSectionsSupplier> const xTextSectionsSupplier(mxComponent,
447 uno::UNO_QUERY);
448 uno::Reference<container::XIndexAccess> const xSections(
449 xTextSectionsSupplier->getTextSections(), uno::UNO_QUERY);
451 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount());
453 uno::Reference<text::XTextContent> const xSection(xSections->getByIndex(0), uno::UNO_QUERY);
454 uno::Reference<text::XTextRange> const xAnchor(xSection->getAnchor());
455 CPPUNIT_ASSERT_EQUAL(OUString("bar" SAL_NEWLINE_STRING "baz" SAL_NEWLINE_STRING),
456 xAnchor->getString());
458 // copy the content of the section to a clipboard document
459 uno::Reference<datatransfer::XTransferableSupplier> const xTS(
460 uno::Reference<frame::XModel>(mxComponent, uno::UNO_QUERY_THROW)->getCurrentController(),
461 uno::UNO_QUERY);
462 uno::Reference<datatransfer::XTransferableTextSupplier> const xTTS(xTS, uno::UNO_QUERY);
463 uno::Reference<datatransfer::XTransferable> const xTransferable(
464 xTTS->getTransferableForTextRange(xAnchor));
466 // check this doesn't throw
467 CPPUNIT_ASSERT(xAnchor->getText().is());
468 CPPUNIT_ASSERT(xAnchor->getStart().is());
469 CPPUNIT_ASSERT(xAnchor->getEnd().is());
471 // replace section content
472 xAnchor->setString("quux");
474 // table in section was deleted, but not section itself
475 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xTables->getCount());
476 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount());
477 CPPUNIT_ASSERT_EQUAL(OUString("\""
478 "quux" /*SAL_NEWLINE_STRING*/ "\""),
479 OUString("\"" + xAnchor->getString() + "\""));
481 // now paste it
482 uno::Reference<text::XTextViewCursorSupplier> const xTVCS(xTS, uno::UNO_QUERY);
483 uno::Reference<text::XTextViewCursor> const xCursor(xTVCS->getViewCursor());
484 xCursor->gotoEnd(false);
485 xTS->insertTransferable(xTransferable);
487 // table in section was pasted, but not section itself
488 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
489 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount());
490 // note: this selects the 2nd section because it calls StartOfSection()
491 // not SttEndDoc() like it should?
492 xCursor->gotoStart(true);
493 CPPUNIT_ASSERT_EQUAL(OUString(/*"quux" SAL_NEWLINE_STRING */
494 "foobar" SAL_NEWLINE_STRING "baz" SAL_NEWLINE_STRING),
495 xCursor->getString());
498 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSectionAnchorCopyTable)
500 // this contains a section that ends with a table (plus another section)
501 createSwDoc("tdf134252_onlytable_protected.fodt");
503 uno::Reference<text::XTextTablesSupplier> const xTextTablesSupplier(mxComponent,
504 uno::UNO_QUERY);
505 uno::Reference<container::XIndexAccess> const xTables(xTextTablesSupplier->getTextTables(),
506 uno::UNO_QUERY);
507 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
509 uno::Reference<text::XTextSectionsSupplier> const xTextSectionsSupplier(mxComponent,
510 uno::UNO_QUERY);
511 uno::Reference<container::XIndexAccess> const xSections(
512 xTextSectionsSupplier->getTextSections(), uno::UNO_QUERY);
514 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
516 uno::Reference<text::XTextContent> const xSection(xSections->getByIndex(0), uno::UNO_QUERY);
517 uno::Reference<text::XTextRange> const xAnchor(xSection->getAnchor());
518 CPPUNIT_ASSERT_EQUAL(OUString("baz" SAL_NEWLINE_STRING), xAnchor->getString());
520 // copy the content of the section to a clipboard document
521 uno::Reference<datatransfer::XTransferableSupplier> const xTS(
522 uno::Reference<frame::XModel>(mxComponent, uno::UNO_QUERY_THROW)->getCurrentController(),
523 uno::UNO_QUERY);
524 uno::Reference<datatransfer::XTransferableTextSupplier> const xTTS(xTS, uno::UNO_QUERY);
525 uno::Reference<datatransfer::XTransferable> const xTransferable(
526 xTTS->getTransferableForTextRange(xAnchor));
528 // check this doesn't throw
529 CPPUNIT_ASSERT(xAnchor->getText().is());
530 CPPUNIT_ASSERT(xAnchor->getStart().is());
531 CPPUNIT_ASSERT(xAnchor->getEnd().is());
533 // replace section content
534 xAnchor->setString("quux");
536 // table in section was deleted, but not section itself
537 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xTables->getCount());
538 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
539 CPPUNIT_ASSERT_EQUAL(OUString("\""
540 "quux" /*SAL_NEWLINE_STRING*/ "\""),
541 OUString("\"" + xAnchor->getString() + "\""));
543 // now paste it
544 uno::Reference<text::XTextViewCursorSupplier> const xTVCS(xTS, uno::UNO_QUERY);
545 uno::Reference<text::XTextViewCursor> const xCursor(xTVCS->getViewCursor());
546 xCursor->gotoEnd(false);
547 xTS->insertTransferable(xTransferable);
549 // table in section was pasted, but not section itself
550 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
551 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
552 xCursor->gotoStart(true);
553 CPPUNIT_ASSERT_EQUAL(
554 OUString("quux" SAL_NEWLINE_STRING "foo" SAL_NEWLINE_STRING "baz" SAL_NEWLINE_STRING),
555 xCursor->getString());
558 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTextRangeInTable)
560 createSwDoc("bookmarkintable.fodt");
562 uno::Reference<text::XBookmarksSupplier> const xBS(mxComponent, uno::UNO_QUERY);
563 uno::Reference<container::XNameAccess> const xMarks(xBS->getBookmarks());
564 uno::Reference<text::XTextContent> const xMark(xMarks->getByName("Bookmark 1"), uno::UNO_QUERY);
565 uno::Reference<container::XEnumerationAccess> const xAnchor(xMark->getAnchor(), uno::UNO_QUERY);
566 uno::Reference<container::XEnumeration> const xEnum(xAnchor->createEnumeration());
567 uno::Reference<lang::XServiceInfo> const xPara(xEnum->nextElement(), uno::UNO_QUERY);
568 // not the top-level table!
569 CPPUNIT_ASSERT(!xPara->supportsService("com.sun.star.text.TextTable"));
570 CPPUNIT_ASSERT(!xEnum->hasMoreElements());
571 uno::Reference<container::XEnumerationAccess> const xParaEA(xPara, uno::UNO_QUERY);
572 uno::Reference<container::XEnumeration> const xPortions(xParaEA->createEnumeration());
573 uno::Reference<beans::XPropertySet> const xP1(xPortions->nextElement(), uno::UNO_QUERY);
574 CPPUNIT_ASSERT_EQUAL(OUString("Bookmark"), getProperty<OUString>(xP1, "TextPortionType"));
575 uno::Reference<beans::XPropertySet> const xP2(xPortions->nextElement(), uno::UNO_QUERY);
576 CPPUNIT_ASSERT_EQUAL(OUString("Text"), getProperty<OUString>(xP2, "TextPortionType"));
577 uno::Reference<text::XTextRange> const xP2R(xP2, uno::UNO_QUERY);
578 CPPUNIT_ASSERT_EQUAL(OUString("foo"), xP2R->getString());
579 uno::Reference<beans::XPropertySet> const xP3(xPortions->nextElement(), uno::UNO_QUERY);
580 CPPUNIT_ASSERT_EQUAL(OUString("Bookmark"), getProperty<OUString>(xP3, "TextPortionType"));
581 CPPUNIT_ASSERT(!xPortions->hasMoreElements());
584 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testXURI)
586 uno::Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
588 // createKnown()
589 uno::Reference<rdf::XURI> xURIcreateKnown(
590 rdf::URI::createKnown(xContext, rdf::URIs::ODF_PREFIX), uno::UNO_SET_THROW);
591 CPPUNIT_ASSERT(xURIcreateKnown.is());
592 CPPUNIT_ASSERT_EQUAL(OUString("http://docs.oasis-open.org/ns/office/1.2/meta/odf#"),
593 xURIcreateKnown->getNamespace());
594 CPPUNIT_ASSERT_EQUAL(OUString("prefix"), xURIcreateKnown->getLocalName());
595 CPPUNIT_ASSERT_EQUAL(OUString("http://docs.oasis-open.org/ns/office/1.2/meta/odf#prefix"),
596 xURIcreateKnown->getStringValue());
598 // createKnown() with invalid constant
599 CPPUNIT_ASSERT_THROW_MESSAGE("We expect an exception on invalid constant",
600 rdf::URI::createKnown(xContext, 12345),
601 lang::IllegalArgumentException);
603 // create()
604 uno::Reference<rdf::XURI> xURIcreate(
605 rdf::URI::create(xContext, "http://example.com/url#somedata"), uno::UNO_SET_THROW);
606 CPPUNIT_ASSERT_EQUAL(OUString("http://example.com/url#"), xURIcreate->getNamespace());
607 CPPUNIT_ASSERT_EQUAL(OUString("somedata"), xURIcreate->getLocalName());
608 CPPUNIT_ASSERT_EQUAL(OUString("http://example.com/url#somedata"), xURIcreate->getStringValue());
610 // create() without local name split with "/"
611 uno::Reference<rdf::XURI> xURIcreate2(rdf::URI::create(xContext, "http://example.com/url"),
612 uno::UNO_SET_THROW);
613 CPPUNIT_ASSERT_EQUAL(OUString("http://example.com/"), xURIcreate2->getNamespace());
614 CPPUNIT_ASSERT_EQUAL(OUString("url"), xURIcreate2->getLocalName());
615 CPPUNIT_ASSERT_EQUAL(OUString("http://example.com/url"), xURIcreate2->getStringValue());
617 // create() without prefix
618 uno::Reference<rdf::XURI> xURIcreate3(rdf::URI::create(xContext, "#somedata"),
619 uno::UNO_SET_THROW);
620 CPPUNIT_ASSERT_EQUAL(OUString("#"), xURIcreate3->getNamespace());
621 CPPUNIT_ASSERT_EQUAL(OUString("somedata"), xURIcreate3->getLocalName());
622 CPPUNIT_ASSERT_EQUAL(OUString("#somedata"), xURIcreate3->getStringValue());
624 // create() with invalid URI
625 CPPUNIT_ASSERT_THROW_MESSAGE("We expect an exception on invalid URI",
626 rdf::URI::create(xContext, "some junk and not URI"),
627 lang::IllegalArgumentException);
629 // createNS()
630 uno::Reference<rdf::XURI> xURIcreateNS(
631 rdf::URI::createNS(xContext, "http://example.com/url#", "somedata"), uno::UNO_SET_THROW);
632 CPPUNIT_ASSERT_EQUAL(OUString("http://example.com/url#"), xURIcreateNS->getNamespace());
633 CPPUNIT_ASSERT_EQUAL(OUString("somedata"), xURIcreateNS->getLocalName());
634 CPPUNIT_ASSERT_EQUAL(OUString("http://example.com/url#somedata"),
635 xURIcreateNS->getStringValue());
637 // TODO: What's going on here? Is such usecase valid?
638 uno::Reference<rdf::XURI> xURIcreateNS2(
639 rdf::URI::createNS(xContext, "http://example.com/url", "somedata"), uno::UNO_SET_THROW);
640 CPPUNIT_ASSERT_EQUAL(OUString("http://example.com/"), xURIcreateNS2->getNamespace());
641 CPPUNIT_ASSERT_EQUAL(OUString("urlsomedata"), xURIcreateNS2->getLocalName());
642 CPPUNIT_ASSERT_EQUAL(OUString("http://example.com/urlsomedata"),
643 xURIcreateNS2->getStringValue());
645 // createNS() some invalid cases
646 CPPUNIT_ASSERT_THROW_MESSAGE("We expect an exception on invalid URI",
647 rdf::URI::createNS(xContext, "bla", "bla"),
648 lang::IllegalArgumentException);
650 CPPUNIT_ASSERT_THROW_MESSAGE("We expect an exception on invalid URI",
651 rdf::URI::createNS(xContext, OUString(), OUString()),
652 lang::IllegalArgumentException);
655 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSetPagePrintSettings)
657 // Create an empty new document with a single char
658 createSwDoc();
660 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
661 uno::Reference<text::XSimpleText> xBodyText = xTextDocument->getText();
662 xBodyText->insertString(xBodyText->getStart(), "x", false);
664 uno::Reference<text::XPagePrintable> xPagePrintable(mxComponent, uno::UNO_QUERY);
666 // set some stuff, try to get it back
667 uno::Sequence<beans::PropertyValue> aProps{
668 comphelper::makePropertyValue("PageColumns", sal_Int16(2)),
669 comphelper::makePropertyValue("IsLandscape", true)
672 xPagePrintable->setPagePrintSettings(aProps);
673 const comphelper::SequenceAsHashMap aMap(xPagePrintable->getPagePrintSettings());
675 CPPUNIT_ASSERT_EQUAL(sal_Int16(2), aMap.getValue("PageColumns").get<short>());
676 CPPUNIT_ASSERT_EQUAL(true, aMap.getValue("IsLandscape").get<bool>());
679 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testDeleteFlyAtCharAtStart)
681 createSwDoc();
682 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
683 CPPUNIT_ASSERT(pTextDoc);
684 SwWrtShell* const pWrtShell(pTextDoc->GetDocShell()->GetWrtShell());
685 SwDoc* const pDoc(pWrtShell->GetDoc());
687 // insert some text
688 IDocumentContentOperations& rIDCO(pDoc->getIDocumentContentOperations());
689 rIDCO.InsertString(*pWrtShell->GetCursor(), "foo bar baz");
691 // insert fly anchored at start of body text
692 pWrtShell->ClearMark();
693 pWrtShell->SttEndDoc(true);
694 SfxItemSet frameSet(pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1>);
695 SfxItemSet grfSet(pDoc->GetAttrPool(), svl::Items<RES_GRFATR_BEGIN, RES_GRFATR_END - 1>);
696 SwFormatAnchor anchor(RndStdIds::FLY_AT_CHAR);
697 frameSet.Put(anchor);
698 Graphic grf;
699 CPPUNIT_ASSERT(rIDCO.InsertGraphic(*pWrtShell->GetCursor(), OUString(), OUString(), &grf,
700 &frameSet, &grfSet, nullptr));
702 // check fly
703 CPPUNIT_ASSERT_EQUAL(1, getShapes());
704 uno::Reference<text::XTextContent> const xShape(getShape(1), uno::UNO_QUERY);
705 // anchored at start of body text?
706 uno::Reference<text::XText> const xText(pTextDoc->getText());
707 uno::Reference<text::XTextRangeCompare> const xTextRC(xText, uno::UNO_QUERY);
708 CPPUNIT_ASSERT_EQUAL(sal_Int16(0),
709 xTextRC->compareRegionStarts(xText->getStart(), xShape->getAnchor()));
711 // delete 1st character
712 uno::Reference<text::XTextCursor> const xCursor(xText->createTextCursor());
713 xCursor->goRight(1, true);
714 xCursor->setString("");
716 // there is exactly one fly
717 CPPUNIT_ASSERT_EQUAL(1, getShapes());
719 // select entire body text
720 xCursor->gotoStart(true);
721 xCursor->gotoEnd(true);
722 xCursor->setString("");
724 // there is no fly
725 CPPUNIT_ASSERT_EQUAL(0, getShapes());
728 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSelectionInTableEnum)
730 createSwDoc("selection-in-table-enum.odt");
731 // Select the A1 cell's text.
732 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
733 CPPUNIT_ASSERT(pTextDoc);
734 SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
735 CPPUNIT_ASSERT(pWrtShell);
736 pWrtShell->Down(/*bSelect=*/false);
737 pWrtShell->EndPara(/*bSelect=*/true);
738 CPPUNIT_ASSERT_EQUAL(OUString("A1"),
739 pWrtShell->GetCursor()->GetPointNode().GetTextNode()->GetText());
741 // Access the selection.
742 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
743 CPPUNIT_ASSERT(xModel.is());
744 uno::Reference<container::XIndexAccess> xSelections(xModel->getCurrentSelection(),
745 uno::UNO_QUERY);
746 CPPUNIT_ASSERT(xSelections.is());
747 uno::Reference<text::XTextRange> xSelection(xSelections->getByIndex(0), uno::UNO_QUERY);
748 CPPUNIT_ASSERT(xSelection.is());
750 // Enumerate paragraphs in the selection.
751 uno::Reference<container::XEnumerationAccess> xCursor(
752 xSelection->getText()->createTextCursorByRange(xSelection), uno::UNO_QUERY);
753 CPPUNIT_ASSERT(xCursor.is());
754 uno::Reference<container::XEnumeration> xEnum = xCursor->createEnumeration();
755 xEnum->nextElement();
756 // Without the accompanying fix in place, this test would have failed: i.e.
757 // the enumeration contained a second paragraph, even if the cell has only
758 // one paragraph.
759 CPPUNIT_ASSERT(!xEnum->hasMoreElements());
762 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSelectionInTableEnumEnd)
764 createSwDoc("selection-in-table-enum.odt");
765 // Select from "Before" till the table end.
766 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
767 CPPUNIT_ASSERT(pTextDoc);
768 SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
769 CPPUNIT_ASSERT(pWrtShell);
770 pWrtShell->Down(/*bSelect=*/true);
772 // Access the selection.
773 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
774 CPPUNIT_ASSERT(xModel.is());
775 uno::Reference<container::XIndexAccess> xSelections(xModel->getCurrentSelection(),
776 uno::UNO_QUERY);
777 CPPUNIT_ASSERT(xSelections.is());
778 uno::Reference<text::XTextRange> xSelection(xSelections->getByIndex(0), uno::UNO_QUERY);
779 CPPUNIT_ASSERT(xSelection.is());
780 CPPUNIT_ASSERT_EQUAL(OUString("Before" SAL_NEWLINE_STRING "A1" SAL_NEWLINE_STRING
781 "B1" SAL_NEWLINE_STRING "C2" SAL_NEWLINE_STRING
782 "A2" SAL_NEWLINE_STRING "B2" SAL_NEWLINE_STRING
783 "C2" SAL_NEWLINE_STRING),
784 xSelection->getString());
786 // Enumerate paragraphs in the selection.
787 uno::Reference<container::XEnumerationAccess> xCursor(
788 xSelection->getText()->createTextCursorByRange(xSelection), uno::UNO_QUERY);
789 CPPUNIT_ASSERT(xCursor.is());
790 uno::Reference<container::XEnumeration> xEnum = xCursor->createEnumeration();
791 // Before.
792 xEnum->nextElement();
793 // Table.
794 xEnum->nextElement();
795 // Without the accompanying fix in place, this test would have failed: i.e.
796 // the enumeration contained the paragraph after the table, but no part of
797 // that paragraph was part of the selection.
798 CPPUNIT_ASSERT(!xEnum->hasMoreElements());
801 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testRenderablePagePosition)
803 createSwDoc("renderable-page-position.odt");
804 // Make sure that the document has 2 pages.
805 uno::Reference<view::XRenderable> xRenderable(mxComponent, uno::UNO_QUERY);
806 CPPUNIT_ASSERT(mxComponent.is());
808 uno::Any aSelection(mxComponent);
810 uno::Reference<awt::XToolkit> xToolkit = VCLUnoHelper::CreateToolkit();
811 uno::Reference<awt::XDevice> xDevice(xToolkit->createScreenCompatibleDevice(32, 32));
813 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
814 uno::Reference<frame::XController> xController = xModel->getCurrentController();
816 beans::PropertyValues aRenderOptions = {
817 comphelper::makePropertyValue("IsPrinter", true),
818 comphelper::makePropertyValue("RenderDevice", xDevice),
819 comphelper::makePropertyValue("View", xController),
820 comphelper::makePropertyValue("RenderToGraphic", true),
823 sal_Int32 nPages = xRenderable->getRendererCount(aSelection, aRenderOptions);
824 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), nPages);
826 // Make sure that the first page has some offset.
827 comphelper::SequenceAsHashMap aRenderer1(
828 xRenderable->getRenderer(0, aSelection, aRenderOptions));
829 // Without the accompanying fix in place, this test would have failed: i.e.
830 // there was no PagePos key in this map.
831 awt::Point aPosition1 = aRenderer1["PagePos"].get<awt::Point>();
832 CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), aPosition1.X);
833 CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), aPosition1.Y);
835 // Make sure that the second page is below the first one.
836 comphelper::SequenceAsHashMap aRenderer2(
837 xRenderable->getRenderer(1, aSelection, aRenderOptions));
838 awt::Point aPosition2 = aRenderer2["PagePos"].get<awt::Point>();
839 CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), aPosition2.X);
840 CPPUNIT_ASSERT_GREATER(aPosition1.Y, aPosition2.Y);
843 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testPasteListener)
845 createSwDoc();
847 // Insert initial string.
848 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
849 uno::Reference<text::XSimpleText> xBodyText = xTextDocument->getText();
850 xBodyText->insertString(xBodyText->getStart(), "ABCDEF", false);
852 // Add paste listener.
853 uno::Reference<text::XPasteBroadcaster> xBroadcaster(mxComponent, uno::UNO_QUERY);
854 uno::Reference<text::XPasteListener> xListener(new PasteListener);
855 auto pListener = static_cast<PasteListener*>(xListener.get());
856 xBroadcaster->addPasteEventListener(xListener);
858 // Cut "DE" and then paste it.
859 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
860 CPPUNIT_ASSERT(pTextDoc);
861 SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
862 CPPUNIT_ASSERT(pWrtShell);
863 pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 3, /*bBasicCall=*/false);
864 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 2, /*bBasicCall=*/false);
865 rtl::Reference<SwTransferable> pTransfer = new SwTransferable(*pWrtShell);
866 pTransfer->Cut();
867 TransferableDataHelper aHelper(pTransfer);
868 SwTransferable::Paste(*pWrtShell, aHelper);
869 // Without working listener registration in place, this test would have
870 // failed with 'Expected: DE; Actual:', i.e. the paste listener was not
871 // invoked.
872 CPPUNIT_ASSERT_EQUAL(OUString("DE"), pListener->GetString());
874 // Make sure that paste did not overwrite anything.
875 CPPUNIT_ASSERT_EQUAL(OUString("ABCDEF"), xBodyText->getString());
877 // Paste again, this time overwriting "BC".
878 pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 4, /*bBasicCall=*/false);
879 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 2, /*bBasicCall=*/false);
880 pListener->GetString().clear();
881 SwTransferable::Paste(*pWrtShell, aHelper);
882 CPPUNIT_ASSERT_EQUAL(OUString("DE"), pListener->GetString());
884 // Make sure that paste overwrote "BC".
885 CPPUNIT_ASSERT_EQUAL(OUString("ADEDEF"), xBodyText->getString());
887 // Test image paste.
888 SwView& rView = pWrtShell->GetView();
889 rView.InsertGraphic(createFileURL(u"test.jpg"), OUString(), /*bAsLink=*/false,
890 &GraphicFilter::GetGraphicFilter());
892 // Test that the pasted image is anchored as-char.
893 SwFlyFrame* pFly = pWrtShell->GetSelectedFlyFrame();
894 CPPUNIT_ASSERT(pFly);
895 SwFrameFormat* pFlyFormat = pFly->GetFormat();
896 CPPUNIT_ASSERT(pFlyFormat);
897 RndStdIds eFlyAnchor = pFlyFormat->GetAnchor().GetAnchorId();
898 // Without the working image listener in place, this test would have
899 // failed, eFlyAnchor was FLY_AT_PARA.
900 CPPUNIT_ASSERT_EQUAL(RndStdIds::FLY_AT_CHAR, eFlyAnchor);
902 pTransfer->Cut();
903 pListener->GetString().clear();
904 SwTransferable::Paste(*pWrtShell, aHelper);
905 // Without the working image listener in place, this test would have
906 // failed, the listener was not invoked in case of a graphic paste.
907 CPPUNIT_ASSERT(pListener->GetTextGraphicObject().is());
908 CPPUNIT_ASSERT(pListener->GetString().isEmpty());
910 // Deregister paste listener, make sure it's not invoked.
911 xBroadcaster->removePasteEventListener(xListener);
912 pListener->GetString().clear();
913 SwTransferable::Paste(*pWrtShell, aHelper);
914 CPPUNIT_ASSERT(pListener->GetString().isEmpty());
917 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testImageCommentAtChar)
919 // Load a document with an at-char image in it (and a comment on the image).
920 createSwDoc("image-comment-at-char.odt");
921 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
922 CPPUNIT_ASSERT(pTextDoc);
923 SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
925 // Verify that we have an annotation mark (comment with a text range) in the document.
926 // Without the accompanying fix in place, this test would have failed, as comments lost their
927 // ranges on load when their range only covered the placeholder character of the comment (which
928 // is also the anchor position of the image).
929 IDocumentMarkAccess* pMarks = pDoc->getIDocumentMarkAccess();
930 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pMarks->getAnnotationMarksCount());
932 uno::Reference<text::XTextRange> xPara = getParagraph(1);
933 CPPUNIT_ASSERT_EQUAL(OUString("Text"),
934 getProperty<OUString>(getRun(xPara, 1), "TextPortionType"));
935 // Without the accompanying fix in place, this test would have failed with 'Expected:
936 // Annotation; Actual: Frame', i.e. the comment-start portion was after the commented image.
937 CPPUNIT_ASSERT_EQUAL(OUString("Annotation"),
938 getProperty<OUString>(getRun(xPara, 2), "TextPortionType"));
939 CPPUNIT_ASSERT_EQUAL(OUString("Frame"),
940 getProperty<OUString>(getRun(xPara, 3), "TextPortionType"));
941 CPPUNIT_ASSERT_EQUAL(OUString("AnnotationEnd"),
942 getProperty<OUString>(getRun(xPara, 4), "TextPortionType"));
943 CPPUNIT_ASSERT_EQUAL(OUString("Text"),
944 getProperty<OUString>(getRun(xPara, 5), "TextPortionType"));
946 // Without the accompanying fix in place, this test would have failed with 'Expected:
947 // 5892; Actual: 1738', i.e. the anchor pos was between the "aaa" and "bbb" portions, not at the
948 // center of the page (horizontally) where the image is. On macOS, though, with the fix in
949 // place the actual value consistently is even greater with 6283 now instead of 5892, for
950 // whatever reason.
951 SwView* pView = pDoc->GetDocShell()->GetView();
952 SwPostItMgr* pPostItMgr = pView->GetPostItMgr();
953 for (const auto& pItem : *pPostItMgr)
955 const SwRect& rAnchor = pItem->mpPostIt->GetAnchorRect();
956 CPPUNIT_ASSERT_GREATEREQUAL(static_cast<tools::Long>(5892), rAnchor.Left());
960 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testChapterNumberingCharStyle)
962 createSwDoc();
964 uno::Reference<lang::XMultiServiceFactory> xDoc(mxComponent, uno::UNO_QUERY);
965 uno::Reference<beans::XPropertySet> xStyle(
966 xDoc->createInstance("com.sun.star.style.CharacterStyle"), uno::UNO_QUERY);
967 uno::Reference<container::XNamed> xStyleN(xStyle, uno::UNO_QUERY);
968 xStyle->setPropertyValue("CharColor", uno::Any(sal_Int32(0x00FF0000)));
969 uno::Reference<style::XStyleFamiliesSupplier> xSFS(mxComponent, uno::UNO_QUERY);
970 uno::Reference<container::XNameContainer> xStyles(
971 xSFS->getStyleFamilies()->getByName("CharacterStyles"), uno::UNO_QUERY);
972 xStyles->insertByName("red", uno::Any(xStyle));
974 uno::Reference<text::XChapterNumberingSupplier> xCNS(mxComponent, uno::UNO_QUERY);
975 uno::Reference<container::XIndexReplace> xOutline(xCNS->getChapterNumberingRules());
977 comphelper::SequenceAsHashMap hashMap(xOutline->getByIndex(0));
978 hashMap["CharStyleName"] <<= OUString("red");
979 uno::Sequence<beans::PropertyValue> props;
980 hashMap >> props;
981 xOutline->replaceByIndex(0, uno::Any(props));
983 // now rename the style
984 xStyleN->setName("reddishred");
986 comphelper::SequenceAsHashMap hashMap(xOutline->getByIndex(0));
988 // tdf#137810 this failed, was old value "red"
989 CPPUNIT_ASSERT_EQUAL(OUString("reddishred"), hashMap["CharStyleName"].get<OUString>());
993 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testViewCursorPageStyle)
995 // Load a document with 2 pages, but a single paragraph.
996 createSwDoc("view-cursor-page-style.fodt");
997 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
998 CPPUNIT_ASSERT(xModel.is());
999 uno::Reference<text::XTextViewCursorSupplier> xController(xModel->getCurrentController(),
1000 uno::UNO_QUERY);
1001 CPPUNIT_ASSERT(xController.is());
1002 uno::Reference<text::XPageCursor> xViewCursor(xController->getViewCursor(), uno::UNO_QUERY);
1003 CPPUNIT_ASSERT(xViewCursor.is());
1005 // Go to the first page, which has an explicit page style.
1006 xViewCursor->jumpToPage(1);
1007 OUString aActualPageStyleName = getProperty<OUString>(xViewCursor, "PageStyleName");
1008 CPPUNIT_ASSERT_EQUAL(OUString("First Page"), aActualPageStyleName);
1010 // Go to the second page, which is still the first paragraph, but the page style is different,
1011 // as the explicit 'First Page' page style has a next style defined (Standard).
1012 xViewCursor->jumpToPage(2);
1013 aActualPageStyleName = getProperty<OUString>(xViewCursor, "PageStyleName");
1014 // Without the accompanying fix in place, this test would have failed with:
1015 // - Expected: Standard
1016 // - Actual : First Page
1017 // i.e. the cursor position was determined only based on the node index, ignoring the content
1018 // index.
1019 CPPUNIT_ASSERT_EQUAL(OUString("Standard"), aActualPageStyleName);
1022 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testXTextCursor_setPropertyValues)
1024 // Create a new document, type a character, pass a set of property/value pairs consisting of one
1025 // unknown property and CharStyleName, assert that it threw UnknownPropertyException (actually
1026 // wrapped into WrappedTargetException), and assert the style was set, not discarded.
1027 createSwDoc();
1029 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
1030 uno::Reference<text::XSimpleText> xBodyText = xTextDocument->getText();
1031 xBodyText->insertString(xBodyText->getStart(), "x", false);
1033 uno::Reference<text::XTextCursor> xCursor(xBodyText->createTextCursor());
1034 xCursor->goLeft(1, true);
1036 uno::Reference<beans::XMultiPropertySet> xCursorProps(xCursor, uno::UNO_QUERY);
1037 uno::Sequence<OUString> aPropNames = { "OneUnknownProperty", "CharStyleName" };
1038 uno::Sequence<uno::Any> aPropValues = { uno::Any(), uno::Any(OUString("Emphasis")) };
1039 CPPUNIT_ASSERT_THROW(xCursorProps->setPropertyValues(aPropNames, aPropValues),
1040 lang::WrappedTargetException);
1041 CPPUNIT_ASSERT_EQUAL(OUString("Emphasis"),
1042 getProperty<OUString>(xCursorProps, "CharStyleName"));
1045 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testShapeAllowOverlap)
1047 // Test the AllowOverlap frame/shape property.
1049 // Create a new document and insert a rectangle.
1050 createSwDoc();
1051 uno::Reference<lang::XMultiServiceFactory> xDocument(mxComponent, uno::UNO_QUERY);
1052 awt::Point aPoint(1000, 1000);
1053 awt::Size aSize(10000, 10000);
1054 uno::Reference<drawing::XShape> xShape(
1055 xDocument->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY);
1056 xShape->setPosition(aPoint);
1057 xShape->setSize(aSize);
1058 uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xDocument, uno::UNO_QUERY);
1059 xDrawPageSupplier->getDrawPage()->add(xShape);
1061 // The property is on by default, turn it off & verify.
1062 uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY);
1063 xShapeProperties->setPropertyValue("AllowOverlap", uno::Any(false));
1064 CPPUNIT_ASSERT(!getProperty<bool>(xShapeProperties, "AllowOverlap"));
1066 // Turn it back to on & verify.
1067 xShapeProperties->setPropertyValue("AllowOverlap", uno::Any(true));
1068 CPPUNIT_ASSERT(getProperty<bool>(xShapeProperties, "AllowOverlap"));
1071 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTextConvertToTableLineSpacing)
1073 // Load a document which has a table with a single cell.
1074 // The cell has both a table style and a paragraph style, with different line spacing
1075 // heights.
1076 createSwDoc("table-line-spacing.docx");
1077 uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
1078 uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(),
1079 uno::UNO_QUERY);
1080 uno::Reference<text::XTextTable> xTable(xTables->getByIndex(0), uno::UNO_QUERY);
1081 uno::Reference<table::XCell> xCell = xTable->getCellByName("A1");
1082 uno::Reference<text::XText> xCellText(xCell, uno::UNO_QUERY);
1083 uno::Reference<text::XTextRange> xParagraph = getParagraphOfText(1, xCellText);
1084 style::LineSpacing aLineSpacing
1085 = getProperty<style::LineSpacing>(xParagraph, "ParaLineSpacing");
1086 // Make sure that we take the line spacing from the paragraph style, not from the table style.
1087 // Without the accompanying fix in place, this test would have failed with:
1088 // - Expected: 388
1089 // - Actual : 635
1090 // I.e. the 360 twips line spacing was taken from the table style, not the 220 twips one from
1091 // the paragraph style.
1092 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(convertTwipToMm100(220)), aLineSpacing.Height);
1095 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testMultiSelect)
1097 // Create a new document and add a text with several repeated sequences.
1098 createSwDoc();
1099 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, css::uno::UNO_QUERY_THROW);
1100 auto xSimpleText = xTextDocument->getText();
1101 xSimpleText->insertString(xSimpleText->getStart(), "Abc aBc abC", false);
1103 // Create a search descriptor and find all occurencies of search string
1104 css::uno::Reference<css::util::XSearchable> xSearchable(mxComponent, css::uno::UNO_QUERY_THROW);
1105 auto xSearchDescriptor = xSearchable->createSearchDescriptor();
1106 xSearchDescriptor->setPropertyValue("SearchStyles", css::uno::Any(false));
1107 xSearchDescriptor->setPropertyValue("SearchCaseSensitive", css::uno::Any(false));
1108 xSearchDescriptor->setPropertyValue("SearchBackwards", css::uno::Any(true));
1109 xSearchDescriptor->setPropertyValue("SearchRegularExpression", css::uno::Any(false));
1110 xSearchDescriptor->setSearchString("abc");
1111 auto xSearchResult = xSearchable->findAll(xSearchDescriptor);
1113 // Select them all
1114 auto xController = xTextDocument->getCurrentController();
1115 css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier(
1116 xController, css::uno::UNO_QUERY_THROW);
1117 xSelectionSupplier->select(css::uno::Any(xSearchResult));
1118 css::uno::Reference<css::container::XIndexAccess> xSelection(xSelectionSupplier->getSelection(),
1119 css::uno::UNO_QUERY_THROW);
1120 // Now check that they all are selected in the reverse order ("SearchBackwards").
1121 CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xSelection->getCount());
1122 css::uno::Reference<css::text::XTextRange> xTextRange(xSelection->getByIndex(0),
1123 css::uno::UNO_QUERY_THROW);
1124 // For #0, result was empty (cursor was put before the last occurrence without selection)
1125 CPPUNIT_ASSERT_EQUAL(OUString("abC"), xTextRange->getString());
1126 xTextRange.set(xSelection->getByIndex(1), css::uno::UNO_QUERY_THROW);
1127 CPPUNIT_ASSERT_EQUAL(OUString("aBc"), xTextRange->getString());
1128 xTextRange.set(xSelection->getByIndex(2), css::uno::UNO_QUERY_THROW);
1129 CPPUNIT_ASSERT_EQUAL(OUString("Abc"), xTextRange->getString());
1132 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTransparentText)
1134 // Test the CharTransparence text portion property.
1136 // Create a new document.
1137 createSwDoc();
1139 // Set a custom transparency.
1140 uno::Reference<beans::XPropertySet> xParagraph(getParagraph(1), uno::UNO_QUERY);
1141 sal_Int16 nExpected = 42;
1142 xParagraph->setPropertyValue("CharTransparence", uno::Any(nExpected));
1144 // Get the transparency & verify.
1145 CPPUNIT_ASSERT_EQUAL(nExpected, getProperty<sal_Int16>(xParagraph, "CharTransparence"));
1148 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTdf129839)
1150 // Create a new document and add a table
1151 createSwDoc();
1152 css::uno::Reference<css::text::XTextDocument> xTextDocument(mxComponent,
1153 css::uno::UNO_QUERY_THROW);
1154 css::uno::Reference<css::lang::XMultiServiceFactory> xFac(xTextDocument,
1155 css::uno::UNO_QUERY_THROW);
1156 css::uno::Reference<css::text::XTextTable> xTable(
1157 xFac->createInstance("com.sun.star.text.TextTable"), css::uno::UNO_QUERY_THROW);
1158 xTable->initialize(4, 4);
1159 auto xSimpleText = xTextDocument->getText();
1160 xSimpleText->insertTextContent(xSimpleText->createTextCursor(), xTable, true);
1161 css::uno::Reference<css::table::XCellRange> xTableCellRange(xTable, css::uno::UNO_QUERY_THROW);
1162 // Get instance of SwXCellRange
1163 css::uno::Reference<css::beans::XPropertySet> xCellRange(
1164 xTableCellRange->getCellRangeByPosition(0, 0, 1, 1), css::uno::UNO_QUERY_THROW);
1165 // Test retrieval of VertOrient property - this crashed
1166 css::uno::Any aOrient = xCellRange->getPropertyValue("VertOrient");
1167 CPPUNIT_ASSERT_EQUAL(css::uno::Any(css::text::VertOrientation::NONE), aOrient);
1170 CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTdf129841)
1172 // Create a new document and add a table
1173 createSwDoc();
1174 css::uno::Reference<css::text::XTextDocument> xTextDocument(mxComponent,
1175 css::uno::UNO_QUERY_THROW);
1176 css::uno::Reference<css::lang::XMultiServiceFactory> xFac(xTextDocument,
1177 css::uno::UNO_QUERY_THROW);
1178 css::uno::Reference<css::text::XTextTable> xTable(
1179 xFac->createInstance("com.sun.star.text.TextTable"), css::uno::UNO_QUERY_THROW);
1180 xTable->initialize(4, 4);
1181 auto xSimpleText = xTextDocument->getText();
1182 xSimpleText->insertTextContent(xSimpleText->createTextCursor(), xTable, true);
1183 // Get SwXTextTableCursor
1184 css::uno::Reference<css::beans::XPropertySet> xTableCursor(xTable->createCursorByCellName("A1"),
1185 css::uno::UNO_QUERY_THROW);
1186 css::uno::Reference<css::table::XCellRange> xTableCellRange(xTable, css::uno::UNO_QUERY_THROW);
1187 // Get SwXCellRange for the same cell
1188 css::uno::Reference<css::beans::XPropertySet> xCellRange(
1189 xTableCellRange->getCellRangeByName("A1:A1"), css::uno::UNO_QUERY_THROW);
1190 static const OUStringLiteral sBackColor = u"BackColor";
1191 // Apply background color to table cursor, and read background color from cell range
1192 css::uno::Any aRefColor(sal_Int32(0x00FF0000));
1193 xTableCursor->setPropertyValue(sBackColor, aRefColor);
1194 css::uno::Any aColor = xCellRange->getPropertyValue(sBackColor);
1195 // This failed
1196 CPPUNIT_ASSERT_EQUAL(aRefColor, aColor);
1197 // Now the other way round
1198 aRefColor <<= sal_Int32(0x0000FF00);
1199 xCellRange->setPropertyValue(sBackColor, aRefColor);
1200 aColor = xTableCursor->getPropertyValue(sBackColor);
1201 CPPUNIT_ASSERT_EQUAL(aRefColor, aColor);
1204 CPPUNIT_PLUGIN_IMPLEMENT();
1206 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */