docthemes: Save themes def. to a file when added to ColorSets
[LibreOffice.git] / sw / qa / extras / tiledrendering / tiledrendering.cxx
blob8b9387d2afb1f905838b0efda39943738f14a64e
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 <swtiledrenderingtest.hxx>
12 #include <string>
13 #include <string_view>
15 #include <boost/property_tree/json_parser.hpp>
17 #include <com/sun/star/frame/XStorable.hpp>
18 #include <com/sun/star/frame/Desktop.hpp>
19 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
20 #include <com/sun/star/text/XTextField.hpp>
21 #include <com/sun/star/text/AuthorDisplayFormat.hpp>
22 #include <com/sun/star/datatransfer/XTransferable2.hpp>
23 #include <com/sun/star/frame/XDispatchResultListener.hpp>
24 #include <com/sun/star/frame/DispatchResultState.hpp>
26 #include <test/helper/transferable.hxx>
27 #include <comphelper/dispatchcommand.hxx>
28 #include <comphelper/propertysequence.hxx>
29 #include <svx/svdpage.hxx>
30 #include <svx/svdview.hxx>
31 #include <vcl/virdev.hxx>
32 #include <vcl/filter/PngImageWriter.hxx>
33 #include <editeng/editview.hxx>
34 #include <editeng/outliner.hxx>
35 #include <editeng/wghtitem.hxx>
36 #include <svl/srchitem.hxx>
37 #include <svl/slstitm.hxx>
38 #include <svl/stritem.hxx>
39 #include <svl/voiditem.hxx>
40 #include <sfx2/viewsh.hxx>
41 #include <sfx2/bindings.hxx>
42 #include <sfx2/dispatch.hxx>
43 #include <sfx2/viewfrm.hxx>
44 #include <vcl/scheduler.hxx>
45 #include <vcl/vclevent.hxx>
46 #include <vcl/BitmapReadAccess.hxx>
47 #include <vcl/ITiledRenderable.hxx>
48 #include <tools/json_writer.hxx>
49 #include <unotools/mediadescriptor.hxx>
50 #include <comphelper/processfactory.hxx>
51 #include <comphelper/propertyvalue.hxx>
52 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
53 #include <sfx2/lokhelper.hxx>
54 #include <comphelper/lok.hxx>
55 #include <comphelper/string.hxx>
57 #include <drawdoc.hxx>
58 #include <ndtxt.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 <txtfrm.hxx>
68 #include <rootfrm.hxx>
69 #include <fmtanchr.hxx>
70 #include <docsh.hxx>
71 #include <wrtsh.hxx>
72 #include <unotxdoc.hxx>
73 #include <textcontentcontrol.hxx>
74 #include <swtestviewcallback.hxx>
76 static std::ostream& operator<<(std::ostream& os, ViewShellId id)
78 os << static_cast<sal_Int32>(id);
79 return os;
82 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRegisterCallback)
84 createDoc("dummy.fodt");
85 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
86 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
87 // Insert a character at the beginning of the document.
88 pWrtShell->Insert(u"x"_ustr);
89 Scheduler::ProcessEventsToIdle();
91 // Check that the top left 256x256px tile would be invalidated.
92 CPPUNIT_ASSERT(!m_aInvalidation.IsEmpty());
93 tools::Rectangle aTopLeft(0, 0, 256*15, 256*15); // 1 px = 15 twips, assuming 96 DPI.
94 CPPUNIT_ASSERT(m_aInvalidation.Overlaps(aTopLeft));
97 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPostKeyEvent)
99 createDoc("dummy.fodt");
100 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
101 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
102 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
103 // Did we manage to go after the first character?
104 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pShellCursor->GetPoint()->GetContentIndex());
106 emulateTyping(u"x");
107 // Did we manage to insert the character after the first one?
108 CPPUNIT_ASSERT_EQUAL(u"Axaa bbb."_ustr, pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
111 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPostMouseEvent)
113 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
114 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
115 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
116 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
117 // Did we manage to go after the first character?
118 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pShellCursor->GetPoint()->GetContentIndex());
120 Point aStart = pShellCursor->GetSttPos();
121 aStart.setX(aStart.getX() - 1000);
122 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
123 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
124 Scheduler::ProcessEventsToIdle();
125 // The new cursor position must be before the first word.
126 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), pShellCursor->GetPoint()->GetContentIndex());
129 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSetTextSelection)
131 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
132 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
133 // Move the cursor into the second word.
134 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 5, /*bBasicCall=*/false);
135 // Create a selection on the word.
136 pWrtShell->SelWrd();
137 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
138 // Did we indeed manage to select the second word?
139 CPPUNIT_ASSERT_EQUAL(u"bbb"_ustr, pShellCursor->GetText());
141 // Now use setTextSelection() to move the start of the selection 1000 twips left.
142 Point aStart = pShellCursor->GetSttPos();
143 aStart.setX(aStart.getX() - 1000);
144 pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_START, aStart.getX(), aStart.getY());
145 // The new selection must include the first word, too -- but not the ending dot.
146 CPPUNIT_ASSERT_EQUAL(u"Aaa bbb"_ustr, pShellCursor->GetText());
148 // Next: test that LOK_SETTEXTSELECTION_RESET + LOK_SETTEXTSELECTION_END can be used to create a selection.
149 pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_RESET, aStart.getX(), aStart.getY());
150 pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_END, aStart.getX() + 1000, aStart.getY());
151 CPPUNIT_ASSERT_EQUAL(u"Aaa b"_ustr, pShellCursor->GetText());
154 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetTextSelection)
156 SwXTextDocument* pXTextDocument = createDoc("shape-with-text.fodt");
157 // No crash, just empty output for unexpected mime type.
158 CPPUNIT_ASSERT_EQUAL(OString(), apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "foo/bar"_ostr));
160 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
161 // Move the cursor into the first word.
162 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2, /*bBasicCall=*/false);
163 // Create a selection by on the word.
164 pWrtShell->SelWrd();
166 // Make sure that we selected text from the body text.
167 CPPUNIT_ASSERT_EQUAL("Hello"_ostr, apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8"_ostr));
169 // Make sure we produce something for HTML.
170 CPPUNIT_ASSERT(!apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/html"_ostr).isEmpty());
172 // Now select some shape text and check again.
173 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
174 SdrObject* pObject = pPage->GetObj(0);
175 SdrView* pView = pWrtShell->GetDrawView();
176 pView->SdrBeginTextEdit(pObject);
177 CPPUNIT_ASSERT(pView->GetTextEditObject());
178 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
179 ESelection aWordSelection(0, 0, 0, 5);
180 rEditView.SetSelection(aWordSelection);
181 CPPUNIT_ASSERT_EQUAL("Shape"_ostr, apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8"_ostr));
184 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetTextSelectionLineLimit)
186 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 ы.");
187 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>.");
189 SwXTextDocument* pXTextDocument = createDoc("estonian.odt");
191 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
192 // Move the cursor into the first word.
193 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2, /*bBasicCall=*/false);
194 // Create a selection.
195 pWrtShell->SelAll();
197 OString sPlainText = apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8"_ostr);
199 CPPUNIT_ASSERT_EQUAL(OString(sOriginalText), sPlainText.trim());
201 OString sHtmlText = apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/html"_ostr);
203 int nStart = sHtmlText.indexOf(u8"Estonian");
205 CPPUNIT_ASSERT(sHtmlText.match(sExpectedHtml, nStart));
208 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetTextSelectionMultiLine)
210 // Test will check if correct number of new line marks / paragraphs is generated
211 static OStringLiteral sOriginalText(u8"Heading\n\
212 Let's have text; we need to be able to select the text inside the shape, but also the various individual ones too:\n\
218 And this is all for Writer shape objects\n\
219 Heading on second page");
221 static OStringLiteral sExpectedHtml(u8"Heading</h2>\n\
222 <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\
223 <p><br/><br/></p>\n\
224 <p><br/><br/></p>\n\
225 <p><br/><br/></p>\n\
226 <p><br/><br/></p>\n\
227 <p><br/><br/></p>\n\
228 <h1 class=\"western\">And this is all for Writer shape objects</h1>\n\
229 <h2 class=\"western\">Heading on second page</h2>");
231 SwXTextDocument* pXTextDocument = createDoc("multiline.odt");
233 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
234 // Create a selection.
235 pWrtShell->SelAll();
237 OString sPlainText = apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8"_ostr);
239 CPPUNIT_ASSERT_EQUAL(OString(sOriginalText), sPlainText.trim());
241 OString sHtmlText = apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/html"_ostr);
243 int nStart = sHtmlText.indexOf(u8"Heading");
245 CPPUNIT_ASSERT(sHtmlText.match(sExpectedHtml, nStart));
248 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSetGraphicSelection)
250 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
251 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
252 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
253 SdrObject* pObject = pPage->GetObj(0);
254 pWrtShell->SelectObj(Point(), 0, pObject);
255 SdrHdlList handleList(nullptr);
256 pObject->AddToHdlList(handleList);
257 // Make sure the rectangle has 8 handles: at each corner and at the center of each edge.
258 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(8), handleList.GetHdlCount());
259 // Take the bottom center one.
260 SdrHdl* pHdl = handleList.GetHdl(6);
261 CPPUNIT_ASSERT_EQUAL(int(SdrHdlKind::Lower), static_cast<int>(pHdl->GetKind()));
262 tools::Rectangle aShapeBefore = pObject->GetSnapRect();
263 // Resize.
264 pXTextDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_START, pHdl->GetPos().getX(), pHdl->GetPos().getY());
265 pXTextDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_END, pHdl->GetPos().getX(), pHdl->GetPos().getY() + 1000);
266 tools::Rectangle aShapeAfter = pObject->GetSnapRect();
267 // Check that a resize happened, but aspect ratio is not kept.
268 CPPUNIT_ASSERT_EQUAL(aShapeBefore.getOpenWidth(), aShapeAfter.getOpenWidth());
269 CPPUNIT_ASSERT_EQUAL(aShapeBefore.getOpenHeight() + 1000, aShapeAfter.getOpenHeight());
272 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testResetSelection)
274 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
275 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
276 // Select one character.
277 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 1, /*bBasicCall=*/false);
278 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
279 // We have a text selection.
280 CPPUNIT_ASSERT(pShellCursor->HasMark());
282 pXTextDocument->resetSelection();
283 // We no longer have a text selection.
284 CPPUNIT_ASSERT(!pShellCursor->HasMark());
286 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
287 SdrObject* pObject = pPage->GetObj(0);
288 Point aPoint = pObject->GetSnapRect().Center();
289 // Select the shape.
290 pWrtShell->EnterSelFrameMode(&aPoint);
291 // We have a graphic selection.
292 CPPUNIT_ASSERT(pWrtShell->IsSelFrameMode());
294 pXTextDocument->resetSelection();
295 // We no longer have a graphic selection.
296 CPPUNIT_ASSERT(!pWrtShell->IsSelFrameMode());
299 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testInsertShape)
301 SwXTextDocument* pXTextDocument = createDoc("2-pages.odt");
302 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
304 pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
305 comphelper::dispatchCommand(u".uno:BasicShapes.circle"_ustr, uno::Sequence<beans::PropertyValue>());
307 // check that the shape was inserted in the visible area, not outside
308 IDocumentDrawModelAccess &rDrawModelAccess = pWrtShell->GetDoc()->getIDocumentDrawModelAccess();
309 SdrPage* pPage = rDrawModelAccess.GetDrawModel()->GetPage(0);
310 SdrObject* pObject = pPage->GetObj(0);
312 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(3299, 299), Size(3403, 3403)), pObject->GetSnapRect());
314 // check that it is in the foreground layer
315 CPPUNIT_ASSERT_EQUAL(rDrawModelAccess.GetHeavenId().get(), pObject->GetLayer().get());
318 static void lcl_search(bool bBackward)
320 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
322 {"SearchItem.SearchString", uno::Any(u"shape"_ustr)},
323 {"SearchItem.Backward", uno::Any(bBackward)}
324 }));
325 comphelper::dispatchCommand(u".uno:ExecuteSearch"_ustr, aPropertyValues);
328 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearch)
330 createDoc("search.odt");
331 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
332 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
333 SwNodeOffset nNode = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
335 // First hit, in the second paragraph, before the shape.
336 lcl_search(false);
337 CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
338 SwNodeOffset nActual = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
339 CPPUNIT_ASSERT_EQUAL(nNode + 1, nActual);
340 /// Make sure we get search result selection for normal find as well, not only find all.
341 CPPUNIT_ASSERT(!m_aSearchResultSelection.empty());
343 // Next hit, in the shape.
344 lcl_search(false);
345 CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
347 // Next hit, in the shape, still.
348 lcl_search(false);
349 CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
351 // Last hit, in the last paragraph, after the shape.
352 lcl_search(false);
353 CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
354 nActual = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
355 CPPUNIT_ASSERT_EQUAL(nNode + 7, nActual);
357 // Now change direction and make sure that the first 2 hits are in the shape, but not the 3rd one.
358 lcl_search(true);
359 CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
360 lcl_search(true);
361 CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
362 lcl_search(true);
363 CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
364 nActual = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
365 CPPUNIT_ASSERT_EQUAL(nNode + 1, nActual);
368 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchViewArea)
370 createDoc("search.odt");
371 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
372 // Go to the second page, 1-based.
373 pWrtShell->GotoPage(2, false);
374 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
375 // Get the ~top left corner of the second page.
376 Point aPoint = pShellCursor->GetSttPos();
378 // Go back to the first page, search while the cursor is there, but the
379 // visible area is the second page.
380 pWrtShell->GotoPage(1, false);
381 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
383 {"SearchItem.SearchString", uno::Any(u"Heading"_ustr)},
384 {"SearchItem.Backward", uno::Any(false)},
385 {"SearchItem.SearchStartPointX", uno::Any(static_cast<sal_Int32>(aPoint.getX()))},
386 {"SearchItem.SearchStartPointY", uno::Any(static_cast<sal_Int32>(aPoint.getY()))}
387 }));
388 comphelper::dispatchCommand(u".uno:ExecuteSearch"_ustr, aPropertyValues);
389 // This was just "Heading", i.e. SwView::SearchAndWrap() did not search from only the top of the second page.
390 CPPUNIT_ASSERT_EQUAL(u"Heading on second page"_ustr, pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
393 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchTextFrame)
395 createDoc("search.odt");
396 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
397 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
398 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
400 {"SearchItem.SearchString", uno::Any(u"TextFrame"_ustr)},
401 {"SearchItem.Backward", uno::Any(false)},
402 }));
403 comphelper::dispatchCommand(u".uno:ExecuteSearch"_ustr, aPropertyValues);
404 // This was empty: nothing was highlighted after searching for 'TextFrame'.
405 CPPUNIT_ASSERT(!m_aTextSelection.isEmpty());
408 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchTextFrameWrapAround)
410 createDoc("search.odt");
411 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
412 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
413 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
415 {"SearchItem.SearchString", uno::Any(u"TextFrame"_ustr)},
416 {"SearchItem.Backward", uno::Any(false)},
417 }));
418 comphelper::dispatchCommand(u".uno:ExecuteSearch"_ustr, aPropertyValues);
419 CPPUNIT_ASSERT(m_bFound);
420 comphelper::dispatchCommand(u".uno:ExecuteSearch"_ustr, aPropertyValues);
421 // This failed, i.e. the second time 'not found' was reported, instead of wrapping around.
422 CPPUNIT_ASSERT(m_bFound);
425 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDocumentSizeChanged)
427 // Get the current document size.
428 SwXTextDocument* pXTextDocument = createDoc("2-pages.odt");
429 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
430 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
431 Size aSize = pXTextDocument->getDocumentSize();
433 // Delete the second page and see how the size changes.
434 pWrtShell->Down(false);
435 pWrtShell->DelLeft();
436 // Document width should not change, this was 0.
437 CPPUNIT_ASSERT_EQUAL(aSize.getWidth(), m_aDocumentSize.getWidth());
438 // Document height should be smaller now.
439 CPPUNIT_ASSERT(aSize.getHeight() > m_aDocumentSize.getHeight());
442 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchAll)
444 createDoc("search.odt");
445 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
446 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
447 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
449 {"SearchItem.SearchString", uno::Any(u"shape"_ustr)},
450 {"SearchItem.Backward", uno::Any(false)},
451 {"SearchItem.Command", uno::Any(static_cast<sal_uInt16>(SvxSearchCmd::FIND_ALL))},
452 }));
453 comphelper::dispatchCommand(u".uno:ExecuteSearch"_ustr, aPropertyValues);
454 // This was 0; should be 2 results in the body text.
455 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), m_aSearchResultSelection.size());
456 // Writer documents are always a single part.
457 CPPUNIT_ASSERT_EQUAL(0, m_aSearchResultPart[0]);
460 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchAllNotifications)
462 createDoc("search.odt");
463 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
464 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
465 // Reset notification counter before search.
466 m_nSelectionBeforeSearchResult = 0;
467 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
469 {"SearchItem.SearchString", uno::Any(u"shape"_ustr)},
470 {"SearchItem.Backward", uno::Any(false)},
471 {"SearchItem.Command", uno::Any(static_cast<sal_uInt16>(SvxSearchCmd::FIND_ALL))},
472 }));
473 comphelper::dispatchCommand(u".uno:ExecuteSearch"_ustr, aPropertyValues);
474 Scheduler::ProcessEventsToIdle();
476 // This was 5, make sure that we get no notifications about selection changes during search.
477 CPPUNIT_ASSERT_EQUAL(0, m_nSelectionBeforeSearchResult);
478 // But we do get the selection afterwards.
479 CPPUNIT_ASSERT(m_nSelectionAfterSearchResult > 0);
482 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPageDownInvalidation)
484 SwXTextDocument* pXTextDocument = createDoc("pagedown-invalidation.odt");
485 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
487 {".uno:HideWhitespace", uno::Any(true)},
488 }));
489 pXTextDocument->initializeForTiledRendering(aPropertyValues);
490 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
491 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
492 comphelper::dispatchCommand(u".uno:PageDown"_ustr, uno::Sequence<beans::PropertyValue>());
494 // This was 2.
495 CPPUNIT_ASSERT_EQUAL(0, m_nInvalidations);
498 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPartHash)
500 SwXTextDocument* pXTextDocument = createDoc("pagedown-invalidation.odt");
501 int nParts = pXTextDocument->getParts();
502 for (int it = 0; it < nParts; it++)
504 CPPUNIT_ASSERT(!pXTextDocument->getPartHash(it).isEmpty());
508 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testMissingInvalidation)
510 // Create two views.
511 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
512 SwTestViewCallback aView1;
513 int nView1 = SfxLokHelper::getView();
514 SfxLokHelper::createView();
515 SwTestViewCallback aView2;
516 int nView2 = SfxLokHelper::getView();
518 // First view: put the cursor into the first word.
519 SfxLokHelper::setView(nView1);
520 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
521 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
523 // Second view: select the first word.
524 SfxLokHelper::setView(nView2);
525 CPPUNIT_ASSERT(getSwDocShell()->GetWrtShell() != pWrtShell);
526 pWrtShell = getSwDocShell()->GetWrtShell();
527 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
528 pWrtShell->SelWrd();
530 // Now delete the selected word and make sure both views are invalidated.
531 Scheduler::ProcessEventsToIdle();
532 aView1.m_bTilesInvalidated = false;
533 aView2.m_bTilesInvalidated = false;
534 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DELETE);
535 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DELETE);
536 Scheduler::ProcessEventsToIdle();
537 CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
538 CPPUNIT_ASSERT(aView2.m_bTilesInvalidated);
541 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewCursors)
543 createDoc("dummy.fodt");
544 SwTestViewCallback aView1;
545 SfxLokHelper::createView();
546 SwTestViewCallback aView2;
548 Scheduler::ProcessEventsToIdle();
549 CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
550 CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
551 CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
552 // This failed: the cursor position of view1 was only known to view2 once
553 // it changed.
554 CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
556 // Make sure that aView1 gets a view-only selection notification, while
557 // aView2 gets a real selection notification.
558 aView1.m_bOwnSelectionSet = false;
559 aView1.m_bViewSelectionSet = false;
560 aView2.m_bOwnSelectionSet = false;
561 aView2.m_bViewSelectionSet = false;
562 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
563 // Move the cursor into the second word.
564 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 5, /*bBasicCall=*/false);
565 // Create a selection on the word.
566 pWrtShell->SelWrd();
567 Scheduler::ProcessEventsToIdle();
568 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
569 // Did we indeed manage to select the second word?
570 CPPUNIT_ASSERT_EQUAL(u"bbb"_ustr, pShellCursor->GetText());
571 CPPUNIT_ASSERT(!aView1.m_bOwnSelectionSet);
572 // This failed, aView1 did not get notification about selection changes in
573 // aView2.
574 CPPUNIT_ASSERT(aView1.m_bViewSelectionSet);
575 CPPUNIT_ASSERT(aView2.m_bOwnSelectionSet);
576 CPPUNIT_ASSERT(!aView2.m_bViewSelectionSet);
579 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeViewCursors)
581 // Load a document and create a view, so we have 2 ones.
582 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
583 SwTestViewCallback aView1;
584 SfxLokHelper::createView();
585 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
586 SwTestViewCallback aView2;
587 SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
589 // Start shape text in the second view.
590 SdrPage* pPage = pWrtShell2->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
591 SdrObject* pObject = pPage->GetObj(0);
592 SdrView* pView = pWrtShell2->GetDrawView();
593 pWrtShell2->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell2->GetWin());
594 emulateTyping(u"x");
595 // Press a key in the second view, while the first one observes this.
596 aView1.m_bViewCursorInvalidated = false;
597 aView2.m_bOwnCursorInvalidated = false;
598 const tools::Rectangle aLastOwnCursor1 = aView1.m_aOwnCursor;
599 const tools::Rectangle aLastViewCursor1 = aView1.m_aViewCursor;
600 const tools::Rectangle aLastOwnCursor2 = aView2.m_aOwnCursor;
601 const tools::Rectangle aLastViewCursor2 = aView2.m_aViewCursor;
603 emulateTyping(u"y");
604 // Make sure that aView1 gets a view-only cursor notification, while
605 // aView2 gets a real cursor notification.
606 CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor, aLastOwnCursor1);
607 CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
608 CPPUNIT_ASSERT(aLastViewCursor1 != aView1.m_aViewCursor);
609 CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
610 CPPUNIT_ASSERT(aLastOwnCursor2 != aView2.m_aOwnCursor);
611 CPPUNIT_ASSERT_EQUAL(aLastViewCursor2, aView2.m_aViewCursor);
614 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewCursorVisibility)
616 // Load a document that has a shape and create two views.
617 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
618 SwTestViewCallback aView1;
619 SfxLokHelper::createView();
620 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
621 SwTestViewCallback aView2;
622 // This failed, initially the view cursor in the second view wasn't visible.
623 CPPUNIT_ASSERT(aView2.m_bViewCursorVisible);
625 // Click on the shape in the second view.
626 aView1.m_bViewCursorVisible = true;
627 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
628 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
629 SdrObject* pObject = pPage->GetObj(0);
630 Point aCenter = pObject->GetSnapRect().Center();
631 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
632 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
633 Scheduler::ProcessEventsToIdle();
634 // Make sure the "view/text" cursor of the first view gets a notification.
635 CPPUNIT_ASSERT(!aView1.m_bViewCursorVisible);
638 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewCursorCleanup)
640 // Load a document that has a shape and create two views.
641 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
642 SwTestViewCallback aView1;
643 int nView2 = SfxLokHelper::createView();
644 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
646 SwTestViewCallback aView2;
648 // Click on the shape in the second view.
649 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
650 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
651 SdrObject* pObject = pPage->GetObj(0);
652 Point aCenter = pObject->GetSnapRect().Center();
653 aView1.m_bGraphicViewSelection = false;
654 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
655 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
656 Scheduler::ProcessEventsToIdle();
657 // Make sure there is a graphic view selection on the first view.
658 CPPUNIT_ASSERT(aView1.m_bGraphicViewSelection);
660 // Now destroy the second view.
661 SfxLokHelper::destroyView(nView2);
662 Scheduler::ProcessEventsToIdle();
663 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), SfxLokHelper::getViewsCount(0));
664 // Make sure that the graphic view selection on the first view is cleaned up.
665 CPPUNIT_ASSERT(!aView1.m_bGraphicViewSelection);
668 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewLock)
670 // Load a document that has a shape and create two views.
671 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
672 SwTestViewCallback aView1;
673 SfxLokHelper::createView();
674 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
675 SwTestViewCallback aView2;
677 // Begin text edit in the second view and assert that the first gets a lock
678 // notification.
679 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
680 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
681 SdrObject* pObject = pPage->GetObj(0);
682 SdrView* pView = pWrtShell->GetDrawView();
683 aView1.m_bViewLock = false;
684 pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
685 CPPUNIT_ASSERT(aView1.m_bViewLock);
687 // End text edit in the second view, and assert that the lock is removed in
688 // the first view.
689 pWrtShell->EndTextEdit();
690 CPPUNIT_ASSERT(!aView1.m_bViewLock);
693 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTextEditViewInvalidations)
695 // Load a document that has a shape and create two views.
696 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
697 SwTestViewCallback aView1;
698 SfxLokHelper::createView();
699 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
700 SwTestViewCallback aView2;
702 // Begin text edit in the second view.
703 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
704 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
705 SdrObject* pObject = pPage->GetObj(0);
706 SdrView* pView = pWrtShell->GetDrawView();
707 pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
708 emulateTyping(u"x");
710 // Assert that both views are invalidated when pressing a key while in text edit.
711 aView1.m_bTilesInvalidated = false;
712 emulateTyping(u"y");
714 CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
716 pWrtShell->EndTextEdit();
719 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoInvalidations)
721 // Load a document and create two views.
722 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
723 SwTestViewCallback aView1;
724 int nView1 = SfxLokHelper::getView();
725 SfxLokHelper::createView();
726 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
727 SwTestViewCallback aView2;
728 SfxLokHelper::setView(nView1);
730 // Insert a character the end of the document.
731 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
732 pWrtShell->EndOfSection();
733 emulateTyping(u"c");
734 // ProcessEventsToIdle resets the view; set it again
735 SfxLokHelper::setView(nView1);
736 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
737 CPPUNIT_ASSERT_EQUAL(u"Aaa bbb.c"_ustr, pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
739 // Undo and assert that both views are invalidated.
740 Scheduler::ProcessEventsToIdle();
741 aView1.m_bTilesInvalidated = false;
742 aView2.m_bTilesInvalidated = false;
743 comphelper::dispatchCommand(u".uno:Undo"_ustr, {});
744 Scheduler::ProcessEventsToIdle();
745 CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
746 // Undo was dispatched on the first view, this second view was not invalidated.
747 CPPUNIT_ASSERT(aView2.m_bTilesInvalidated);
750 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoLimiting)
752 // Load a document and create two views.
753 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
754 SwWrtShell* pWrtShell1 = getSwDocShell()->GetWrtShell();
755 int nView1 = SfxLokHelper::getView();
756 int nView2 = SfxLokHelper::createView();
757 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
759 // Insert a character the end of the document in the second view.
760 SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
761 pWrtShell2->EndOfSection();
762 emulateTyping(u"c");
763 SwShellCursor* pShellCursor = pWrtShell2->getShellCursor(false);
764 CPPUNIT_ASSERT_EQUAL(u"Aaa bbb.c"_ustr, pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
766 // Assert that the first view can't undo, but the second view can.
767 CPPUNIT_ASSERT(!pWrtShell1->GetLastUndoInfo(nullptr, nullptr, &pWrtShell1->GetView()));
768 CPPUNIT_ASSERT(pWrtShell2->GetLastUndoInfo(nullptr, nullptr, &pWrtShell2->GetView()));
770 SfxLokHelper::setView(nView1);
771 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
772 SfxLokHelper::setView(nView2);
773 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
776 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReordering)
778 // Create two views and a document of 2 paragraphs.
779 SwXTextDocument* pXTextDocument = createDoc();
780 SwWrtShell* pWrtShell1 = getSwDocShell()->GetWrtShell();
781 int nView1 = SfxLokHelper::getView();
782 int nView2 = SfxLokHelper::createView();
783 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
784 SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
785 pWrtShell2->SplitNode();
786 SfxLokHelper::setView(nView1);
787 pWrtShell1->SttEndDoc(/*bStt=*/true);
788 SwTextNode* pTextNode1 = pWrtShell1->GetCursor()->GetPointNode().GetTextNode();
789 // View 1 types into the first paragraph.
790 emulateTyping(u"a");
791 SfxLokHelper::setView(nView2);
792 pWrtShell2->SttEndDoc(/*bStt=*/false);
793 SwTextNode* pTextNode2 = pWrtShell2->GetCursor()->GetPointNode().GetTextNode();
794 // View 2 types into the second paragraph.
795 emulateTyping(u"z");
796 CPPUNIT_ASSERT_EQUAL(u"a"_ustr, pTextNode1->GetText());
797 CPPUNIT_ASSERT_EQUAL(u"z"_ustr, pTextNode2->GetText());
799 // When view 1 presses undo:
800 SfxLokHelper::setView(nView1);
801 dispatchCommand(mxComponent, u".uno:Undo"_ustr, {});
803 // Then make sure view 1's last undo action is invoked, out of order:
804 // Without the accompanying fix in place, this test would have failed with:
805 // - Expression: pTextNode1->GetText().isEmpty()
806 // i.e. the "a" in the first paragraph was not removed.
807 CPPUNIT_ASSERT(pTextNode1->GetText().isEmpty());
808 // Last undo action is not invoked, as it belongs to view 2.
809 CPPUNIT_ASSERT_EQUAL(u"z"_ustr, pTextNode2->GetText());
810 SfxLokHelper::setView(nView1);
811 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
812 SfxLokHelper::setView(nView2);
813 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
816 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReorderingRedo)
818 // Create two views and a document of 2 paragraphs.
819 SwXTextDocument* pXTextDocument = createDoc();
820 SwWrtShell* pWrtShell1 = getSwDocShell()->GetWrtShell();
821 int nView1 = SfxLokHelper::getView();
822 int nView2 = SfxLokHelper::createView();
823 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
824 SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
825 pWrtShell2->SplitNode();
826 SfxLokHelper::setView(nView1);
827 pWrtShell1->SttEndDoc(/*bStt=*/true);
828 SwTextNode* pTextNode1 = pWrtShell1->GetCursor()->GetPointNode().GetTextNode();
829 // View 1 types into the first paragraph, twice.
830 emulateTyping(u"f");
831 // Go to the start of the paragraph, to avoid grouping.
832 pWrtShell1->SttEndDoc(/*bStt=*/true);
833 emulateTyping(u"s");
834 SfxLokHelper::setView(nView2);
835 pWrtShell2->SttEndDoc(/*bStt=*/false);
836 SwTextNode* pTextNode2 = pWrtShell2->GetCursor()->GetPointNode().GetTextNode();
837 // View 2 types into the second paragraph.
838 emulateTyping(u"z");
839 CPPUNIT_ASSERT_EQUAL(u"sf"_ustr, pTextNode1->GetText());
840 CPPUNIT_ASSERT_EQUAL(u"z"_ustr, pTextNode2->GetText());
842 // When view 1 presses undo, twice:
843 SfxLokHelper::setView(nView1);
844 dispatchCommand(mxComponent, u".uno:Undo"_ustr, {});
845 // First just s(econd) is erased:
846 CPPUNIT_ASSERT_EQUAL(u"f"_ustr, pTextNode1->GetText());
847 dispatchCommand(mxComponent, u".uno:Undo"_ustr, {});
849 // Then make sure view 1's undo actions are invoked, out of order:
850 // Without the accompanying fix in place, this test would have failed with:
851 // - Expression: pTextNode1->GetText().isEmpty()
852 // i.e. out of order undo was executed only once, not twice.
853 CPPUNIT_ASSERT(pTextNode1->GetText().isEmpty());
854 // The top undo action is not invoked, as it belongs to view 2.
855 CPPUNIT_ASSERT_EQUAL(u"z"_ustr, pTextNode2->GetText());
856 SfxLokHelper::setView(nView1);
857 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
858 SfxLokHelper::setView(nView2);
859 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
862 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReorderingRedo2)
864 // Create two views.
865 SwXTextDocument* pXTextDocument = createDoc();
866 SwWrtShell* pWrtShell1 = getSwDocShell()->GetWrtShell();
867 int nView1 = SfxLokHelper::getView();
868 int nView2 = SfxLokHelper::createView();
869 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
870 SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
872 // Type in the first view.
873 SfxLokHelper::setView(nView1);
874 pWrtShell1->SttEndDoc(/*bStt=*/true);
875 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'f', 0);
876 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'f', 0);
877 Scheduler::ProcessEventsToIdle();
879 // Type to the same paragraph in the second view.
880 SfxLokHelper::setView(nView2);
881 pWrtShell2->SttEndDoc(/*bStt=*/true);
882 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 's', 0);
883 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 's', 0);
884 Scheduler::ProcessEventsToIdle();
886 // Delete in the first view and undo.
887 SfxLokHelper::setView(nView1);
888 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::BACKSPACE);
889 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::BACKSPACE);
890 Scheduler::ProcessEventsToIdle();
891 dispatchCommand(mxComponent, u".uno:Undo"_ustr, {});
892 Scheduler::ProcessEventsToIdle();
894 // Query the undo state, now that a "delete" is on the redo stack and an "insert" belongs to the
895 // view on the undo stack, so the types are different.
896 SwUndoId nUndoId(SwUndoId::EMPTY);
897 // Without the accompanying fix in place, this test would have failed with:
898 // runtime error: downcast which does not point to an object of type 'const SwUndoInsert'
899 // note: object is of type 'SwUndoDelete'
900 // in an UBSan build.
901 pWrtShell1->GetLastUndoInfo(nullptr, &nUndoId, &pWrtShell1->GetView());
904 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReorderingMulti)
906 // Create two views and a document of 2 paragraphs.
907 SwXTextDocument* pXTextDocument = createDoc();
908 SwWrtShell* pWrtShell1 = getSwDocShell()->GetWrtShell();
909 int nView1 = SfxLokHelper::getView();
910 int nView2 = SfxLokHelper::createView();
911 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
912 SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
913 pWrtShell2->SplitNode();
914 SfxLokHelper::setView(nView1);
915 pWrtShell1->SttEndDoc(/*bStt=*/true);
916 SwTextNode* pTextNode1 = pWrtShell1->GetCursor()->GetPointNode().GetTextNode();
917 // View 1 types into the first paragraph.
918 emulateTyping(u"a");
919 SfxLokHelper::setView(nView2);
920 pWrtShell2->SttEndDoc(/*bStt=*/false);
921 SwTextNode* pTextNode2 = pWrtShell2->GetCursor()->GetPointNode().GetTextNode();
922 // View 2 types into the second paragraph, twice.
923 emulateTyping(u"x");
924 // Go to the start of the paragraph, to avoid grouping.
925 pWrtShell2->SttPara();
926 emulateTyping(u"y");
927 CPPUNIT_ASSERT_EQUAL(u"a"_ustr, pTextNode1->GetText());
928 CPPUNIT_ASSERT_EQUAL(u"yx"_ustr, pTextNode2->GetText());
930 // When view 1 presses undo:
931 SfxLokHelper::setView(nView1);
932 dispatchCommand(mxComponent, u".uno:Undo"_ustr, {});
934 // Then make sure view 1's undo action is invoked, out of order:
935 // Without the accompanying fix in place, this test would have failed with:
936 // - Expression: pTextNode1->GetText().isEmpty()
937 // i.e. out of order undo was not executed, the first paragraph was still "a".
938 CPPUNIT_ASSERT(pTextNode1->GetText().isEmpty());
939 // The top 2 undo actions are not invoked, as they belong to view 2.
940 CPPUNIT_ASSERT_EQUAL(u"yx"_ustr, pTextNode2->GetText());
941 SfxLokHelper::setView(nView1);
942 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
943 SfxLokHelper::setView(nView2);
944 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
947 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoShapeLimiting)
949 // Load a document and create a view.
950 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
951 SwWrtShell* pWrtShell1 = getSwDocShell()->GetWrtShell();
952 int nView1 = SfxLokHelper::getView();
953 int nView2 = SfxLokHelper::createView();
954 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
955 SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
957 // Start shape text in the second view.
958 SdrPage* pPage = pWrtShell2->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
959 SdrObject* pObject = pPage->GetObj(0);
960 SdrView* pView = pWrtShell2->GetDrawView();
961 pWrtShell2->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell2->GetWin());
962 emulateTyping(u"x");
963 pWrtShell2->EndTextEdit();
965 // Assert that the first view can't and the second view can undo the insertion.
966 SwDoc* pDoc = getSwDocShell()->GetDoc();
967 sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
968 rUndoManager.SetView(&pWrtShell1->GetView());
969 // This was 1: first view could undo the change of the second view.
970 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), rUndoManager.GetUndoActionCount());
971 rUndoManager.SetView(&pWrtShell2->GetView());
972 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
974 rUndoManager.SetView(nullptr);
976 SfxLokHelper::setView(nView1);
977 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
978 SfxLokHelper::setView(nView2);
979 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
982 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoDispatch)
984 // Load a document and create two views.
985 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
986 int nView1 = SfxLokHelper::getView();
987 SfxLokHelper::createView();
988 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
989 int nView2 = SfxLokHelper::getView();
991 // Insert a character in the first view.
992 SfxLokHelper::setView(nView1);
993 emulateTyping(u"c");
995 // Click before the first word in the second view.
996 SfxLokHelper::setView(nView2);
997 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
998 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
999 Point aStart = pShellCursor->GetSttPos();
1000 aStart.setX(aStart.getX() - 1000);
1001 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
1002 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
1003 Scheduler::ProcessEventsToIdle();
1004 uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(comphelper::getProcessComponentContext());
1005 uno::Reference<frame::XFrame> xFrame2 = xDesktop->getActiveFrame();
1007 // Now switch back to the first view, and make sure that the active frame is updated.
1008 SfxLokHelper::setView(nView1);
1009 uno::Reference<frame::XFrame> xFrame1 = xDesktop->getActiveFrame();
1010 // This failed: setView() did not update the active frame.
1011 CPPUNIT_ASSERT(xFrame1 != xFrame2);
1013 SfxLokHelper::setView(nView1);
1014 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1015 SfxLokHelper::setView(nView2);
1016 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1019 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoRepairDispatch)
1021 // Load a document and create two views.
1022 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1023 int nView1 = SfxLokHelper::getView();
1024 SfxLokHelper::createView();
1025 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1026 int nView2 = SfxLokHelper::getView();
1028 // Insert a character in the first view.
1029 SfxLokHelper::setView(nView1);
1030 emulateTyping(u"c");
1032 // Assert that by default the second view can't undo the action.
1033 SfxLokHelper::setView(nView2);
1034 SwDoc* pDoc = getSwDocShell()->GetDoc();
1035 sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
1036 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
1037 comphelper::dispatchCommand(u".uno:Undo"_ustr, {});
1038 Scheduler::ProcessEventsToIdle();
1039 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
1041 // But the same is allowed in repair mode.
1042 SfxLokHelper::setView(nView2);
1043 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
1044 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
1046 {"Repair", uno::Any(true)}
1047 }));
1048 comphelper::dispatchCommand(u".uno:Undo"_ustr, aPropertyValues);
1049 Scheduler::ProcessEventsToIdle();
1050 // This was 1: repair mode couldn't undo the action, either.
1051 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), rUndoManager.GetUndoActionCount());
1053 SfxLokHelper::setView(nView1);
1054 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1055 SfxLokHelper::setView(nView2);
1056 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1059 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeTextUndoShells)
1061 // Load a document and create a view.
1062 createDoc("shape.fodt");
1063 sal_Int32 nView1 = SfxLokHelper::getView();
1065 // Begin text edit.
1066 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
1067 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1068 SdrObject* pObject = pPage->GetObj(0);
1069 SdrView* pView = pWrtShell->GetDrawView();
1070 pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
1071 emulateTyping(u"x");
1072 pWrtShell->EndTextEdit();
1074 // Make sure that the undo item remembers who created it.
1075 SwDoc* pDoc = getSwDocShell()->GetDoc();
1076 sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
1077 CPPUNIT_ASSERT_EQUAL(size_t(1), rUndoManager.GetUndoActionCount());
1078 CPPUNIT_ASSERT_EQUAL(u"Edit text of Shape 'Shape1'"_ustr, rUndoManager.GetUndoActionComment(0));
1080 // This was -1: the view shell id for the undo action wasn't known.
1081 CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), rUndoManager.GetUndoAction()->GetViewShellId());
1084 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeTextUndoGroupShells)
1086 // Load a document and create a view.
1087 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
1088 SwTestViewCallback aView1;
1089 sal_Int32 nView1 = SfxLokHelper::getView();
1091 // Begin text edit.
1092 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
1093 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1094 SdrObject* pObject = pPage->GetObj(0);
1095 SdrView* pView = pWrtShell->GetDrawView();
1096 pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
1097 emulateTyping(u"x");
1098 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::BACKSPACE);
1099 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::BACKSPACE);
1100 Scheduler::ProcessEventsToIdle();
1102 // Make sure that the undo item remembers who created it.
1103 SwDoc* pDoc = getSwDocShell()->GetDoc();
1104 sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
1105 CPPUNIT_ASSERT_EQUAL(size_t(0), rUndoManager.GetUndoActionCount());
1107 pWrtShell->EndTextEdit();
1108 pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
1110 CPPUNIT_ASSERT_EQUAL(size_t(1), rUndoManager.GetUndoActionCount());
1111 CPPUNIT_ASSERT_EQUAL(u"Edit text of Shape 'Shape1'"_ustr, rUndoManager.GetUndoActionComment(0));
1113 // This was -1: the view shell id for the (top) undo list action wasn't known.
1114 CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), rUndoManager.GetUndoAction()->GetViewShellId());
1116 // Create an editeng text selection in the first view.
1117 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
1118 emulateTyping(u"x");
1119 // 0th para, 0th char -> 0th para, 1st char.
1120 ESelection aWordSelection(0, 0, 0, 1);
1121 rEditView.SetSelection(aWordSelection);
1123 // Create a second view, and make sure that the new view sees the same
1124 // cursor position as the old one.
1125 SfxLokHelper::createView();
1126 pXTextDocument->initializeForTiledRendering({});
1127 SwTestViewCallback aView2;
1128 // Difference was 935 twips, the new view didn't see the editeng cursor of
1129 // the old one. The new difference should be <1px, but here we deal with twips.
1130 CPPUNIT_ASSERT(std::abs(aView1.m_aOwnCursor.Top() - aView2.m_aViewCursor.Top()) < 10);
1131 // This was false, editeng text selection of the first view wasn't noticed
1132 // by the second view.
1133 CPPUNIT_ASSERT(aView2.m_bViewSelectionSet);
1134 // This was false, the new view wasn't aware of the shape text lock created
1135 // by the old view.
1136 CPPUNIT_ASSERT(aView2.m_bViewLock);
1139 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChanges)
1141 // Load a document.
1142 createDoc("dummy.fodt");
1144 // Turn on track changes, type "zzz" at the end, and move to the start.
1145 uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
1146 xPropertySet->setPropertyValue(u"RecordChanges"_ustr, uno::Any(true));
1147 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
1148 SwTestViewCallback aView(pWrtShell->GetSfxViewShell());
1149 pWrtShell->EndOfSection();
1150 pWrtShell->Insert(u"zzz"_ustr);
1151 pWrtShell->StartOfSection();
1153 // Get the redline just created
1154 const SwRedlineTable& rTable = pWrtShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1155 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), rTable.size());
1156 SwRangeRedline* pRedline = rTable[0];
1158 // Reject the change by id, while the cursor does not cover the tracked change.
1159 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
1161 {"RejectTrackedChange", uno::Any(o3tl::narrowing<sal_uInt16>(pRedline->GetId()))}
1162 }));
1163 comphelper::dispatchCommand(u".uno:RejectTrackedChange"_ustr, aPropertyValues);
1164 Scheduler::ProcessEventsToIdle();
1166 // Assert that the reject was performed.
1167 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
1168 // This was 'Aaa bbb.zzz', the change wasn't rejected.
1169 CPPUNIT_ASSERT_EQUAL(u"Aaa bbb."_ustr, pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
1172 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChangesCallback)
1174 // Load a document.
1175 createDoc("dummy.fodt");
1176 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
1177 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
1179 // Turn on track changes and type "x".
1180 uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
1181 xPropertySet->setPropertyValue(u"RecordChanges"_ustr, uno::Any(true));
1182 m_nRedlineTableSizeChanged = 0;
1183 pWrtShell->Insert(u"x"_ustr);
1185 // Assert that we get exactly one notification about the redline insert.
1186 // This was 0, as LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED wasn't sent.
1187 CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableSizeChanged);
1189 CPPUNIT_ASSERT_EQUAL(-1, m_nTrackedChangeIndex);
1190 pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
1191 SfxItemSet aSet(pWrtShell->GetDoc()->GetAttrPool(), svl::Items<FN_REDLINE_ACCEPT_DIRECT, FN_REDLINE_ACCEPT_DIRECT>);
1192 SfxVoidItem aItem(FN_REDLINE_ACCEPT_DIRECT);
1193 aSet.Put(aItem);
1194 pWrtShell->GetView().GetState(aSet);
1195 // This failed, LOK_CALLBACK_STATE_CHANGED wasn't sent.
1196 CPPUNIT_ASSERT_EQUAL(0, m_nTrackedChangeIndex);
1199 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlineUpdateCallback)
1201 // Load a document.
1202 createDoc("dummy.fodt");
1203 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
1204 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
1206 // Turn on track changes, type "xx" and delete the second one.
1207 uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
1208 xPropertySet->setPropertyValue(u"RecordChanges"_ustr, uno::Any(true));
1209 pWrtShell->Insert(u"xx"_ustr);
1210 m_nRedlineTableEntryModified = 0;
1211 pWrtShell->DelLeft();
1213 // Assert that we get exactly one notification about the redline update.
1214 // This was 0, as LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED wasn't sent.
1215 CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableEntryModified);
1217 // Turn off the change tracking mode, make some modification to left of the
1218 // redline so that its position changes
1219 xPropertySet->setPropertyValue(u"RecordChanges"_ustr, uno::Any(false));
1220 pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
1221 pWrtShell->Insert(u"This text is left of the redline"_ustr);
1223 // Position of the redline has changed => Modify callback
1224 CPPUNIT_ASSERT_EQUAL(2, m_nRedlineTableEntryModified);
1226 pWrtShell->DelLeft();
1227 // Deletion also emits Modify callback
1228 CPPUNIT_ASSERT_EQUAL(3, m_nRedlineTableEntryModified);
1230 // Make changes to the right of the redline => no position change in redline
1231 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 100/*Go enough right */, /*bBasicCall=*/false);
1232 pWrtShell->Insert(u"This text is right of the redline"_ustr);
1234 // No Modify callbacks
1235 CPPUNIT_ASSERT_EQUAL(3, m_nRedlineTableEntryModified);
1238 static void addDarkLightThemes(const Color& rDarkColor, const Color& rLightColor)
1240 // Add a minimal dark scheme
1242 svtools::EditableColorConfig aColorConfig;
1243 svtools::ColorConfigValue aValue;
1244 aValue.bIsVisible = true;
1245 // aValue.nColor is just used for caching, so we need to set nDarkColor or nLightColor to specify the light/dark
1246 // color values for an element. which one to use is decided based on the application's appearance setting.
1247 // see commit message of Change: I1a7f70dfe44b81f863814f87e8d46e146c0e3d5a
1248 aValue.nLightColor = rDarkColor;
1249 aValue.nDarkColor = rDarkColor;
1250 aColorConfig.SetColorValue(svtools::DOCCOLOR, aValue);
1251 aColorConfig.AddScheme(u"Dark"_ustr);
1253 // Add a minimal light scheme
1255 svtools::EditableColorConfig aColorConfig;
1256 svtools::ColorConfigValue aValue;
1257 aValue.bIsVisible = true;
1258 aValue.nLightColor = rLightColor;
1259 aValue.nDarkColor = rLightColor;
1260 aColorConfig.SetColorValue(svtools::DOCCOLOR, aValue);
1261 aColorConfig.AddScheme(u"Light"_ustr);
1265 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetViewRenderState)
1267 addDarkLightThemes(COL_BLACK, COL_WHITE);
1268 SwXTextDocument* pXTextDocument = createDoc();
1269 int nFirstViewId = SfxLokHelper::getView();
1270 SwTestViewCallback aView1;
1272 SwViewOption aViewOptions;
1273 aViewOptions.SetViewMetaChars(true);
1274 aViewOptions.SetOnlineSpell(true);
1275 getSwDocShell()->GetWrtShell()->ApplyViewOptions(aViewOptions);
1277 CPPUNIT_ASSERT_EQUAL("PS;Default"_ostr, pXTextDocument->getViewRenderState());
1279 // Create a second view
1280 SfxLokHelper::createView();
1281 int nSecondViewId = SfxLokHelper::getView();
1282 SwTestViewCallback aView2;
1284 // Give the second view different options
1285 SwViewOption aViewOptions;
1286 aViewOptions.SetViewMetaChars(false);
1287 aViewOptions.SetOnlineSpell(true);
1288 getSwDocShell()->GetWrtShell()->ApplyViewOptions(aViewOptions);
1290 CPPUNIT_ASSERT_EQUAL("S;Default"_ostr, pXTextDocument->getViewRenderState());
1292 // Switch back to the first view, and check that the options string is the same
1293 SfxLokHelper::setView(nFirstViewId);
1294 CPPUNIT_ASSERT_EQUAL("PS;Default"_ostr, pXTextDocument->getViewRenderState());
1296 // Switch back to the second view, and change to dark mode
1297 SfxLokHelper::setView(nSecondViewId);
1299 SwView* pView = getSwDocShell()->GetView();
1300 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1301 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1303 { "NewTheme", uno::Any(u"Dark"_ustr) },
1306 comphelper::dispatchCommand(u".uno:ChangeTheme"_ustr, xFrame, aPropertyValues);
1308 CPPUNIT_ASSERT_EQUAL("S;Dark"_ostr, pXTextDocument->getViewRenderState());
1309 // Switch back to the first view, and check that the options string is the same
1310 SfxLokHelper::setView(nFirstViewId);
1311 CPPUNIT_ASSERT_EQUAL("PS;Default"_ostr, pXTextDocument->getViewRenderState());
1314 // Helper function to get a tile to a bitmap
1315 static Bitmap getTile(SwXTextDocument* pXTextDocument)
1317 size_t nCanvasSize = 1024;
1318 size_t nTileSize = 256;
1319 std::vector<unsigned char> aPixmap(nCanvasSize * nCanvasSize * 4, 0);
1320 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
1321 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
1322 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasSize, nCanvasSize),
1323 Fraction(1.0), Point(), aPixmap.data());
1324 pXTextDocument->paintTile(*pDevice, nCanvasSize, nCanvasSize, 0, 0, 15360, 7680);
1325 pDevice->EnableMapMode(false);
1326 return pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
1329 // Helper function to get a tile to a bitmap and check the pixel color
1330 static Color getTilePixelColor(SwXTextDocument* pXTextDocument, int nPixelX, int nPixelY)
1332 Bitmap aBitmap = getTile(pXTextDocument);
1333 BitmapScopedReadAccess pAccess(aBitmap);
1334 Color aActualColor(pAccess->GetPixel(nPixelX, nPixelY));
1335 return aActualColor;
1338 // Test that changing the theme in one view doesn't change it in the other view
1339 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testThemeViewSeparation)
1341 Color aDarkColor(0x1c, 0x1c, 0x1c);
1342 addDarkLightThemes(aDarkColor, COL_WHITE);
1343 SwXTextDocument* pXTextDocument = createDoc();
1344 int nFirstViewId = SfxLokHelper::getView();
1345 SwTestViewCallback aView1;
1346 // Set first view to light scheme
1348 SwView* pView = getSwDocShell()->GetView();
1349 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1350 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1352 { "NewTheme", uno::Any(u"Light"_ustr) },
1355 comphelper::dispatchCommand(u".uno:ChangeTheme"_ustr, xFrame, aPropertyValues);
1357 // First view is in light scheme
1358 CPPUNIT_ASSERT_EQUAL(COL_WHITE, getTilePixelColor(pXTextDocument, 255, 255));
1359 // Create second view
1360 SfxLokHelper::createView();
1361 int nSecondViewId = SfxLokHelper::getView();
1362 SwTestViewCallback aView2;
1363 // Set second view to dark scheme
1365 SwView* pView = getSwDocShell()->GetView();
1366 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1367 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1369 { "NewTheme", uno::Any(u"Dark"_ustr) },
1372 comphelper::dispatchCommand(u".uno:ChangeTheme"_ustr, xFrame, aPropertyValues);
1374 CPPUNIT_ASSERT_EQUAL(aDarkColor, getTilePixelColor(pXTextDocument, 255, 255));
1375 // First view still in light scheme
1376 SfxLokHelper::setView(nFirstViewId);
1377 CPPUNIT_ASSERT_EQUAL(COL_WHITE, getTilePixelColor(pXTextDocument, 255, 255));
1378 // Second view still in dark scheme
1379 SfxLokHelper::setView(nSecondViewId);
1380 CPPUNIT_ASSERT_EQUAL(aDarkColor, getTilePixelColor(pXTextDocument, 255, 255));
1381 // Switch second view back to light scheme
1383 SwView* pView = getSwDocShell()->GetView();
1384 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1385 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1387 { "NewTheme", uno::Any(u"Light"_ustr) },
1390 comphelper::dispatchCommand(u".uno:ChangeTheme"_ustr, xFrame, aPropertyValues);
1392 // Now in light scheme
1393 CPPUNIT_ASSERT_EQUAL(COL_WHITE, getTilePixelColor(pXTextDocument, 255, 255));
1396 // Test that changing the theme in one view doesn't change it in the other view
1397 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testInvertBackgroundViewSeparation)
1399 Color aDarkColor(0x1c, 0x1c, 0x1c);
1400 addDarkLightThemes(aDarkColor, COL_WHITE);
1401 SwXTextDocument* pXTextDocument = createDoc();
1402 int nFirstViewId = SfxLokHelper::getView();
1403 SwTestViewCallback aView1;
1404 // Set view to dark scheme
1406 SwView* pView = getSwDocShell()->GetView();
1407 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1408 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1410 { "NewTheme", uno::Any(OUString("Dark")) },
1413 comphelper::dispatchCommand(".uno:ChangeTheme", xFrame, aPropertyValues);
1415 // First view is in dark scheme
1416 CPPUNIT_ASSERT_EQUAL(aDarkColor, getTilePixelColor(pXTextDocument, 255, 255));
1417 // Create second view
1418 SfxLokHelper::createView();
1419 int nSecondViewId = SfxLokHelper::getView();
1420 SwTestViewCallback aView2;
1421 // Set second view to dark scheme
1423 SwView* pView = getSwDocShell()->GetView();
1424 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1425 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1427 { "NewTheme", uno::Any(OUString("Dark")) },
1430 comphelper::dispatchCommand(".uno:ChangeTheme", xFrame, aPropertyValues);
1432 CPPUNIT_ASSERT_EQUAL(aDarkColor, getTilePixelColor(pXTextDocument, 255, 255));
1434 // Set view 1 to invert document background
1435 SfxLokHelper::setView(nFirstViewId);
1437 SwView* pView = getSwDocShell()->GetView();
1438 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1439 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1441 { "NewTheme", uno::Any(OUString("Light")) },
1444 comphelper::dispatchCommand(".uno:InvertBackground", xFrame, aPropertyValues);
1446 // First view has inverted background
1447 CPPUNIT_ASSERT_EQUAL(COL_WHITE, getTilePixelColor(pXTextDocument, 255, 255));
1449 SfxLokHelper::setView(nSecondViewId);
1450 // Second view still has regular background
1451 CPPUNIT_ASSERT_EQUAL(aDarkColor, getTilePixelColor(pXTextDocument, 255, 255));
1453 // Set view 2 to invert document background
1455 SwView* pView = getSwDocShell()->GetView();
1456 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1457 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1459 { "NewTheme", uno::Any(OUString("Light")) },
1462 comphelper::dispatchCommand(".uno:InvertBackground", xFrame, aPropertyValues);
1464 // Second view has inverted background
1465 CPPUNIT_ASSERT_EQUAL(COL_WHITE, getTilePixelColor(pXTextDocument, 255, 255));
1467 SfxLokHelper::setView(nFirstViewId);
1468 // First view still has inverted background
1469 CPPUNIT_ASSERT_EQUAL(COL_WHITE, getTilePixelColor(pXTextDocument, 255, 255));
1471 SfxLokHelper::setView(nSecondViewId);
1472 // Set view 2 to regular document background
1474 SwView* pView = getSwDocShell()->GetView();
1475 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1476 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1478 { "NewTheme", uno::Any(OUString("Dark")) },
1481 comphelper::dispatchCommand(".uno:InvertBackground", xFrame, aPropertyValues);
1483 // Second view has regular background
1484 CPPUNIT_ASSERT_EQUAL(aDarkColor, getTilePixelColor(pXTextDocument, 255, 255));
1486 SfxLokHelper::setView(nFirstViewId);
1487 // First view still has inverted background
1488 CPPUNIT_ASSERT_EQUAL(COL_WHITE, getTilePixelColor(pXTextDocument, 255, 255));
1491 // Test that changing the theme sends the document background color as LOK_CALLBACK_DOCUMENT_BACKGROUND_COLOR
1492 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testThemeChangeBackgroundCallback)
1494 Color aDarkColor(0x1c, 0x1c, 0x1c);
1495 addDarkLightThemes(aDarkColor, COL_WHITE);
1496 createDoc();
1497 SwTestViewCallback aView;
1499 SwView* pView = getSwDocShell()->GetView();
1500 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1503 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1505 { "NewTheme", uno::Any(OUString("Dark")) },
1508 comphelper::dispatchCommand(".uno:ChangeTheme", xFrame, aPropertyValues);
1510 CPPUNIT_ASSERT_EQUAL("1c1c1c"_ostr, aView.m_aDocColor);
1513 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1515 { "NewTheme", uno::Any(OUString("Light")) },
1518 comphelper::dispatchCommand(".uno:ChangeTheme", xFrame, aPropertyValues);
1520 CPPUNIT_ASSERT_EQUAL("ffffff"_ostr, aView.m_aDocColor);
1523 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSetViewGraphicSelection)
1525 // Load a document.
1526 SwXTextDocument* pXTextDocument = createDoc("frame.odt");
1527 int nView1 = SfxLokHelper::getView();
1528 SwTestViewCallback aView1;
1529 // Create a second view, and switch back to the first view.
1530 SfxLokHelper::createView();
1531 pXTextDocument->initializeForTiledRendering({});
1532 SfxLokHelper::setView(nView1);
1534 // Mark the textframe in the first view.
1535 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
1536 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1537 SdrObject* pObject = pPage->GetObj(0);
1538 SdrView* pView = pWrtShell->GetDrawView();
1539 pView->MarkObj(pObject, pView->GetSdrPageView());
1540 CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
1542 // Now start to switch to the second view (part of setView()).
1543 pWrtShell->ShellLoseFocus();
1544 // This failed, mark handles were hidden in the first view.
1545 CPPUNIT_ASSERT(!pView->areMarkHandlesHidden());
1548 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCreateViewGraphicSelection)
1550 // Load a document.
1551 createDoc("frame.odt");
1552 SwTestViewCallback aView1;
1554 // Mark the textframe in the first view.
1555 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
1556 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1557 SdrObject* pObject = pPage->GetObj(0);
1558 SdrView* pView = pWrtShell->GetDrawView();
1559 aView1.m_bGraphicSelection = true;
1560 pView->MarkObj(pObject, pView->GetSdrPageView());
1561 pWrtShell->HideCursor();
1562 CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
1564 // Create a second view.
1565 SfxLokHelper::createView();
1566 // This was false, creating a second view cleared the selection of the
1567 // first one.
1568 CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
1570 // Make sure that the hidden text cursor isn't visible in the second view, either.
1571 SwTestViewCallback aView2(SfxViewShell::Current(),
1572 [](SwTestViewCallback& rView) { rView.m_bViewCursorVisible = true; });
1573 // This was true, the second view didn't get the visibility of the text
1574 // cursor of the first view.
1575 CPPUNIT_ASSERT(!aView2.m_bViewCursorVisible);
1576 // This was false, the second view didn't get the graphic selection of the
1577 // first view.
1578 CPPUNIT_ASSERT(aView2.m_bGraphicViewSelection);
1581 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCreateViewTextSelection)
1583 // Load a document.
1584 createDoc("dummy.fodt");
1585 SwTestViewCallback aView1;
1587 // Create a text selection:
1588 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
1589 // Move the cursor into the second word.
1590 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 5, /*bBasicCall=*/false);
1591 // Create a selection on the word.
1592 pWrtShell->SelWrd();
1593 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
1594 // Did we indeed manage to select the second word?
1595 CPPUNIT_ASSERT_EQUAL(u"bbb"_ustr, pShellCursor->GetText());
1597 // Create a second view.
1598 SfxLokHelper::createView();
1600 // Make sure that the text selection is visible in the second view.
1601 SwTestViewCallback aView2;
1602 // This failed, the second view didn't get the text selection of the first view.
1603 CPPUNIT_ASSERT(!aView2.m_aViewSelection.isEmpty());
1606 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlineColors)
1608 // Load a document.
1609 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1611 // Turn on track changes, type "zzz" at the end.
1612 uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
1613 xPropertySet->setPropertyValue(u"RecordChanges"_ustr, uno::Any(true));
1614 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
1615 pWrtShell->EndOfSection();
1616 pWrtShell->Insert(u"zzz"_ustr);
1618 // Assert that info about exactly one author is returned.
1619 tools::JsonWriter aJsonWriter;
1620 pXTextDocument->getTrackedChangeAuthors(aJsonWriter);
1621 std::stringstream aStream((std::string(aJsonWriter.finishAndGetAsOString())));
1622 boost::property_tree::ptree aTree;
1623 boost::property_tree::read_json(aStream, aTree);
1624 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree.get_child("authors").size());
1627 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCommentEndTextEdit)
1629 // Create a document, type a character and remember the cursor position.
1630 SwXTextDocument* pXTextDocument = createDoc();
1631 SwTestViewCallback aView1;
1632 emulateTyping(u"x");
1633 tools::Rectangle aBodyCursor = aView1.m_aOwnCursor;
1635 // Create a comment and type a character there as well.
1636 const int nCtrlAltC = KEY_MOD1 + KEY_MOD2 + 512 + 'c' - 'a';
1637 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', nCtrlAltC);
1638 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', nCtrlAltC);
1639 emulateTyping(u"x");
1640 // End comment text edit by clicking in the body text area, and assert that
1641 // no unexpected cursor callbacks are emitted at origin (top left corner of
1642 // the document).
1643 aView1.m_bOwnCursorAtOrigin = false;
1644 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aBodyCursor.Left(), aBodyCursor.Top(), 1, MOUSE_LEFT, 0);
1645 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aBodyCursor.Left(), aBodyCursor.Top(), 1, MOUSE_LEFT, 0);
1646 Scheduler::ProcessEventsToIdle();
1647 // This failed, the cursor was at 0, 0 at some point during end text edit
1648 // of the comment.
1649 CPPUNIT_ASSERT(!aView1.m_bOwnCursorAtOrigin);
1651 // Hit enter and expect invalidation.
1652 Scheduler::ProcessEventsToIdle();
1653 aView1.m_bTilesInvalidated = false;
1654 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
1655 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
1656 Scheduler::ProcessEventsToIdle();
1657 CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
1660 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCommentInsert)
1662 // Load a document with an as-char image in it.
1663 comphelper::LibreOfficeKit::setTiledAnnotations(false);
1664 createDoc("image-comment.odt");
1665 SwView* pView = getSwDocShell()->GetView();
1667 selectShape(1);
1669 // Add a comment.
1670 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
1671 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
1673 {"Text", uno::Any(u"some text"_ustr)},
1674 {"Author", uno::Any(u"me"_ustr)},
1676 SwTestViewCallback aView;
1677 comphelper::dispatchCommand(u".uno:InsertAnnotation"_ustr, xFrame, aPropertyValues);
1678 Scheduler::ProcessEventsToIdle();
1679 OString aAnchorPos(aView.m_aComment.get_child("anchorPos").get_value<std::string>());
1680 // Without the accompanying fix in place, this test would have failed with
1681 // - Expected: 1418, 1418, 0, 0
1682 // - Actual : 1418, 1418, 1024, 1024
1683 // i.e. the anchor position was a non-empty rectangle.
1684 CPPUNIT_ASSERT_EQUAL("1418, 1418, 0, 0"_ostr, aAnchorPos);
1685 comphelper::LibreOfficeKit::setTiledAnnotations(true);
1688 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCursorPosition)
1690 // Load a document and register a callback, should get an own cursor.
1691 SwXTextDocument* pXTextDocument = createDoc();
1692 SwTestViewCallback aView1;
1694 // Create a second view, so the first view gets a collaborative cursor.
1695 SfxLokHelper::createView();
1696 pXTextDocument->initializeForTiledRendering({});
1697 SwTestViewCallback aView2;
1699 // Make sure the two are exactly the same.
1700 // This failed, own cursor was at '1418, 1418', collaborative cursor was at
1701 // '1425, 1425', due to pixel alignment.
1702 CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor.toString(), aView1.m_aViewCursor.toString());
1705 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPaintCallbacks)
1707 // Test that paintTile() never results in callbacks, which can cause a
1708 // paint <-> invalidate loop.
1710 // Load a document and register a callback for the first view.
1711 SwXTextDocument* pXTextDocument = createDoc();
1712 SwTestViewCallback aView1;
1714 // Create a second view and paint a tile on that second view.
1715 SfxLokHelper::createView();
1716 int nCanvasWidth = 256;
1717 int nCanvasHeight = 256;
1718 std::vector<unsigned char> aBuffer(nCanvasWidth * nCanvasHeight * 4);
1719 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
1720 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), aBuffer.data());
1721 // Make sure that painting a tile in the second view doesn't invoke
1722 // callbacks on the first view.
1723 aView1.m_bCalled = false;
1724 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0, /*nTilePosY=*/0, /*nTileWidth=*/3840, /*nTileHeight=*/3840);
1725 CPPUNIT_ASSERT(!aView1.m_bCalled);
1728 namespace
1730 class TestResultListener : public cppu::WeakImplHelper<css::frame::XDispatchResultListener>
1732 public:
1733 sal_uInt32 m_nDocRepair;
1735 TestResultListener()
1736 : m_nDocRepair(0)
1740 virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& rEvent) override
1742 if (rEvent.State == frame::DispatchResultState::SUCCESS)
1744 rEvent.Result >>= m_nDocRepair;
1748 virtual void SAL_CALL disposing(const css::lang::EventObject&) override {}
1752 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoRepairResult)
1754 // Load a document and create two views.
1755 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1756 int nView1 = SfxLokHelper::getView();
1757 SfxLokHelper::createView();
1758 rtl::Reference<TestResultListener> pResult2 = new TestResultListener();
1759 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1760 int nView2 = SfxLokHelper::getView();
1762 // Insert a character in the second view.
1763 SfxLokHelper::setView(nView2);
1764 emulateTyping(u"b");
1766 // Insert a character in the first view.
1767 SfxLokHelper::setView(nView1);
1768 emulateTyping(u"a");
1770 // Assert that by default the second view can't undo the action.
1771 SfxLokHelper::setView(nView2);
1772 comphelper::dispatchCommand(u".uno:Undo"_ustr, {}, pResult2);
1773 Scheduler::ProcessEventsToIdle();
1774 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pResult2->m_nDocRepair);
1776 SfxLokHelper::setView(nView1);
1777 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1778 SfxLokHelper::setView(nView2);
1779 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1782 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedoRepairResult)
1784 // Load a document and create two views.
1785 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1786 int nView1 = SfxLokHelper::getView();
1787 SfxLokHelper::createView();
1788 rtl::Reference<TestResultListener> pResult2 = new TestResultListener();
1789 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1790 int nView2 = SfxLokHelper::getView();
1792 // Insert a character in the second view.
1793 SfxLokHelper::setView(nView2);
1794 emulateTyping(u"b");
1796 // Insert a character in the first view.
1797 SfxLokHelper::setView(nView1);
1798 emulateTyping(u"a");
1800 comphelper::dispatchCommand(u".uno:Undo"_ustr, {}, pResult2);
1801 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(0), pResult2->m_nDocRepair);
1803 // Assert that by default the second view can't redo the action.
1804 SfxLokHelper::setView(nView2);
1805 comphelper::dispatchCommand(u".uno:Redo"_ustr, {}, pResult2);
1806 Scheduler::ProcessEventsToIdle();
1807 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pResult2->m_nDocRepair);
1809 SfxLokHelper::setView(nView1);
1810 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1811 SfxLokHelper::setView(nView2);
1812 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1815 namespace {
1817 void checkUndoRepairStates(SwXTextDocument* pXTextDocument, SwView* pView1, SwView* pView2)
1819 SfxItemSet aItemSet1(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
1820 SfxItemSet aItemSet2(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
1821 // first view, undo enabled
1822 pView1->GetState(aItemSet1);
1823 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet1.GetItemState(SID_UNDO));
1824 const SfxUInt32Item *pUnsetItem = dynamic_cast<const SfxUInt32Item*>(aItemSet1.GetItem(SID_UNDO));
1825 CPPUNIT_ASSERT(!pUnsetItem);
1826 // second view, undo conflict
1827 pView2->GetState(aItemSet2);
1828 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet2.GetItemState(SID_UNDO));
1829 const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item*>(aItemSet2.GetItem(SID_UNDO));
1830 CPPUNIT_ASSERT(pUInt32Item);
1831 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pUInt32Item->GetValue());
1836 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDisableUndoRepair)
1838 // Create two views.
1839 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1840 SwTestViewCallback aView1;
1841 SwView* pView1 = dynamic_cast<SwView*>(SfxViewShell::Current());
1842 CPPUNIT_ASSERT(pView1);
1843 int nView1 = SfxLokHelper::getView();
1844 SfxLokHelper::createView();
1845 SwTestViewCallback aView2;
1846 SwView* pView2 = dynamic_cast<SwView*>(SfxViewShell::Current());
1847 CPPUNIT_ASSERT(pView2);
1848 int nView2 = SfxLokHelper::getView();
1851 SfxItemSet aItemSet1(getSwDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
1852 SfxItemSet aItemSet2(getSwDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
1853 pView1->GetState(aItemSet1);
1854 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, aItemSet1.GetItemState(SID_UNDO));
1855 pView2->GetState(aItemSet2);
1856 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, aItemSet2.GetItemState(SID_UNDO));
1859 // Insert a character in the first view.
1860 SfxLokHelper::setView(nView1);
1861 emulateTyping(u"k");
1862 checkUndoRepairStates(pXTextDocument, pView1, pView2);
1864 // Insert a character in the second view.
1865 SfxLokHelper::setView(nView2);
1866 emulateTyping(u"u");
1868 SfxItemSet aItemSet1(getSwDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
1869 SfxItemSet aItemSet2(getSwDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
1870 // second view, undo enabled
1871 pView2->GetState(aItemSet2);
1872 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet2.GetItemState(SID_UNDO));
1873 const SfxUInt32Item *pUnsetItem = dynamic_cast<const SfxUInt32Item*>(aItemSet2.GetItem(SID_UNDO));
1874 CPPUNIT_ASSERT(!pUnsetItem);
1875 // first view, undo conflict
1876 pView1->GetState(aItemSet1);
1877 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet1.GetItemState(SID_UNDO));
1878 const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item*>(aItemSet1.GetItem(SID_UNDO));
1879 CPPUNIT_ASSERT(pUInt32Item);
1880 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pUInt32Item->GetValue());
1883 // Insert a character in the first view.
1884 SfxLokHelper::setView(nView1);
1885 emulateTyping(u"l");
1886 checkUndoRepairStates(pXTextDocument, pView1, pView2);
1889 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAllTrackedChanges)
1891 // Load a document.
1892 createDoc("dummy.fodt");
1894 uno::Reference<beans::XPropertySet> xPropSet(mxComponent, uno::UNO_QUERY);
1895 xPropSet->setPropertyValue(u"RecordChanges"_ustr, uno::Any(true));
1897 // view #1
1898 SwView* pView1 = dynamic_cast<SwView*>(SfxViewShell::Current());
1899 CPPUNIT_ASSERT(pView1);
1900 SwWrtShell* pWrtShell1 = pView1->GetWrtShellPtr();
1902 // view #2
1903 int nView1 = SfxLokHelper::getView();
1904 int nView2 = SfxLokHelper::createView();
1905 SwView* pView2 = dynamic_cast<SwView*>(SfxViewShell::Current());
1906 CPPUNIT_ASSERT(pView2);
1907 CPPUNIT_ASSERT(pView1 != pView2);
1908 SwWrtShell* pWrtShell2 = pView2->GetWrtShellPtr();
1909 // Insert text and reject all
1911 pWrtShell1->StartOfSection();
1912 pWrtShell1->Insert(u"hxx"_ustr);
1914 pWrtShell2->EndOfSection();
1915 pWrtShell2->Insert(u"cxx"_ustr);
1918 // Get the redline
1919 const SwRedlineTable& rTable = pWrtShell2->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1920 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(2), rTable.size());
1922 SfxVoidItem aItem(FN_REDLINE_REJECT_ALL);
1923 pView1->GetViewFrame().GetDispatcher()->ExecuteList(FN_REDLINE_REJECT_ALL,
1924 SfxCallMode::SYNCHRON, { &aItem });
1927 // The reject all was performed.
1928 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(0), rTable.size());
1930 SwShellCursor* pShellCursor = pWrtShell1->getShellCursor(false);
1931 CPPUNIT_ASSERT_EQUAL(u"Aaa bbb."_ustr, pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
1934 // Insert text and accept all
1936 pWrtShell1->StartOfSection();
1937 pWrtShell1->Insert(u"hyy"_ustr);
1939 pWrtShell2->EndOfSection();
1940 pWrtShell2->Insert(u"cyy"_ustr);
1943 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(2), rTable.size());
1945 SfxVoidItem aItem(FN_REDLINE_ACCEPT_ALL);
1946 pView1->GetViewFrame().GetDispatcher()->ExecuteList(FN_REDLINE_ACCEPT_ALL,
1947 SfxCallMode::SYNCHRON, { &aItem });
1950 // The accept all was performed
1951 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(0), rTable.size());
1953 SwShellCursor* pShellCursor = pWrtShell2->getShellCursor(false);
1954 CPPUNIT_ASSERT_EQUAL(u"hyyAaa bbb.cyy"_ustr, pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
1957 SfxLokHelper::setView(nView1);
1958 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1959 SfxLokHelper::setView(nView2);
1960 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1963 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDocumentRepair)
1965 // Create two views.
1966 createDoc("dummy.fodt");
1967 // view #1
1968 SfxViewShell* pView1 = SfxViewShell::Current();
1970 // view #2
1971 int nView1 = SfxLokHelper::getView();
1972 SfxLokHelper::createView();
1973 SfxViewShell* pView2 = SfxViewShell::Current();
1974 int nView2 = SfxLokHelper::getView();
1975 CPPUNIT_ASSERT(pView1 != pView2);
1977 std::unique_ptr<SfxBoolItem> pItem1;
1978 std::unique_ptr<SfxBoolItem> pItem2;
1979 pView1->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem1);
1980 pView2->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem2);
1981 CPPUNIT_ASSERT(pItem1);
1982 CPPUNIT_ASSERT(pItem2);
1983 CPPUNIT_ASSERT_EQUAL(false, pItem1->GetValue());
1984 CPPUNIT_ASSERT_EQUAL(false, pItem2->GetValue());
1987 // Insert a character in the second view.
1988 SfxLokHelper::setView(nView2);
1989 emulateTyping(u"u");
1991 std::unique_ptr<SfxBoolItem> pItem1;
1992 std::unique_ptr<SfxBoolItem> pItem2;
1993 pView1->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem1);
1994 pView2->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem2);
1995 CPPUNIT_ASSERT(pItem1);
1996 CPPUNIT_ASSERT(pItem2);
1997 CPPUNIT_ASSERT_EQUAL(true, pItem1->GetValue());
1998 CPPUNIT_ASSERT_EQUAL(true, pItem2->GetValue());
2001 SfxLokHelper::setView(nView1);
2002 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2003 SfxLokHelper::setView(nView2);
2004 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2007 namespace {
2009 void checkPageHeaderOrFooter(const SfxViewShell* pViewShell, TypedWhichId<SfxStringListItem> nWhich, bool bValue)
2011 uno::Sequence<OUString> aSeq;
2012 SfxPoolItemHolder aResult;
2013 pViewShell->GetDispatcher()->QueryState(nWhich, aResult);
2014 const SfxStringListItem* pListItem(static_cast<const SfxStringListItem*>(aResult.getItem()));
2015 CPPUNIT_ASSERT(pListItem);
2016 pListItem->GetStringList(aSeq);
2017 if (bValue)
2019 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aSeq.getLength());
2020 CPPUNIT_ASSERT_EQUAL(u"Default Page Style"_ustr, aSeq[0]);
2022 else
2023 CPPUNIT_ASSERT(!aSeq.hasElements());
2028 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPageHeader)
2030 createDoc("dummy.fodt");
2031 SfxViewShell* pViewShell = SfxViewShell::Current();
2032 // Check Page Header State
2033 checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEHEADER, false);
2034 // Insert Page Header
2036 SfxStringItem aStyle(FN_INSERT_PAGEHEADER, u"Default Page Style"_ustr);
2037 SfxBoolItem aItem(FN_PARAM_1, true);
2038 pViewShell->GetDispatcher()->ExecuteList(FN_INSERT_PAGEHEADER, SfxCallMode::API | SfxCallMode::SYNCHRON, {&aStyle, &aItem});
2040 // Check Page Header State
2041 checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEHEADER, true);
2043 // Remove Page Header
2045 SfxStringItem aStyle(FN_INSERT_PAGEHEADER, u"Default Page Style"_ustr);
2046 SfxBoolItem aItem(FN_PARAM_1, false);
2047 pViewShell->GetDispatcher()->ExecuteList(FN_INSERT_PAGEHEADER, SfxCallMode::API | SfxCallMode::SYNCHRON, {&aStyle, &aItem});
2049 // Check Page Header State
2050 checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEHEADER, false);
2053 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPageFooter)
2055 createDoc("dummy.fodt");
2056 SfxViewShell* pViewShell = SfxViewShell::Current();
2057 // Check Page Footer State
2058 checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEFOOTER, false);
2059 // Insert Page Footer
2061 SfxStringItem aPageStyle(FN_INSERT_PAGEFOOTER, u"Default Page Style"_ustr);
2062 SfxBoolItem aItem(FN_PARAM_1, true);
2063 pViewShell->GetDispatcher()->ExecuteList(FN_INSERT_PAGEFOOTER, SfxCallMode::API | SfxCallMode::SYNCHRON, {&aPageStyle, &aItem});
2065 // Check Page Footer State
2066 checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEFOOTER, true);
2068 // Remove Page Footer
2070 SfxStringItem aPageStyle(FN_INSERT_PAGEFOOTER, u"Default Page Style"_ustr);
2071 SfxBoolItem aItem(FN_PARAM_1, false);
2072 pViewShell->GetDispatcher()->ExecuteList(FN_INSERT_PAGEFOOTER, SfxCallMode::API | SfxCallMode::SYNCHRON, {&aPageStyle, &aItem});
2074 // Check Footer State
2075 checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEFOOTER, false);
2078 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTdf115088)
2080 // We have three lines in the test document and we try to copy the second and third line
2081 // To the beginning of the document
2082 SwXTextDocument* pXTextDocument = createDoc("tdf115088.odt");
2084 // Select and copy second and third line
2085 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
2086 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
2087 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2088 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2089 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_SHIFT);
2090 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_SHIFT);
2091 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT | KEY_SHIFT);
2092 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT | KEY_SHIFT);
2093 Scheduler::ProcessEventsToIdle();
2094 comphelper::dispatchCommand(u".uno:Copy"_ustr, uno::Sequence<beans::PropertyValue>());
2096 // Move cursor to the beginning of the first line and paste
2097 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
2098 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
2099 Scheduler::ProcessEventsToIdle();
2100 comphelper::dispatchCommand(u".uno:PasteUnformatted"_ustr, uno::Sequence<beans::PropertyValue>());
2101 Scheduler::ProcessEventsToIdle();
2103 // Check the resulting text in the document. (it was 1Text\n1\n1\n1)
2104 CPPUNIT_ASSERT_EQUAL(u"1\n1Text\n1\n1"_ustr, pXTextDocument->getText()->getString());
2106 mxComponent->dispose();
2107 mxComponent.clear();
2110 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlineField)
2112 // Load a document.
2113 createDoc("dummy.fodt");
2114 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
2116 // Turn on track changes and type "x".
2117 uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
2118 xPropertySet->setPropertyValue(u"RecordChanges"_ustr, uno::Any(true));
2120 SwDateTimeField aDate(static_cast<SwDateTimeFieldType*>(pWrtShell->GetFieldType(0, SwFieldIds::DateTime)));
2121 //aDate->SetDateTime(::DateTime(::DateTime::SYSTEM));
2122 pWrtShell->InsertField2(aDate);
2124 // Get the redline just created
2125 const SwRedlineTable& rTable = pWrtShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
2126 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), rTable.size());
2127 SwRangeRedline* pRedline = rTable[0];
2128 CPPUNIT_ASSERT(pRedline->GetDescr().indexOf(aDate.GetFieldName())!= -1);
2131 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testIMESupport)
2133 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2134 VclPtr<vcl::Window> pDocWindow = pXTextDocument->getDocWindow();
2136 SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
2137 assert(pView);
2138 SwWrtShell* pWrtShell = pView->GetWrtShellPtr();
2140 // sequence of chinese IME compositions when 'nihao' is typed in an IME
2141 const std::vector<OString> aUtf8Inputs{ "年"_ostr, "你"_ostr, "你好"_ostr, "你哈"_ostr, "你好"_ostr, "你好"_ostr };
2142 std::vector<OUString> aInputs;
2143 std::transform(aUtf8Inputs.begin(), aUtf8Inputs.end(),
2144 std::back_inserter(aInputs), [](OString aInput) {
2145 return OUString::fromUtf8(aInput);
2147 for (const auto& aInput: aInputs)
2149 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, aInput);
2151 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, u""_ustr);
2153 // the cursor should be at position 2nd
2154 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
2155 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), pShellCursor->GetPoint()->GetContentIndex());
2157 // content contains only the last IME composition, not all
2158 CPPUNIT_ASSERT_EQUAL(OUString(aInputs[aInputs.size() - 1] + "Aaa bbb."), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
2161 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testIMEFormattingAtEndOfParagraph)
2163 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2164 VclPtr<vcl::Window> pDocWindow = pXTextDocument->getDocWindow();
2166 SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
2167 assert(pView);
2168 SwWrtShell* pWrtShell = pView->GetWrtShellPtr();
2170 // delete all characters
2172 for (int i = 0; i < 9; i++)
2174 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
2175 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
2178 Scheduler::ProcessEventsToIdle();
2180 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, u"a"_ustr);
2181 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, u""_ustr);
2183 // status: "a"
2185 comphelper::dispatchCommand(u".uno:Bold"_ustr, uno::Sequence<beans::PropertyValue>());
2186 Scheduler::ProcessEventsToIdle();
2188 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, u"b"_ustr);
2189 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, u""_ustr);
2191 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
2192 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
2193 Scheduler::ProcessEventsToIdle();
2195 // status: "a<bold>b</bold>\n"
2197 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, u"a"_ustr);
2198 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, u""_ustr);
2200 std::unique_ptr<SvxWeightItem> pWeightItem;
2201 pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem);
2202 CPPUNIT_ASSERT(pWeightItem);
2204 CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_BOLD, pWeightItem->GetWeight());
2206 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
2207 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
2208 Scheduler::ProcessEventsToIdle();
2210 // status: "a<bold>b</bold>\n
2211 // <bold>a</bold>\n"
2213 comphelper::dispatchCommand(u".uno:Bold"_ustr, uno::Sequence<beans::PropertyValue>());
2214 Scheduler::ProcessEventsToIdle();
2216 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, u"b"_ustr);
2217 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, u""_ustr);
2219 std::unique_ptr<SvxWeightItem> pWeightItem2;
2220 pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem2);
2221 CPPUNIT_ASSERT(pWeightItem2);
2223 CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_NORMAL, pWeightItem2->GetWeight());
2225 // status: "a<bold>b</bold>\n
2226 // <bold>a</bold>\n"
2227 // b"
2229 comphelper::dispatchCommand(u".uno:Bold"_ustr, uno::Sequence<beans::PropertyValue>());
2230 Scheduler::ProcessEventsToIdle();
2232 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, u"a"_ustr);
2233 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, u""_ustr);
2235 std::unique_ptr<SvxWeightItem> pWeightItem3;
2236 pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem3);
2237 CPPUNIT_ASSERT(pWeightItem3);
2239 CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_BOLD, pWeightItem3->GetWeight());
2241 comphelper::dispatchCommand(u".uno:Bold"_ustr, uno::Sequence<beans::PropertyValue>());
2242 Scheduler::ProcessEventsToIdle();
2244 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, u"b"_ustr);
2245 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, u""_ustr);
2247 std::unique_ptr<SvxWeightItem> pWeightItem4;
2248 pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem4);
2249 CPPUNIT_ASSERT(pWeightItem4);
2251 CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_NORMAL, pWeightItem4->GetWeight());
2253 // status: "a<bold>b</bold>\n
2254 // <bold>a</bold>\n"
2255 // b<bold>a</bold>b"
2257 // the cursor should be at position 3rd
2258 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
2259 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3), pShellCursor->GetPoint()->GetContentIndex());
2261 // check the content
2262 CPPUNIT_ASSERT_EQUAL(u"bab"_ustr, pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
2264 // check the actual weight format of the text
2265 SvxWeightItem aBoldWeightItem(WEIGHT_BOLD, RES_CHRATR_WEIGHT);
2266 SfxItemSet aSet(getSwDocShell()->GetDoc()->GetAttrPool(), svl::Items<RES_CHRATR_WEIGHT, RES_CHRATR_WEIGHT>);
2267 pShellCursor->GetPoint()->GetNode().GetTextNode()->GetParaAttr(aSet, 0, 1);
2268 SfxPoolItem const* pPoolItem = aSet.GetItem(RES_CHRATR_WEIGHT);
2269 CPPUNIT_ASSERT(*pPoolItem != aBoldWeightItem); // b not bold
2270 aSet.ClearItem();
2271 pShellCursor->GetPoint()->GetNode().GetTextNode()->GetParaAttr(aSet, 1, 2);
2272 pPoolItem = aSet.GetItem(RES_CHRATR_WEIGHT);
2273 CPPUNIT_ASSERT(pPoolItem->operator==(aBoldWeightItem)); // a bold
2274 aSet.ClearItem();
2275 pShellCursor->GetPoint()->GetNode().GetTextNode()->GetParaAttr(aSet, 2, 3);
2276 pPoolItem = aSet.GetItem(RES_CHRATR_WEIGHT);
2277 CPPUNIT_ASSERT(*pPoolItem != aBoldWeightItem); // b not bold
2280 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testIMEFormattingAfterHeader)
2282 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2283 VclPtr<vcl::Window> pDocWindow = pXTextDocument->getDocWindow();
2285 SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
2286 assert(pView);
2288 // delete all characters
2290 comphelper::dispatchCommand(u".uno:SelectAll"_ustr, uno::Sequence<beans::PropertyValue>());
2291 Scheduler::ProcessEventsToIdle();
2293 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
2294 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
2296 Scheduler::ProcessEventsToIdle();
2298 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, u"a"_ustr);
2299 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, u""_ustr);
2301 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
2302 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
2303 Scheduler::ProcessEventsToIdle();
2305 // status: "a\n"
2307 comphelper::dispatchCommand(
2308 u".uno:StyleApply?Style:string=Heading 2&FamilyName:string=ParagraphStyles"_ustr,
2309 uno::Sequence<beans::PropertyValue>());
2310 Scheduler::ProcessEventsToIdle();
2312 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, u"b"_ustr);
2313 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, u""_ustr);
2315 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, u"b"_ustr);
2316 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, u""_ustr);
2317 Scheduler::ProcessEventsToIdle();
2319 std::unique_ptr<SvxWeightItem> pWeightItem;
2320 pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem);
2321 CPPUNIT_ASSERT(pWeightItem);
2323 CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_BOLD, pWeightItem->GetWeight());
2325 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
2326 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
2327 Scheduler::ProcessEventsToIdle();
2329 // status: "a\n
2330 // <h2>bb</h2>\n"
2332 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, u"c"_ustr);
2333 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, u""_ustr);
2334 Scheduler::ProcessEventsToIdle();
2336 // status: "a\n
2337 // <h2>bb</h2>\n"
2338 // c"
2340 std::unique_ptr<SvxWeightItem> pWeightItem2;
2341 pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem2);
2342 CPPUNIT_ASSERT(pWeightItem2);
2344 CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_NORMAL, pWeightItem2->GetWeight());
2347 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSplitNodeRedlineCallback)
2349 // Load a document.
2350 SwXTextDocument* pXTextDocument = createDoc("splitnode_redline_callback.fodt");
2351 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
2352 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
2354 // 1. test case
2355 // Move cursor between the two tracked changes
2356 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2357 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2358 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2359 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2360 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2361 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2362 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2363 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2364 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2365 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2366 Scheduler::ProcessEventsToIdle();
2368 // Add a new line
2369 m_nRedlineTableEntryModified = 0;
2370 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
2371 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
2372 Scheduler::ProcessEventsToIdle();
2374 // Assert that we get a notification about redline modification
2375 // The redline after the inserted node gets a different vertical position
2376 CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableEntryModified);
2378 // 2. test case
2379 // Move cursor back to the first line, so adding new line will affect both tracked changes
2380 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
2381 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
2382 Scheduler::ProcessEventsToIdle();
2384 // Add a new line
2385 m_nRedlineTableEntryModified = 0;
2386 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
2387 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
2388 Scheduler::ProcessEventsToIdle();
2389 CPPUNIT_ASSERT_EQUAL(2, m_nRedlineTableEntryModified);
2391 // 3. test case
2392 // Move cursor to the end of the document, so adding a new line won't affect any tracked changes
2393 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_END | KEY_MOD1);
2394 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_END | KEY_MOD1);
2395 Scheduler::ProcessEventsToIdle();
2397 // Add a new line
2398 m_nRedlineTableEntryModified = 0;
2399 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
2400 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
2401 Scheduler::ProcessEventsToIdle();
2402 CPPUNIT_ASSERT_EQUAL(0, m_nRedlineTableEntryModified);
2405 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDeleteNodeRedlineCallback)
2407 // Load a document.
2408 SwXTextDocument* pXTextDocument = createDoc("removenode_redline_callback.fodt");
2409 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
2410 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
2412 // 1. test case
2413 // Move cursor between the two tracked changes
2414 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2415 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2416 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2417 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2418 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2419 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2420 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2421 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2422 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2423 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2424 Scheduler::ProcessEventsToIdle();
2426 // Remove one (empty) line
2427 m_nRedlineTableEntryModified = 0;
2428 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
2429 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
2430 Scheduler::ProcessEventsToIdle();
2432 // Assert that we get a notification about redline modification
2433 // The redline after the removed node gets a different vertical position
2434 CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableEntryModified);
2436 // 2. test case
2437 // Move cursor back to the first line, so removing one line will affect both tracked changes
2438 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
2439 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
2440 Scheduler::ProcessEventsToIdle();
2442 // Remove a new line
2443 m_nRedlineTableEntryModified = 0;
2444 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
2445 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
2446 Scheduler::ProcessEventsToIdle();
2447 CPPUNIT_ASSERT_EQUAL(2, m_nRedlineTableEntryModified);
2449 // 3. test case
2450 // Move cursor to the end of the document, so removing one line won't affect any tracked changes
2451 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_END | KEY_MOD1);
2452 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_END | KEY_MOD1);
2453 Scheduler::ProcessEventsToIdle();
2455 // Remove a line
2456 m_nRedlineTableEntryModified = 0;
2457 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_BACKSPACE);
2458 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_BACKSPACE);
2459 Scheduler::ProcessEventsToIdle();
2460 CPPUNIT_ASSERT_EQUAL(0, m_nRedlineTableEntryModified);
2464 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testVisCursorInvalidation)
2466 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2467 SwTestViewCallback aView1;
2468 int nView1 = SfxLokHelper::getView();
2470 SfxLokHelper::createView();
2471 int nView2 = SfxLokHelper::getView();
2472 SwTestViewCallback aView2;
2473 Scheduler::ProcessEventsToIdle();
2475 // Move visible cursor in the first view
2476 SfxLokHelper::setView(nView1);
2477 Scheduler::ProcessEventsToIdle();
2479 aView1.m_bOwnCursorInvalidated = false;
2480 aView1.m_bViewCursorInvalidated = false;
2481 aView2.m_bOwnCursorInvalidated = false;
2482 aView2.m_bViewCursorInvalidated = false;
2484 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT);
2485 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT);
2486 Scheduler::ProcessEventsToIdle();
2488 CPPUNIT_ASSERT(!aView1.m_bViewCursorInvalidated);
2489 CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
2490 CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
2491 CPPUNIT_ASSERT(!aView2.m_bOwnCursorInvalidated);
2493 // Insert text in the second view which moves the other view's cursor too
2494 SfxLokHelper::setView(nView2);
2496 Scheduler::ProcessEventsToIdle();
2497 aView1.m_bOwnCursorInvalidated = false;
2498 aView1.m_bViewCursorInvalidated = false;
2499 aView2.m_bOwnCursorInvalidated = false;
2500 aView2.m_bViewCursorInvalidated = false;
2502 emulateTyping(u"x");
2504 CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
2505 CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
2506 CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
2507 CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
2508 // Check that views have correct location for the other's cursor.
2509 CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor, aView2.m_aViewCursor);
2510 CPPUNIT_ASSERT_EQUAL(aView2.m_aOwnCursor, aView1.m_aViewCursor);
2511 // Their cursors should be on the same line, first view's more to the right.
2512 CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor.getY(), aView2.m_aOwnCursor.getY());
2513 CPPUNIT_ASSERT_GREATER(aView2.m_aOwnCursor.getX(), aView1.m_aOwnCursor.getX());
2515 // Do the same as before, but set the related compatibility flag first
2516 SfxLokHelper::setView(nView2);
2518 comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
2520 Scheduler::ProcessEventsToIdle();
2521 aView1.m_bOwnCursorInvalidated = false;
2522 aView1.m_bViewCursorInvalidated = false;
2523 aView2.m_bOwnCursorInvalidated = false;
2524 aView2.m_bViewCursorInvalidated = false;
2526 emulateTyping(u"x");
2528 CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
2529 CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
2530 CPPUNIT_ASSERT_EQUAL(nView2, aView1.m_nOwnCursorInvalidatedBy);
2531 CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
2532 CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
2533 CPPUNIT_ASSERT_EQUAL(nView2, aView2.m_nOwnCursorInvalidatedBy);
2534 CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor, aView2.m_aViewCursor);
2535 CPPUNIT_ASSERT_EQUAL(aView2.m_aOwnCursor, aView1.m_aViewCursor);
2536 // Their cursors should be on the same line, first view's more to the right.
2537 CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor.getY(), aView2.m_aOwnCursor.getY());
2538 CPPUNIT_ASSERT_GREATER(aView2.m_aOwnCursor.getX(), aView1.m_aOwnCursor.getX());
2540 comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(false);
2543 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDeselectCustomShape)
2545 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
2546 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
2547 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
2548 Point aStart = pShellCursor->GetSttPos();
2549 aStart.setX(aStart.getX() - 1000);
2550 aStart.setY(aStart.getY() - 1000);
2552 comphelper::dispatchCommand(u".uno:BasicShapes.hexagon"_ustr, uno::Sequence<beans::PropertyValue>());
2553 Scheduler::ProcessEventsToIdle();
2554 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pWrtShell->GetDrawView()->GetMarkedObjectList().GetMarkCount());
2556 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
2557 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
2558 Scheduler::ProcessEventsToIdle();
2559 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), pWrtShell->GetDrawView()->GetMarkedObjectList().GetMarkCount());
2562 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSemiTransparent)
2564 // Load a document where the top left tile contains a semi-transparent rectangle shape.
2565 SwXTextDocument* pXTextDocument = createDoc("semi-transparent.odt");
2567 // Render a larger area, and then get the color of the bottom right corner of our tile.
2568 size_t nCanvasWidth = 1024;
2569 size_t nCanvasHeight = 512;
2570 size_t nTileSize = 256;
2571 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
2572 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2573 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2574 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
2575 Fraction(1.0), Point(), aPixmap.data());
2576 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
2577 /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
2578 pDevice->EnableMapMode(false);
2579 Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
2580 BitmapScopedReadAccess pAccess(aBitmap);
2581 Color aColor(pAccess->GetPixel(255, 255));
2583 // Without the accompanying fix in place, this test would have failed with 'Expected greater or
2584 // equal than: 190; Actual: 159'. This means the semi-transparent gray rectangle was darker than
2585 // expected, as it was painted twice.
2586 CPPUNIT_ASSERT_GREATEREQUAL(190, static_cast<int>(aColor.GetRed()));
2587 CPPUNIT_ASSERT_GREATEREQUAL(190, static_cast<int>(aColor.GetGreen()));
2588 CPPUNIT_ASSERT_GREATEREQUAL(190, static_cast<int>(aColor.GetBlue()));
2591 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testHighlightNumbering)
2593 // Load a document where the top left tile contains a semi-transparent rectangle shape.
2594 SwXTextDocument* pXTextDocument = createDoc("tdf114799_highlight.docx");
2596 // Render a larger area, and then get the color of the bottom right corner of our tile.
2597 size_t nCanvasWidth = 1024;
2598 size_t nCanvasHeight = 512;
2599 size_t nTileSize = 256;
2600 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
2601 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2602 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2603 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
2604 Fraction(1.0), Point(), aPixmap.data());
2605 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
2606 /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
2607 pDevice->EnableMapMode(false);
2608 Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
2609 BitmapScopedReadAccess pAccess(aBitmap);
2611 // Yellow highlighting over numbering
2612 Color aColor(pAccess->GetPixel(103, 148));
2613 CPPUNIT_ASSERT_EQUAL(COL_YELLOW, aColor);
2616 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testHighlightNumbering_shd)
2618 // Load a document where the top left tile contains a semi-transparent rectangle shape.
2619 SwXTextDocument* pXTextDocument = createDoc("tdf114799_shd.docx");
2621 // Render a larger area, and then get the color of the bottom right corner of our tile.
2622 size_t nCanvasWidth = 1024;
2623 size_t nCanvasHeight = 512;
2624 size_t nTileSize = 256;
2625 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
2626 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2627 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2628 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
2629 Fraction(1.0), Point(), aPixmap.data());
2630 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
2631 /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
2632 pDevice->EnableMapMode(false);
2633 Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
2634 BitmapScopedReadAccess pAccess(aBitmap);
2636 // No highlighting over numbering - w:shd does not apply to numbering.
2637 Color aColor(pAccess->GetPixel(103, 148));
2638 CPPUNIT_ASSERT_EQUAL(COL_WHITE, aColor);
2641 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPilcrowRedlining)
2643 // Load a document where the top left tile contains
2644 // paragraph and line break symbols with redlining.
2645 SwXTextDocument* pXTextDocument = createDoc("pilcrow-redlining.fodt");
2647 // show non printing characters, including pilcrow and
2648 // line break symbols with redlining
2649 comphelper::dispatchCommand(u".uno:ControlCodes"_ustr, {});
2651 // Render a larger area, and then get the color of the bottom right corner of our tile.
2652 size_t nCanvasWidth = 2048;
2653 size_t nCanvasHeight = 1024;
2654 size_t nTileSize = 512;
2655 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
2656 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2657 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2658 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
2659 Fraction(1.0), Point(), aPixmap.data());
2660 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
2661 /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
2662 pDevice->EnableMapMode(false);
2663 Bitmap aBitmap = pDevice->GetBitmap(Point(100, 100), Size(nTileSize, nTileSize));
2664 BitmapScopedReadAccess pAccess(aBitmap);
2666 const char* aTexts[] = {
2667 "Insert paragraph break",
2668 "Insert paragraph break (empty line)",
2669 "Delete paragraph break",
2670 "Delete paragraph break (empty line)",
2671 "Insert line break",
2672 "Insert line break (empty line)",
2673 "Delete line break",
2674 "Delete line break (empty line)"
2677 // Check redlining (strike out and underline) over the paragraph and line break symbols
2678 for (int nLine = 0; nLine < 8; ++nLine)
2680 bool bHasRedlineColor = false;
2681 for (int i = 0; i < 36 && !bHasRedlineColor; ++i)
2683 int nY = 96 + nLine * 36 + i;
2684 for (sal_uInt32 j = 0; j < nTileSize - 1; ++j)
2686 Color aColor(pAccess->GetPixel(nY, j));
2687 Color aColor2(pAccess->GetPixel(nY+1, j));
2688 Color aColor3(pAccess->GetPixel(nY, j+1));
2689 Color aColor4(pAccess->GetPixel(nY+1, j+1));
2690 // 4-pixel same color square sign strike out or underline of redlining
2691 // if its color is not white, black or non-printing character color
2692 if ( aColor == aColor2 && aColor == aColor3 && aColor == aColor4 &&
2693 aColor != COL_WHITE && aColor != COL_BLACK &&
2694 aColor != Color(0x268BD2) )
2696 bHasRedlineColor = true;
2697 break;
2702 CPPUNIT_ASSERT_MESSAGE(aTexts[nLine], bHasRedlineColor);
2705 comphelper::dispatchCommand(u".uno:ControlCodes"_ustr, {});
2708 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDoubleUnderlineAndStrikeOut)
2710 // Load a document where the tracked text moving is visible with
2711 // double underline and strike out character formatting
2712 SwXTextDocument* pXTextDocument = createDoc("double-underline_and_strike-out.fodt");
2714 // Render a larger area, and then get the color of the bottom right corner of our tile.
2715 size_t nCanvasWidth = 700;
2716 size_t nCanvasHeight = 350;
2717 size_t nTileSize = 350;
2718 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
2719 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2720 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2721 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
2722 Fraction(1.0), Point(), aPixmap.data());
2723 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
2724 /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
2725 pDevice->EnableMapMode(false);
2726 Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
2727 BitmapScopedReadAccess pAccess(aBitmap);
2728 bool bGreenLine = false;
2729 size_t nGreenLine = 0;
2730 // count green horizontal lines by tracking a column of pixels counting the
2731 // separated continuous green pixel sequences.
2732 for (size_t nLine = 0; nLine < nTileSize; ++nLine)
2734 Color aColor(pAccess->GetPixel(nLine, 100));
2735 if ( aColor == COL_GREEN )
2737 if ( bGreenLine == false )
2739 ++nGreenLine;
2740 bGreenLine = true;
2743 else
2744 bGreenLine = false;
2746 // tdf#152214 this was 0 (missing double underline and double strike out)
2747 CPPUNIT_ASSERT_EQUAL(size_t(4), nGreenLine);
2750 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTdf43244_SpacesOnMargin)
2752 // Load a document where the top left tile contains
2753 // paragraph and line break symbols with redlining.
2754 SwXTextDocument* pXTextDocument = createDoc("tdf43244_SpacesOnMargin.odt");
2756 // show non printing characters, including pilcrow and
2757 // line break symbols with redlining
2758 comphelper::dispatchCommand(u".uno:ControlCodes"_ustr, {});
2760 // Render a larger area, and then get the colors from the right side of the page.
2761 size_t nCanvasWidth = 1024;
2762 size_t nCanvasHeight = 512;
2763 size_t nTileSize = 64;
2764 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
2765 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2766 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2767 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
2768 Fraction(1.0), Point(), aPixmap.data());
2769 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
2770 /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
2771 pDevice->EnableMapMode(false);
2772 Bitmap aBitmap = pDevice->GetBitmap(Point(730, 120), Size(nTileSize, nTileSize));
2773 BitmapScopedReadAccess pAccess(aBitmap);
2775 //Test if we see any spaces on the right margin in a 47x48 rectangle
2776 bool bSpaceFound = false;
2777 for (int i = 1; i < 48 && !bSpaceFound; i++)
2779 for (int j = 0; j < i; j++)
2781 Color aColor2(pAccess->GetPixel(j, i));
2782 Color aColor1(pAccess->GetPixel(i, j + 1));
2784 if (aColor1.GetRed() < 255 || aColor2.GetRed() < 255)
2786 bSpaceFound = true;
2787 break;
2791 CPPUNIT_ASSERT(bSpaceFound);
2793 comphelper::dispatchCommand(u".uno:ControlCodes"_ustr, {});
2796 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testClipText)
2798 // Load a document where the top left tile contains table text with
2799 // too small line height, but with top and bottom paragraph margins,
2800 // avoiding of clipping top and bottom parts of the characters.
2801 SwXTextDocument* pXTextDocument = createDoc("tdf117448.fodt");
2803 // Render a larger area, and then get the top and bottom of the text in that tile
2804 size_t nCanvasWidth = 1024;
2805 size_t nCanvasHeight = 512;
2806 size_t nTileSize = 256;
2807 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
2808 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2809 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2810 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
2811 Fraction(1.0), Point(), aPixmap.data());
2812 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
2813 /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
2814 pDevice->EnableMapMode(false);
2815 Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
2816 BitmapScopedReadAccess pAccess(aBitmap);
2818 // check top margin, it's not white completely (i.e. showing top of letter "T")
2819 bool bClipTop = true;
2820 for (int i = 0; i < 150; i++)
2822 Color aTopTextColor(pAccess->GetPixel(98, 98 + i));
2823 if (aTopTextColor.GetRed() < 255)
2825 bClipTop = false;
2826 break;
2829 CPPUNIT_ASSERT(!bClipTop);
2830 // switch off because of false alarm on some platform, maybe related to font replacements
2831 #if 0
2832 // check bottom margin, it's not white completely (i.e. showing bottom of letter "g")
2833 bool bClipBottom = true;
2834 for (int i = 0; i < 150; i++)
2836 Color aBottomTextColor(pAccess->GetPixel(110, 98 + i));
2837 if (aBottomTextColor.R < 255)
2839 bClipBottom = false;
2840 break;
2843 CPPUNIT_ASSERT(!bClipBottom);
2844 #endif
2847 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAnchorTypes)
2849 createDoc("shape.fodt");
2850 selectShape(1);
2852 SwDoc* pDoc = getSwDocShell()->GetDoc();
2853 SwView* pView = getSwDocShell()->GetView();
2854 SfxItemSet aSet(pDoc->GetAttrPool(), svl::Items<FN_TOOL_ANCHOR_PAGE, FN_TOOL_ANCHOR_PAGE>);
2855 SfxBoolItem aItem(FN_TOOL_ANCHOR_PAGE);
2856 aSet.Put(aItem);
2857 auto pShell = dynamic_cast<SwBaseShell*>(pView->GetCurShell());
2858 pShell->GetState(aSet);
2859 // Without the accompanying fix in place, this test would have failed, setting the anchor type
2860 // to other than as/at-char was possible.
2861 CPPUNIT_ASSERT(!aSet.HasItem(FN_TOOL_ANCHOR_PAGE));
2864 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testLanguageStatus)
2866 createDoc("dummy.fodt");
2867 SwView* pView = getSwDocShell()->GetView();
2868 std::unique_ptr<SfxPoolItem> pItem;
2869 pView->GetViewFrame().GetBindings().QueryState(SID_LANGUAGE_STATUS, pItem);
2870 auto pStringListItem = dynamic_cast<SfxStringListItem*>(pItem.get());
2871 CPPUNIT_ASSERT(pStringListItem);
2873 uno::Sequence< OUString > aList;
2874 pStringListItem->GetStringList(aList);
2875 CPPUNIT_ASSERT_EQUAL(u"English (USA);en-US"_ustr, aList[0]);
2878 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlineNotificationDuringSave)
2880 // Load a document with redlines which are hidden at a layout level.
2881 // It's an empty document, just settings.xml and content.xml are custom.
2882 createDoc("redline-notification-during-save.odt");
2883 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
2884 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
2886 // Save the document.
2887 utl::MediaDescriptor aMediaDescriptor;
2888 aMediaDescriptor[u"FilterName"_ustr] <<= u"writer8"_ustr;
2889 uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
2890 // Without the accompanying fix in place, this test would have never returned due to an infinite
2891 // loop while sending not needed LOK notifications for redline changes during save.
2892 xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
2895 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testHyperlink)
2897 comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
2898 SwXTextDocument* pXTextDocument = createDoc("hyperlink.odt");
2899 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
2900 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
2901 m_callbackWrapper.setLOKViewId(SfxLokHelper::getView(pWrtShell->GetSfxViewShell()));
2902 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
2904 Point aStart = pShellCursor->GetSttPos();
2905 aStart.setX(aStart.getX() + 1800);
2906 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1,
2907 MOUSE_LEFT, 0);
2908 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1,
2909 MOUSE_LEFT, 0);
2910 Scheduler::ProcessEventsToIdle();
2912 CPPUNIT_ASSERT_EQUAL("hyperlink"_ostr, m_sHyperlinkText);
2913 CPPUNIT_ASSERT_EQUAL("http://example.com/"_ostr, m_sHyperlinkLink);
2916 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testFieldmark)
2918 // Without the accompanying fix in place, this crashed on load.
2919 createDoc("fieldmark.docx");
2922 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownFormFieldButton)
2924 SwXTextDocument* pXTextDocument = createDoc("drop_down_form_field.odt");
2925 pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
2927 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
2928 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
2930 // Move the cursor to trigger displaying of the field button.
2931 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
2932 CPPUNIT_ASSERT(m_aFormFieldButton.isEmpty());
2934 // Do a tile rendering to trigger the button message with a valid text area
2935 size_t nCanvasWidth = 1024;
2936 size_t nCanvasHeight = 512;
2937 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
2938 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2939 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2940 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
2941 Fraction(1.0), Point(), aPixmap.data());
2942 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
2943 /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
2945 CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
2947 std::stringstream aStream((std::string(m_aFormFieldButton)));
2948 boost::property_tree::ptree aTree;
2949 boost::property_tree::read_json(aStream, aTree);
2951 OString sAction( aTree.get_child("action").get_value<std::string>() );
2952 CPPUNIT_ASSERT_EQUAL("show"_ostr, sAction);
2954 OString sType( aTree.get_child("type").get_value<std::string>() );
2955 CPPUNIT_ASSERT_EQUAL("drop-down"_ostr, sType);
2957 OString sTextArea( aTree.get_child("textArea").get_value<std::string>() );
2958 CPPUNIT_ASSERT_EQUAL("1538, 1418, 1025, 275"_ostr, sTextArea);
2960 boost::property_tree::ptree aItems = aTree.get_child("params").get_child("items");
2961 CPPUNIT_ASSERT_EQUAL(size_t(6), aItems.size());
2963 OStringBuffer aItemList;
2964 for (auto &item : aItems)
2966 aItemList.append(item.second.get_value<std::string>().c_str()
2967 + OString::Concat(";"));
2969 CPPUNIT_ASSERT_EQUAL("2019/2020;2020/2021;2021/2022;2022/2023;2023/2024;2024/2025;"_ostr, aItemList.toString());
2971 OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
2972 CPPUNIT_ASSERT_EQUAL("1"_ostr, sSelected);
2974 OString sPlaceholder( aTree.get_child("params").get_child("placeholderText").get_value<std::string>() );
2975 CPPUNIT_ASSERT_EQUAL("No Item specified"_ostr, sPlaceholder);
2978 // Move the cursor back so the button becomes hidden.
2979 pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
2981 CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
2983 std::stringstream aStream((std::string(m_aFormFieldButton)));
2984 boost::property_tree::ptree aTree;
2985 boost::property_tree::read_json(aStream, aTree);
2987 OString sAction( aTree.get_child("action").get_value<std::string>() );
2988 CPPUNIT_ASSERT_EQUAL("hide"_ostr, sAction);
2990 OString sType( aTree.get_child("type").get_value<std::string>() );
2991 CPPUNIT_ASSERT_EQUAL("drop-down"_ostr, sType);
2995 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownFormFieldButtonEditing)
2997 SwXTextDocument* pXTextDocument = createDoc("drop_down_form_field2.odt");
2998 pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
3000 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3001 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3003 // Move the cursor to trigger displaying of the field button.
3004 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
3005 CPPUNIT_ASSERT(m_aFormFieldButton.isEmpty());
3007 // Do a tile rendering to trigger the button message with a valid text area
3008 size_t nCanvasWidth = 1024;
3009 size_t nCanvasHeight = 512;
3010 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
3011 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
3012 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3013 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
3014 Fraction(1.0), Point(), aPixmap.data());
3015 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
3016 /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
3018 // The item with the index '1' is selected by default
3019 CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
3021 std::stringstream aStream((std::string(m_aFormFieldButton)));
3022 boost::property_tree::ptree aTree;
3023 boost::property_tree::read_json(aStream, aTree);
3025 OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
3026 CPPUNIT_ASSERT_EQUAL("1"_ostr, sSelected);
3028 m_aFormFieldButton = ""_ostr;
3030 // Trigger a form field event to select a different item.
3031 vcl::ITiledRenderable::StringMap aArguments;
3032 aArguments[u"type"_ustr] = "drop-down";
3033 aArguments[u"cmd"_ustr] = "selected";
3034 aArguments[u"data"_ustr] = "3";
3035 pXTextDocument->executeFromFieldEvent(aArguments);
3037 // Do a tile rendering to trigger the button message.
3038 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
3039 /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
3041 CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
3043 std::stringstream aStream((std::string(m_aFormFieldButton)));
3044 boost::property_tree::ptree aTree;
3045 boost::property_tree::read_json(aStream, aTree);
3047 OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
3048 CPPUNIT_ASSERT_EQUAL("3"_ostr, sSelected);
3052 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownFormFieldButtonNoSelection)
3054 SwXTextDocument* pXTextDocument = createDoc("drop_down_form_field_noselection.odt");
3055 pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
3057 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3058 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3060 // Move the cursor to trigger displaying of the field button.
3061 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
3062 CPPUNIT_ASSERT(m_aFormFieldButton.isEmpty());
3064 // Do a tile rendering to trigger the button message with a valid text area
3065 size_t nCanvasWidth = 1024;
3066 size_t nCanvasHeight = 512;
3067 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
3068 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
3069 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3070 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
3071 Fraction(1.0), Point(), aPixmap.data());
3072 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
3073 /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
3075 // None of the items is selected
3076 CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
3078 std::stringstream aStream((std::string(m_aFormFieldButton)));
3079 boost::property_tree::ptree aTree;
3080 boost::property_tree::read_json(aStream, aTree);
3082 OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
3083 CPPUNIT_ASSERT_EQUAL("-1"_ostr, sSelected);
3087 static void lcl_extractHandleParameters(std::string_view selection, sal_Int32& id, sal_Int32& x, sal_Int32& y)
3089 OString extraInfo( selection.substr(selection.find("{")) );
3090 std::stringstream aStream((std::string(extraInfo)));
3091 boost::property_tree::ptree aTree;
3092 boost::property_tree::read_json(aStream, aTree);
3093 boost::property_tree::ptree
3094 handle0 = aTree
3095 .get_child("handles")
3096 .get_child("kinds")
3097 .get_child("rectangle")
3098 .get_child("1")
3099 .begin()->second;
3100 id = handle0.get_child("id").get_value<int>();
3101 x = handle0.get_child("point").get_child("x").get_value<int>();
3102 y = handle0.get_child("point").get_child("y").get_value<int>();
3105 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testMoveShapeHandle)
3107 createDoc("shape.fodt");
3109 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3110 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3111 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
3112 SdrObject* pObject = pPage->GetObj(0);
3113 pWrtShell->SelectObj(Point(), 0, pObject);
3114 Scheduler::ProcessEventsToIdle();
3116 CPPUNIT_ASSERT(!m_ShapeSelection.isEmpty());
3118 sal_Int32 id, x, y;
3119 lcl_extractHandleParameters(m_ShapeSelection, id, x ,y);
3120 sal_Int32 oldX = x;
3121 sal_Int32 oldY = y;
3122 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
3124 {"HandleNum", uno::Any(id)},
3125 {"NewPosX", uno::Any(x+1)},
3126 {"NewPosY", uno::Any(y+1)}
3127 }));
3128 comphelper::dispatchCommand(u".uno:MoveShapeHandle"_ustr, aPropertyValues);
3129 Scheduler::ProcessEventsToIdle();
3130 CPPUNIT_ASSERT(!m_ShapeSelection.isEmpty());
3131 lcl_extractHandleParameters(m_ShapeSelection, id, x ,y);
3132 CPPUNIT_ASSERT_EQUAL(x-1, oldX);
3133 CPPUNIT_ASSERT_EQUAL(y-1, oldY);
3137 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownFormFieldButtonNoItem)
3139 SwXTextDocument* pXTextDocument = createDoc("drop_down_form_field_noitem.odt");
3140 pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
3142 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3143 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3145 // Move the cursor to trigger displaying of the field button.
3146 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
3147 CPPUNIT_ASSERT(m_aFormFieldButton.isEmpty());
3149 // Do a tile rendering to trigger the button message with a valid text area
3150 size_t nCanvasWidth = 1024;
3151 size_t nCanvasHeight = 512;
3152 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
3153 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
3154 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3155 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
3156 Fraction(1.0), Point(), aPixmap.data());
3157 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
3158 /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
3160 // There is not item specified for the field
3161 CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
3163 std::stringstream aStream((std::string(m_aFormFieldButton)));
3164 boost::property_tree::ptree aTree;
3165 boost::property_tree::read_json(aStream, aTree);
3167 boost::property_tree::ptree aItems = aTree.get_child("params").get_child("items");
3168 CPPUNIT_ASSERT_EQUAL(size_t(0), aItems.size());
3170 OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
3171 CPPUNIT_ASSERT_EQUAL("-1"_ostr, sSelected);
3175 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTablePaintInvalidate)
3177 // Load a document with a table in it.
3178 SwXTextDocument* pXTextDocument = createDoc("table-paint-invalidate.odt");
3179 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3180 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3181 // Enter the table.
3182 pWrtShell->Down(/*bSelect=*/false);
3183 Scheduler::ProcessEventsToIdle();
3184 m_nInvalidations = 0;
3186 // Paint a tile.
3187 size_t nCanvasWidth = 256;
3188 size_t nCanvasHeight = 256;
3189 std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
3190 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
3191 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3192 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
3193 Fraction(1.0), Point(), aPixmap.data());
3194 pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, m_aInvalidation.Left(),
3195 m_aInvalidation.Top(), /*nTileWidth=*/1000,
3196 /*nTileHeight=*/1000);
3197 Scheduler::ProcessEventsToIdle();
3199 // Without the accompanying fix in place, this test would have failed with
3200 // - Expected: 0
3201 // - Actual : 5
3202 // i.e. paint generated an invalidation, which caused a loop.
3203 CPPUNIT_ASSERT_EQUAL(0, m_nInvalidations);
3206 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTableCommentRemoveCallback)
3208 comphelper::LibreOfficeKit::setTiledAnnotations(false);
3210 // Load a document with a comment in a table.
3211 SwXTextDocument* pXTextDocument = createDoc("testTableCommentRemoveCallback.odt");
3212 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3213 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3214 SwTestViewCallback aView;
3216 // delete all characters
3217 comphelper::dispatchCommand(u".uno:SelectAll"_ustr, uno::Sequence<beans::PropertyValue>());
3218 Scheduler::ProcessEventsToIdle();
3219 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
3220 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
3221 Scheduler::ProcessEventsToIdle();
3223 //check for comment remove callback
3224 OString sAction(aView.m_aComment.get_child("action").get_value<std::string>());
3225 CPPUNIT_ASSERT_EQUAL("Remove"_ostr, sAction);
3228 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSpellOnlineRenderParameter)
3230 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
3231 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3232 const SwViewOption* pOpt = pWrtShell->GetViewOptions();
3233 bool bSet = pOpt->IsOnlineSpell();
3235 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
3237 {".uno:SpellOnline", uno::Any(!bSet)},
3238 }));
3239 pXTextDocument->initializeForTiledRendering(aPropertyValues);
3240 CPPUNIT_ASSERT_EQUAL(!bSet, pOpt->IsOnlineSpell());
3243 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testExtTextInputReadOnly)
3245 // Create a document with a protected section + a normal paragraph after it.
3246 SwXTextDocument* pXTextDocument = createDoc();
3247 uno::Reference<text::XTextViewCursorSupplier> xController(
3248 pXTextDocument->getCurrentController(), uno::UNO_QUERY);
3249 uno::Reference<text::XTextViewCursor> xCursor = xController->getViewCursor();
3250 uno::Reference<text::XText> xText = xCursor->getText();
3251 uno::Reference<text::XTextContent> xSection(
3252 pXTextDocument->createInstance(u"com.sun.star.text.TextSection"_ustr), uno::UNO_QUERY);
3253 uno::Reference<beans::XPropertySet> xSectionProps(xSection, uno::UNO_QUERY);
3254 xSectionProps->setPropertyValue(u"IsProtected"_ustr, uno::Any(true));
3255 xText->insertTextContent(xCursor, xSection, /*bAbsorb=*/true);
3257 // First paragraph is the protected section, is it empty?
3258 VclPtr<vcl::Window> pEditWin = pXTextDocument->getDocWindow();
3259 CPPUNIT_ASSERT(pEditWin);
3260 CPPUNIT_ASSERT(getParagraph(1)->getString().isEmpty());
3262 // Try to type into the protected section, is it still empty?
3263 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3264 pWrtShell->SttEndDoc(/*bStt=*/true);
3265 SfxLokHelper::postExtTextEventAsync(pEditWin, LOK_EXT_TEXTINPUT, u"x"_ustr);
3266 SfxLokHelper::postExtTextEventAsync(pEditWin, LOK_EXT_TEXTINPUT_END, u"x"_ustr);
3267 Scheduler::ProcessEventsToIdle();
3268 // Without the accompanying fix in place, this test would have failed, as it was possible to
3269 // type into the protected section.
3270 CPPUNIT_ASSERT(getParagraph(1)->getString().isEmpty());
3272 // Second paragraph is a normal paragraph, is it empty?
3273 pWrtShell->Down(/*bSelect=*/false);
3274 CPPUNIT_ASSERT(getParagraph(2)->getString().isEmpty());
3276 // Try to type into the protected section, does it have the typed content?
3277 SfxLokHelper::postExtTextEventAsync(pEditWin, LOK_EXT_TEXTINPUT, u"x"_ustr);
3278 SfxLokHelper::postExtTextEventAsync(pEditWin, LOK_EXT_TEXTINPUT_END, u"x"_ustr);
3279 Scheduler::ProcessEventsToIdle();
3280 CPPUNIT_ASSERT_EQUAL(u"x"_ustr, getParagraph(2)->getString());
3283 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testBulletDeleteInvalidation)
3285 // Given a document with 3 paragraphs: first 2 is bulleted, the last is not.
3286 createDoc();
3287 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3288 pWrtShell->SplitNode();
3289 pWrtShell->Up(/*bSelect=*/false);
3290 pWrtShell->StartAllAction();
3291 pWrtShell->BulletOn();
3292 pWrtShell->EndAllAction();
3293 pWrtShell->Insert2(u"a"_ustr);
3294 pWrtShell->SplitNode();
3295 pWrtShell->Insert2(u"b"_ustr);
3296 pWrtShell->Down(/*bSelect=*/false);
3297 pWrtShell->GetLayout()->PaintSwFrame(*pWrtShell->GetOut(),
3298 pWrtShell->GetLayout()->getFrameArea());
3299 Scheduler::ProcessEventsToIdle();
3300 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3301 m_aInvalidations = tools::Rectangle();
3303 // When pressing backspace in the last paragraph.
3304 pWrtShell->DelLeft();
3306 // Then the first paragraph should not be invalidated.
3307 SwRootFrame* pRoot = pWrtShell->GetLayout();
3308 SwFrame* pPage = pRoot->GetLower();
3309 SwFrame* pBody = pPage->GetLower();
3310 SwFrame* pFirstText = pBody->GetLower();
3311 tools::Rectangle aFirstTextRect = pFirstText->getFrameArea().SVRect();
3312 CPPUNIT_ASSERT(!aFirstTextRect.Overlaps(m_aInvalidations));
3315 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTdf155349)
3317 createDoc();
3318 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3319 Scheduler::ProcessEventsToIdle();
3320 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3321 pWrtShell->Insert2(u"a"_ustr);
3322 Scheduler::ProcessEventsToIdle();
3323 pWrtShell->Insert2(u"b"_ustr);
3324 m_bFullInvalidateSeen = false;
3325 Scheduler::ProcessEventsToIdle();
3326 // before fix for tdf#155349 the total area got invalidated when changing one line
3327 CPPUNIT_ASSERT(!m_bFullInvalidateSeen);
3330 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testBulletNoNumInvalidation)
3332 // Given a document with 3 paragraphs: all are bulleted.
3333 createDoc();
3334 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3335 pWrtShell->StartAllAction();
3336 pWrtShell->BulletOn();
3337 pWrtShell->EndAllAction();
3338 pWrtShell->Insert2(u"a"_ustr);
3339 pWrtShell->SplitNode();
3340 pWrtShell->Insert2(u"b"_ustr);
3341 pWrtShell->SplitNode();
3342 pWrtShell->GetLayout()->PaintSwFrame(*pWrtShell->GetOut(),
3343 pWrtShell->GetLayout()->getFrameArea());
3344 Scheduler::ProcessEventsToIdle();
3345 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3346 m_aInvalidations = tools::Rectangle();
3348 // When pressing backspace in the last paragraph to turn bullets off.
3349 pWrtShell->StartAllAction();
3350 pWrtShell->NumOrNoNum(/*bDelete=*/false);
3351 pWrtShell->EndAllAction();
3353 // Then the first paragraph should not be invalidated.
3354 SwRootFrame* pRoot = pWrtShell->GetLayout();
3355 SwFrame* pPage = pRoot->GetLower();
3356 SwFrame* pBody = pPage->GetLower();
3357 SwFrame* pFirstText = pBody->GetLower();
3358 tools::Rectangle aFirstTextRect = pFirstText->getFrameArea().SVRect();
3359 CPPUNIT_ASSERT(!aFirstTextRect.Overlaps(m_aInvalidations));
3362 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testBulletMultiDeleteInvalidation)
3364 // Given a document with 5 paragraphs: all are bulleted.
3365 createDoc();
3366 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3367 pWrtShell->StartAllAction();
3368 pWrtShell->BulletOn();
3369 pWrtShell->EndAllAction();
3370 // There is already an initial text node, so type 5 times, but split 4 times.
3371 for (int i = 0; i < 4; ++i)
3373 pWrtShell->Insert2(u"a"_ustr);
3374 pWrtShell->SplitNode();
3376 pWrtShell->Insert2(u"a"_ustr);
3377 // Go to the end of the 4th para.
3378 pWrtShell->Up(/*bSelect=*/false);
3379 pWrtShell->GetLayout()->PaintSwFrame(*pWrtShell->GetOut(),
3380 pWrtShell->GetLayout()->getFrameArea());
3381 Scheduler::ProcessEventsToIdle();
3382 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3383 m_aInvalidations = tools::Rectangle();
3385 // When selecting and deleting several bullets: select till the end of the 2nd para and delete.
3386 pWrtShell->Up(/*bSelect=*/true, /*nCount=*/2);
3387 pWrtShell->DelRight();
3389 // Then the first paragraph should not be invalidated.
3390 SwRootFrame* pRoot = pWrtShell->GetLayout();
3391 SwFrame* pPage = pRoot->GetLower();
3392 SwFrame* pBody = pPage->GetLower();
3393 SwFrame* pFirstText = pBody->GetLower();
3394 tools::Rectangle aFirstTextRect = pFirstText->getFrameArea().SVRect();
3395 CPPUNIT_ASSERT(!aFirstTextRect.Overlaps(m_aInvalidations));
3398 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCondCollCopy)
3400 // Given a document with a custom Text Body cond style:
3401 SwXTextDocument* pXTextDocument = createDoc("cond-coll-copy.odt");
3402 uno::Sequence<beans::PropertyValue> aPropertyValues
3403 = { comphelper::makePropertyValue(u"Style"_ustr, u"Text body"_ustr),
3404 comphelper::makePropertyValue(u"FamilyName"_ustr, u"ParagraphStyles"_ustr) };
3405 dispatchCommand(mxComponent, u".uno:StyleApply"_ustr, aPropertyValues);
3406 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3407 pWrtShell->SelAll();
3409 // When getting the text selection, then make sure it doesn't crash:
3410 uno::Reference<datatransfer::XTransferable2> xTransferable(pXTextDocument->getSelection(),
3411 css::uno::UNO_QUERY);
3412 datatransfer::DataFlavor aFlavor;
3413 aFlavor.MimeType = "text/plain;charset=utf-16";
3414 aFlavor.DataType = cppu::UnoType<OUString>::get();
3415 CPPUNIT_ASSERT(xTransferable->isDataFlavorSupported(aFlavor));
3416 // Without the accompanying fix in place, this test would have crashed.
3417 xTransferable->getTransferData(aFlavor);
3420 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlinePortions)
3422 // Given a document with 3 portions: before insert redline (foo), the insert redline (ins) and after insert
3423 // redline (bar):
3424 createDoc();
3425 SwDocShell* pDocShell = getSwDocShell();
3426 SwView* pView = pDocShell->GetView();
3427 pView->SetRedlineAuthor(u"first"_ustr);
3428 pDocShell->SetView(pView);
3429 SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
3430 pWrtShell->Insert(u"foo"_ustr);
3431 pDocShell->SetChangeRecording(true);
3432 pWrtShell->Insert(u"ins"_ustr);
3433 pDocShell->SetChangeRecording(false);
3434 pWrtShell->Insert(u"bar after"_ustr);
3436 // When deleting "fooinsbar":
3437 pView->SetRedlineAuthor(u"second"_ustr);
3438 pDocShell->SetView(pView);
3439 pWrtShell->SttEndDoc(/*bStt*/true);
3440 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, /*nCount=*/9, /*bBasicCall=*/false);
3441 pDocShell->SetChangeRecording(true);
3442 pWrtShell->Delete();
3444 // Then make sure that the portion list is updated, so "bar" can be marked as deleted without
3445 // marking " after" as well:
3446 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
3447 assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[1]", "portion", u"foo");
3448 assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[2]", "portion", u"ins");
3449 // Without the accompanying fix in place, this test would have failed width:
3450 // - Expected: bar
3451 // - Actual : bar after
3452 // i.e. the portion list was outdated, even " after" was marked as deleted.
3453 assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[3]", "portion", u"bar");
3454 assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[4]", "portion", u" after");
3457 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testContentControl)
3459 // Given a document with a content control:
3460 SwXTextDocument* pXTextDocument = createDoc();
3461 uno::Reference<text::XText> xText = pXTextDocument->getText();
3462 uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
3463 xText->insertString(xCursor, u"test"_ustr, /*bAbsorb=*/false);
3464 xCursor->gotoStart(/*bExpand=*/false);
3465 xCursor->gotoEnd(/*bExpand=*/true);
3466 uno::Reference<text::XTextContent> xContentControl(
3467 pXTextDocument->createInstance(u"com.sun.star.text.ContentControl"_ustr), uno::UNO_QUERY);
3468 uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
3469 xContentControlProps->setPropertyValue(u"Alias"_ustr, uno::Any(u"my alias"_ustr));
3470 xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
3471 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3472 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3473 pWrtShell->SttEndDoc(/*bStt=*/true);
3474 m_aContentControl.clear();
3476 // When entering that content control (chars 2-7 are the content control):
3477 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, /*nCount=*/5, /*bBasicCall=*/false);
3479 // Then make sure that the callback is emitted:
3480 // Without the accompanying fix in place, this test would have failed, no callback was emitted.
3481 CPPUNIT_ASSERT(!m_aContentControl.isEmpty());
3483 std::stringstream aStream((std::string(m_aContentControl)));
3484 boost::property_tree::ptree aTree;
3485 boost::property_tree::read_json(aStream, aTree);
3486 OString sAction( aTree.get_child("action").get_value<std::string>() );
3487 CPPUNIT_ASSERT_EQUAL("show"_ostr, sAction);
3488 OString sRectangles( aTree.get_child("rectangles").get_value<std::string>() );
3489 CPPUNIT_ASSERT(!sRectangles.isEmpty());
3490 // Without the accompanying fix in place, this test would have failed width:
3491 // uncaught exception of type std::exception (or derived).
3492 // - No such node (alias)
3493 OString sAlias( aTree.get_child("alias").get_value<std::string>() );
3494 CPPUNIT_ASSERT_EQUAL("my alias"_ostr, sAlias);
3497 // And when leaving that content control:
3498 pWrtShell->SttEndDoc(/*bStt=*/true);
3500 // Then make sure that the callback is emitted again:
3501 std::stringstream aStream((std::string(m_aContentControl)));
3502 boost::property_tree::ptree aTree;
3503 boost::property_tree::read_json(aStream, aTree);
3504 OString sAction( aTree.get_child("action").get_value<std::string>() );
3505 CPPUNIT_ASSERT_EQUAL("hide"_ostr, sAction);
3508 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownContentControl)
3510 // Given a document with a dropdown content control:
3511 SwXTextDocument* pXTextDocument = createDoc();
3512 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3513 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3514 uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
3515 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
3516 uno::Reference<text::XText> xText = xTextDocument->getText();
3517 uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
3518 xText->insertString(xCursor, u"choose an item"_ustr, /*bAbsorb=*/false);
3519 xCursor->gotoStart(/*bExpand=*/false);
3520 xCursor->gotoEnd(/*bExpand=*/true);
3521 uno::Reference<text::XTextContent> xContentControl(
3522 xMSF->createInstance(u"com.sun.star.text.ContentControl"_ustr), uno::UNO_QUERY);
3523 uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
3525 uno::Sequence<beans::PropertyValues> aListItems = {
3527 comphelper::makePropertyValue(u"DisplayText"_ustr, uno::Any(u"red"_ustr)),
3528 comphelper::makePropertyValue(u"Value"_ustr, uno::Any(u"R"_ustr)),
3531 comphelper::makePropertyValue(u"DisplayText"_ustr, uno::Any(u"green"_ustr)),
3532 comphelper::makePropertyValue(u"Value"_ustr, uno::Any(u"G"_ustr)),
3535 comphelper::makePropertyValue(u"DisplayText"_ustr, uno::Any(u"blue"_ustr)),
3536 comphelper::makePropertyValue(u"Value"_ustr, uno::Any(u"B"_ustr)),
3539 xContentControlProps->setPropertyValue(u"ListItems"_ustr, uno::Any(aListItems));
3541 xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
3542 pWrtShell->SttEndDoc(/*bStt=*/true);
3543 m_aContentControl.clear();
3545 // When entering that content control:
3546 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false);
3548 // Then make sure that the callback is emitted:
3549 CPPUNIT_ASSERT(!m_aContentControl.isEmpty());
3551 std::stringstream aStream((std::string(m_aContentControl)));
3552 boost::property_tree::ptree aTree;
3553 boost::property_tree::read_json(aStream, aTree);
3554 OString sAction( aTree.get_child("action").get_value<std::string>() );
3555 CPPUNIT_ASSERT_EQUAL("show"_ostr, sAction);
3556 OString sRectangles( aTree.get_child("rectangles").get_value<std::string>() );
3557 CPPUNIT_ASSERT(!sRectangles.isEmpty());
3558 boost::optional<boost::property_tree::ptree&> oItems = aTree.get_child_optional("items");
3559 CPPUNIT_ASSERT(oItems);
3560 static const std::vector<std::string> vExpected = { "red", "green", "blue" };
3561 size_t i = 0;
3562 for (const auto& rItem : *oItems)
3564 CPPUNIT_ASSERT_EQUAL(vExpected[i++], rItem.second.get_value<std::string>());
3568 // And when selecting the 2nd item (green):
3569 std::map<OUString, OUString> aArguments;
3570 aArguments.emplace("type", "drop-down");
3571 aArguments.emplace("selected", "1");
3572 pXTextDocument->executeContentControlEvent(aArguments);
3574 // Then make sure that the document is updated accordingly:
3575 SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
3576 // Without the accompanying fix in place, this test would have failed with:
3577 // - Expected: green
3578 // - Actual : choose an item
3579 // i.e. the document text was not updated.
3580 CPPUNIT_ASSERT_EQUAL(u"green"_ustr, pTextNode->GetExpandText(pWrtShell->GetLayout()));
3583 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPictureContentControl)
3585 // Given a document with a picture content control:
3586 SwXTextDocument* pXTextDocument = createDoc();
3587 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3588 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3589 uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
3590 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
3591 uno::Reference<text::XText> xText = xTextDocument->getText();
3592 uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
3593 uno::Reference<beans::XPropertySet> xTextGraphic(
3594 xMSF->createInstance(u"com.sun.star.text.TextGraphicObject"_ustr), uno::UNO_QUERY);
3595 xTextGraphic->setPropertyValue(u"AnchorType"_ustr,
3596 uno::Any(text::TextContentAnchorType_AS_CHARACTER));
3597 uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY);
3598 xText->insertTextContent(xCursor, xTextContent, false);
3599 xCursor->gotoStart(/*bExpand=*/false);
3600 xCursor->gotoEnd(/*bExpand=*/true);
3601 uno::Reference<text::XTextContent> xContentControl(
3602 xMSF->createInstance(u"com.sun.star.text.ContentControl"_ustr), uno::UNO_QUERY);
3603 uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
3604 xContentControlProps->setPropertyValue(u"ShowingPlaceHolder"_ustr, uno::Any(true));
3605 xContentControlProps->setPropertyValue(u"Picture"_ustr, uno::Any(true));
3606 xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
3607 pWrtShell->SttEndDoc(/*bStt=*/true);
3608 m_aContentControl.clear();
3610 // When clicking on that content control:
3611 pWrtShell->GotoObj(/*bNext=*/true, GotoObjFlags::Any);
3612 pWrtShell->EnterSelFrameMode();
3613 const SwFrameFormat* pFlyFormat = pWrtShell->GetFlyFrameFormat();
3614 const SwFormatAnchor& rFormatAnchor = pFlyFormat->GetAnchor();
3615 const SwNode* pAnchorNode = rFormatAnchor.GetAnchorNode();
3616 const SwTextNode* pTextNode = pAnchorNode->GetTextNode();
3617 SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
3618 auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
3619 auto& rFormatContentControl
3620 = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
3621 pWrtShell->GotoContentControl(rFormatContentControl);
3623 // Then make sure that the callback is emitted:
3624 // Without the accompanying fix in place, this test would have failed, no callback was emitted.
3625 CPPUNIT_ASSERT(!m_aContentControl.isEmpty());
3626 std::stringstream aStream((std::string(m_aContentControl)));
3627 boost::property_tree::ptree aTree;
3628 boost::property_tree::read_json(aStream, aTree);
3629 OString sAction( aTree.get_child("action").get_value<std::string>() );
3630 CPPUNIT_ASSERT_EQUAL("change-picture"_ostr, sAction);
3632 // And when replacing the image:
3633 std::map<OUString, OUString> aArguments;
3634 aArguments.emplace("type", "picture");
3635 OUString aURL = m_directories.getURLFromSrc(u"sw/qa/extras/uiwriter/data/ole2.png");
3636 aArguments.emplace("changed", aURL);
3637 pXTextDocument->executeContentControlEvent(aArguments);
3639 // Then make sure that the document is updated accordingly:
3640 uno::Reference<drawing::XShape> xShape = getShape(1);
3641 auto xGraphic = getProperty<uno::Reference<beans::XPropertySet>>(xShape, u"Graphic"_ustr);
3642 // Without the accompanying fix in place, this test would have failed, xGraphic was empty after
3643 // executeContentControlEvent().
3644 CPPUNIT_ASSERT(xGraphic.is());
3645 CPPUNIT_ASSERT_EQUAL(u"image/png"_ustr, getProperty<OUString>(xGraphic, u"MimeType"_ustr));
3649 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDateContentControl)
3651 // Given a document with a date content control:
3652 SwXTextDocument* pXTextDocument = createDoc();
3653 SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
3654 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3655 uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
3656 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
3657 uno::Reference<text::XText> xText = xTextDocument->getText();
3658 uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
3659 xText->insertString(xCursor, u"choose a date"_ustr, /*bAbsorb=*/false);
3660 xCursor->gotoStart(/*bExpand=*/false);
3661 xCursor->gotoEnd(/*bExpand=*/true);
3662 uno::Reference<text::XTextContent> xContentControl(
3663 xMSF->createInstance(u"com.sun.star.text.ContentControl"_ustr), uno::UNO_QUERY);
3664 uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
3665 xContentControlProps->setPropertyValue(u"Date"_ustr, uno::Any(true));
3666 xContentControlProps->setPropertyValue(u"DateFormat"_ustr, uno::Any(u"YYYY-MM-DD"_ustr));
3667 xContentControlProps->setPropertyValue(u"DateLanguage"_ustr, uno::Any(u"en-US"_ustr));
3668 xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
3669 pWrtShell->SttEndDoc(/*bStt=*/true);
3670 m_aContentControl.clear();
3672 // When entering that content control:
3673 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false);
3675 // Then make sure that the callback is emitted:
3676 CPPUNIT_ASSERT(!m_aContentControl.isEmpty());
3678 std::stringstream aStream((std::string(m_aContentControl)));
3679 boost::property_tree::ptree aTree;
3680 boost::property_tree::read_json(aStream, aTree);
3681 OString sAction( aTree.get_child("action").get_value<std::string>() );
3682 CPPUNIT_ASSERT_EQUAL("show"_ostr, sAction);
3683 OString sRectangles( aTree.get_child("rectangles").get_value<std::string>() );
3684 CPPUNIT_ASSERT(!sRectangles.isEmpty());
3685 boost::optional<boost::property_tree::ptree&> oDate = aTree.get_child_optional("date");
3686 CPPUNIT_ASSERT(oDate);
3689 // And when selecting a date:
3690 std::map<OUString, OUString> aArguments;
3691 aArguments.emplace("type", "date");
3692 aArguments.emplace("selected", "2022-05-30T00:00:00Z");
3693 pXTextDocument->executeContentControlEvent(aArguments);
3695 // Then make sure that the document is updated accordingly:
3696 SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
3697 // Without the accompanying fix in place, this test would have failed with:
3698 // - Expected: 2022-05-30
3699 // - Actual : choose a date
3700 // i.e. the document text was not updated.
3701 CPPUNIT_ASSERT_EQUAL(u"2022-05-30"_ustr, pTextNode->GetExpandText(pWrtShell->GetLayout()));
3704 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAuthorField)
3706 SwXTextDocument* pXTextDocument = createDoc();
3707 static constexpr OUString sAuthor(u"Abcd Xyz"_ustr);
3709 uno::Sequence<beans::PropertyValue> aPropertyValues1(comphelper::InitPropertySequence(
3711 {".uno:Author", uno::Any(sAuthor)},
3712 }));
3713 pXTextDocument->initializeForTiledRendering(aPropertyValues1);
3715 auto insertAuthorField = [this]()
3717 uno::Reference<lang::XMultiServiceFactory> const xMSF(mxComponent, uno::UNO_QUERY_THROW);
3718 uno::Reference<text::XTextDocument> const xTD(mxComponent, uno::UNO_QUERY_THROW);
3720 auto const xText = xTD->getText();
3721 auto const xTextCursor = xText->createTextCursor();
3722 CPPUNIT_ASSERT(xTextCursor.is());
3724 xTextCursor->gotoEnd(false);
3726 uno::Reference<text::XTextField> const xTextField(
3727 xMSF->createInstance(u"com.sun.star.text.textfield.Author"_ustr), uno::UNO_QUERY_THROW);
3729 uno::Reference<beans::XPropertySet> xTextFieldProps(xTextField, uno::UNO_QUERY_THROW);
3730 xTextFieldProps->setPropertyValue(u"FullName"_ustr, uno::Any(true));
3732 xText->insertTextContent(xTextCursor, xTextField, false);
3735 insertAuthorField();
3736 Scheduler::ProcessEventsToIdle();
3738 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
3740 assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion[1]/SwLineLayout[1]/SwFieldPortion[1]", "expand", sAuthor);
3743 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSavedAuthorField)
3745 SwXTextDocument* pXTextDocument = createDoc("savedauthorfield.odt");
3746 static constexpr OUString sAuthor(u"XYZ ABCD"_ustr);
3747 uno::Sequence<beans::PropertyValue> aPropertyValues1(comphelper::InitPropertySequence(
3749 {".uno:Author", uno::Any(sAuthor)},
3750 }));
3751 pXTextDocument->initializeForTiledRendering(aPropertyValues1);
3753 Scheduler::ProcessEventsToIdle();
3755 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
3756 assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion[1]/SwLineLayout[1]/SwFieldPortion[1]", "expand", sAuthor);
3759 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlineTooltip)
3761 SwXTextDocument* pXTextDoc = createDoc();
3762 SwWrtShell* pWrtShell = pXTextDoc->GetDocShell()->GetWrtShell();
3763 CPPUNIT_ASSERT(pWrtShell);
3764 setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
3765 pWrtShell->SetRedlineFlagsAndCheckInsMode(RedlineFlags::On | RedlineFlags::ShowMask);
3766 uno::Reference<text::XText> xText(pXTextDoc->getText(), uno::UNO_SET_THROW);
3767 xText->insertString(xText->getEnd(), u"test"_ustr, /*bAbsorb=*/false);
3769 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
3770 CPPUNIT_ASSERT(pShellCursor);
3772 pWrtShell->EndOfSection(/*bSelect=*/false);
3773 Point aEnd = pShellCursor->GetSttPos();
3774 pWrtShell->StartOfSection(/*bSelect=*/false);
3775 Point aStart = pShellCursor->GetSttPos();
3776 Point aMiddle((aStart.getX() + aEnd.getX()) / 2, (aStart.getY() + aEnd.getY()) / 2);
3777 pXTextDoc->postMouseEvent(LOK_MOUSEEVENT_MOUSEMOVE, aMiddle.getX(), aMiddle.getY(), 1, 0, 0);
3778 Scheduler::ProcessEventsToIdle();
3780 CPPUNIT_ASSERT(m_aTooltip.text.starts_with("Inserted: "));
3782 std::vector<OUString> vec = comphelper::string::split(OUString::fromUtf8(m_aTooltip.rect), ',');
3783 CPPUNIT_ASSERT_EQUAL(size_t(4), vec.size());
3784 CPPUNIT_ASSERT(vec[0].toInt32() != 0);
3785 CPPUNIT_ASSERT(vec[1].toInt32() != 0);
3786 CPPUNIT_ASSERT(vec[2].toInt32() != 0);
3787 CPPUNIT_ASSERT(vec[3].toInt32() != 0);
3790 // toggling Formatting Marks on/off for one view should have no effect on other views
3791 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testToggleFormattingMarks)
3793 SwXTextDocument* pXTextDocument = createDoc();
3794 int nView1 = SfxLokHelper::getView();
3796 SfxLokHelper::createView();
3797 int nView2 = SfxLokHelper::getView();
3798 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
3800 SfxLokHelper::setView(nView1);
3801 SwTestViewCallback aView1;
3803 SfxLokHelper::setView(nView2);
3804 SwTestViewCallback aView2;
3806 OString sOrigView2RenderState = pXTextDocument->getViewRenderState();
3808 comphelper::dispatchCommand(u".uno:ControlCodes"_ustr, {});
3810 Scheduler::ProcessEventsToIdle();
3812 // 1. change to view #2 shouldn't result in an update to view #1 renderstate
3813 CPPUNIT_ASSERT(aView1.m_aViewRenderState.isEmpty());
3814 // 2. toggling on ControlCodes should result in view #2 render state reporting
3815 // 'P' for Pilcrow
3816 CPPUNIT_ASSERT_EQUAL(OString("P" + sOrigView2RenderState), aView2.m_aViewRenderState);
3819 // toggling chart into dark mode should switch not leave text as black
3820 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSwitchingChartToDarkMode)
3822 addDarkLightThemes(COL_BLACK, COL_WHITE);
3823 SwXTextDocument* pXTextDocument = createDoc("large-chart-labels.odt");
3824 CPPUNIT_ASSERT(pXTextDocument);
3826 SwView* pView = getSwDocShell()->GetView();
3827 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
3828 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
3830 { "NewTheme", uno::Any(u"Dark"_ustr) },
3833 comphelper::dispatchCommand(u".uno:ChangeTheme"_ustr, xFrame, aPropertyValues);
3834 CPPUNIT_ASSERT_EQUAL("S;Dark"_ostr, pXTextDocument->getViewRenderState());
3836 Bitmap aBitmap(getTile(pXTextDocument));
3837 Size aSize = aBitmap.GetSizePixel();
3839 #ifdef DBGDUMP
3840 SvFileStream aNew("/tmp/dump.png", StreamMode::WRITE | StreamMode::TRUNC);
3841 vcl::PngImageWriter aPNGWriter(aNew);
3842 aPNGWriter.write(BitmapEx(aBitmap));
3843 #endif
3845 int nBlackPixels = 0;
3846 int nWhitePixels = 0;
3847 BitmapScopedReadAccess pAccess(aBitmap);
3848 for (tools::Long x = 0; x < aSize.Width(); ++x)
3850 for (tools::Long y = 0; y < aSize.Height(); ++y)
3852 Color aActualColor(pAccess->GetPixel(y, x));
3853 if (aActualColor.IsDark()) // ignore antialiasing
3854 ++nBlackPixels;
3855 else
3856 ++nWhitePixels;
3859 // text in white on black background should have both colors dominated by black
3860 // background
3861 CPPUNIT_ASSERT(nBlackPixels > 0);
3862 CPPUNIT_ASSERT(nWhitePixels > 0);
3863 CPPUNIT_ASSERT(nBlackPixels > nWhitePixels);
3866 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTdf159626_yellowPatternFill)
3868 SwXTextDocument* pXTextDocument = createDoc("tdf159626_yellowPatternFill.docx");
3869 CPPUNIT_ASSERT(pXTextDocument);
3871 SwView* pView = getSwDocShell()->GetView();
3872 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
3874 Bitmap aBitmap(getTile(pXTextDocument));
3875 Size aSize = aBitmap.GetSizePixel();
3877 int nPureYellowPixels = 0;
3878 int nEdgePlusGrayPlusAntialiasPixels = 0;
3879 BitmapScopedReadAccess pAccess(aBitmap);
3880 for (tools::Long x = 0; x < aSize.Width(); ++x)
3882 for (tools::Long y = 0; y < aSize.Height(); ++y)
3884 Color aActualColor(pAccess->GetPixel(y, x));
3885 if (aActualColor == COL_YELLOW)
3886 ++nPureYellowPixels;
3887 else
3888 ++nEdgePlusGrayPlusAntialiasPixels;
3891 // The page background pattern is 62 yellow/2 gray pixels - first pixel is gray(foreground)
3892 // Without the patch, the document was primarily gray.
3893 CPPUNIT_ASSERT(nPureYellowPixels > 0);
3894 CPPUNIT_ASSERT(nPureYellowPixels / 2 > nEdgePlusGrayPlusAntialiasPixels);
3897 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTdf159626_yellowPatternFillB)
3899 SwXTextDocument* pXTextDocument = createDoc("tdf159626_yellowPatternFillB.docx");
3900 CPPUNIT_ASSERT(pXTextDocument);
3902 SwView* pView = getSwDocShell()->GetView();
3903 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
3905 Bitmap aBitmap(getTile(pXTextDocument));
3906 Size aSize = aBitmap.GetSizePixel();
3908 int nPureYellowPixels = 0;
3909 int nEdgePlusGrayPlusAntialiasPixels = 0;
3910 BitmapScopedReadAccess pAccess(aBitmap);
3911 for (tools::Long x = 0; x < aSize.Width(); ++x)
3913 for (tools::Long y = 0; y < aSize.Height(); ++y)
3915 Color aActualColor(pAccess->GetPixel(y, x));
3916 if (aActualColor == COL_YELLOW)
3917 ++nPureYellowPixels;
3918 else
3919 ++nEdgePlusGrayPlusAntialiasPixels;
3922 // The page background pattern is 62 yellow/2 gray pixels - first pixel is yellow(background)
3923 // LO already imported this correctly, as primarily yellow - ensure it stays that way.
3924 CPPUNIT_ASSERT(nPureYellowPixels > 0);
3925 CPPUNIT_ASSERT(nPureYellowPixels / 2 > nEdgePlusGrayPlusAntialiasPixels);
3928 CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTdf159626_blackPatternFill)
3930 SwXTextDocument* pXTextDocument = createDoc("tdf159626_blackPatternFill.docx");
3932 SwView* pView = getSwDocShell()->GetView();
3933 uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
3935 Bitmap aBitmap(getTile(pXTextDocument));
3936 Size aSize = aBitmap.GetSizePixel();
3938 int nPureBlackPixels = 0;
3939 int nEdgePlusWhitePlusAntialiasPixels = 0;
3940 BitmapScopedReadAccess pAccess(aBitmap);
3941 for (tools::Long x = 0; x < aSize.Width(); ++x)
3943 for (tools::Long y = 0; y < aSize.Height(); ++y)
3945 Color aActualColor(pAccess->GetPixel(y, x));
3946 if (aActualColor == COL_BLACK)
3947 ++nPureBlackPixels;
3948 else
3949 ++nEdgePlusWhitePlusAntialiasPixels;
3952 // Both the foreground and background are defined as black, represented by a pattern with
3953 // 48 white/16 black pixels.
3954 // The document should be entirely black (except for text margin markings).
3955 CPPUNIT_ASSERT(nEdgePlusWhitePlusAntialiasPixels > 0);
3956 CPPUNIT_ASSERT(nPureBlackPixels / 10 > nEdgePlusWhitePlusAntialiasPixels);
3959 CPPUNIT_PLUGIN_IMPLEMENT();
3961 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */