Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / qa / extras / tiledrendering / tiledrendering.cxx
bloba0345e7127142baca73b6528692943affae89991
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 <string>
13 #include <string_view>
15 #include <boost/property_tree/json_parser.hpp>
17 #include <com/sun/star/frame/DispatchResultState.hpp>
18 #include <com/sun/star/frame/XDispatchResultListener.hpp>
19 #include <com/sun/star/frame/XStorable.hpp>
20 #include <com/sun/star/frame/Desktop.hpp>
21 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
22 #include <com/sun/star/text/XTextField.hpp>
23 #include <com/sun/star/text/AuthorDisplayFormat.hpp>
24 #include <com/sun/star/datatransfer/XTransferable2.hpp>
26 #include <test/helper/transferable.hxx>
27 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
28 #include <comphelper/dispatchcommand.hxx>
29 #include <comphelper/propertysequence.hxx>
30 #include <comphelper/string.hxx>
31 #include <comphelper/lok.hxx>
32 #include <svx/svdpage.hxx>
33 #include <svx/svdview.hxx>
34 #include <vcl/virdev.hxx>
35 #include <editeng/editview.hxx>
36 #include <editeng/outliner.hxx>
37 #include <editeng/wghtitem.hxx>
38 #include <svl/srchitem.hxx>
39 #include <svl/slstitm.hxx>
40 #include <svl/stritem.hxx>
41 #include <sfx2/viewsh.hxx>
42 #include <sfx2/bindings.hxx>
43 #include <sfx2/dispatch.hxx>
44 #include <sfx2/viewfrm.hxx>
45 #include <sfx2/lokhelper.hxx>
46 #include <vcl/scheduler.hxx>
47 #include <vcl/vclevent.hxx>
48 #include <vcl/BitmapReadAccess.hxx>
49 #include <vcl/ITiledRenderable.hxx>
50 #include <tools/json_writer.hxx>
51 #include <unotools/mediadescriptor.hxx>
52 #include <comphelper/processfactory.hxx>
53 #include <comphelper/propertyvalue.hxx>
54 #include <test/lokcallback.hxx>
56 #include <drawdoc.hxx>
57 #include <ndtxt.hxx>
58 #include <wrtsh.hxx>
59 #include <view.hxx>
60 #include <UndoManager.hxx>
61 #include <cmdid.h>
62 #include <redline.hxx>
63 #include <IDocumentDrawModelAccess.hxx>
64 #include <IDocumentRedlineAccess.hxx>
65 #include <flddat.hxx>
66 #include <basesh.hxx>
67 #include <unotxdoc.hxx>
68 #include <docsh.hxx>
69 #include <txtfrm.hxx>
70 #include <rootfrm.hxx>
71 #include <fmtanchr.hxx>
72 #include <textcontentcontrol.hxx>
73 #include <swmodule.hxx>
74 #include <swdll.hxx>
76 static std::ostream& operator<<(std::ostream& os, ViewShellId id)
78 os << static_cast<sal_Int32>(id);
79 return os;
82 /// Testsuite for the SwXTextDocument methods implementing the vcl::ITiledRenderable interface.
83 class SwTiledRenderingTest : public SwModelTestBase
85 public:
86 SwTiledRenderingTest();
87 virtual void setUp() override;
88 virtual void tearDown() override;
90 protected:
91 SwXTextDocument* createDoc(const char* pName = nullptr);
92 void setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell);
93 static void callback(int nType, const char* pPayload, void* pData);
94 void callbackImpl(int nType, const char* pPayload);
95 // First invalidation.
96 tools::Rectangle m_aInvalidation;
97 /// Union of all invalidations.
98 tools::Rectangle m_aInvalidations;
99 Size m_aDocumentSize;
100 OString m_aTextSelection;
101 bool m_bFound;
102 std::vector<OString> m_aSearchResultSelection;
103 std::vector<int> m_aSearchResultPart;
104 int m_nSelectionBeforeSearchResult;
105 int m_nSelectionAfterSearchResult;
106 int m_nInvalidations;
107 int m_nRedlineTableSizeChanged;
108 int m_nRedlineTableEntryModified;
109 int m_nTrackedChangeIndex;
110 bool m_bFullInvalidateSeen;
111 OString m_sHyperlinkText;
112 OString m_sHyperlinkLink;
113 OString m_aFormFieldButton;
114 OString m_aContentControl;
115 OString m_ShapeSelection;
116 TestLokCallbackWrapper m_callbackWrapper;
119 SwTiledRenderingTest::SwTiledRenderingTest()
120 : SwModelTestBase("/sw/qa/extras/tiledrendering/data/"),
121 m_bFound(true),
122 m_nSelectionBeforeSearchResult(0),
123 m_nSelectionAfterSearchResult(0),
124 m_nInvalidations(0),
125 m_nRedlineTableSizeChanged(0),
126 m_nRedlineTableEntryModified(0),
127 m_nTrackedChangeIndex(-1),
128 m_bFullInvalidateSeen(false),
129 m_callbackWrapper(&callback, this)
133 void SwTiledRenderingTest::setUp()
135 SwModelTestBase::setUp();
137 SwGlobals::ensure();
138 SW_MOD()->ClearRedlineAuthors();
140 comphelper::LibreOfficeKit::setActive(true);
143 void SwTiledRenderingTest::tearDown()
145 if (mxComponent.is())
147 SwXTextDocument* pTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
148 if (pTextDocument)
150 SwWrtShell* pWrtShell = pTextDocument->GetDocShell()->GetWrtShell();
151 if (pWrtShell)
153 pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(nullptr);
156 mxComponent->dispose();
157 mxComponent.clear();
159 m_callbackWrapper.clear();
160 comphelper::LibreOfficeKit::setActive(false);
162 test::BootstrapFixture::tearDown();
165 SwXTextDocument* SwTiledRenderingTest::createDoc(const char* pName)
167 if (!pName)
168 createSwDoc();
169 else
170 createSwDoc(pName);
172 SwXTextDocument* pTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
173 CPPUNIT_ASSERT(pTextDocument);
174 pTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
175 return pTextDocument;
178 void SwTiledRenderingTest::setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell)
180 pViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
181 m_callbackWrapper.setLOKViewId(SfxLokHelper::getView(pViewShell));
184 void SwTiledRenderingTest::callback(int nType, const char* pPayload, void* pData)
186 static_cast<SwTiledRenderingTest*>(pData)->callbackImpl(nType, pPayload);
189 void SwTiledRenderingTest::callbackImpl(int nType, const char* pPayload)
191 OString aPayload(pPayload);
192 switch (nType)
194 case LOK_CALLBACK_INVALIDATE_TILES:
196 tools::Rectangle aInvalidation;
197 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload));
198 if (std::string_view("EMPTY") == pPayload)
200 m_bFullInvalidateSeen = true;
201 return;
204 CPPUNIT_ASSERT(aSeq.getLength() == 4 || aSeq.getLength() == 5);
205 aInvalidation.SetLeft(aSeq[0].toInt32());
206 aInvalidation.SetTop(aSeq[1].toInt32());
207 aInvalidation.setWidth(aSeq[2].toInt32());
208 aInvalidation.setHeight(aSeq[3].toInt32());
209 if (m_aInvalidation.IsEmpty())
211 m_aInvalidation = aInvalidation;
213 m_aInvalidations.Union(aInvalidation);
214 ++m_nInvalidations;
216 break;
217 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
219 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload));
220 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), aSeq.getLength());
221 m_aDocumentSize.setWidth(aSeq[0].toInt32());
222 m_aDocumentSize.setHeight(aSeq[1].toInt32());
224 break;
225 case LOK_CALLBACK_TEXT_SELECTION:
227 m_aTextSelection = pPayload;
228 if (m_aSearchResultSelection.empty())
229 ++m_nSelectionBeforeSearchResult;
230 else
231 ++m_nSelectionAfterSearchResult;
233 break;
234 case LOK_CALLBACK_SEARCH_NOT_FOUND:
236 m_bFound = false;
238 break;
239 case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
241 m_aSearchResultSelection.clear();
242 boost::property_tree::ptree aTree;
243 std::stringstream aStream(pPayload);
244 boost::property_tree::read_json(aStream, aTree);
245 for (const boost::property_tree::ptree::value_type& rValue : aTree.get_child("searchResultSelection"))
247 m_aSearchResultSelection.emplace_back(rValue.second.get<std::string>("rectangles").c_str());
248 m_aSearchResultPart.push_back(std::atoi(rValue.second.get<std::string>("part").c_str()));
251 break;
252 case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED:
254 ++m_nRedlineTableSizeChanged;
256 break;
257 case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED:
259 ++m_nRedlineTableEntryModified;
261 break;
262 case LOK_CALLBACK_STATE_CHANGED:
264 OString aTrackedChangeIndexPrefix(".uno:TrackedChangeIndex=");
265 if (aPayload.startsWith(aTrackedChangeIndexPrefix))
267 OString sIndex = aPayload.copy(aTrackedChangeIndexPrefix.getLength());
268 if (sIndex.isEmpty())
269 m_nTrackedChangeIndex = -1;
270 else
271 m_nTrackedChangeIndex = sIndex.toInt32();
274 break;
275 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
277 if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
279 boost::property_tree::ptree aTree;
280 std::stringstream aStream(pPayload);
281 boost::property_tree::read_json(aStream, aTree);
282 boost::property_tree::ptree &aChild = aTree.get_child("hyperlink");
283 m_sHyperlinkText = OString(aChild.get("text", ""));
284 m_sHyperlinkLink = OString(aChild.get("link", ""));
287 break;
288 case LOK_CALLBACK_FORM_FIELD_BUTTON:
290 m_aFormFieldButton = OString(pPayload);
292 break;
293 case LOK_CALLBACK_CONTENT_CONTROL:
295 m_aContentControl = OString(pPayload);
297 break;
298 case LOK_CALLBACK_GRAPHIC_SELECTION:
300 m_ShapeSelection = OString(pPayload);
302 break;
307 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRegisterCallback)
309 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
310 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
311 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
312 // Insert a character at the beginning of the document.
313 pWrtShell->Insert("x");
314 Scheduler::ProcessEventsToIdle();
316 // Check that the top left 256x256px tile would be invalidated.
317 CPPUNIT_ASSERT(!m_aInvalidation.IsEmpty());
318 tools::Rectangle aTopLeft(0, 0, 256*15, 256*15); // 1 px = 15 twips, assuming 96 DPI.
319 CPPUNIT_ASSERT(m_aInvalidation.Overlaps(aTopLeft));
322 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPostKeyEvent)
324 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
325 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
326 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
327 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
328 // Did we manage to go after the first character?
329 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pShellCursor->GetPoint()->GetContentIndex());
331 emulateTyping(*pXTextDocument, u"x");
332 // Did we manage to insert the character after the first one?
333 CPPUNIT_ASSERT_EQUAL(OUString("Axaa bbb."), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
336 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPostMouseEvent)
338 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
339 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
340 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
341 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
342 // Did we manage to go after the first character?
343 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pShellCursor->GetPoint()->GetContentIndex());
345 Point aStart = pShellCursor->GetSttPos();
346 aStart.setX(aStart.getX() - 1000);
347 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
348 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
349 Scheduler::ProcessEventsToIdle();
350 // The new cursor position must be before the first word.
351 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), pShellCursor->GetPoint()->GetContentIndex());
354 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSetTextSelection)
356 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
357 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
358 // Move the cursor into the second word.
359 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 5, /*bBasicCall=*/false);
360 // Create a selection on the word.
361 pWrtShell->SelWrd();
362 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
363 // Did we indeed manage to select the second word?
364 CPPUNIT_ASSERT_EQUAL(OUString("bbb"), pShellCursor->GetText());
366 // Now use setTextSelection() to move the start of the selection 1000 twips left.
367 Point aStart = pShellCursor->GetSttPos();
368 aStart.setX(aStart.getX() - 1000);
369 pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_START, aStart.getX(), aStart.getY());
370 // The new selection must include the first word, too -- but not the ending dot.
371 CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb"), pShellCursor->GetText());
373 // Next: test that LOK_SETTEXTSELECTION_RESET + LOK_SETTEXTSELECTION_END can be used to create a selection.
374 pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_RESET, aStart.getX(), aStart.getY());
375 pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_END, aStart.getX() + 1000, aStart.getY());
376 CPPUNIT_ASSERT_EQUAL(OUString("Aaa b"), pShellCursor->GetText());
379 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetTextSelection)
381 SwXTextDocument* pXTextDocument = createDoc("shape-with-text.fodt");
382 // No crash, just empty output for unexpected mime type.
383 CPPUNIT_ASSERT_EQUAL(OString(), apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "foo/bar"));
385 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
386 // Move the cursor into the first word.
387 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2, /*bBasicCall=*/false);
388 // Create a selection by on the word.
389 pWrtShell->SelWrd();
391 // Make sure that we selected text from the body text.
392 CPPUNIT_ASSERT_EQUAL(OString("Hello"), apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8"));
394 // Make sure we produce something for HTML.
395 CPPUNIT_ASSERT(!apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/html").isEmpty());
397 // Now select some shape text and check again.
398 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
399 SdrObject* pObject = pPage->GetObj(0);
400 SdrView* pView = pWrtShell->GetDrawView();
401 pView->SdrBeginTextEdit(pObject);
402 CPPUNIT_ASSERT(pView->GetTextEditObject());
403 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
404 ESelection aWordSelection(0, 0, 0, 5);
405 rEditView.SetSelection(aWordSelection);
406 CPPUNIT_ASSERT_EQUAL(OString("Shape"), apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8"));
409 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetTextSelectionLineLimit)
411 static OStringLiteral sOriginalText(u8"Estonian employs the Latin script as the basis for its alphabet, which adds the letters ä, ö, ü, and õ, plus the later additions š and ž. The letters c, q, w, x and y are limited to proper names of foreign origin, and f, z, š, and ž appear in loanwords and foreign names only. Ö and Ü are pronounced similarly to their equivalents in Swedish and German. Unlike in standard German but like Swedish (when followed by 'r') and Finnish, Ä is pronounced [æ], as in English mat. The vowels Ä, Ö and Ü are clearly separate phonemes and inherent in Estonian, although the letter shapes come from German. The letter õ denotes /ɤ/, unrounded /o/, or a close-mid back unrounded vowel. It is almost identical to the Bulgarian ъ /ɤ̞/ and the Vietnamese ơ, and is also used to transcribe the Russian ы.");
412 static OStringLiteral sExpectedHtml(u8"Estonian employs the <a href=\"https://en.wikipedia.org/wiki/Latin_script\">Latin script</a> as the basis for <a href=\"https://en.wikipedia.org/wiki/Estonian_alphabet\">its alphabet</a>, which adds the letters <a href=\"https://en.wikipedia.org/wiki/%C3%84\"><i>ä</i></a>, <a href=\"https://en.wikipedia.org/wiki/%C3%96\"><i>ö</i></a>, <a href=\"https://en.wikipedia.org/wiki/%C3%9C\"><i>ü</i></a>, and <a href=\"https://en.wikipedia.org/wiki/%C3%95\"><i>õ</i></a>, plus the later additions <a href=\"https://en.wikipedia.org/wiki/%C5%A0\"><i>š</i></a> and <a href=\"https://en.wikipedia.org/wiki/%C5%BD\"><i>ž</i></a>. The letters <i>c</i>, <i>q</i>, <i>w</i>, <i>x</i> and <i>y</i> are limited to <a href=\"https://en.wikipedia.org/wiki/Proper_names\">proper names</a> of foreign origin, and <i>f</i>, <i>z</i>, <i>š</i>, and <i>ž</i> appear in loanwords and foreign names only. <i>Ö</i> and <i>Ü</i> are pronounced similarly to their equivalents in Swedish and German. Unlike in standard German but like Swedish (when followed by 'r') and Finnish, <i>Ä</i> is pronounced [æ], as in English <i>mat</i>. The vowels Ä, Ö and Ü are clearly separate <a href=\"https://en.wikipedia.org/wiki/Phonemes\">phonemes</a> and inherent in Estonian, although the letter shapes come from German. The letter <a href=\"https://en.wikipedia.org/wiki/%C3%95\"><i>õ</i></a> denotes /ɤ/, unrounded /o/, or a <a href=\"https://en.wikipedia.org/wiki/Close-mid_back_unrounded_vowel\">close-mid back unrounded vowel</a>. It is almost identical to the <a href=\"https://en.wikipedia.org/wiki/Bulgarian_language\">Bulgarian</a> <a href=\"https://en.wikipedia.org/wiki/%D0%AA\">ъ</a> /ɤ̞/ and the <a href=\"https://en.wikipedia.org/wiki/Vietnamese_language\">Vietnamese</a> <a href=\"https://en.wikipedia.org/wiki/%C6%A0\">ơ</a>, and is also used to transcribe the Russian <a href=\"https://en.wikipedia.org/wiki/%D0%AB\">ы</a>.");
414 SwXTextDocument* pXTextDocument = createDoc("estonian.odt");
416 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
417 // Move the cursor into the first word.
418 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2, /*bBasicCall=*/false);
419 // Create a selection.
420 pWrtShell->SelAll();
422 OString sPlainText = apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8");
424 CPPUNIT_ASSERT_EQUAL(OString(sOriginalText), sPlainText.trim());
426 OString sHtmlText = apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/html");
428 int nStart = sHtmlText.indexOf(u8"Estonian");
430 CPPUNIT_ASSERT(sHtmlText.match(sExpectedHtml, nStart));
433 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetTextSelectionMultiLine)
435 // Test will check if correct number of new line marks / paragraphs is generated
436 static OStringLiteral sOriginalText(u8"Heading\n\
437 Let's have text; we need to be able to select the text inside the shape, but also the various individual ones too:\n\
443 And this is all for Writer shape objects\n\
444 Heading on second page");
446 static OStringLiteral sExpectedHtml(u8"Heading</h2>\n\
447 <p>Let's have text; we need to be able to select the text inside the shape, but also the various individual ones too:</p>\n\
448 <p><br/><br/></p>\n\
449 <p><br/><br/></p>\n\
450 <p><br/><br/></p>\n\
451 <p><br/><br/></p>\n\
452 <p><br/><br/></p>\n\
453 <h1 class=\"western\">And this is all for Writer shape objects</h1>\n\
454 <h2 class=\"western\">Heading on second page</h2>");
456 SwXTextDocument* pXTextDocument = createDoc("multiline.odt");
458 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
459 // Create a selection.
460 pWrtShell->SelAll();
462 OString sPlainText = apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8");
464 CPPUNIT_ASSERT_EQUAL(OString(sOriginalText), sPlainText.trim());
466 OString sHtmlText = apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/html");
468 int nStart = sHtmlText.indexOf(u8"Heading");
470 CPPUNIT_ASSERT(sHtmlText.match(sExpectedHtml, nStart));
473 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSetGraphicSelection)
475 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
476 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
477 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
478 SdrObject* pObject = pPage->GetObj(0);
479 pWrtShell->SelectObj(Point(), 0, pObject);
480 SdrHdlList handleList(nullptr);
481 pObject->AddToHdlList(handleList);
482 // Make sure the rectangle has 8 handles: at each corner and at the center of each edge.
483 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(8), handleList.GetHdlCount());
484 // Take the bottom center one.
485 SdrHdl* pHdl = handleList.GetHdl(6);
486 CPPUNIT_ASSERT_EQUAL(int(SdrHdlKind::Lower), static_cast<int>(pHdl->GetKind()));
487 tools::Rectangle aShapeBefore = pObject->GetSnapRect();
488 // Resize.
489 pXTextDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_START, pHdl->GetPos().getX(), pHdl->GetPos().getY());
490 pXTextDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_END, pHdl->GetPos().getX(), pHdl->GetPos().getY() + 1000);
491 tools::Rectangle aShapeAfter = pObject->GetSnapRect();
492 // Check that a resize happened, but aspect ratio is not kept.
493 CPPUNIT_ASSERT_EQUAL(aShapeBefore.getOpenWidth(), aShapeAfter.getOpenWidth());
494 CPPUNIT_ASSERT_EQUAL(aShapeBefore.getOpenHeight() + 1000, aShapeAfter.getOpenHeight());
497 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testResetSelection)
499 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
500 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
501 // Select one character.
502 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 1, /*bBasicCall=*/false);
503 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
504 // We have a text selection.
505 CPPUNIT_ASSERT(pShellCursor->HasMark());
507 pXTextDocument->resetSelection();
508 // We no longer have a text selection.
509 CPPUNIT_ASSERT(!pShellCursor->HasMark());
511 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
512 SdrObject* pObject = pPage->GetObj(0);
513 Point aPoint = pObject->GetSnapRect().Center();
514 // Select the shape.
515 pWrtShell->EnterSelFrameMode(&aPoint);
516 // We have a graphic selection.
517 CPPUNIT_ASSERT(pWrtShell->IsSelFrameMode());
519 pXTextDocument->resetSelection();
520 // We no longer have a graphic selection.
521 CPPUNIT_ASSERT(!pWrtShell->IsSelFrameMode());
524 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testInsertShape)
526 SwXTextDocument* pXTextDocument = createDoc("2-pages.odt");
527 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
529 pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
530 comphelper::dispatchCommand(".uno:BasicShapes.circle", uno::Sequence<beans::PropertyValue>());
532 // check that the shape was inserted in the visible area, not outside
533 IDocumentDrawModelAccess &rDrawModelAccess = pWrtShell->GetDoc()->getIDocumentDrawModelAccess();
534 SdrPage* pPage = rDrawModelAccess.GetDrawModel()->GetPage(0);
535 SdrObject* pObject = pPage->GetObj(0);
537 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(3299, 299), Size(3403, 3403)), pObject->GetSnapRect());
539 // check that it is in the foreground layer
540 CPPUNIT_ASSERT_EQUAL(rDrawModelAccess.GetHeavenId().get(), pObject->GetLayer().get());
543 static void lcl_search(bool bBackward)
545 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
547 {"SearchItem.SearchString", uno::Any(OUString("shape"))},
548 {"SearchItem.Backward", uno::Any(bBackward)}
549 }));
550 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
553 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearch)
555 SwXTextDocument* pXTextDocument = createDoc("search.odt");
556 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
557 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
558 SwNodeOffset nNode = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
560 // First hit, in the second paragraph, before the shape.
561 lcl_search(false);
562 CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
563 SwNodeOffset nActual = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
564 CPPUNIT_ASSERT_EQUAL(nNode + 1, nActual);
565 /// Make sure we get search result selection for normal find as well, not only find all.
566 CPPUNIT_ASSERT(!m_aSearchResultSelection.empty());
568 // Next hit, in the shape.
569 lcl_search(false);
570 CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
572 // Next hit, in the shape, still.
573 lcl_search(false);
574 CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
576 // Last hit, in the last paragraph, after the shape.
577 lcl_search(false);
578 CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
579 nActual = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
580 CPPUNIT_ASSERT_EQUAL(nNode + 7, nActual);
582 // Now change direction and make sure that the first 2 hits are in the shape, but not the 3rd one.
583 lcl_search(true);
584 CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
585 lcl_search(true);
586 CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
587 lcl_search(true);
588 CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
589 nActual = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
590 CPPUNIT_ASSERT_EQUAL(nNode + 1, nActual);
593 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchViewArea)
595 SwXTextDocument* pXTextDocument = createDoc("search.odt");
596 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
597 // Go to the second page, 1-based.
598 pWrtShell->GotoPage(2, false);
599 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
600 // Get the ~top left corner of the second page.
601 Point aPoint = pShellCursor->GetSttPos();
603 // Go back to the first page, search while the cursor is there, but the
604 // visible area is the second page.
605 pWrtShell->GotoPage(1, false);
606 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
608 {"SearchItem.SearchString", uno::Any(OUString("Heading"))},
609 {"SearchItem.Backward", uno::Any(false)},
610 {"SearchItem.SearchStartPointX", uno::Any(static_cast<sal_Int32>(aPoint.getX()))},
611 {"SearchItem.SearchStartPointY", uno::Any(static_cast<sal_Int32>(aPoint.getY()))}
612 }));
613 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
614 // This was just "Heading", i.e. SwView::SearchAndWrap() did not search from only the top of the second page.
615 CPPUNIT_ASSERT_EQUAL(OUString("Heading on second page"), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
618 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchTextFrame)
620 SwXTextDocument* pXTextDocument = createDoc("search.odt");
621 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
622 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
623 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
625 {"SearchItem.SearchString", uno::Any(OUString("TextFrame"))},
626 {"SearchItem.Backward", uno::Any(false)},
627 }));
628 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
629 // This was empty: nothing was highlighted after searching for 'TextFrame'.
630 CPPUNIT_ASSERT(!m_aTextSelection.isEmpty());
633 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchTextFrameWrapAround)
635 SwXTextDocument* pXTextDocument = createDoc("search.odt");
636 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
637 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
638 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
640 {"SearchItem.SearchString", uno::Any(OUString("TextFrame"))},
641 {"SearchItem.Backward", uno::Any(false)},
642 }));
643 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
644 CPPUNIT_ASSERT(m_bFound);
645 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
646 // This failed, i.e. the second time 'not found' was reported, instead of wrapping around.
647 CPPUNIT_ASSERT(m_bFound);
650 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDocumentSizeChanged)
652 // Get the current document size.
653 SwXTextDocument* pXTextDocument = createDoc("2-pages.odt");
654 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
655 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
656 Size aSize = pXTextDocument->getDocumentSize();
658 // Delete the second page and see how the size changes.
659 pWrtShell->Down(false);
660 pWrtShell->DelLeft();
661 // Document width should not change, this was 0.
662 CPPUNIT_ASSERT_EQUAL(aSize.getWidth(), m_aDocumentSize.getWidth());
663 // Document height should be smaller now.
664 CPPUNIT_ASSERT(aSize.getHeight() > m_aDocumentSize.getHeight());
667 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchAll)
669 SwXTextDocument* pXTextDocument = createDoc("search.odt");
670 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
671 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
672 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
674 {"SearchItem.SearchString", uno::Any(OUString("shape"))},
675 {"SearchItem.Backward", uno::Any(false)},
676 {"SearchItem.Command", uno::Any(static_cast<sal_uInt16>(SvxSearchCmd::FIND_ALL))},
677 }));
678 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
679 // This was 0; should be 2 results in the body text.
680 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), m_aSearchResultSelection.size());
681 // Writer documents are always a single part.
682 CPPUNIT_ASSERT_EQUAL(0, m_aSearchResultPart[0]);
685 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchAllNotifications)
687 SwXTextDocument* pXTextDocument = createDoc("search.odt");
688 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
689 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
690 // Reset notification counter before search.
691 m_nSelectionBeforeSearchResult = 0;
692 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
694 {"SearchItem.SearchString", uno::Any(OUString("shape"))},
695 {"SearchItem.Backward", uno::Any(false)},
696 {"SearchItem.Command", uno::Any(static_cast<sal_uInt16>(SvxSearchCmd::FIND_ALL))},
697 }));
698 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
699 Scheduler::ProcessEventsToIdle();
701 // This was 5, make sure that we get no notifications about selection changes during search.
702 CPPUNIT_ASSERT_EQUAL(0, m_nSelectionBeforeSearchResult);
703 // But we do get the selection afterwards.
704 CPPUNIT_ASSERT(m_nSelectionAfterSearchResult > 0);
707 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPageDownInvalidation)
709 SwXTextDocument* pXTextDocument = createDoc("pagedown-invalidation.odt");
710 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
712 {".uno:HideWhitespace", uno::Any(true)},
713 }));
714 pXTextDocument->initializeForTiledRendering(aPropertyValues);
715 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
716 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
717 comphelper::dispatchCommand(".uno:PageDown", uno::Sequence<beans::PropertyValue>());
719 // This was 2.
720 CPPUNIT_ASSERT_EQUAL(0, m_nInvalidations);
723 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPartHash)
725 SwXTextDocument* pXTextDocument = createDoc("pagedown-invalidation.odt");
726 int nParts = pXTextDocument->getParts();
727 for (int it = 0; it < nParts; it++)
729 CPPUNIT_ASSERT(!pXTextDocument->getPartHash(it).isEmpty());
733 namespace {
735 /// A view callback tracks callbacks invoked on one specific view.
736 class ViewCallback final
738 SfxViewShell* mpViewShell;
739 int mnView;
740 public:
741 bool m_bOwnCursorInvalidated;
742 int m_nOwnCursorInvalidatedBy;
743 bool m_bOwnCursorAtOrigin;
744 tools::Rectangle m_aOwnCursor;
745 bool m_bViewCursorInvalidated;
746 tools::Rectangle m_aViewCursor;
747 bool m_bOwnSelectionSet;
748 bool m_bViewSelectionSet;
749 OString m_aViewSelection;
750 bool m_bTilesInvalidated;
751 bool m_bViewCursorVisible;
752 bool m_bGraphicViewSelection;
753 bool m_bGraphicSelection;
754 bool m_bViewLock;
755 /// Set if any callback was invoked.
756 bool m_bCalled;
757 /// Redline table size changed payload
758 boost::property_tree::ptree m_aRedlineTableChanged;
759 /// Redline table modified payload
760 boost::property_tree::ptree m_aRedlineTableModified;
761 /// Post-it / annotation payload.
762 boost::property_tree::ptree m_aComment;
763 TestLokCallbackWrapper m_callbackWrapper;
765 ViewCallback(SfxViewShell* pViewShell = nullptr, std::function<void(ViewCallback&)> const & rBeforeInstallFunc = {})
766 : m_bOwnCursorInvalidated(false),
767 m_nOwnCursorInvalidatedBy(-1),
768 m_bOwnCursorAtOrigin(false),
769 m_bViewCursorInvalidated(false),
770 m_bOwnSelectionSet(false),
771 m_bViewSelectionSet(false),
772 m_bTilesInvalidated(false),
773 m_bViewCursorVisible(false),
774 m_bGraphicViewSelection(false),
775 m_bGraphicSelection(false),
776 m_bViewLock(false),
777 m_bCalled(false),
778 m_callbackWrapper(&callback, this)
780 // Because one call-site wants to set the bool fields up before the callback is installed
781 if (rBeforeInstallFunc)
782 rBeforeInstallFunc(*this);
784 mpViewShell = pViewShell ? pViewShell : SfxViewShell::Current();
785 mpViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
786 mnView = SfxLokHelper::getView();
787 m_callbackWrapper.setLOKViewId( mnView );
790 ~ViewCallback()
792 SfxLokHelper::setView(mnView);
793 mpViewShell->setLibreOfficeKitViewCallback(nullptr);
796 static void callback(int nType, const char* pPayload, void* pData)
798 static_cast<ViewCallback*>(pData)->callbackImpl(nType, pPayload);
801 void callbackImpl(int nType, const char* pPayload)
803 OString aPayload(pPayload);
804 m_bCalled = true;
805 switch (nType)
807 case LOK_CALLBACK_INVALIDATE_TILES:
809 m_bTilesInvalidated = true;
811 break;
812 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
814 m_bOwnCursorInvalidated = true;
816 OString sRect;
817 if(comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
819 std::stringstream aStream(pPayload);
820 boost::property_tree::ptree aTree;
821 boost::property_tree::read_json(aStream, aTree);
822 sRect = OString(aTree.get_child("rectangle").get_value<std::string>());
823 m_nOwnCursorInvalidatedBy = aTree.get_child("viewId").get_value<int>();
825 else
826 sRect = aPayload;
827 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::fromUtf8(sRect));
828 if (std::string_view("EMPTY") == pPayload)
829 return;
830 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength());
831 m_aOwnCursor.SetLeft(aSeq[0].toInt32());
832 m_aOwnCursor.SetTop(aSeq[1].toInt32());
833 m_aOwnCursor.setWidth(aSeq[2].toInt32());
834 m_aOwnCursor.setHeight(aSeq[3].toInt32());
835 if (m_aOwnCursor.Left() == 0 && m_aOwnCursor.Top() == 0)
836 m_bOwnCursorAtOrigin = true;
838 break;
839 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
841 m_bViewCursorInvalidated = true;
842 std::stringstream aStream(pPayload);
843 boost::property_tree::ptree aTree;
844 boost::property_tree::read_json(aStream, aTree);
845 OString aRect( aTree.get_child("rectangle").get_value<std::string>() );
847 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::fromUtf8(aRect));
848 if (std::string_view("EMPTY") == pPayload)
849 return;
850 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength());
851 m_aViewCursor.SetLeft(aSeq[0].toInt32());
852 m_aViewCursor.SetTop(aSeq[1].toInt32());
853 m_aViewCursor.setWidth(aSeq[2].toInt32());
854 m_aViewCursor.setHeight(aSeq[3].toInt32());
856 break;
857 case LOK_CALLBACK_TEXT_SELECTION:
859 m_bOwnSelectionSet = true;
861 break;
862 case LOK_CALLBACK_TEXT_VIEW_SELECTION:
864 m_bViewSelectionSet = true;
865 m_aViewSelection = aPayload;
867 break;
868 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
870 std::stringstream aStream(pPayload);
871 boost::property_tree::ptree aTree;
872 boost::property_tree::read_json(aStream, aTree);
873 m_bViewCursorVisible = aTree.get_child("visible").get_value<std::string>() == "true";
875 break;
876 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
878 std::stringstream aStream(pPayload);
879 boost::property_tree::ptree aTree;
880 boost::property_tree::read_json(aStream, aTree);
881 m_bGraphicViewSelection = aTree.get_child("selection").get_value<std::string>() != "EMPTY";
883 break;
884 case LOK_CALLBACK_GRAPHIC_SELECTION:
886 m_bGraphicSelection = aPayload != "EMPTY";
888 break;
889 case LOK_CALLBACK_VIEW_LOCK:
891 std::stringstream aStream(pPayload);
892 boost::property_tree::ptree aTree;
893 boost::property_tree::read_json(aStream, aTree);
894 m_bViewLock = aTree.get_child("rectangle").get_value<std::string>() != "EMPTY";
896 break;
897 case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED:
899 m_aRedlineTableChanged.clear();
900 std::stringstream aStream(pPayload);
901 boost::property_tree::read_json(aStream, m_aRedlineTableChanged);
902 m_aRedlineTableChanged = m_aRedlineTableChanged.get_child("redline");
904 break;
905 case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED:
907 m_aRedlineTableModified.clear();
908 std::stringstream aStream(pPayload);
909 boost::property_tree::read_json(aStream, m_aRedlineTableModified);
910 m_aRedlineTableModified = m_aRedlineTableModified.get_child("redline");
912 break;
913 case LOK_CALLBACK_COMMENT:
915 m_aComment.clear();
916 std::stringstream aStream(pPayload);
917 boost::property_tree::read_json(aStream, m_aComment);
918 m_aComment = m_aComment.get_child("comment");
920 break;
925 class TestResultListener : public cppu::WeakImplHelper<css::frame::XDispatchResultListener>
927 public:
928 sal_uInt32 m_nDocRepair;
930 TestResultListener() : m_nDocRepair(0)
934 virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& rEvent) override
936 if (rEvent.State == frame::DispatchResultState::SUCCESS)
938 rEvent.Result >>= m_nDocRepair;
942 virtual void SAL_CALL disposing(const css::lang::EventObject&) override
949 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testMissingInvalidation)
951 // Create two views.
952 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
953 ViewCallback aView1;
954 int nView1 = SfxLokHelper::getView();
955 SfxLokHelper::createView();
956 ViewCallback aView2;
957 int nView2 = SfxLokHelper::getView();
959 // First view: put the cursor into the first word.
960 SfxLokHelper::setView(nView1);
961 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
962 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
964 // Second view: select the first word.
965 SfxLokHelper::setView(nView2);
966 CPPUNIT_ASSERT(pXTextDocument->GetDocShell()->GetWrtShell() != pWrtShell);
967 pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
968 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
969 pWrtShell->SelWrd();
971 // Now delete the selected word and make sure both views are invalidated.
972 Scheduler::ProcessEventsToIdle();
973 aView1.m_bTilesInvalidated = false;
974 aView2.m_bTilesInvalidated = false;
975 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DELETE);
976 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DELETE);
977 Scheduler::ProcessEventsToIdle();
978 CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
979 CPPUNIT_ASSERT(aView2.m_bTilesInvalidated);
982 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewCursors)
984 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
985 ViewCallback aView1;
986 SfxLokHelper::createView();
987 ViewCallback aView2;
989 Scheduler::ProcessEventsToIdle();
990 CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
991 CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
992 CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
993 // This failed: the cursor position of view1 was only known to view2 once
994 // it changed.
995 CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
997 // Make sure that aView1 gets a view-only selection notification, while
998 // aView2 gets a real selection notification.
999 aView1.m_bOwnSelectionSet = false;
1000 aView1.m_bViewSelectionSet = false;
1001 aView2.m_bOwnSelectionSet = false;
1002 aView2.m_bViewSelectionSet = false;
1003 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1004 // Move the cursor into the second word.
1005 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 5, /*bBasicCall=*/false);
1006 // Create a selection on the word.
1007 pWrtShell->SelWrd();
1008 Scheduler::ProcessEventsToIdle();
1009 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
1010 // Did we indeed manage to select the second word?
1011 CPPUNIT_ASSERT_EQUAL(OUString("bbb"), pShellCursor->GetText());
1012 CPPUNIT_ASSERT(!aView1.m_bOwnSelectionSet);
1013 // This failed, aView1 did not get notification about selection changes in
1014 // aView2.
1015 CPPUNIT_ASSERT(aView1.m_bViewSelectionSet);
1016 CPPUNIT_ASSERT(aView2.m_bOwnSelectionSet);
1017 CPPUNIT_ASSERT(!aView2.m_bViewSelectionSet);
1020 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeViewCursors)
1022 // Load a document and create a view, so we have 2 ones.
1023 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
1024 ViewCallback aView1;
1025 SfxLokHelper::createView();
1026 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1027 ViewCallback aView2;
1028 SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
1030 // Start shape text in the second view.
1031 SdrPage* pPage = pWrtShell2->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1032 SdrObject* pObject = pPage->GetObj(0);
1033 SdrView* pView = pWrtShell2->GetDrawView();
1034 pWrtShell2->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell2->GetWin());
1035 emulateTyping(*pXTextDocument, u"x");
1036 // Press a key in the second view, while the first one observes this.
1037 aView1.m_bViewCursorInvalidated = false;
1038 aView2.m_bOwnCursorInvalidated = false;
1039 const tools::Rectangle aLastOwnCursor1 = aView1.m_aOwnCursor;
1040 const tools::Rectangle aLastViewCursor1 = aView1.m_aViewCursor;
1041 const tools::Rectangle aLastOwnCursor2 = aView2.m_aOwnCursor;
1042 const tools::Rectangle aLastViewCursor2 = aView2.m_aViewCursor;
1044 emulateTyping(*pXTextDocument, u"y");
1045 // Make sure that aView1 gets a view-only cursor notification, while
1046 // aView2 gets a real cursor notification.
1047 CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor, aLastOwnCursor1);
1048 CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
1049 CPPUNIT_ASSERT(aLastViewCursor1 != aView1.m_aViewCursor);
1050 CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
1051 CPPUNIT_ASSERT(aLastOwnCursor2 != aView2.m_aOwnCursor);
1052 CPPUNIT_ASSERT_EQUAL(aLastViewCursor2, aView2.m_aViewCursor);
1055 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewCursorVisibility)
1057 // Load a document that has a shape and create two views.
1058 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
1059 ViewCallback aView1;
1060 SfxLokHelper::createView();
1061 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1062 ViewCallback aView2;
1063 // This failed, initially the view cursor in the second view wasn't visible.
1064 CPPUNIT_ASSERT(aView2.m_bViewCursorVisible);
1066 // Click on the shape in the second view.
1067 aView1.m_bViewCursorVisible = true;
1068 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1069 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1070 SdrObject* pObject = pPage->GetObj(0);
1071 Point aCenter = pObject->GetSnapRect().Center();
1072 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
1073 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
1074 Scheduler::ProcessEventsToIdle();
1075 // Make sure the "view/text" cursor of the first view gets a notification.
1076 CPPUNIT_ASSERT(!aView1.m_bViewCursorVisible);
1079 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewCursorCleanup)
1081 // Load a document that has a shape and create two views.
1082 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
1083 ViewCallback aView1;
1084 int nView2 = SfxLokHelper::createView();
1085 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1087 ViewCallback aView2;
1089 // Click on the shape in the second view.
1090 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1091 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1092 SdrObject* pObject = pPage->GetObj(0);
1093 Point aCenter = pObject->GetSnapRect().Center();
1094 aView1.m_bGraphicViewSelection = false;
1095 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
1096 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
1097 Scheduler::ProcessEventsToIdle();
1098 // Make sure there is a graphic view selection on the first view.
1099 CPPUNIT_ASSERT(aView1.m_bGraphicViewSelection);
1101 // Now destroy the second view.
1102 SfxLokHelper::destroyView(nView2);
1103 Scheduler::ProcessEventsToIdle();
1104 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), SfxLokHelper::getViewsCount(0));
1105 // Make sure that the graphic view selection on the first view is cleaned up.
1106 CPPUNIT_ASSERT(!aView1.m_bGraphicViewSelection);
1109 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewLock)
1111 // Load a document that has a shape and create two views.
1112 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
1113 ViewCallback aView1;
1114 SfxLokHelper::createView();
1115 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1116 ViewCallback aView2;
1118 // Begin text edit in the second view and assert that the first gets a lock
1119 // notification.
1120 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1121 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1122 SdrObject* pObject = pPage->GetObj(0);
1123 SdrView* pView = pWrtShell->GetDrawView();
1124 aView1.m_bViewLock = false;
1125 pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
1126 CPPUNIT_ASSERT(aView1.m_bViewLock);
1128 // End text edit in the second view, and assert that the lock is removed in
1129 // the first view.
1130 pWrtShell->EndTextEdit();
1131 CPPUNIT_ASSERT(!aView1.m_bViewLock);
1134 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTextEditViewInvalidations)
1136 // Load a document that has a shape and create two views.
1137 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
1138 ViewCallback aView1;
1139 SfxLokHelper::createView();
1140 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1141 ViewCallback aView2;
1143 // Begin text edit in the second view.
1144 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1145 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1146 SdrObject* pObject = pPage->GetObj(0);
1147 SdrView* pView = pWrtShell->GetDrawView();
1148 pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
1149 emulateTyping(*pXTextDocument, u"x");
1151 // Assert that both views are invalidated when pressing a key while in text edit.
1152 aView1.m_bTilesInvalidated = false;
1153 emulateTyping(*pXTextDocument, u"y");
1155 CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
1157 pWrtShell->EndTextEdit();
1160 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoInvalidations)
1162 // Load a document and create two views.
1163 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1164 ViewCallback aView1;
1165 int nView1 = SfxLokHelper::getView();
1166 SfxLokHelper::createView();
1167 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1168 ViewCallback aView2;
1169 SfxLokHelper::setView(nView1);
1171 // Insert a character the end of the document.
1172 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1173 pWrtShell->EndOfSection();
1174 emulateTyping(*pXTextDocument, u"c");
1175 // ProcessEventsToIdle resets the view; set it again
1176 SfxLokHelper::setView(nView1);
1177 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
1178 CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb.c"), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
1180 // Undo and assert that both views are invalidated.
1181 Scheduler::ProcessEventsToIdle();
1182 aView1.m_bTilesInvalidated = false;
1183 aView2.m_bTilesInvalidated = false;
1184 comphelper::dispatchCommand(".uno:Undo", {});
1185 Scheduler::ProcessEventsToIdle();
1186 CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
1187 // Undo was dispatched on the first view, this second view was not invalidated.
1188 CPPUNIT_ASSERT(aView2.m_bTilesInvalidated);
1191 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoLimiting)
1193 // Load a document and create two views.
1194 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1195 SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
1196 int nView1 = SfxLokHelper::getView();
1197 int nView2 = SfxLokHelper::createView();
1198 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1200 // Insert a character the end of the document in the second view.
1201 SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
1202 pWrtShell2->EndOfSection();
1203 emulateTyping(*pXTextDocument, u"c");
1204 SwShellCursor* pShellCursor = pWrtShell2->getShellCursor(false);
1205 CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb.c"), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
1207 // Assert that the first view can't undo, but the second view can.
1208 CPPUNIT_ASSERT(!pWrtShell1->GetLastUndoInfo(nullptr, nullptr, &pWrtShell1->GetView()));
1209 CPPUNIT_ASSERT(pWrtShell2->GetLastUndoInfo(nullptr, nullptr, &pWrtShell2->GetView()));
1211 SfxLokHelper::setView(nView1);
1212 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1213 SfxLokHelper::setView(nView2);
1214 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1217 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReordering)
1219 // Create two views and a document of 2 paragraphs.
1220 SwXTextDocument* pXTextDocument = createDoc();
1221 SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
1222 int nView1 = SfxLokHelper::getView();
1223 int nView2 = SfxLokHelper::createView();
1224 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1225 SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
1226 pWrtShell2->SplitNode();
1227 SfxLokHelper::setView(nView1);
1228 pWrtShell1->SttEndDoc(/*bStt=*/true);
1229 SwTextNode* pTextNode1 = pWrtShell1->GetCursor()->GetPointNode().GetTextNode();
1230 // View 1 types into the first paragraph.
1231 emulateTyping(*pXTextDocument, u"a");
1232 SfxLokHelper::setView(nView2);
1233 pWrtShell2->SttEndDoc(/*bStt=*/false);
1234 SwTextNode* pTextNode2 = pWrtShell2->GetCursor()->GetPointNode().GetTextNode();
1235 // View 2 types into the second paragraph.
1236 emulateTyping(*pXTextDocument, u"z");
1237 CPPUNIT_ASSERT_EQUAL(OUString("a"), pTextNode1->GetText());
1238 CPPUNIT_ASSERT_EQUAL(OUString("z"), pTextNode2->GetText());
1240 // When view 1 presses undo:
1241 SfxLokHelper::setView(nView1);
1242 dispatchCommand(mxComponent, ".uno:Undo", {});
1244 // Then make sure view 1's last undo action is invoked, out of order:
1245 // Without the accompanying fix in place, this test would have failed with:
1246 // - Expression: pTextNode1->GetText().isEmpty()
1247 // i.e. the "a" in the first paragraph was not removed.
1248 CPPUNIT_ASSERT(pTextNode1->GetText().isEmpty());
1249 // Last undo action is not invoked, as it belongs to view 2.
1250 CPPUNIT_ASSERT_EQUAL(OUString("z"), pTextNode2->GetText());
1251 SfxLokHelper::setView(nView1);
1252 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1253 SfxLokHelper::setView(nView2);
1254 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1257 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReorderingRedo)
1259 // Create two views and a document of 2 paragraphs.
1260 SwXTextDocument* pXTextDocument = createDoc();
1261 SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
1262 int nView1 = SfxLokHelper::getView();
1263 int nView2 = SfxLokHelper::createView();
1264 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1265 SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
1266 pWrtShell2->SplitNode();
1267 SfxLokHelper::setView(nView1);
1268 pWrtShell1->SttEndDoc(/*bStt=*/true);
1269 SwTextNode* pTextNode1 = pWrtShell1->GetCursor()->GetPointNode().GetTextNode();
1270 // View 1 types into the first paragraph, twice.
1271 emulateTyping(*pXTextDocument, u"f");
1272 // Go to the start of the paragraph, to avoid grouping.
1273 pWrtShell1->SttEndDoc(/*bStt=*/true);
1274 emulateTyping(*pXTextDocument, u"s");
1275 SfxLokHelper::setView(nView2);
1276 pWrtShell2->SttEndDoc(/*bStt=*/false);
1277 SwTextNode* pTextNode2 = pWrtShell2->GetCursor()->GetPointNode().GetTextNode();
1278 // View 2 types into the second paragraph.
1279 emulateTyping(*pXTextDocument, u"z");
1280 CPPUNIT_ASSERT_EQUAL(OUString("sf"), pTextNode1->GetText());
1281 CPPUNIT_ASSERT_EQUAL(OUString("z"), pTextNode2->GetText());
1283 // When view 1 presses undo, twice:
1284 SfxLokHelper::setView(nView1);
1285 dispatchCommand(mxComponent, ".uno:Undo", {});
1286 // First just s(econd) is erased:
1287 CPPUNIT_ASSERT_EQUAL(OUString("f"), pTextNode1->GetText());
1288 dispatchCommand(mxComponent, ".uno:Undo", {});
1290 // Then make sure view 1's undo actions are invoked, out of order:
1291 // Without the accompanying fix in place, this test would have failed with:
1292 // - Expression: pTextNode1->GetText().isEmpty()
1293 // i.e. out of order undo was executed only once, not twice.
1294 CPPUNIT_ASSERT(pTextNode1->GetText().isEmpty());
1295 // The top undo action is not invoked, as it belongs to view 2.
1296 CPPUNIT_ASSERT_EQUAL(OUString("z"), pTextNode2->GetText());
1297 SfxLokHelper::setView(nView1);
1298 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1299 SfxLokHelper::setView(nView2);
1300 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1303 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReorderingRedo2)
1305 // Create two views.
1306 SwXTextDocument* pXTextDocument = createDoc();
1307 SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
1308 int nView1 = SfxLokHelper::getView();
1309 int nView2 = SfxLokHelper::createView();
1310 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1311 SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
1313 // Type in the first view.
1314 SfxLokHelper::setView(nView1);
1315 pWrtShell1->SttEndDoc(/*bStt=*/true);
1316 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'f', 0);
1317 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'f', 0);
1318 Scheduler::ProcessEventsToIdle();
1320 // Type to the same paragraph in the second view.
1321 SfxLokHelper::setView(nView2);
1322 pWrtShell2->SttEndDoc(/*bStt=*/true);
1323 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 's', 0);
1324 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 's', 0);
1325 Scheduler::ProcessEventsToIdle();
1327 // Delete in the first view and undo.
1328 SfxLokHelper::setView(nView1);
1329 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::BACKSPACE);
1330 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::BACKSPACE);
1331 Scheduler::ProcessEventsToIdle();
1332 dispatchCommand(mxComponent, ".uno:Undo", {});
1333 Scheduler::ProcessEventsToIdle();
1335 // Query the undo state, now that a "delete" is on the redo stack and an "insert" belongs to the
1336 // view on the undo stack, so the types are different.
1337 SwUndoId nUndoId(SwUndoId::EMPTY);
1338 // Without the accompanying fix in place, this test would have failed with:
1339 // runtime error: downcast which does not point to an object of type 'const SwUndoInsert'
1340 // note: object is of type 'SwUndoDelete'
1341 // in an UBSan build.
1342 pWrtShell1->GetLastUndoInfo(nullptr, &nUndoId, &pWrtShell1->GetView());
1345 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReorderingMulti)
1347 // Create two views and a document of 2 paragraphs.
1348 SwXTextDocument* pXTextDocument = createDoc();
1349 SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
1350 int nView1 = SfxLokHelper::getView();
1351 int nView2 = SfxLokHelper::createView();
1352 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1353 SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
1354 pWrtShell2->SplitNode();
1355 SfxLokHelper::setView(nView1);
1356 pWrtShell1->SttEndDoc(/*bStt=*/true);
1357 SwTextNode* pTextNode1 = pWrtShell1->GetCursor()->GetPointNode().GetTextNode();
1358 // View 1 types into the first paragraph.
1359 emulateTyping(*pXTextDocument, u"a");
1360 SfxLokHelper::setView(nView2);
1361 pWrtShell2->SttEndDoc(/*bStt=*/false);
1362 SwTextNode* pTextNode2 = pWrtShell2->GetCursor()->GetPointNode().GetTextNode();
1363 // View 2 types into the second paragraph, twice.
1364 emulateTyping(*pXTextDocument, u"x");
1365 // Go to the start of the paragraph, to avoid grouping.
1366 pWrtShell2->SttPara();
1367 emulateTyping(*pXTextDocument, u"y");
1368 CPPUNIT_ASSERT_EQUAL(OUString("a"), pTextNode1->GetText());
1369 CPPUNIT_ASSERT_EQUAL(OUString("yx"), pTextNode2->GetText());
1371 // When view 1 presses undo:
1372 SfxLokHelper::setView(nView1);
1373 dispatchCommand(mxComponent, ".uno:Undo", {});
1375 // Then make sure view 1's undo action is invoked, out of order:
1376 // Without the accompanying fix in place, this test would have failed with:
1377 // - Expression: pTextNode1->GetText().isEmpty()
1378 // i.e. out of order undo was not executed, the first paragrph was still "a".
1379 CPPUNIT_ASSERT(pTextNode1->GetText().isEmpty());
1380 // The top 2 undo actions are not invoked, as they belong to view 2.
1381 CPPUNIT_ASSERT_EQUAL(OUString("yx"), pTextNode2->GetText());
1382 SfxLokHelper::setView(nView1);
1383 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1384 SfxLokHelper::setView(nView2);
1385 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1388 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoShapeLimiting)
1390 // Load a document and create a view.
1391 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
1392 SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
1393 int nView1 = SfxLokHelper::getView();
1394 int nView2 = SfxLokHelper::createView();
1395 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1396 SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
1398 // Start shape text in the second view.
1399 SdrPage* pPage = pWrtShell2->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1400 SdrObject* pObject = pPage->GetObj(0);
1401 SdrView* pView = pWrtShell2->GetDrawView();
1402 pWrtShell2->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell2->GetWin());
1403 emulateTyping(*pXTextDocument, u"x");
1404 pWrtShell2->EndTextEdit();
1406 // Assert that the first view can't and the second view can undo the insertion.
1407 SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
1408 sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
1409 rUndoManager.SetView(&pWrtShell1->GetView());
1410 // This was 1: first view could undo the change of the second view.
1411 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), rUndoManager.GetUndoActionCount());
1412 rUndoManager.SetView(&pWrtShell2->GetView());
1413 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
1415 rUndoManager.SetView(nullptr);
1417 SfxLokHelper::setView(nView1);
1418 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1419 SfxLokHelper::setView(nView2);
1420 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1423 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoDispatch)
1425 // Load a document and create two views.
1426 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1427 int nView1 = SfxLokHelper::getView();
1428 SfxLokHelper::createView();
1429 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1430 int nView2 = SfxLokHelper::getView();
1432 // Insert a character in the first view.
1433 SfxLokHelper::setView(nView1);
1434 emulateTyping(*pXTextDocument, u"c");
1436 // Click before the first word in the second view.
1437 SfxLokHelper::setView(nView2);
1438 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1439 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
1440 Point aStart = pShellCursor->GetSttPos();
1441 aStart.setX(aStart.getX() - 1000);
1442 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
1443 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
1444 Scheduler::ProcessEventsToIdle();
1445 uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(comphelper::getProcessComponentContext());
1446 uno::Reference<frame::XFrame> xFrame2 = xDesktop->getActiveFrame();
1448 // Now switch back to the first view, and make sure that the active frame is updated.
1449 SfxLokHelper::setView(nView1);
1450 uno::Reference<frame::XFrame> xFrame1 = xDesktop->getActiveFrame();
1451 // This failed: setView() did not update the active frame.
1452 CPPUNIT_ASSERT(xFrame1 != xFrame2);
1454 SfxLokHelper::setView(nView1);
1455 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1456 SfxLokHelper::setView(nView2);
1457 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1460 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoRepairDispatch)
1462 // Load a document and create two views.
1463 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1464 int nView1 = SfxLokHelper::getView();
1465 SfxLokHelper::createView();
1466 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1467 int nView2 = SfxLokHelper::getView();
1469 // Insert a character in the first view.
1470 SfxLokHelper::setView(nView1);
1471 emulateTyping(*pXTextDocument, u"c");
1473 // Assert that by default the second view can't undo the action.
1474 SfxLokHelper::setView(nView2);
1475 SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
1476 sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
1477 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
1478 comphelper::dispatchCommand(".uno:Undo", {});
1479 Scheduler::ProcessEventsToIdle();
1480 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
1482 // But the same is allowed in repair mode.
1483 SfxLokHelper::setView(nView2);
1484 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
1485 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
1487 {"Repair", uno::Any(true)}
1488 }));
1489 comphelper::dispatchCommand(".uno:Undo", aPropertyValues);
1490 Scheduler::ProcessEventsToIdle();
1491 // This was 1: repair mode couldn't undo the action, either.
1492 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), rUndoManager.GetUndoActionCount());
1494 SfxLokHelper::setView(nView1);
1495 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1496 SfxLokHelper::setView(nView2);
1497 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1500 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeTextUndoShells)
1502 // Load a document and create a view.
1503 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
1504 sal_Int32 nView1 = SfxLokHelper::getView();
1506 // Begin text edit.
1507 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1508 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1509 SdrObject* pObject = pPage->GetObj(0);
1510 SdrView* pView = pWrtShell->GetDrawView();
1511 pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
1512 emulateTyping(*pXTextDocument, u"x");
1513 pWrtShell->EndTextEdit();
1515 // Make sure that the undo item remembers who created it.
1516 SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
1517 sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
1518 CPPUNIT_ASSERT_EQUAL(size_t(1), rUndoManager.GetUndoActionCount());
1519 CPPUNIT_ASSERT_EQUAL(OUString("Edit text of Shape 'Shape1'"), rUndoManager.GetUndoActionComment(0));
1521 // This was -1: the view shell id for the undo action wasn't known.
1522 CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), rUndoManager.GetUndoAction()->GetViewShellId());
1525 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeTextUndoGroupShells)
1527 // Load a document and create a view.
1528 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
1529 ViewCallback aView1;
1530 sal_Int32 nView1 = SfxLokHelper::getView();
1532 // Begin text edit.
1533 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1534 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1535 SdrObject* pObject = pPage->GetObj(0);
1536 SdrView* pView = pWrtShell->GetDrawView();
1537 pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
1538 emulateTyping(*pXTextDocument, u"x");
1539 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::BACKSPACE);
1540 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::BACKSPACE);
1541 Scheduler::ProcessEventsToIdle();
1543 // Make sure that the undo item remembers who created it.
1544 SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
1545 sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
1546 CPPUNIT_ASSERT_EQUAL(size_t(0), rUndoManager.GetUndoActionCount());
1548 pWrtShell->EndTextEdit();
1549 pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
1551 CPPUNIT_ASSERT_EQUAL(size_t(1), rUndoManager.GetUndoActionCount());
1552 CPPUNIT_ASSERT_EQUAL(OUString("Edit text of Shape 'Shape1'"), rUndoManager.GetUndoActionComment(0));
1554 // This was -1: the view shell id for the (top) undo list action wasn't known.
1555 CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), rUndoManager.GetUndoAction()->GetViewShellId());
1557 // Create an editeng text selection in the first view.
1558 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
1559 emulateTyping(*pXTextDocument, u"x");
1560 // 0th para, 0th char -> 0th para, 1st char.
1561 ESelection aWordSelection(0, 0, 0, 1);
1562 rEditView.SetSelection(aWordSelection);
1564 // Create a second view, and make sure that the new view sees the same
1565 // cursor position as the old one.
1566 SfxLokHelper::createView();
1567 pXTextDocument->initializeForTiledRendering({});
1568 ViewCallback aView2;
1569 // Difference was 935 twips, the new view didn't see the editeng cursor of
1570 // the old one. The new difference should be <1px, but here we deal with twips.
1571 CPPUNIT_ASSERT(std::abs(aView1.m_aOwnCursor.Top() - aView2.m_aViewCursor.Top()) < 10);
1572 // This was false, editeng text selection of the first view wasn't noticed
1573 // by the second view.
1574 CPPUNIT_ASSERT(aView2.m_bViewSelectionSet);
1575 // This was false, the new view wasn't aware of the shape text lock created
1576 // by the old view.
1577 CPPUNIT_ASSERT(aView2.m_bViewLock);
1580 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChanges)
1582 // Load a document.
1583 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1585 // Turn on track changes, type "zzz" at the end, and move to the start.
1586 uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
1587 xPropertySet->setPropertyValue("RecordChanges", uno::Any(true));
1588 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1589 ViewCallback aView(pWrtShell->GetSfxViewShell());
1590 pWrtShell->EndOfSection();
1591 pWrtShell->Insert("zzz");
1592 pWrtShell->StartOfSection();
1594 // Get the redline just created
1595 const SwRedlineTable& rTable = pWrtShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1596 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), rTable.size());
1597 SwRangeRedline* pRedline = rTable[0];
1599 // Reject the change by id, while the cursor does not cover the tracked change.
1600 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
1602 {"RejectTrackedChange", uno::Any(o3tl::narrowing<sal_uInt16>(pRedline->GetId()))}
1603 }));
1604 comphelper::dispatchCommand(".uno:RejectTrackedChange", aPropertyValues);
1605 Scheduler::ProcessEventsToIdle();
1607 // Assert that the reject was performed.
1608 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
1609 // This was 'Aaa bbb.zzz', the change wasn't rejected.
1610 CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb."), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
1613 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChangesCallback)
1615 // Load a document.
1616 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1617 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1618 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
1620 // Turn on track changes and type "x".
1621 uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
1622 xPropertySet->setPropertyValue("RecordChanges", uno::Any(true));
1623 m_nRedlineTableSizeChanged = 0;
1624 pWrtShell->Insert("x");
1626 // Assert that we get exactly one notification about the redline insert.
1627 // This was 0, as LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED wasn't sent.
1628 CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableSizeChanged);
1630 CPPUNIT_ASSERT_EQUAL(-1, m_nTrackedChangeIndex);
1631 pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
1632 SfxItemSet aSet(pWrtShell->GetDoc()->GetAttrPool(), svl::Items<FN_REDLINE_ACCEPT_DIRECT, FN_REDLINE_ACCEPT_DIRECT>);
1633 SfxVoidItem aItem(FN_REDLINE_ACCEPT_DIRECT);
1634 aSet.Put(aItem);
1635 pWrtShell->GetView().GetState(aSet);
1636 // This failed, LOK_CALLBACK_STATE_CHANGED wasn't sent.
1637 CPPUNIT_ASSERT_EQUAL(0, m_nTrackedChangeIndex);
1640 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlineUpdateCallback)
1642 // Load a document.
1643 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1644 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1645 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
1647 // Turn on track changes, type "xx" and delete the second one.
1648 uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
1649 xPropertySet->setPropertyValue("RecordChanges", uno::Any(true));
1650 pWrtShell->Insert("xx");
1651 m_nRedlineTableEntryModified = 0;
1652 pWrtShell->DelLeft();
1654 // Assert that we get exactly one notification about the redline update.
1655 // This was 0, as LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED wasn't sent.
1656 CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableEntryModified);
1658 // Turn off the change tracking mode, make some modification to left of the
1659 // redline so that its position changes
1660 xPropertySet->setPropertyValue("RecordChanges", uno::Any(false));
1661 pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
1662 pWrtShell->Insert("This text is left of the redline");
1664 // Position of the redline has changed => Modify callback
1665 CPPUNIT_ASSERT_EQUAL(2, m_nRedlineTableEntryModified);
1667 pWrtShell->DelLeft();
1668 // Deletion also emits Modify callback
1669 CPPUNIT_ASSERT_EQUAL(3, m_nRedlineTableEntryModified);
1671 // Make changes to the right of the redline => no position change in redline
1672 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 100/*Go enough right */, /*bBasicCall=*/false);
1673 pWrtShell->Insert("This text is right of the redline");
1675 // No Modify callbacks
1676 CPPUNIT_ASSERT_EQUAL(3, m_nRedlineTableEntryModified);
1679 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetViewRenderState)
1681 SwXTextDocument* pXTextDocument = createDoc();
1682 int nFirstViewId = SfxLokHelper::getView();
1683 ViewCallback aView1;
1685 SwViewOption aViewOptions;
1686 aViewOptions.SetViewMetaChars(true);
1687 aViewOptions.SetOnlineSpell(true);
1688 pXTextDocument->GetDocShell()->GetWrtShell()->ApplyViewOptions(aViewOptions);
1690 CPPUNIT_ASSERT_EQUAL(OString("PS;Default"), pXTextDocument->getViewRenderState());
1692 // Create a second view
1693 SfxLokHelper::createView();
1694 int nSecondViewId = SfxLokHelper::getView();
1695 ViewCallback aView2;
1697 // Give the second view different options
1698 SwViewOption aViewOptions;
1699 aViewOptions.SetViewMetaChars(false);
1700 aViewOptions.SetOnlineSpell(true);
1701 pXTextDocument->GetDocShell()->GetWrtShell()->ApplyViewOptions(aViewOptions);
1703 CPPUNIT_ASSERT_EQUAL(OString("S;Default"), pXTextDocument->getViewRenderState());
1705 // Switch back to the first view, and check that the options string is the same
1706 SfxLokHelper::setView(nFirstViewId);
1707 CPPUNIT_ASSERT_EQUAL(OString("PS;Default"), pXTextDocument->getViewRenderState());
1709 // Switch back to the second view, and change to dark mode
1710 SfxLokHelper::setView(nSecondViewId);
1712 SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
1713 SwView* pView = pDoc->GetDocShell()->GetView();
1714 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1715 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1717 { "NewTheme", uno::Any(OUString("Dark")) },
1720 comphelper::dispatchCommand(".uno:ChangeTheme", xFrame, aPropertyValues);
1722 CPPUNIT_ASSERT_EQUAL(OString("S;Dark"), pXTextDocument->getViewRenderState());
1723 // Switch back to the first view, and check that the options string is the same
1724 SfxLokHelper::setView(nFirstViewId);
1725 CPPUNIT_ASSERT_EQUAL(OString("PS;Default"), pXTextDocument->getViewRenderState());
1728 // Helper function to get a tile to a bitmap and check the pixel color
1729 static void assertTilePixelColor(SwXTextDocument* pXTextDocument, int nPixelX, int nPixelY, Color aColor)
1731 size_t nCanvasSize = 1024;
1732 size_t nTileSize = 256;
1733 std::vector<unsigned char> aPixmap(nCanvasSize * nCanvasSize * 4, 0);
1734 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
1735 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
1736 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasSize, nCanvasSize),
1737 Fraction(1.0), Point(), aPixmap.data());
1738 pXTextDocument->paintTile(*pDevice, nCanvasSize, nCanvasSize, 0, 0, 15360, 7680);
1739 pDevice->EnableMapMode(false);
1740 Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
1741 Bitmap::ScopedReadAccess pAccess(aBitmap);
1742 Color aActualColor(pAccess->GetPixel(nPixelX, nPixelY));
1743 CPPUNIT_ASSERT_EQUAL(aColor, aActualColor);
1746 // Test that changing the theme in one view doesn't change it in the other view
1747 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testThemeViewSeparation)
1749 Color aDarkColor(0x1c, 0x1c, 0x1c);
1750 // Add a minimal dark scheme
1752 svtools::EditableColorConfig aColorConfig;
1753 svtools::ColorConfigValue aValue;
1754 aValue.bIsVisible = true;
1755 aValue.nColor = aDarkColor;
1756 aColorConfig.SetColorValue(svtools::DOCCOLOR, aValue);
1757 aColorConfig.AddScheme(u"Dark");
1759 // Add a minimal light scheme
1761 svtools::EditableColorConfig aColorConfig;
1762 svtools::ColorConfigValue aValue;
1763 aValue.bIsVisible = true;
1764 aValue.nColor = COL_WHITE;
1765 aColorConfig.SetColorValue(svtools::DOCCOLOR, aValue);
1766 aColorConfig.AddScheme(u"Light");
1768 SwXTextDocument* pXTextDocument = createDoc();
1769 int nFirstViewId = SfxLokHelper::getView();
1770 ViewCallback aView1;
1771 // Set first view to light scheme
1773 SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
1774 SwView* pView = pDoc->GetDocShell()->GetView();
1775 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1776 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1778 { "NewTheme", uno::Any(OUString("Light")) },
1781 comphelper::dispatchCommand(".uno:ChangeTheme", xFrame, aPropertyValues);
1783 // First view is in light scheme
1784 assertTilePixelColor(pXTextDocument, 255, 255, COL_WHITE);
1785 // Create second view
1786 SfxLokHelper::createView();
1787 int nSecondViewId = SfxLokHelper::getView();
1788 ViewCallback aView2;
1789 // Set second view to dark scheme
1791 SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
1792 SwView* pView = pDoc->GetDocShell()->GetView();
1793 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1794 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1796 { "NewTheme", uno::Any(OUString("Dark")) },
1799 comphelper::dispatchCommand(".uno:ChangeTheme", xFrame, aPropertyValues);
1801 assertTilePixelColor(pXTextDocument, 255, 255, aDarkColor);
1802 // First view still in light scheme
1803 SfxLokHelper::setView(nFirstViewId);
1804 assertTilePixelColor(pXTextDocument, 255, 255, COL_WHITE);
1805 // Second view still in dark scheme
1806 SfxLokHelper::setView(nSecondViewId);
1807 assertTilePixelColor(pXTextDocument, 255, 255, aDarkColor);
1808 // Switch second view back to light scheme
1810 SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
1811 SwView* pView = pDoc->GetDocShell()->GetView();
1812 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1813 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1815 { "NewTheme", uno::Any(OUString("Light")) },
1818 comphelper::dispatchCommand(".uno:ChangeTheme", xFrame, aPropertyValues);
1820 // Now in light scheme
1821 assertTilePixelColor(pXTextDocument, 255, 255, COL_WHITE);
1824 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSetViewGraphicSelection)
1826 // Load a document.
1827 SwXTextDocument* pXTextDocument = createDoc("frame.odt");
1828 int nView1 = SfxLokHelper::getView();
1829 ViewCallback aView1;
1830 // Create a second view, and switch back to the first view.
1831 SfxLokHelper::createView();
1832 pXTextDocument->initializeForTiledRendering({});
1833 SfxLokHelper::setView(nView1);
1835 // Mark the textframe in the first view.
1836 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1837 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1838 SdrObject* pObject = pPage->GetObj(0);
1839 SdrView* pView = pWrtShell->GetDrawView();
1840 pView->MarkObj(pObject, pView->GetSdrPageView());
1841 CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
1843 // Now start to switch to the second view (part of setView()).
1844 pWrtShell->ShellLoseFocus();
1845 // This failed, mark handles were hidden in the first view.
1846 CPPUNIT_ASSERT(!pView->areMarkHandlesHidden());
1849 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCreateViewGraphicSelection)
1851 // Load a document.
1852 SwXTextDocument* pXTextDocument = createDoc("frame.odt");
1853 ViewCallback aView1;
1855 // Mark the textframe in the first view.
1856 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1857 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1858 SdrObject* pObject = pPage->GetObj(0);
1859 SdrView* pView = pWrtShell->GetDrawView();
1860 aView1.m_bGraphicSelection = true;
1861 pView->MarkObj(pObject, pView->GetSdrPageView());
1862 pWrtShell->HideCursor();
1863 CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
1865 // Create a second view.
1866 SfxLokHelper::createView();
1867 // This was false, creating a second view cleared the selection of the
1868 // first one.
1869 CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
1871 // Make sure that the hidden text cursor isn't visible in the second view, either.
1872 ViewCallback aView2(SfxViewShell::Current(),
1873 [](ViewCallback& rView) { rView.m_bViewCursorVisible = true; });
1874 // This was true, the second view didn't get the visibility of the text
1875 // cursor of the first view.
1876 CPPUNIT_ASSERT(!aView2.m_bViewCursorVisible);
1877 // This was false, the second view didn't get the graphic selection of the
1878 // first view.
1879 CPPUNIT_ASSERT(aView2.m_bGraphicViewSelection);
1882 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCreateViewTextSelection)
1884 // Load a document.
1885 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1886 ViewCallback aView1;
1888 // Create a text selection:
1889 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1890 // Move the cursor into the second word.
1891 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 5, /*bBasicCall=*/false);
1892 // Create a selection on the word.
1893 pWrtShell->SelWrd();
1894 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
1895 // Did we indeed manage to select the second word?
1896 CPPUNIT_ASSERT_EQUAL(OUString("bbb"), pShellCursor->GetText());
1898 // Create a second view.
1899 SfxLokHelper::createView();
1901 // Make sure that the text selection is visible in the second view.
1902 ViewCallback aView2;
1903 // This failed, the second view didn't get the text selection of the first view.
1904 CPPUNIT_ASSERT(!aView2.m_aViewSelection.isEmpty());
1907 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlineColors)
1909 // Load a document.
1910 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1912 // Turn on track changes, type "zzz" at the end.
1913 uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
1914 xPropertySet->setPropertyValue("RecordChanges", uno::Any(true));
1915 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1916 pWrtShell->EndOfSection();
1917 pWrtShell->Insert("zzz");
1919 // Assert that info about exactly one author is returned.
1920 tools::JsonWriter aJsonWriter;
1921 pXTextDocument->getTrackedChangeAuthors(aJsonWriter);
1922 std::stringstream aStream((std::string(aJsonWriter.finishAndGetAsOString())));
1923 boost::property_tree::ptree aTree;
1924 boost::property_tree::read_json(aStream, aTree);
1925 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree.get_child("authors").size());
1928 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCommentEndTextEdit)
1930 // Create a document, type a character and remember the cursor position.
1931 SwXTextDocument* pXTextDocument = createDoc();
1932 ViewCallback aView1;
1933 emulateTyping(*pXTextDocument, u"x");
1934 tools::Rectangle aBodyCursor = aView1.m_aOwnCursor;
1936 // Create a comment and type a character there as well.
1937 const int nCtrlAltC = KEY_MOD1 + KEY_MOD2 + 512 + 'c' - 'a';
1938 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', nCtrlAltC);
1939 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', nCtrlAltC);
1940 emulateTyping(*pXTextDocument, u"x");
1941 // End comment text edit by clicking in the body text area, and assert that
1942 // no unexpected cursor callbacks are emitted at origin (top left corner of
1943 // the document).
1944 aView1.m_bOwnCursorAtOrigin = false;
1945 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aBodyCursor.Left(), aBodyCursor.Top(), 1, MOUSE_LEFT, 0);
1946 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aBodyCursor.Left(), aBodyCursor.Top(), 1, MOUSE_LEFT, 0);
1947 Scheduler::ProcessEventsToIdle();
1948 // This failed, the cursor was at 0, 0 at some point during end text edit
1949 // of the comment.
1950 CPPUNIT_ASSERT(!aView1.m_bOwnCursorAtOrigin);
1952 // Hit enter and expect invalidation.
1953 Scheduler::ProcessEventsToIdle();
1954 aView1.m_bTilesInvalidated = false;
1955 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
1956 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
1957 Scheduler::ProcessEventsToIdle();
1958 CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
1961 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCommentInsert)
1963 // Load a document with an as-char image in it.
1964 comphelper::LibreOfficeKit::setTiledAnnotations(false);
1965 SwXTextDocument* pXTextDocument = createDoc("image-comment.odt");
1966 SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
1967 SwView* pView = pDoc->GetDocShell()->GetView();
1969 selectShape(1);
1971 // Add a comment.
1972 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1973 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1975 {"Text", uno::Any(OUString("some text"))},
1976 {"Author", uno::Any(OUString("me"))},
1978 ViewCallback aView;
1979 comphelper::dispatchCommand(".uno:InsertAnnotation", xFrame, aPropertyValues);
1980 Scheduler::ProcessEventsToIdle();
1981 OString aAnchorPos(aView.m_aComment.get_child("anchorPos").get_value<std::string>());
1982 // Without the accompanying fix in place, this test would have failed with
1983 // - Expected: 1418, 1418, 0, 0
1984 // - Actual : 1418, 1418, 1024, 1024
1985 // i.e. the anchor position was a non-empty rectangle.
1986 CPPUNIT_ASSERT_EQUAL(OString("1418, 1418, 0, 0"), aAnchorPos);
1987 comphelper::LibreOfficeKit::setTiledAnnotations(true);
1990 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCursorPosition)
1992 // Load a document and register a callback, should get an own cursor.
1993 SwXTextDocument* pXTextDocument = createDoc();
1994 ViewCallback aView1;
1996 // Create a second view, so the first view gets a collaborative cursor.
1997 SfxLokHelper::createView();
1998 pXTextDocument->initializeForTiledRendering({});
1999 ViewCallback aView2;
2001 // Make sure the two are exactly the same.
2002 // This failed, own cursor was at '1418, 1418', collaborative cursor was at
2003 // '1425, 1425', due to pixel alignment.
2004 CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor.toString(), aView1.m_aViewCursor.toString());
2007 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPaintCallbacks)
2009 // Test that paintTile() never results in callbacks, which can cause a
2010 // paint <-> invalidate loop.
2012 // Load a document and register a callback for the first view.
2013 SwXTextDocument* pXTextDocument = createDoc();
2014 ViewCallback aView1;
2016 // Create a second view and paint a tile on that second view.
2017 SfxLokHelper::createView();
2018 int nCanvasWidth = 256;
2019 int nCanvasHeight = 256;
2020 std::vector<unsigned char> aBuffer(nCanvasWidth * nCanvasHeight * 4);
2021 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2022 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), aBuffer.data());
2023 // Make sure that painting a tile in the second view doesn't invoke
2024 // callbacks on the first view.
2025 aView1.m_bCalled = false;
2026 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0, /*nTilePosY=*/0, /*nTileWidth=*/3840, /*nTileHeight=*/3840);
2027 CPPUNIT_ASSERT(!aView1.m_bCalled);
2030 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoRepairResult)
2032 // Load a document and create two views.
2033 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2034 int nView1 = SfxLokHelper::getView();
2035 SfxLokHelper::createView();
2036 rtl::Reference<TestResultListener> pResult2 = new TestResultListener();
2037 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
2038 int nView2 = SfxLokHelper::getView();
2040 // Insert a character in the second view.
2041 SfxLokHelper::setView(nView2);
2042 emulateTyping(*pXTextDocument, u"b");
2044 // Insert a character in the first view.
2045 SfxLokHelper::setView(nView1);
2046 emulateTyping(*pXTextDocument, u"a");
2048 // Assert that by default the second view can't undo the action.
2049 SfxLokHelper::setView(nView2);
2050 comphelper::dispatchCommand(".uno:Undo", {}, pResult2);
2051 Scheduler::ProcessEventsToIdle();
2052 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pResult2->m_nDocRepair);
2054 SfxLokHelper::setView(nView1);
2055 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2056 SfxLokHelper::setView(nView2);
2057 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2060 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedoRepairResult)
2062 // Load a document and create two views.
2063 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2064 int nView1 = SfxLokHelper::getView();
2065 SfxLokHelper::createView();
2066 rtl::Reference<TestResultListener> pResult2 = new TestResultListener();
2067 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
2068 int nView2 = SfxLokHelper::getView();
2070 // Insert a character in the second view.
2071 SfxLokHelper::setView(nView2);
2072 emulateTyping(*pXTextDocument, u"b");
2074 // Insert a character in the first view.
2075 SfxLokHelper::setView(nView1);
2076 emulateTyping(*pXTextDocument, u"a");
2078 comphelper::dispatchCommand(".uno:Undo", {}, pResult2);
2079 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(0), pResult2->m_nDocRepair);
2081 // Assert that by default the second view can't redo the action.
2082 SfxLokHelper::setView(nView2);
2083 comphelper::dispatchCommand(".uno:Redo", {}, pResult2);
2084 Scheduler::ProcessEventsToIdle();
2085 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pResult2->m_nDocRepair);
2087 SfxLokHelper::setView(nView1);
2088 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2089 SfxLokHelper::setView(nView2);
2090 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2093 namespace {
2095 void checkUndoRepairStates(SwXTextDocument* pXTextDocument, SwView* pView1, SwView* pView2)
2097 SfxItemSet aItemSet1(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
2098 SfxItemSet aItemSet2(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
2099 // first view, undo enabled
2100 pView1->GetState(aItemSet1);
2101 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet1.GetItemState(SID_UNDO));
2102 const SfxUInt32Item *pUnsetItem = dynamic_cast<const SfxUInt32Item*>(aItemSet1.GetItem(SID_UNDO));
2103 CPPUNIT_ASSERT(!pUnsetItem);
2104 // second view, undo conflict
2105 pView2->GetState(aItemSet2);
2106 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet2.GetItemState(SID_UNDO));
2107 const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item*>(aItemSet2.GetItem(SID_UNDO));
2108 CPPUNIT_ASSERT(pUInt32Item);
2109 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pUInt32Item->GetValue());
2114 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDisableUndoRepair)
2116 // Create two views.
2117 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2118 ViewCallback aView1;
2119 SwView* pView1 = dynamic_cast<SwView*>(SfxViewShell::Current());
2120 CPPUNIT_ASSERT(pView1);
2121 int nView1 = SfxLokHelper::getView();
2122 SfxLokHelper::createView();
2123 ViewCallback aView2;
2124 SwView* pView2 = dynamic_cast<SwView*>(SfxViewShell::Current());
2125 CPPUNIT_ASSERT(pView2);
2126 int nView2 = SfxLokHelper::getView();
2129 SfxItemSet aItemSet1(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
2130 SfxItemSet aItemSet2(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
2131 pView1->GetState(aItemSet1);
2132 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, aItemSet1.GetItemState(SID_UNDO));
2133 pView2->GetState(aItemSet2);
2134 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, aItemSet2.GetItemState(SID_UNDO));
2137 // Insert a character in the first view.
2138 SfxLokHelper::setView(nView1);
2139 emulateTyping(*pXTextDocument, u"k");
2140 checkUndoRepairStates(pXTextDocument, pView1, pView2);
2142 // Insert a character in the second view.
2143 SfxLokHelper::setView(nView2);
2144 emulateTyping(*pXTextDocument, u"u");
2146 SfxItemSet aItemSet1(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
2147 SfxItemSet aItemSet2(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
2148 // second view, undo enabled
2149 pView2->GetState(aItemSet2);
2150 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet2.GetItemState(SID_UNDO));
2151 const SfxUInt32Item *pUnsetItem = dynamic_cast<const SfxUInt32Item*>(aItemSet2.GetItem(SID_UNDO));
2152 CPPUNIT_ASSERT(!pUnsetItem);
2153 // first view, undo conflict
2154 pView1->GetState(aItemSet1);
2155 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet1.GetItemState(SID_UNDO));
2156 const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item*>(aItemSet1.GetItem(SID_UNDO));
2157 CPPUNIT_ASSERT(pUInt32Item);
2158 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pUInt32Item->GetValue());
2161 // Insert a character in the first view.
2162 SfxLokHelper::setView(nView1);
2163 emulateTyping(*pXTextDocument, u"l");
2164 checkUndoRepairStates(pXTextDocument, pView1, pView2);
2167 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAllTrackedChanges)
2169 // Load a document.
2170 createDoc("dummy.fodt");
2172 uno::Reference<beans::XPropertySet> xPropSet(mxComponent, uno::UNO_QUERY);
2173 xPropSet->setPropertyValue("RecordChanges", uno::Any(true));
2175 // view #1
2176 SwView* pView1 = dynamic_cast<SwView*>(SfxViewShell::Current());
2177 CPPUNIT_ASSERT(pView1);
2178 SwWrtShell* pWrtShell1 = pView1->GetWrtShellPtr();
2180 // view #2
2181 int nView1 = SfxLokHelper::getView();
2182 int nView2 = SfxLokHelper::createView();
2183 SwView* pView2 = dynamic_cast<SwView*>(SfxViewShell::Current());
2184 CPPUNIT_ASSERT(pView2);
2185 CPPUNIT_ASSERT(pView1 != pView2);
2186 SwWrtShell* pWrtShell2 = pView2->GetWrtShellPtr();
2187 // Insert text and reject all
2189 pWrtShell1->StartOfSection();
2190 pWrtShell1->Insert("hxx");
2192 pWrtShell2->EndOfSection();
2193 pWrtShell2->Insert("cxx");
2196 // Get the redline
2197 const SwRedlineTable& rTable = pWrtShell2->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
2198 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(2), rTable.size());
2200 SfxVoidItem aItem(FN_REDLINE_REJECT_ALL);
2201 pView1->GetViewFrame().GetDispatcher()->ExecuteList(FN_REDLINE_REJECT_ALL,
2202 SfxCallMode::SYNCHRON, { &aItem });
2205 // The reject all was performed.
2206 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(0), rTable.size());
2208 SwShellCursor* pShellCursor = pWrtShell1->getShellCursor(false);
2209 CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb."), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
2212 // Insert text and accept all
2214 pWrtShell1->StartOfSection();
2215 pWrtShell1->Insert("hyy");
2217 pWrtShell2->EndOfSection();
2218 pWrtShell2->Insert("cyy");
2221 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(2), rTable.size());
2223 SfxVoidItem aItem(FN_REDLINE_ACCEPT_ALL);
2224 pView1->GetViewFrame().GetDispatcher()->ExecuteList(FN_REDLINE_ACCEPT_ALL,
2225 SfxCallMode::SYNCHRON, { &aItem });
2228 // The accept all was performed
2229 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(0), rTable.size());
2231 SwShellCursor* pShellCursor = pWrtShell2->getShellCursor(false);
2232 CPPUNIT_ASSERT_EQUAL(OUString("hyyAaa bbb.cyy"), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
2235 SfxLokHelper::setView(nView1);
2236 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2237 SfxLokHelper::setView(nView2);
2238 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2241 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDocumentRepair)
2243 // Create two views.
2244 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2245 // view #1
2246 SfxViewShell* pView1 = SfxViewShell::Current();
2248 // view #2
2249 int nView1 = SfxLokHelper::getView();
2250 SfxLokHelper::createView();
2251 SfxViewShell* pView2 = SfxViewShell::Current();
2252 int nView2 = SfxLokHelper::getView();
2253 CPPUNIT_ASSERT(pView1 != pView2);
2255 std::unique_ptr<SfxBoolItem> pItem1;
2256 std::unique_ptr<SfxBoolItem> pItem2;
2257 pView1->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem1);
2258 pView2->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem2);
2259 CPPUNIT_ASSERT(pItem1);
2260 CPPUNIT_ASSERT(pItem2);
2261 CPPUNIT_ASSERT_EQUAL(false, pItem1->GetValue());
2262 CPPUNIT_ASSERT_EQUAL(false, pItem2->GetValue());
2265 // Insert a character in the second view.
2266 SfxLokHelper::setView(nView2);
2267 emulateTyping(*pXTextDocument, u"u");
2269 std::unique_ptr<SfxBoolItem> pItem1;
2270 std::unique_ptr<SfxBoolItem> pItem2;
2271 pView1->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem1);
2272 pView2->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem2);
2273 CPPUNIT_ASSERT(pItem1);
2274 CPPUNIT_ASSERT(pItem2);
2275 CPPUNIT_ASSERT_EQUAL(true, pItem1->GetValue());
2276 CPPUNIT_ASSERT_EQUAL(true, pItem2->GetValue());
2279 SfxLokHelper::setView(nView1);
2280 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2281 SfxLokHelper::setView(nView2);
2282 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2285 namespace {
2287 void checkPageHeaderOrFooter(const SfxViewShell* pViewShell, TypedWhichId<SfxStringListItem> nWhich, bool bValue)
2289 uno::Sequence<OUString> aSeq;
2290 const SfxStringListItem* pListItem = nullptr;
2291 pViewShell->GetDispatcher()->QueryState(nWhich, pListItem);
2292 CPPUNIT_ASSERT(pListItem);
2293 pListItem->GetStringList(aSeq);
2294 if (bValue)
2296 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aSeq.getLength());
2297 CPPUNIT_ASSERT_EQUAL(OUString("Default Page Style"), aSeq[0]);
2299 else
2300 CPPUNIT_ASSERT(!aSeq.hasElements());
2305 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPageHeader)
2307 createDoc("dummy.fodt");
2308 SfxViewShell* pViewShell = SfxViewShell::Current();
2309 // Check Page Header State
2310 checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEHEADER, false);
2311 // Insert Page Header
2313 SfxStringItem aStyle(FN_INSERT_PAGEHEADER, "Default Page Style");
2314 SfxBoolItem aItem(FN_PARAM_1, true);
2315 pViewShell->GetDispatcher()->ExecuteList(FN_INSERT_PAGEHEADER, SfxCallMode::API | SfxCallMode::SYNCHRON, {&aStyle, &aItem});
2317 // Check Page Header State
2318 checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEHEADER, true);
2320 // Remove Page Header
2322 SfxStringItem aStyle(FN_INSERT_PAGEHEADER, "Default Page Style");
2323 SfxBoolItem aItem(FN_PARAM_1, false);
2324 pViewShell->GetDispatcher()->ExecuteList(FN_INSERT_PAGEHEADER, SfxCallMode::API | SfxCallMode::SYNCHRON, {&aStyle, &aItem});
2326 // Check Page Header State
2327 checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEHEADER, false);
2330 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPageFooter)
2332 createDoc("dummy.fodt");
2333 SfxViewShell* pViewShell = SfxViewShell::Current();
2334 // Check Page Footer State
2335 checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEFOOTER, false);
2336 // Insert Page Footer
2338 SfxStringItem aPageStyle(FN_INSERT_PAGEFOOTER, "Default Page Style");
2339 SfxBoolItem aItem(FN_PARAM_1, true);
2340 pViewShell->GetDispatcher()->ExecuteList(FN_INSERT_PAGEFOOTER, SfxCallMode::API | SfxCallMode::SYNCHRON, {&aPageStyle, &aItem});
2342 // Check Page Footer State
2343 checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEFOOTER, true);
2345 // Remove Page Footer
2347 SfxStringItem aPageStyle(FN_INSERT_PAGEFOOTER, "Default Page Style");
2348 SfxBoolItem aItem(FN_PARAM_1, false);
2349 pViewShell->GetDispatcher()->ExecuteList(FN_INSERT_PAGEFOOTER, SfxCallMode::API | SfxCallMode::SYNCHRON, {&aPageStyle, &aItem});
2351 // Check Footer State
2352 checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEFOOTER, false);
2355 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTdf115088)
2357 // We have three lines in the test document and we try to copy the second and third line
2358 // To the beginning of the document
2359 SwXTextDocument* pXTextDocument = createDoc("tdf115088.odt");
2361 // Select and copy second and third line
2362 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
2363 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
2364 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2365 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2366 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_SHIFT);
2367 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_SHIFT);
2368 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT | KEY_SHIFT);
2369 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT | KEY_SHIFT);
2370 Scheduler::ProcessEventsToIdle();
2371 comphelper::dispatchCommand(".uno:Copy", uno::Sequence<beans::PropertyValue>());
2373 // Move cursor to the beginning of the first line and paste
2374 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
2375 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
2376 Scheduler::ProcessEventsToIdle();
2377 comphelper::dispatchCommand(".uno:PasteUnformatted", uno::Sequence<beans::PropertyValue>());
2378 Scheduler::ProcessEventsToIdle();
2380 // Check the resulting text in the document. (it was 1Text\n1\n1\n1)
2381 CPPUNIT_ASSERT_EQUAL(OUString("1\n1Text\n1\n1"), pXTextDocument->getText()->getString());
2383 mxComponent->dispose();
2384 mxComponent.clear();
2385 comphelper::LibreOfficeKit::setActive(false);
2388 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlineField)
2390 // Load a document.
2391 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2392 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
2394 // Turn on track changes and type "x".
2395 uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
2396 xPropertySet->setPropertyValue("RecordChanges", uno::Any(true));
2398 SwDateTimeField aDate(static_cast<SwDateTimeFieldType*>(pWrtShell->GetFieldType(0, SwFieldIds::DateTime)));
2399 //aDate->SetDateTime(::DateTime(::DateTime::SYSTEM));
2400 pWrtShell->InsertField2(aDate);
2402 // Get the redline just created
2403 const SwRedlineTable& rTable = pWrtShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
2404 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), rTable.size());
2405 SwRangeRedline* pRedline = rTable[0];
2406 CPPUNIT_ASSERT(pRedline->GetDescr().indexOf(aDate.GetFieldName())!= -1);
2409 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testIMESupport)
2411 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2412 VclPtr<vcl::Window> pDocWindow = pXTextDocument->getDocWindow();
2414 SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
2415 assert(pView);
2416 SwWrtShell* pWrtShell = pView->GetWrtShellPtr();
2418 // sequence of chinese IME compositions when 'nihao' is typed in an IME
2419 const std::vector<OString> aUtf8Inputs{ "年", "你", "你好", "你哈", "你好", "你好" };
2420 std::vector<OUString> aInputs;
2421 std::transform(aUtf8Inputs.begin(), aUtf8Inputs.end(),
2422 std::back_inserter(aInputs), [](OString aInput) {
2423 return OUString::fromUtf8(aInput);
2425 for (const auto& aInput: aInputs)
2427 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, aInput);
2429 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
2431 // the cursor should be at position 2nd
2432 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
2433 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), pShellCursor->GetPoint()->GetContentIndex());
2435 // content contains only the last IME composition, not all
2436 CPPUNIT_ASSERT_EQUAL(OUString(aInputs[aInputs.size() - 1] + "Aaa bbb."), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
2439 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testIMEFormattingAtEndOfParagraph)
2441 comphelper::LibreOfficeKit::setActive();
2442 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2443 VclPtr<vcl::Window> pDocWindow = pXTextDocument->getDocWindow();
2445 SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
2446 assert(pView);
2447 SwWrtShell* pWrtShell = pView->GetWrtShellPtr();
2449 // delete all characters
2451 for (int i = 0; i < 9; i++)
2453 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
2454 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
2457 Scheduler::ProcessEventsToIdle();
2459 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "a");
2460 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
2462 // status: "a"
2464 comphelper::dispatchCommand(".uno:Bold", uno::Sequence<beans::PropertyValue>());
2465 Scheduler::ProcessEventsToIdle();
2467 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "b");
2468 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
2470 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
2471 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
2472 Scheduler::ProcessEventsToIdle();
2474 // status: "a<bold>b</bold>\n"
2476 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "a");
2477 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
2479 std::unique_ptr<SvxWeightItem> pWeightItem;
2480 pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem);
2481 CPPUNIT_ASSERT(pWeightItem);
2483 CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_BOLD, pWeightItem->GetWeight());
2485 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
2486 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
2487 Scheduler::ProcessEventsToIdle();
2489 // status: "a<bold>b</bold>\n
2490 // <bold>a</bold>\n"
2492 comphelper::dispatchCommand(".uno:Bold", uno::Sequence<beans::PropertyValue>());
2493 Scheduler::ProcessEventsToIdle();
2495 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "b");
2496 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
2498 std::unique_ptr<SvxWeightItem> pWeightItem2;
2499 pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem2);
2500 CPPUNIT_ASSERT(pWeightItem2);
2502 CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_NORMAL, pWeightItem2->GetWeight());
2504 // status: "a<bold>b</bold>\n
2505 // <bold>a</bold>\n"
2506 // b"
2508 comphelper::dispatchCommand(".uno:Bold", uno::Sequence<beans::PropertyValue>());
2509 Scheduler::ProcessEventsToIdle();
2511 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "a");
2512 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
2514 std::unique_ptr<SvxWeightItem> pWeightItem3;
2515 pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem3);
2516 CPPUNIT_ASSERT(pWeightItem3);
2518 CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_BOLD, pWeightItem3->GetWeight());
2520 comphelper::dispatchCommand(".uno:Bold", uno::Sequence<beans::PropertyValue>());
2521 Scheduler::ProcessEventsToIdle();
2523 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "b");
2524 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
2526 std::unique_ptr<SvxWeightItem> pWeightItem4;
2527 pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem4);
2528 CPPUNIT_ASSERT(pWeightItem4);
2530 CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_NORMAL, pWeightItem4->GetWeight());
2532 // status: "a<bold>b</bold>\n
2533 // <bold>a</bold>\n"
2534 // b<bold>a</bold>b"
2536 // the cursor should be at position 3rd
2537 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
2538 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3), pShellCursor->GetPoint()->GetContentIndex());
2540 // check the content
2541 CPPUNIT_ASSERT_EQUAL(OUString("bab"), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
2544 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testIMEFormattingAfterHeader)
2546 comphelper::LibreOfficeKit::setActive();
2547 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2548 VclPtr<vcl::Window> pDocWindow = pXTextDocument->getDocWindow();
2550 SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
2551 assert(pView);
2553 // delete all characters
2555 comphelper::dispatchCommand(".uno:SelectAll", uno::Sequence<beans::PropertyValue>());
2556 Scheduler::ProcessEventsToIdle();
2558 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
2559 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
2561 Scheduler::ProcessEventsToIdle();
2563 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "a");
2564 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
2566 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
2567 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
2568 Scheduler::ProcessEventsToIdle();
2570 // status: "a\n"
2572 comphelper::dispatchCommand(
2573 ".uno:StyleApply?Style:string=Heading 2&FamilyName:string=ParagraphStyles",
2574 uno::Sequence<beans::PropertyValue>());
2575 Scheduler::ProcessEventsToIdle();
2577 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "b");
2578 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
2580 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "b");
2581 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
2582 Scheduler::ProcessEventsToIdle();
2584 std::unique_ptr<SvxWeightItem> pWeightItem;
2585 pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem);
2586 CPPUNIT_ASSERT(pWeightItem);
2588 CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_BOLD, pWeightItem->GetWeight());
2590 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
2591 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
2592 Scheduler::ProcessEventsToIdle();
2594 // status: "a\n
2595 // <h2>bb</h2>\n"
2597 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "c");
2598 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
2599 Scheduler::ProcessEventsToIdle();
2601 // status: "a\n
2602 // <h2>bb</h2>\n"
2603 // c"
2605 std::unique_ptr<SvxWeightItem> pWeightItem2;
2606 pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem2);
2607 CPPUNIT_ASSERT(pWeightItem2);
2609 CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_NORMAL, pWeightItem2->GetWeight());
2612 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSplitNodeRedlineCallback)
2614 // Load a document.
2615 SwXTextDocument* pXTextDocument = createDoc("splitnode_redline_callback.fodt");
2616 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
2617 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
2619 // 1. test case
2620 // Move cursor between the two tracked changes
2621 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2622 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2623 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2624 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2625 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2626 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2627 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2628 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2629 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2630 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2631 Scheduler::ProcessEventsToIdle();
2633 // Add a new line
2634 m_nRedlineTableEntryModified = 0;
2635 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
2636 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
2637 Scheduler::ProcessEventsToIdle();
2639 // Assert that we get a notification about redline modification
2640 // The redline after the inserted node gets a different vertical position
2641 CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableEntryModified);
2643 // 2. test case
2644 // Move cursor back to the first line, so adding new line will affect both tracked changes
2645 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
2646 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
2647 Scheduler::ProcessEventsToIdle();
2649 // Add a new line
2650 m_nRedlineTableEntryModified = 0;
2651 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
2652 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
2653 Scheduler::ProcessEventsToIdle();
2654 CPPUNIT_ASSERT_EQUAL(2, m_nRedlineTableEntryModified);
2656 // 3. test case
2657 // Move cursor to the end of the document, so adding a new line won't affect any tracked changes
2658 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_END | KEY_MOD1);
2659 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_END | KEY_MOD1);
2660 Scheduler::ProcessEventsToIdle();
2662 // Add a new line
2663 m_nRedlineTableEntryModified = 0;
2664 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
2665 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
2666 Scheduler::ProcessEventsToIdle();
2667 CPPUNIT_ASSERT_EQUAL(0, m_nRedlineTableEntryModified);
2670 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDeleteNodeRedlineCallback)
2672 // Load a document.
2673 SwXTextDocument* pXTextDocument = createDoc("removenode_redline_callback.fodt");
2674 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
2675 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
2677 // 1. test case
2678 // Move cursor between the two tracked changes
2679 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2680 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2681 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2682 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2683 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2684 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2685 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2686 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2687 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2688 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2689 Scheduler::ProcessEventsToIdle();
2691 // Remove one (empty) line
2692 m_nRedlineTableEntryModified = 0;
2693 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
2694 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
2695 Scheduler::ProcessEventsToIdle();
2697 // Assert that we get a notification about redline modification
2698 // The redline after the removed node gets a different vertical position
2699 CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableEntryModified);
2701 // 2. test case
2702 // Move cursor back to the first line, so removing one line will affect both tracked changes
2703 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
2704 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
2705 Scheduler::ProcessEventsToIdle();
2707 // Remove a new line
2708 m_nRedlineTableEntryModified = 0;
2709 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
2710 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
2711 Scheduler::ProcessEventsToIdle();
2712 CPPUNIT_ASSERT_EQUAL(2, m_nRedlineTableEntryModified);
2714 // 3. test case
2715 // Move cursor to the end of the document, so removing one line won't affect any tracked changes
2716 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_END | KEY_MOD1);
2717 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_END | KEY_MOD1);
2718 Scheduler::ProcessEventsToIdle();
2720 // Remove a line
2721 m_nRedlineTableEntryModified = 0;
2722 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_BACKSPACE);
2723 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_BACKSPACE);
2724 Scheduler::ProcessEventsToIdle();
2725 CPPUNIT_ASSERT_EQUAL(0, m_nRedlineTableEntryModified);
2729 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testVisCursorInvalidation)
2731 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2732 ViewCallback aView1;
2733 int nView1 = SfxLokHelper::getView();
2735 SfxLokHelper::createView();
2736 int nView2 = SfxLokHelper::getView();
2737 ViewCallback aView2;
2738 Scheduler::ProcessEventsToIdle();
2740 // Move visible cursor in the first view
2741 SfxLokHelper::setView(nView1);
2742 Scheduler::ProcessEventsToIdle();
2744 aView1.m_bOwnCursorInvalidated = false;
2745 aView1.m_bViewCursorInvalidated = false;
2746 aView2.m_bOwnCursorInvalidated = false;
2747 aView2.m_bViewCursorInvalidated = false;
2749 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT);
2750 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT);
2751 Scheduler::ProcessEventsToIdle();
2753 CPPUNIT_ASSERT(!aView1.m_bViewCursorInvalidated);
2754 CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
2755 CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
2756 CPPUNIT_ASSERT(!aView2.m_bOwnCursorInvalidated);
2758 // Insert text in the second view which moves the other view's cursor too
2759 SfxLokHelper::setView(nView2);
2761 Scheduler::ProcessEventsToIdle();
2762 aView1.m_bOwnCursorInvalidated = false;
2763 aView1.m_bViewCursorInvalidated = false;
2764 aView2.m_bOwnCursorInvalidated = false;
2765 aView2.m_bViewCursorInvalidated = false;
2767 emulateTyping(*pXTextDocument, u"x");
2769 CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
2770 CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
2771 CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
2772 CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
2773 // Check that views have correct location for the other's cursor.
2774 CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor, aView2.m_aViewCursor);
2775 CPPUNIT_ASSERT_EQUAL(aView2.m_aOwnCursor, aView1.m_aViewCursor);
2776 // Their cursors should be on the same line, first view's more to the right.
2777 CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor.getY(), aView2.m_aOwnCursor.getY());
2778 CPPUNIT_ASSERT_GREATER(aView2.m_aOwnCursor.getX(), aView1.m_aOwnCursor.getX());
2780 // Do the same as before, but set the related compatibility flag first
2781 SfxLokHelper::setView(nView2);
2783 comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
2785 Scheduler::ProcessEventsToIdle();
2786 aView1.m_bOwnCursorInvalidated = false;
2787 aView1.m_bViewCursorInvalidated = false;
2788 aView2.m_bOwnCursorInvalidated = false;
2789 aView2.m_bViewCursorInvalidated = false;
2791 emulateTyping(*pXTextDocument, u"x");
2793 CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
2794 CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
2795 CPPUNIT_ASSERT_EQUAL(nView2, aView1.m_nOwnCursorInvalidatedBy);
2796 CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
2797 CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
2798 CPPUNIT_ASSERT_EQUAL(nView2, aView2.m_nOwnCursorInvalidatedBy);
2799 CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor, aView2.m_aViewCursor);
2800 CPPUNIT_ASSERT_EQUAL(aView2.m_aOwnCursor, aView1.m_aViewCursor);
2801 // Their cursors should be on the same line, first view's more to the right.
2802 CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor.getY(), aView2.m_aOwnCursor.getY());
2803 CPPUNIT_ASSERT_GREATER(aView2.m_aOwnCursor.getX(), aView1.m_aOwnCursor.getX());
2805 comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(false);
2808 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDeselectCustomShape)
2810 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2811 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
2812 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
2813 Point aStart = pShellCursor->GetSttPos();
2814 aStart.setX(aStart.getX() - 1000);
2815 aStart.setY(aStart.getY() - 1000);
2817 comphelper::dispatchCommand(".uno:BasicShapes.hexagon", uno::Sequence<beans::PropertyValue>());
2818 Scheduler::ProcessEventsToIdle();
2819 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pWrtShell->GetDrawView()->GetMarkedObjectList().GetMarkCount());
2821 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
2822 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
2823 Scheduler::ProcessEventsToIdle();
2824 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), pWrtShell->GetDrawView()->GetMarkedObjectList().GetMarkCount());
2827 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSemiTransparent)
2829 // Load a document where the top left tile contains a semi-transparent rectangle shape.
2830 SwXTextDocument* pXTextDocument = createDoc("semi-transparent.odt");
2832 // Render a larger area, and then get the color of the bottom right corner of our tile.
2833 size_t nCanvasWidth = 1024;
2834 size_t nCanvasHeight = 512;
2835 size_t nTileSize = 256;
2836 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
2837 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2838 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2839 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
2840 Fraction(1.0), Point(), aPixmap.data());
2841 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
2842 /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
2843 pDevice->EnableMapMode(false);
2844 Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
2845 Bitmap::ScopedReadAccess pAccess(aBitmap);
2846 Color aColor(pAccess->GetPixel(255, 255));
2848 // Without the accompanying fix in place, this test would have failed with 'Expected greater or
2849 // equal than: 190; Actual: 159'. This means the semi-transparent gray rectangle was darker than
2850 // expected, as it was painted twice.
2851 CPPUNIT_ASSERT_GREATEREQUAL(190, static_cast<int>(aColor.GetRed()));
2852 CPPUNIT_ASSERT_GREATEREQUAL(190, static_cast<int>(aColor.GetGreen()));
2853 CPPUNIT_ASSERT_GREATEREQUAL(190, static_cast<int>(aColor.GetBlue()));
2856 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testHighlightNumbering)
2858 // Load a document where the top left tile contains a semi-transparent rectangle shape.
2859 SwXTextDocument* pXTextDocument = createDoc("tdf114799_highlight.docx");
2861 // Render a larger area, and then get the color of the bottom right corner of our tile.
2862 size_t nCanvasWidth = 1024;
2863 size_t nCanvasHeight = 512;
2864 size_t nTileSize = 256;
2865 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
2866 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2867 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2868 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
2869 Fraction(1.0), Point(), aPixmap.data());
2870 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
2871 /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
2872 pDevice->EnableMapMode(false);
2873 Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
2874 Bitmap::ScopedReadAccess pAccess(aBitmap);
2876 // Yellow highlighting over numbering
2877 Color aColor(pAccess->GetPixel(103, 148));
2878 CPPUNIT_ASSERT_EQUAL(COL_YELLOW, aColor);
2881 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testHighlightNumbering_shd)
2883 // Load a document where the top left tile contains a semi-transparent rectangle shape.
2884 SwXTextDocument* pXTextDocument = createDoc("tdf114799_shd.docx");
2886 // Render a larger area, and then get the color of the bottom right corner of our tile.
2887 size_t nCanvasWidth = 1024;
2888 size_t nCanvasHeight = 512;
2889 size_t nTileSize = 256;
2890 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
2891 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2892 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2893 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
2894 Fraction(1.0), Point(), aPixmap.data());
2895 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
2896 /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
2897 pDevice->EnableMapMode(false);
2898 Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
2899 Bitmap::ScopedReadAccess pAccess(aBitmap);
2901 // No highlighting over numbering - w:shd does not apply to numbering.
2902 Color aColor(pAccess->GetPixel(103, 148));
2903 CPPUNIT_ASSERT_EQUAL(COL_WHITE, aColor);
2906 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPilcrowRedlining)
2908 // Load a document where the top left tile contains
2909 // paragraph and line break symbols with redlining.
2910 SwXTextDocument* pXTextDocument = createDoc("pilcrow-redlining.fodt");
2912 // show non printing characters, including pilcrow and
2913 // line break symbols with redlining
2914 comphelper::dispatchCommand(".uno:ControlCodes", {});
2916 // Render a larger area, and then get the color of the bottom right corner of our tile.
2917 size_t nCanvasWidth = 2048;
2918 size_t nCanvasHeight = 1024;
2919 size_t nTileSize = 512;
2920 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
2921 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2922 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2923 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
2924 Fraction(1.0), Point(), aPixmap.data());
2925 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
2926 /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
2927 pDevice->EnableMapMode(false);
2928 Bitmap aBitmap = pDevice->GetBitmap(Point(100, 100), Size(nTileSize, nTileSize));
2929 Bitmap::ScopedReadAccess pAccess(aBitmap);
2931 const char* aTexts[] = {
2932 "Insert paragraph break",
2933 "Insert paragraph break (empty line)",
2934 "Delete paragraph break",
2935 "Delete paragraph break (empty line)",
2936 "Insert line break",
2937 "Insert line break (empty line)",
2938 "Delete line break",
2939 "Delete line break (empty line)"
2942 // Check redlining (strike out and underline) over the paragraph and line break symbols
2943 for (int nLine = 0; nLine < 8; ++nLine)
2945 bool bHasRedlineColor = false;
2946 for (int i = 0; i < 36 && !bHasRedlineColor; ++i)
2948 int nY = 96 + nLine * 36 + i;
2949 for (sal_uInt32 j = 0; j < nTileSize - 1; ++j)
2951 Color aColor(pAccess->GetPixel(nY, j));
2952 Color aColor2(pAccess->GetPixel(nY+1, j));
2953 Color aColor3(pAccess->GetPixel(nY, j+1));
2954 Color aColor4(pAccess->GetPixel(nY+1, j+1));
2955 // 4-pixel same color square sign strike out or underline of redlining
2956 // if its color is not white, black or non-printing character color
2957 if ( aColor == aColor2 && aColor == aColor3 && aColor == aColor4 &&
2958 aColor != COL_WHITE && aColor != COL_BLACK &&
2959 aColor != NON_PRINTING_CHARACTER_COLOR )
2961 bHasRedlineColor = true;
2962 break;
2967 CPPUNIT_ASSERT_MESSAGE(aTexts[nLine], bHasRedlineColor);
2970 comphelper::dispatchCommand(".uno:ControlCodes", {});
2973 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDoubleUnderlineAndStrikeOut)
2975 // Load a document where the tracked text moving is visible with
2976 // double underline and strike out character formatting
2977 SwXTextDocument* pXTextDocument = createDoc("double-underline_and_strike-out.fodt");
2979 // Render a larger area, and then get the color of the bottom right corner of our tile.
2980 size_t nCanvasWidth = 700;
2981 size_t nCanvasHeight = 350;
2982 size_t nTileSize = 350;
2983 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
2984 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2985 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2986 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
2987 Fraction(1.0), Point(), aPixmap.data());
2988 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
2989 /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
2990 pDevice->EnableMapMode(false);
2991 Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
2992 Bitmap::ScopedReadAccess pAccess(aBitmap);
2993 bool bGreenLine = false;
2994 size_t nGreenLine = 0;
2995 // count green horizontal lines by tracking a column of pixels counting the
2996 // separated continuous green pixel sequences.
2997 for (size_t nLine = 0; nLine < nTileSize; ++nLine)
2999 Color aColor(pAccess->GetPixel(nLine, 100));
3000 if ( aColor == COL_GREEN )
3002 if ( bGreenLine == false )
3004 ++nGreenLine;
3005 bGreenLine = true;
3008 else
3009 bGreenLine = false;
3011 // tdf#152214 this was 0 (missing double underline and double strike out)
3012 CPPUNIT_ASSERT_EQUAL(size_t(4), nGreenLine);
3015 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTdf43244_SpacesOnMargin)
3017 // Load a document where the top left tile contains
3018 // paragraph and line break symbols with redlining.
3019 SwXTextDocument* pXTextDocument = createDoc("tdf43244_SpacesOnMargin.odt");
3021 // show non printing characters, including pilcrow and
3022 // line break symbols with redlining
3023 comphelper::dispatchCommand(".uno:ControlCodes", {});
3025 // Render a larger area, and then get the colors from the right side of the page.
3026 size_t nCanvasWidth = 1024;
3027 size_t nCanvasHeight = 512;
3028 size_t nTileSize = 64;
3029 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
3030 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
3031 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3032 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
3033 Fraction(1.0), Point(), aPixmap.data());
3034 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
3035 /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
3036 pDevice->EnableMapMode(false);
3037 Bitmap aBitmap = pDevice->GetBitmap(Point(730, 120), Size(nTileSize, nTileSize));
3038 Bitmap::ScopedReadAccess pAccess(aBitmap);
3040 //Test if we see any spaces on the right margin in a 47x48 rectangle
3041 bool bSpaceFound = false;
3042 for (int i = 1; i < 48 && !bSpaceFound; i++)
3044 for (int j = 0; j < i; j++)
3046 Color aColor2(pAccess->GetPixel(j, i));
3047 Color aColor1(pAccess->GetPixel(i, j + 1));
3049 if (aColor1.GetRed() < 255 || aColor2.GetRed() < 255)
3051 bSpaceFound = true;
3052 break;
3056 CPPUNIT_ASSERT(bSpaceFound);
3058 comphelper::dispatchCommand(".uno:ControlCodes", {});
3061 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testClipText)
3063 // Load a document where the top left tile contains table text with
3064 // too small line height, but with top and bottom paragraph margins,
3065 // avoiding of clipping top and bottom parts of the characters.
3066 SwXTextDocument* pXTextDocument = createDoc("tdf117448.fodt");
3068 // Render a larger area, and then get the top and bottom of the text in that tile
3069 size_t nCanvasWidth = 1024;
3070 size_t nCanvasHeight = 512;
3071 size_t nTileSize = 256;
3072 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
3073 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
3074 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3075 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
3076 Fraction(1.0), Point(), aPixmap.data());
3077 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
3078 /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
3079 pDevice->EnableMapMode(false);
3080 Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
3081 Bitmap::ScopedReadAccess pAccess(aBitmap);
3083 // check top margin, it's not white completely (i.e. showing top of letter "T")
3084 bool bClipTop = true;
3085 for (int i = 0; i < 150; i++)
3087 Color aTopTextColor(pAccess->GetPixel(98, 98 + i));
3088 if (aTopTextColor.GetRed() < 255)
3090 bClipTop = false;
3091 break;
3094 CPPUNIT_ASSERT(!bClipTop);
3095 // switch off because of false alarm on some platform, maybe related to font replacements
3096 #if 0
3097 // check bottom margin, it's not white completely (i.e. showing bottom of letter "g")
3098 bool bClipBottom = true;
3099 for (int i = 0; i < 150; i++)
3101 Color aBottomTextColor(pAccess->GetPixel(110, 98 + i));
3102 if (aBottomTextColor.R < 255)
3104 bClipBottom = false;
3105 break;
3108 CPPUNIT_ASSERT(!bClipBottom);
3109 #endif
3112 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAnchorTypes)
3114 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
3115 selectShape(1);
3117 SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
3118 SwView* pView = pXTextDocument->GetDocShell()->GetView();
3119 SfxItemSet aSet(pDoc->GetAttrPool(), svl::Items<FN_TOOL_ANCHOR_PAGE, FN_TOOL_ANCHOR_PAGE>);
3120 SfxBoolItem aItem(FN_TOOL_ANCHOR_PAGE);
3121 aSet.Put(aItem);
3122 auto pShell = dynamic_cast<SwBaseShell*>(pView->GetCurShell());
3123 pShell->GetState(aSet);
3124 // Without the accompanying fix in place, this test would have failed, setting the anchor type
3125 // to other than as/at-char was possible.
3126 CPPUNIT_ASSERT(!aSet.HasItem(FN_TOOL_ANCHOR_PAGE));
3129 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testLanguageStatus)
3131 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
3132 SwView* pView = pXTextDocument->GetDocShell()->GetView();
3133 std::unique_ptr<SfxPoolItem> pItem;
3134 pView->GetViewFrame().GetBindings().QueryState(SID_LANGUAGE_STATUS, pItem);
3135 auto pStringListItem = dynamic_cast<SfxStringListItem*>(pItem.get());
3136 CPPUNIT_ASSERT(pStringListItem);
3138 uno::Sequence< OUString > aList;
3139 pStringListItem->GetStringList(aList);
3140 CPPUNIT_ASSERT_EQUAL(OUString("English (USA);en-US"), aList[0]);
3143 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlineNotificationDuringSave)
3145 // Load a document with redlines which are hidden at a layout level.
3146 // It's an empty document, just settings.xml and content.xml are custom.
3147 SwXTextDocument* pXTextDocument = createDoc("redline-notification-during-save.odt");
3148 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3149 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3151 // Save the document.
3152 utl::MediaDescriptor aMediaDescriptor;
3153 aMediaDescriptor["FilterName"] <<= OUString("writer8");
3154 uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
3155 // Without the accompanying fix in place, this test would have never returned due to an infinite
3156 // loop while sending not needed LOK notifications for redline changes during save.
3157 xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
3160 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testHyperlink)
3162 comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
3163 SwXTextDocument* pXTextDocument = createDoc("hyperlink.odt");
3164 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3165 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3166 m_callbackWrapper.setLOKViewId(SfxLokHelper::getView(pWrtShell->GetSfxViewShell()));
3167 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
3169 Point aStart = pShellCursor->GetSttPos();
3170 aStart.setX(aStart.getX() + 1800);
3171 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1,
3172 MOUSE_LEFT, 0);
3173 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1,
3174 MOUSE_LEFT, 0);
3175 Scheduler::ProcessEventsToIdle();
3177 CPPUNIT_ASSERT_EQUAL(OString("hyperlink"), m_sHyperlinkText);
3178 CPPUNIT_ASSERT_EQUAL(OString("http://example.com/"), m_sHyperlinkLink);
3181 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testFieldmark)
3183 // Without the accompanying fix in place, this crashed on load.
3184 createDoc("fieldmark.docx");
3187 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownFormFieldButton)
3189 SwXTextDocument* pXTextDocument = createDoc("drop_down_form_field.odt");
3190 pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
3192 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3193 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3195 // Move the cursor to trigger displaying of the field button.
3196 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
3197 CPPUNIT_ASSERT(m_aFormFieldButton.isEmpty());
3199 // Do a tile rendering to trigger the button message with a valid text area
3200 size_t nCanvasWidth = 1024;
3201 size_t nCanvasHeight = 512;
3202 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
3203 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
3204 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3205 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
3206 Fraction(1.0), Point(), aPixmap.data());
3207 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
3208 /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
3210 CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
3212 std::stringstream aStream((std::string(m_aFormFieldButton)));
3213 boost::property_tree::ptree aTree;
3214 boost::property_tree::read_json(aStream, aTree);
3216 OString sAction( aTree.get_child("action").get_value<std::string>() );
3217 CPPUNIT_ASSERT_EQUAL(OString("show"), sAction);
3219 OString sType( aTree.get_child("type").get_value<std::string>() );
3220 CPPUNIT_ASSERT_EQUAL(OString("drop-down"), sType);
3222 OString sTextArea( aTree.get_child("textArea").get_value<std::string>() );
3223 CPPUNIT_ASSERT_EQUAL(OString("1538, 1418, 1026, 275"), sTextArea);
3225 boost::property_tree::ptree aItems = aTree.get_child("params").get_child("items");
3226 CPPUNIT_ASSERT_EQUAL(size_t(6), aItems.size());
3228 OStringBuffer aItemList;
3229 for (auto &item : aItems)
3231 aItemList.append(item.second.get_value<std::string>().c_str()
3232 + OString::Concat(";"));
3234 CPPUNIT_ASSERT_EQUAL(OString("2019/2020;2020/2021;2021/2022;2022/2023;2023/2024;2024/2025;"), aItemList.toString());
3236 OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
3237 CPPUNIT_ASSERT_EQUAL(OString("1"), sSelected);
3239 OString sPlaceholder( aTree.get_child("params").get_child("placeholderText").get_value<std::string>() );
3240 CPPUNIT_ASSERT_EQUAL(OString("No Item specified"), sPlaceholder);
3243 // Move the cursor back so the button becomes hidden.
3244 pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
3246 CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
3248 std::stringstream aStream((std::string(m_aFormFieldButton)));
3249 boost::property_tree::ptree aTree;
3250 boost::property_tree::read_json(aStream, aTree);
3252 OString sAction( aTree.get_child("action").get_value<std::string>() );
3253 CPPUNIT_ASSERT_EQUAL(OString("hide"), sAction);
3255 OString sType( aTree.get_child("type").get_value<std::string>() );
3256 CPPUNIT_ASSERT_EQUAL(OString("drop-down"), sType);
3260 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownFormFieldButtonEditing)
3262 SwXTextDocument* pXTextDocument = createDoc("drop_down_form_field2.odt");
3263 pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
3265 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3266 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3268 // Move the cursor to trigger displaying of the field button.
3269 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
3270 CPPUNIT_ASSERT(m_aFormFieldButton.isEmpty());
3272 // Do a tile rendering to trigger the button message with a valid text area
3273 size_t nCanvasWidth = 1024;
3274 size_t nCanvasHeight = 512;
3275 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
3276 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
3277 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3278 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
3279 Fraction(1.0), Point(), aPixmap.data());
3280 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
3281 /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
3283 // The item with the index '1' is selected by default
3284 CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
3286 std::stringstream aStream((std::string(m_aFormFieldButton)));
3287 boost::property_tree::ptree aTree;
3288 boost::property_tree::read_json(aStream, aTree);
3290 OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
3291 CPPUNIT_ASSERT_EQUAL(OString("1"), sSelected);
3293 m_aFormFieldButton = "";
3295 // Trigger a form field event to select a different item.
3296 vcl::ITiledRenderable::StringMap aArguments;
3297 aArguments["type"] = "drop-down";
3298 aArguments["cmd"] = "selected";
3299 aArguments["data"] = "3";
3300 pXTextDocument->executeFromFieldEvent(aArguments);
3302 // Do a tile rendering to trigger the button message.
3303 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
3304 /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
3306 CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
3308 std::stringstream aStream((std::string(m_aFormFieldButton)));
3309 boost::property_tree::ptree aTree;
3310 boost::property_tree::read_json(aStream, aTree);
3312 OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
3313 CPPUNIT_ASSERT_EQUAL(OString("3"), sSelected);
3317 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownFormFieldButtonNoSelection)
3319 SwXTextDocument* pXTextDocument = createDoc("drop_down_form_field_noselection.odt");
3320 pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
3322 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3323 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3325 // Move the cursor to trigger displaying of the field button.
3326 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
3327 CPPUNIT_ASSERT(m_aFormFieldButton.isEmpty());
3329 // Do a tile rendering to trigger the button message with a valid text area
3330 size_t nCanvasWidth = 1024;
3331 size_t nCanvasHeight = 512;
3332 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
3333 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
3334 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3335 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
3336 Fraction(1.0), Point(), aPixmap.data());
3337 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
3338 /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
3340 // None of the items is selected
3341 CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
3343 std::stringstream aStream((std::string(m_aFormFieldButton)));
3344 boost::property_tree::ptree aTree;
3345 boost::property_tree::read_json(aStream, aTree);
3347 OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
3348 CPPUNIT_ASSERT_EQUAL(OString("-1"), sSelected);
3352 static void lcl_extractHandleParameters(std::string_view selection, sal_Int32& id, sal_Int32& x, sal_Int32& y)
3354 OString extraInfo( selection.substr(selection.find("{")) );
3355 std::stringstream aStream((std::string(extraInfo)));
3356 boost::property_tree::ptree aTree;
3357 boost::property_tree::read_json(aStream, aTree);
3358 boost::property_tree::ptree
3359 handle0 = aTree
3360 .get_child("handles")
3361 .get_child("kinds")
3362 .get_child("rectangle")
3363 .get_child("1")
3364 .begin()->second;
3365 id = handle0.get_child("id").get_value<int>();
3366 x = handle0.get_child("point").get_child("x").get_value<int>();
3367 y = handle0.get_child("point").get_child("y").get_value<int>();
3370 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testMoveShapeHandle)
3372 comphelper::LibreOfficeKit::setActive();
3373 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
3375 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3376 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3377 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
3378 SdrObject* pObject = pPage->GetObj(0);
3379 pWrtShell->SelectObj(Point(), 0, pObject);
3380 Scheduler::ProcessEventsToIdle();
3382 CPPUNIT_ASSERT(!m_ShapeSelection.isEmpty());
3384 sal_Int32 id, x, y;
3385 lcl_extractHandleParameters(m_ShapeSelection, id, x ,y);
3386 sal_Int32 oldX = x;
3387 sal_Int32 oldY = y;
3388 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
3390 {"HandleNum", uno::Any(id)},
3391 {"NewPosX", uno::Any(x+1)},
3392 {"NewPosY", uno::Any(y+1)}
3393 }));
3394 comphelper::dispatchCommand(".uno:MoveShapeHandle", aPropertyValues);
3395 Scheduler::ProcessEventsToIdle();
3396 CPPUNIT_ASSERT(!m_ShapeSelection.isEmpty());
3397 lcl_extractHandleParameters(m_ShapeSelection, id, x ,y);
3398 CPPUNIT_ASSERT_EQUAL(x-1, oldX);
3399 CPPUNIT_ASSERT_EQUAL(y-1, oldY);
3403 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownFormFieldButtonNoItem)
3405 SwXTextDocument* pXTextDocument = createDoc("drop_down_form_field_noitem.odt");
3406 pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
3408 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3409 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3411 // Move the cursor to trigger displaying of the field button.
3412 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
3413 CPPUNIT_ASSERT(m_aFormFieldButton.isEmpty());
3415 // Do a tile rendering to trigger the button message with a valid text area
3416 size_t nCanvasWidth = 1024;
3417 size_t nCanvasHeight = 512;
3418 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
3419 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
3420 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3421 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
3422 Fraction(1.0), Point(), aPixmap.data());
3423 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
3424 /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
3426 // There is not item specified for the field
3427 CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
3429 std::stringstream aStream((std::string(m_aFormFieldButton)));
3430 boost::property_tree::ptree aTree;
3431 boost::property_tree::read_json(aStream, aTree);
3433 boost::property_tree::ptree aItems = aTree.get_child("params").get_child("items");
3434 CPPUNIT_ASSERT_EQUAL(size_t(0), aItems.size());
3436 OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
3437 CPPUNIT_ASSERT_EQUAL(OString("-1"), sSelected);
3441 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTablePaintInvalidate)
3443 // Load a document with a table in it.
3444 SwXTextDocument* pXTextDocument = createDoc("table-paint-invalidate.odt");
3445 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3446 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3447 // Enter the table.
3448 pWrtShell->Down(/*bSelect=*/false);
3449 Scheduler::ProcessEventsToIdle();
3450 m_nInvalidations = 0;
3452 // Paint a tile.
3453 size_t nCanvasWidth = 256;
3454 size_t nCanvasHeight = 256;
3455 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
3456 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
3457 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3458 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
3459 Fraction(1.0), Point(), aPixmap.data());
3460 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, m_aInvalidation.Left(),
3461 m_aInvalidation.Top(), /*nTileWidth=*/1000,
3462 /*nTileHeight=*/1000);
3463 Scheduler::ProcessEventsToIdle();
3465 // Without the accompanying fix in place, this test would have failed with
3466 // - Expected: 0
3467 // - Actual : 5
3468 // i.e. paint generated an invalidation, which caused a loop.
3469 CPPUNIT_ASSERT_EQUAL(0, m_nInvalidations);
3472 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTableCommentRemoveCallback)
3474 comphelper::LibreOfficeKit::setActive();
3475 comphelper::LibreOfficeKit::setTiledAnnotations(false);
3477 // Load a document with a comment in a table.
3478 SwXTextDocument* pXTextDocument = createDoc("testTableCommentRemoveCallback.odt");
3479 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3480 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3481 ViewCallback aView;
3483 // delete all characters
3484 comphelper::dispatchCommand(".uno:SelectAll", uno::Sequence<beans::PropertyValue>());
3485 Scheduler::ProcessEventsToIdle();
3486 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
3487 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
3488 Scheduler::ProcessEventsToIdle();
3490 //check for comment remove callback
3491 OString sAction(aView.m_aComment.get_child("action").get_value<std::string>());
3492 CPPUNIT_ASSERT_EQUAL(OString("Remove"), sAction);
3495 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSpellOnlineRenderParameter)
3497 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
3498 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3499 const SwViewOption* pOpt = pWrtShell->GetViewOptions();
3500 bool bSet = pOpt->IsOnlineSpell();
3502 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
3504 {".uno:SpellOnline", uno::Any(!bSet)},
3505 }));
3506 pXTextDocument->initializeForTiledRendering(aPropertyValues);
3507 CPPUNIT_ASSERT_EQUAL(!bSet, pOpt->IsOnlineSpell());
3510 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testExtTextInputReadOnly)
3512 // Create a document with a protected section + a normal paragraph after it.
3513 SwXTextDocument* pXTextDocument = createDoc();
3514 uno::Reference<text::XTextViewCursorSupplier> xController(
3515 pXTextDocument->getCurrentController(), uno::UNO_QUERY);
3516 uno::Reference<text::XTextViewCursor> xCursor = xController->getViewCursor();
3517 uno::Reference<text::XText> xText = xCursor->getText();
3518 uno::Reference<text::XTextContent> xSection(
3519 pXTextDocument->createInstance("com.sun.star.text.TextSection"), uno::UNO_QUERY);
3520 uno::Reference<beans::XPropertySet> xSectionProps(xSection, uno::UNO_QUERY);
3521 xSectionProps->setPropertyValue("IsProtected", uno::Any(true));
3522 xText->insertTextContent(xCursor, xSection, /*bAbsorb=*/true);
3524 // First paragraph is the protected section, is it empty?
3525 VclPtr<vcl::Window> pEditWin = pXTextDocument->getDocWindow();
3526 CPPUNIT_ASSERT(pEditWin);
3527 CPPUNIT_ASSERT(getParagraph(1)->getString().isEmpty());
3529 // Try to type into the protected section, is it still empty?
3530 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3531 pWrtShell->SttEndDoc(/*bStt=*/true);
3532 SfxLokHelper::postExtTextEventAsync(pEditWin, LOK_EXT_TEXTINPUT, "x");
3533 SfxLokHelper::postExtTextEventAsync(pEditWin, LOK_EXT_TEXTINPUT_END, "x");
3534 Scheduler::ProcessEventsToIdle();
3535 // Without the accompanying fix in place, this test would have failed, as it was possible to
3536 // type into the protected section.
3537 CPPUNIT_ASSERT(getParagraph(1)->getString().isEmpty());
3539 // Second paragraph is a normal paragraph, is it empty?
3540 pWrtShell->Down(/*bSelect=*/false);
3541 CPPUNIT_ASSERT(getParagraph(2)->getString().isEmpty());
3543 // Try to type into the protected section, does it have the typed content?
3544 SfxLokHelper::postExtTextEventAsync(pEditWin, LOK_EXT_TEXTINPUT, "x");
3545 SfxLokHelper::postExtTextEventAsync(pEditWin, LOK_EXT_TEXTINPUT_END, "x");
3546 Scheduler::ProcessEventsToIdle();
3547 CPPUNIT_ASSERT_EQUAL(OUString("x"), getParagraph(2)->getString());
3550 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testBulletDeleteInvalidation)
3552 // Given a document with 3 paragraphs: first 2 is bulleted, the last is not.
3553 SwXTextDocument* pXTextDocument = createDoc();
3554 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3555 pWrtShell->SplitNode();
3556 pWrtShell->Up(/*bSelect=*/false);
3557 pWrtShell->StartAllAction();
3558 pWrtShell->BulletOn();
3559 pWrtShell->EndAllAction();
3560 pWrtShell->Insert2("a");
3561 pWrtShell->SplitNode();
3562 pWrtShell->Insert2("b");
3563 pWrtShell->Down(/*bSelect=*/false);
3564 pWrtShell->GetLayout()->PaintSwFrame(*pWrtShell->GetOut(),
3565 pWrtShell->GetLayout()->getFrameArea());
3566 Scheduler::ProcessEventsToIdle();
3567 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3568 m_aInvalidations = tools::Rectangle();
3570 // When pressing backspace in the last paragraph.
3571 pWrtShell->DelLeft();
3573 // Then the first paragraph should not be invalidated.
3574 SwRootFrame* pRoot = pWrtShell->GetLayout();
3575 SwFrame* pPage = pRoot->GetLower();
3576 SwFrame* pBody = pPage->GetLower();
3577 SwFrame* pFirstText = pBody->GetLower();
3578 tools::Rectangle aFirstTextRect = pFirstText->getFrameArea().SVRect();
3579 CPPUNIT_ASSERT(!aFirstTextRect.Overlaps(m_aInvalidations));
3582 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTdf155349)
3584 SwXTextDocument* pXTextDocument = createDoc();
3585 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3586 Scheduler::ProcessEventsToIdle();
3587 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3588 pWrtShell->Insert2("a");
3589 Scheduler::ProcessEventsToIdle();
3590 pWrtShell->Insert2("b");
3591 m_bFullInvalidateSeen = false;
3592 Scheduler::ProcessEventsToIdle();
3593 // before fix for tdf#155349 the total area got invalidated when changing one line
3594 CPPUNIT_ASSERT(!m_bFullInvalidateSeen);
3597 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testBulletNoNumInvalidation)
3599 // Given a document with 3 paragraphs: all are bulleted.
3600 SwXTextDocument* pXTextDocument = createDoc();
3601 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3602 pWrtShell->StartAllAction();
3603 pWrtShell->BulletOn();
3604 pWrtShell->EndAllAction();
3605 pWrtShell->Insert2("a");
3606 pWrtShell->SplitNode();
3607 pWrtShell->Insert2("b");
3608 pWrtShell->SplitNode();
3609 pWrtShell->GetLayout()->PaintSwFrame(*pWrtShell->GetOut(),
3610 pWrtShell->GetLayout()->getFrameArea());
3611 Scheduler::ProcessEventsToIdle();
3612 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3613 m_aInvalidations = tools::Rectangle();
3615 // When pressing backspace in the last paragraph to turn bullets off.
3616 pWrtShell->StartAllAction();
3617 pWrtShell->NumOrNoNum(/*bDelete=*/false);
3618 pWrtShell->EndAllAction();
3620 // Then the first paragraph should not be invalidated.
3621 SwRootFrame* pRoot = pWrtShell->GetLayout();
3622 SwFrame* pPage = pRoot->GetLower();
3623 SwFrame* pBody = pPage->GetLower();
3624 SwFrame* pFirstText = pBody->GetLower();
3625 tools::Rectangle aFirstTextRect = pFirstText->getFrameArea().SVRect();
3626 CPPUNIT_ASSERT(!aFirstTextRect.Overlaps(m_aInvalidations));
3629 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testBulletMultiDeleteInvalidation)
3631 // Given a document with 5 paragraphs: all are bulleted.
3632 SwXTextDocument* pXTextDocument = createDoc();
3633 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3634 pWrtShell->StartAllAction();
3635 pWrtShell->BulletOn();
3636 pWrtShell->EndAllAction();
3637 // There is already an initial text node, so type 5 times, but split 4 times.
3638 for (int i = 0; i < 4; ++i)
3640 pWrtShell->Insert2("a");
3641 pWrtShell->SplitNode();
3643 pWrtShell->Insert2("a");
3644 // Go to the end of the 4th para.
3645 pWrtShell->Up(/*bSelect=*/false);
3646 pWrtShell->GetLayout()->PaintSwFrame(*pWrtShell->GetOut(),
3647 pWrtShell->GetLayout()->getFrameArea());
3648 Scheduler::ProcessEventsToIdle();
3649 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3650 m_aInvalidations = tools::Rectangle();
3652 // When selecting and deleting several bullets: select till the end of the 2nd para and delete.
3653 pWrtShell->Up(/*bSelect=*/true, /*nCount=*/2);
3654 pWrtShell->DelRight();
3656 // Then the first paragraph should not be invalidated.
3657 SwRootFrame* pRoot = pWrtShell->GetLayout();
3658 SwFrame* pPage = pRoot->GetLower();
3659 SwFrame* pBody = pPage->GetLower();
3660 SwFrame* pFirstText = pBody->GetLower();
3661 tools::Rectangle aFirstTextRect = pFirstText->getFrameArea().SVRect();
3662 CPPUNIT_ASSERT(!aFirstTextRect.Overlaps(m_aInvalidations));
3665 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCondCollCopy)
3667 // Given a document with a custom Text Body cond style:
3668 SwXTextDocument* pXTextDocument = createDoc("cond-coll-copy.odt");
3669 uno::Sequence<beans::PropertyValue> aPropertyValues
3670 = { comphelper::makePropertyValue("Style", OUString("Text body")),
3671 comphelper::makePropertyValue("FamilyName", OUString("ParagraphStyles")) };
3672 dispatchCommand(mxComponent, ".uno:StyleApply", aPropertyValues);
3673 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3674 pWrtShell->SelAll();
3676 // When getting the text selection, then make sure it doesn't crash:
3677 uno::Reference<datatransfer::XTransferable2> xTransferable(pXTextDocument->getSelection(),
3678 css::uno::UNO_QUERY);
3679 datatransfer::DataFlavor aFlavor;
3680 aFlavor.MimeType = "text/plain;charset=utf-16";
3681 aFlavor.DataType = cppu::UnoType<OUString>::get();
3682 CPPUNIT_ASSERT(xTransferable->isDataFlavorSupported(aFlavor));
3683 // Without the accompanying fix in place, this test would have crashed.
3684 xTransferable->getTransferData(aFlavor);
3687 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlinePortions)
3689 // Given a document with 3 portions: before insert redline (foo), the insert redline (ins) and after insert
3690 // redline (bar):
3691 SwXTextDocument* pXTextDocument = createDoc();
3692 SwDocShell* pDocShell = pXTextDocument->GetDocShell();
3693 SwView* pView = pDocShell->GetView();
3694 pView->SetRedlineAuthor("first");
3695 pDocShell->SetView(pView);
3696 SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
3697 pWrtShell->Insert("foo");
3698 pDocShell->SetChangeRecording(true);
3699 pWrtShell->Insert("ins");
3700 pDocShell->SetChangeRecording(false);
3701 pWrtShell->Insert("bar after");
3703 // When deleting "fooinsbar":
3704 pView->SetRedlineAuthor("second");
3705 pDocShell->SetView(pView);
3706 pWrtShell->SttEndDoc(/*bStt*/true);
3707 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, /*nCount=*/9, /*bBasicCall=*/false);
3708 pDocShell->SetChangeRecording(true);
3709 pWrtShell->Delete();
3711 // Then make sure that the portion list is updated, so "bar" can be marked as deleted without
3712 // marking " after" as well:
3713 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
3714 assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[1]", "portion", "foo");
3715 assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[2]", "portion", "ins");
3716 // Without the accompanying fix in place, this test would have failed width:
3717 // - Expected: bar
3718 // - Actual : bar after
3719 // i.e. the portion list was outdated, even " after" was marked as deleted.
3720 assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[3]", "portion", "bar");
3721 assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[4]", "portion", " after");
3724 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testContentControl)
3726 // Given a document with a content control:
3727 SwXTextDocument* pXTextDocument = createDoc();
3728 uno::Reference<text::XText> xText = pXTextDocument->getText();
3729 uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
3730 xText->insertString(xCursor, "test", /*bAbsorb=*/false);
3731 xCursor->gotoStart(/*bExpand=*/false);
3732 xCursor->gotoEnd(/*bExpand=*/true);
3733 uno::Reference<text::XTextContent> xContentControl(
3734 pXTextDocument->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
3735 uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
3736 xContentControlProps->setPropertyValue("Alias", uno::Any(OUString("my alias")));
3737 xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
3738 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3739 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3740 pWrtShell->SttEndDoc(/*bStt=*/true);
3741 m_aContentControl.clear();
3743 // When entering that content control (chars 2-7 are the content control):
3744 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, /*nCount=*/5, /*bBasicCall=*/false);
3746 // Then make sure that the callback is emitted:
3747 // Without the accompanying fix in place, this test would have failed, no callback was emitted.
3748 CPPUNIT_ASSERT(!m_aContentControl.isEmpty());
3750 std::stringstream aStream((std::string(m_aContentControl)));
3751 boost::property_tree::ptree aTree;
3752 boost::property_tree::read_json(aStream, aTree);
3753 OString sAction( aTree.get_child("action").get_value<std::string>() );
3754 CPPUNIT_ASSERT_EQUAL(OString("show"), sAction);
3755 OString sRectangles( aTree.get_child("rectangles").get_value<std::string>() );
3756 CPPUNIT_ASSERT(!sRectangles.isEmpty());
3757 // Without the accompanying fix in place, this test would have failed width:
3758 // uncaught exception of type std::exception (or derived).
3759 // - No such node (alias)
3760 OString sAlias( aTree.get_child("alias").get_value<std::string>() );
3761 CPPUNIT_ASSERT_EQUAL(OString("my alias"), sAlias);
3764 // And when leaving that content control:
3765 pWrtShell->SttEndDoc(/*bStt=*/true);
3767 // Then make sure that the callback is emitted again:
3768 std::stringstream aStream((std::string(m_aContentControl)));
3769 boost::property_tree::ptree aTree;
3770 boost::property_tree::read_json(aStream, aTree);
3771 OString sAction( aTree.get_child("action").get_value<std::string>() );
3772 CPPUNIT_ASSERT_EQUAL(OString("hide"), sAction);
3775 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownContentControl)
3777 // Given a document with a dropdown content control:
3778 SwXTextDocument* pXTextDocument = createDoc();
3779 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3780 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3781 uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
3782 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
3783 uno::Reference<text::XText> xText = xTextDocument->getText();
3784 uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
3785 xText->insertString(xCursor, "choose an item", /*bAbsorb=*/false);
3786 xCursor->gotoStart(/*bExpand=*/false);
3787 xCursor->gotoEnd(/*bExpand=*/true);
3788 uno::Reference<text::XTextContent> xContentControl(
3789 xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
3790 uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
3792 uno::Sequence<beans::PropertyValues> aListItems = {
3794 comphelper::makePropertyValue("DisplayText", uno::Any(OUString("red"))),
3795 comphelper::makePropertyValue("Value", uno::Any(OUString("R"))),
3798 comphelper::makePropertyValue("DisplayText", uno::Any(OUString("green"))),
3799 comphelper::makePropertyValue("Value", uno::Any(OUString("G"))),
3802 comphelper::makePropertyValue("DisplayText", uno::Any(OUString("blue"))),
3803 comphelper::makePropertyValue("Value", uno::Any(OUString("B"))),
3806 xContentControlProps->setPropertyValue("ListItems", uno::Any(aListItems));
3808 xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
3809 pWrtShell->SttEndDoc(/*bStt=*/true);
3810 m_aContentControl.clear();
3812 // When entering that content control:
3813 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false);
3815 // Then make sure that the callback is emitted:
3816 CPPUNIT_ASSERT(!m_aContentControl.isEmpty());
3818 std::stringstream aStream((std::string(m_aContentControl)));
3819 boost::property_tree::ptree aTree;
3820 boost::property_tree::read_json(aStream, aTree);
3821 OString sAction( aTree.get_child("action").get_value<std::string>() );
3822 CPPUNIT_ASSERT_EQUAL(OString("show"), sAction);
3823 OString sRectangles( aTree.get_child("rectangles").get_value<std::string>() );
3824 CPPUNIT_ASSERT(!sRectangles.isEmpty());
3825 boost::optional<boost::property_tree::ptree&> oItems = aTree.get_child_optional("items");
3826 CPPUNIT_ASSERT(oItems);
3827 static const std::vector<std::string> vExpected = { "red", "green", "blue" };
3828 size_t i = 0;
3829 for (const auto& rItem : *oItems)
3831 CPPUNIT_ASSERT_EQUAL(vExpected[i++], rItem.second.get_value<std::string>());
3835 // And when selecting the 2nd item (green):
3836 std::map<OUString, OUString> aArguments;
3837 aArguments.emplace("type", "drop-down");
3838 aArguments.emplace("selected", "1");
3839 pXTextDocument->executeContentControlEvent(aArguments);
3841 // Then make sure that the document is updated accordingly:
3842 SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
3843 // Without the accompanying fix in place, this test would have failed with:
3844 // - Expected: green
3845 // - Actual : choose an item
3846 // i.e. the document text was not updated.
3847 CPPUNIT_ASSERT_EQUAL(OUString("green"), pTextNode->GetExpandText(pWrtShell->GetLayout()));
3850 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPictureContentControl)
3852 // Given a document with a picture content control:
3853 SwXTextDocument* pXTextDocument = createDoc();
3854 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3855 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3856 uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
3857 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
3858 uno::Reference<text::XText> xText = xTextDocument->getText();
3859 uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
3860 uno::Reference<beans::XPropertySet> xTextGraphic(
3861 xMSF->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
3862 xTextGraphic->setPropertyValue("AnchorType",
3863 uno::Any(text::TextContentAnchorType_AS_CHARACTER));
3864 uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY);
3865 xText->insertTextContent(xCursor, xTextContent, false);
3866 xCursor->gotoStart(/*bExpand=*/false);
3867 xCursor->gotoEnd(/*bExpand=*/true);
3868 uno::Reference<text::XTextContent> xContentControl(
3869 xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
3870 uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
3871 xContentControlProps->setPropertyValue("ShowingPlaceHolder", uno::Any(true));
3872 xContentControlProps->setPropertyValue("Picture", uno::Any(true));
3873 xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
3874 pWrtShell->SttEndDoc(/*bStt=*/true);
3875 m_aContentControl.clear();
3877 // When clicking on that content control:
3878 pWrtShell->GotoObj(/*bNext=*/true, GotoObjFlags::Any);
3879 pWrtShell->EnterSelFrameMode();
3880 const SwFrameFormat* pFlyFormat = pWrtShell->GetFlyFrameFormat();
3881 const SwFormatAnchor& rFormatAnchor = pFlyFormat->GetAnchor();
3882 const SwNode* pAnchorNode = rFormatAnchor.GetAnchorNode();
3883 const SwTextNode* pTextNode = pAnchorNode->GetTextNode();
3884 SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
3885 auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
3886 auto& rFormatContentControl
3887 = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
3888 pWrtShell->GotoContentControl(rFormatContentControl);
3890 // Then make sure that the callback is emitted:
3891 // Without the accompanying fix in place, this test would have failed, no callback was emitted.
3892 CPPUNIT_ASSERT(!m_aContentControl.isEmpty());
3893 std::stringstream aStream((std::string(m_aContentControl)));
3894 boost::property_tree::ptree aTree;
3895 boost::property_tree::read_json(aStream, aTree);
3896 OString sAction( aTree.get_child("action").get_value<std::string>() );
3897 CPPUNIT_ASSERT_EQUAL(OString("change-picture"), sAction);
3899 // And when replacing the image:
3900 std::map<OUString, OUString> aArguments;
3901 aArguments.emplace("type", "picture");
3902 OUString aURL = m_directories.getURLFromSrc(u"sw/qa/extras/uiwriter/data/ole2.png");
3903 aArguments.emplace("changed", aURL);
3904 pXTextDocument->executeContentControlEvent(aArguments);
3906 // Then make sure that the document is updated accordingly:
3907 uno::Reference<drawing::XShape> xShape = getShape(1);
3908 auto xGraphic = getProperty<uno::Reference<beans::XPropertySet>>(xShape, "Graphic");
3909 // Without the accompanying fix in place, this test would have failed, xGraphic was empty after
3910 // executeContentControlEvent().
3911 CPPUNIT_ASSERT(xGraphic.is());
3912 CPPUNIT_ASSERT_EQUAL(OUString("image/png"), getProperty<OUString>(xGraphic, "MimeType"));
3916 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDateContentControl)
3918 // Given a document with a date content control:
3919 SwXTextDocument* pXTextDocument = createDoc();
3920 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
3921 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3922 uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
3923 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
3924 uno::Reference<text::XText> xText = xTextDocument->getText();
3925 uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
3926 xText->insertString(xCursor, "choose a date", /*bAbsorb=*/false);
3927 xCursor->gotoStart(/*bExpand=*/false);
3928 xCursor->gotoEnd(/*bExpand=*/true);
3929 uno::Reference<text::XTextContent> xContentControl(
3930 xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
3931 uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
3932 xContentControlProps->setPropertyValue("Date", uno::Any(true));
3933 xContentControlProps->setPropertyValue("DateFormat", uno::Any(OUString("YYYY-MM-DD")));
3934 xContentControlProps->setPropertyValue("DateLanguage", uno::Any(OUString("en-US")));
3935 xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
3936 pWrtShell->SttEndDoc(/*bStt=*/true);
3937 m_aContentControl.clear();
3939 // When entering that content control:
3940 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false);
3942 // Then make sure that the callback is emitted:
3943 CPPUNIT_ASSERT(!m_aContentControl.isEmpty());
3945 std::stringstream aStream((std::string(m_aContentControl)));
3946 boost::property_tree::ptree aTree;
3947 boost::property_tree::read_json(aStream, aTree);
3948 OString sAction( aTree.get_child("action").get_value<std::string>() );
3949 CPPUNIT_ASSERT_EQUAL(OString("show"), sAction);
3950 OString sRectangles( aTree.get_child("rectangles").get_value<std::string>() );
3951 CPPUNIT_ASSERT(!sRectangles.isEmpty());
3952 boost::optional<boost::property_tree::ptree&> oDate = aTree.get_child_optional("date");
3953 CPPUNIT_ASSERT(oDate);
3956 // And when selecting a date:
3957 std::map<OUString, OUString> aArguments;
3958 aArguments.emplace("type", "date");
3959 aArguments.emplace("selected", "2022-05-30T00:00:00Z");
3960 pXTextDocument->executeContentControlEvent(aArguments);
3962 // Then make sure that the document is updated accordingly:
3963 SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
3964 // Without the accompanying fix in place, this test would have failed with:
3965 // - Expected: 2022-05-30
3966 // - Actual : choose a date
3967 // i.e. the document text was not updated.
3968 CPPUNIT_ASSERT_EQUAL(OUString("2022-05-30"), pTextNode->GetExpandText(pWrtShell->GetLayout()));
3971 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAuthorField)
3973 SwXTextDocument* pXTextDocument = createDoc();
3974 const OUString sAuthor("Abcd Xyz");
3976 uno::Sequence<beans::PropertyValue> aPropertyValues1(comphelper::InitPropertySequence(
3978 {".uno:Author", uno::Any(sAuthor)},
3979 }));
3980 pXTextDocument->initializeForTiledRendering(aPropertyValues1);
3982 auto insertAuthorField = [this]()
3984 uno::Reference<lang::XMultiServiceFactory> const xMSF(mxComponent, uno::UNO_QUERY_THROW);
3985 uno::Reference<text::XTextDocument> const xTD(mxComponent, uno::UNO_QUERY_THROW);
3987 auto const xText = xTD->getText();
3988 auto const xTextCursor = xText->createTextCursor();
3989 CPPUNIT_ASSERT(xTextCursor.is());
3991 xTextCursor->gotoEnd(false);
3993 uno::Reference<text::XTextField> const xTextField(
3994 xMSF->createInstance("com.sun.star.text.textfield.Author"), uno::UNO_QUERY_THROW);
3996 uno::Reference<beans::XPropertySet> xTextFieldProps(xTextField, uno::UNO_QUERY_THROW);
3997 xTextFieldProps->setPropertyValue("FullName", uno::Any(true));
3999 xText->insertTextContent(xTextCursor, xTextField, false);
4002 insertAuthorField();
4003 Scheduler::ProcessEventsToIdle();
4005 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
4007 assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion[1]/SwLineLayout[1]/SwFieldPortion[1]", "expand", sAuthor);
4010 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSavedAuthorField)
4012 SwXTextDocument* pXTextDocument = createDoc("savedauthorfield.odt");
4013 const OUString sAuthor("XYZ ABCD");
4014 uno::Sequence<beans::PropertyValue> aPropertyValues1(comphelper::InitPropertySequence(
4016 {".uno:Author", uno::Any(sAuthor)},
4017 }));
4018 pXTextDocument->initializeForTiledRendering(aPropertyValues1);
4020 Scheduler::ProcessEventsToIdle();
4022 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
4023 assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion[1]/SwLineLayout[1]/SwFieldPortion[1]", "expand", sAuthor);
4026 CPPUNIT_PLUGIN_IMPLEMENT();
4028 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */