lokit: add .uno:DocumentRepair command
[LibreOffice.git] / sw / qa / extras / tiledrendering / tiledrendering.cxx
blobd6f95df456e7fd43f2518431611e8e8827f747bf
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 <string>
11 #include <boost/property_tree/json_parser.hpp>
13 #include <com/sun/star/frame/DispatchResultState.hpp>
14 #include <swmodeltestbase.hxx>
15 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
16 #include <comphelper/dispatchcommand.hxx>
17 #include <comphelper/propertysequence.hxx>
18 #include <comphelper/string.hxx>
19 #include <comphelper/lok.hxx>
20 #include <svx/svdpage.hxx>
21 #include <svx/svdview.hxx>
22 #include <vcl/svapp.hxx>
23 #include <editeng/editview.hxx>
24 #include <editeng/outliner.hxx>
25 #include <svl/srchitem.hxx>
26 #include <drawdoc.hxx>
27 #include <ndtxt.hxx>
28 #include <wrtsh.hxx>
29 #include <view.hxx>
30 #include <UndoManager.hxx>
31 #include <cmdid.h>
32 #include <sfx2/viewsh.hxx>
33 #include <sfx2/bindings.hxx>
34 #include <sfx2/dispatch.hxx>
35 #include <sfx2/viewfrm.hxx>
36 #include <sfx2/lokhelper.hxx>
37 #include <redline.hxx>
38 #include <IDocumentRedlineAccess.hxx>
39 #include <vcl/scheduler.hxx>
41 static char const DATA_DIRECTORY[] = "/sw/qa/extras/tiledrendering/data/";
43 static std::ostream& operator<<(std::ostream& os, ViewShellId id)
45 os << (sal_Int32)id;
46 return os;
49 /// Testsuite for the SwXTextDocument methods implementing the vcl::ITiledRenderable interface.
50 class SwTiledRenderingTest : public SwModelTestBase
52 public:
53 SwTiledRenderingTest();
54 void testRegisterCallback();
55 void testPostKeyEvent();
56 void testPostMouseEvent();
57 void testSetTextSelection();
58 void testGetTextSelection();
59 void testSetGraphicSelection();
60 void testResetSelection();
61 void testSearch();
62 void testSearchViewArea();
63 void testSearchTextFrame();
64 void testSearchTextFrameWrapAround();
65 void testDocumentSizeChanged();
66 void testSearchAll();
67 void testSearchAllNotifications();
68 void testPageDownInvalidation();
69 void testPartHash();
70 void testViewCursors();
71 void testShapeViewCursors();
72 void testMissingInvalidation();
73 void testViewCursorVisibility();
74 void testViewCursorCleanup();
75 void testViewLock();
76 void testTextEditViewInvalidations();
77 void testUndoInvalidations();
78 void testUndoLimiting();
79 void testUndoShapeLimiting();
80 void testUndoDispatch();
81 void testUndoRepairDispatch();
82 void testShapeTextUndoShells();
83 void testShapeTextUndoGroupShells();
84 void testTrackChanges();
85 void testTrackChangesCallback();
86 void testRedlineUpdateCallback();
87 void testSetViewGraphicSelection();
88 void testCreateViewGraphicSelection();
89 void testCreateViewTextSelection();
90 void testRedlineColors();
91 void testCommentEndTextEdit();
92 void testCursorPosition();
93 void testPaintCallbacks();
94 void testUndoRepairResult();
95 void testRedoRepairResult();
96 void testDisableUndoRepair();
97 void testAllTrackedChanges();
98 void testDocumentRepair();
100 CPPUNIT_TEST_SUITE(SwTiledRenderingTest);
101 CPPUNIT_TEST(testRegisterCallback);
102 CPPUNIT_TEST(testPostKeyEvent);
103 CPPUNIT_TEST(testPostMouseEvent);
104 CPPUNIT_TEST(testSetTextSelection);
105 CPPUNIT_TEST(testGetTextSelection);
106 CPPUNIT_TEST(testSetGraphicSelection);
107 CPPUNIT_TEST(testResetSelection);
108 CPPUNIT_TEST(testSearch);
109 CPPUNIT_TEST(testSearchViewArea);
110 CPPUNIT_TEST(testSearchTextFrame);
111 CPPUNIT_TEST(testSearchTextFrameWrapAround);
112 CPPUNIT_TEST(testDocumentSizeChanged);
113 CPPUNIT_TEST(testSearchAll);
114 CPPUNIT_TEST(testSearchAllNotifications);
115 CPPUNIT_TEST(testPageDownInvalidation);
116 CPPUNIT_TEST(testPartHash);
117 CPPUNIT_TEST(testViewCursors);
118 CPPUNIT_TEST(testShapeViewCursors);
119 CPPUNIT_TEST(testMissingInvalidation);
120 CPPUNIT_TEST(testViewCursorVisibility);
121 CPPUNIT_TEST(testViewCursorCleanup);
122 CPPUNIT_TEST(testViewLock);
123 CPPUNIT_TEST(testTextEditViewInvalidations);
124 CPPUNIT_TEST(testUndoInvalidations);
125 CPPUNIT_TEST(testUndoLimiting);
126 CPPUNIT_TEST(testUndoShapeLimiting);
127 CPPUNIT_TEST(testUndoDispatch);
128 CPPUNIT_TEST(testUndoRepairDispatch);
129 CPPUNIT_TEST(testShapeTextUndoShells);
130 CPPUNIT_TEST(testShapeTextUndoGroupShells);
131 CPPUNIT_TEST(testTrackChanges);
132 CPPUNIT_TEST(testTrackChangesCallback);
133 CPPUNIT_TEST(testRedlineUpdateCallback);
134 CPPUNIT_TEST(testSetViewGraphicSelection);
135 CPPUNIT_TEST(testCreateViewGraphicSelection);
136 CPPUNIT_TEST(testCreateViewTextSelection);
137 CPPUNIT_TEST(testRedlineColors);
138 CPPUNIT_TEST(testCommentEndTextEdit);
139 CPPUNIT_TEST(testCursorPosition);
140 CPPUNIT_TEST(testPaintCallbacks);
141 CPPUNIT_TEST(testUndoRepairResult);
142 CPPUNIT_TEST(testRedoRepairResult);
143 CPPUNIT_TEST(testDisableUndoRepair);
144 CPPUNIT_TEST(testAllTrackedChanges);
145 CPPUNIT_TEST(testDocumentRepair);
147 CPPUNIT_TEST_SUITE_END();
149 private:
150 SwXTextDocument* createDoc(const char* pName = nullptr);
151 static void callback(int nType, const char* pPayload, void* pData);
152 void callbackImpl(int nType, const char* pPayload);
153 tools::Rectangle m_aInvalidation;
154 Size m_aDocumentSize;
155 OString m_aTextSelection;
156 bool m_bFound;
157 std::vector<OString> m_aSearchResultSelection;
158 std::vector<int> m_aSearchResultPart;
159 int m_nSelectionBeforeSearchResult;
160 int m_nSelectionAfterSearchResult;
161 int m_nInvalidations;
162 int m_nRedlineTableSizeChanged;
163 int m_nRedlineTableEntryModified;
164 int m_nTrackedChangeIndex;
167 SwTiledRenderingTest::SwTiledRenderingTest()
168 : m_bFound(true),
169 m_nSelectionBeforeSearchResult(0),
170 m_nSelectionAfterSearchResult(0),
171 m_nInvalidations(0),
172 m_nRedlineTableSizeChanged(0),
173 m_nRedlineTableEntryModified(0),
174 m_nTrackedChangeIndex(-1)
178 SwXTextDocument* SwTiledRenderingTest::createDoc(const char* pName)
180 if (!pName)
181 loadURL("private:factory/swriter", nullptr);
182 else
183 load(DATA_DIRECTORY, pName);
185 SwXTextDocument* pTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
186 CPPUNIT_ASSERT(pTextDocument);
187 pTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
188 return pTextDocument;
191 void SwTiledRenderingTest::callback(int nType, const char* pPayload, void* pData)
193 static_cast<SwTiledRenderingTest*>(pData)->callbackImpl(nType, pPayload);
196 void SwTiledRenderingTest::callbackImpl(int nType, const char* pPayload)
198 OString aPayload(pPayload);
199 switch (nType)
201 case LOK_CALLBACK_INVALIDATE_TILES:
203 if (m_aInvalidation.IsEmpty())
205 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload));
206 if (OString("EMPTY") == pPayload)
207 return;
208 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength());
209 m_aInvalidation.setX(aSeq[0].toInt32());
210 m_aInvalidation.setY(aSeq[1].toInt32());
211 m_aInvalidation.setWidth(aSeq[2].toInt32());
212 m_aInvalidation.setHeight(aSeq[3].toInt32());
214 ++m_nInvalidations;
216 break;
217 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
219 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload));
220 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), aSeq.getLength());
221 m_aDocumentSize.setWidth(aSeq[0].toInt32());
222 m_aDocumentSize.setHeight(aSeq[1].toInt32());
224 break;
225 case LOK_CALLBACK_TEXT_SELECTION:
227 m_aTextSelection = pPayload;
228 if (m_aSearchResultSelection.empty())
229 ++m_nSelectionBeforeSearchResult;
230 else
231 ++m_nSelectionAfterSearchResult;
233 break;
234 case LOK_CALLBACK_SEARCH_NOT_FOUND:
236 m_bFound = false;
238 break;
239 case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
241 m_aSearchResultSelection.clear();
242 boost::property_tree::ptree aTree;
243 std::stringstream aStream(pPayload);
244 boost::property_tree::read_json(aStream, aTree);
245 for (boost::property_tree::ptree::value_type& rValue : aTree.get_child("searchResultSelection"))
247 m_aSearchResultSelection.emplace_back(rValue.second.get<std::string>("rectangles").c_str());
248 m_aSearchResultPart.push_back(std::atoi(rValue.second.get<std::string>("part").c_str()));
251 break;
252 case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED:
254 ++m_nRedlineTableSizeChanged;
256 break;
257 case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED:
259 ++m_nRedlineTableEntryModified;
261 break;
262 case LOK_CALLBACK_STATE_CHANGED:
264 OString aTrackedChangeIndexPrefix(".uno:TrackedChangeIndex=");
265 if (aPayload.startsWith(aTrackedChangeIndexPrefix))
267 OString sIndex = aPayload.copy(aTrackedChangeIndexPrefix.getLength());
268 if (sIndex.isEmpty())
269 m_nTrackedChangeIndex = -1;
270 else
271 m_nTrackedChangeIndex = sIndex.toInt32();
274 break;
278 void SwTiledRenderingTest::testRegisterCallback()
280 comphelper::LibreOfficeKit::setActive();
281 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
282 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
283 pWrtShell->GetSfxViewShell()->registerLibreOfficeKitViewCallback(&SwTiledRenderingTest::callback, this);
284 // Insert a character at the beginning of the document.
285 pWrtShell->Insert("x");
287 // Check that the top left 256x256px tile would be invalidated.
288 CPPUNIT_ASSERT(!m_aInvalidation.IsEmpty());
289 tools::Rectangle aTopLeft(0, 0, 256*15, 256*15); // 1 px = 15 twips, assuming 96 DPI.
290 CPPUNIT_ASSERT(m_aInvalidation.IsOver(aTopLeft));
291 comphelper::LibreOfficeKit::setActive(false);
294 void SwTiledRenderingTest::testPostKeyEvent()
296 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
297 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
298 pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
299 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
300 // Did we manage to go after the first character?
301 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pShellCursor->GetPoint()->nContent.GetIndex());
303 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
304 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
305 // Did we manage to insert the character after the first one?
306 CPPUNIT_ASSERT_EQUAL(OUString("Axaa bbb."), pShellCursor->GetPoint()->nNode.GetNode().GetTextNode()->GetText());
309 void SwTiledRenderingTest::testPostMouseEvent()
311 comphelper::LibreOfficeKit::setActive();
312 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
313 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
314 pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
315 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
316 // Did we manage to go after the first character?
317 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pShellCursor->GetPoint()->nContent.GetIndex());
319 Point aStart = pShellCursor->GetSttPos();
320 aStart.setX(aStart.getX() - 1000);
321 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
322 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
323 // The new cursor position must be before the first word.
324 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), pShellCursor->GetPoint()->nContent.GetIndex());
325 comphelper::LibreOfficeKit::setActive(false);
328 void SwTiledRenderingTest::testSetTextSelection()
330 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
331 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
332 // Move the cursor into the second word.
333 pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 5, /*bBasicCall=*/false);
334 // Create a selection on the word.
335 pWrtShell->SelWrd();
336 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
337 // Did we indeed manage to select the second word?
338 CPPUNIT_ASSERT_EQUAL(OUString("bbb"), pShellCursor->GetText());
340 // Now use setTextSelection() to move the start of the selection 1000 twips left.
341 Point aStart = pShellCursor->GetSttPos();
342 aStart.setX(aStart.getX() - 1000);
343 pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_START, aStart.getX(), aStart.getY());
344 // The new selection must include the first word, too -- but not the ending dot.
345 CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb"), pShellCursor->GetText());
347 // Next: test that LOK_SETTEXTSELECTION_RESET + LOK_SETTEXTSELECTION_END can be used to create a selection.
348 pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_RESET, aStart.getX(), aStart.getY());
349 pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_END, aStart.getX() + 1000, aStart.getY());
350 CPPUNIT_ASSERT_EQUAL(OUString("Aaa b"), pShellCursor->GetText());
353 void SwTiledRenderingTest::testGetTextSelection()
355 comphelper::LibreOfficeKit::setActive();
357 SwXTextDocument* pXTextDocument = createDoc("shape-with-text.fodt");
358 // No crash, just empty output for unexpected mime type.
359 OString aUsedFormat;
360 CPPUNIT_ASSERT_EQUAL(OString(), pXTextDocument->getTextSelection("foo/bar", aUsedFormat));
362 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
363 // Move the cursor into the first word.
364 pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false);
365 // Create a selection by on the word.
366 pWrtShell->SelWrd();
368 // Make sure that we selected text from the body text.
369 CPPUNIT_ASSERT_EQUAL(OString("Hello"), pXTextDocument->getTextSelection("text/plain;charset=utf-8", aUsedFormat));
371 // Make sure we produce something for HTML.
372 CPPUNIT_ASSERT(!pXTextDocument->getTextSelection("text/html", aUsedFormat).isEmpty());
374 // Now select some shape text and check again.
375 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
376 SdrObject* pObject = pPage->GetObj(0);
377 SdrView* pView = pWrtShell->GetDrawView();
378 pView->SdrBeginTextEdit(pObject);
379 CPPUNIT_ASSERT(pView->GetTextEditObject());
380 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
381 ESelection aWordSelection(0, 0, 0, 5);
382 rEditView.SetSelection(aWordSelection);
383 CPPUNIT_ASSERT_EQUAL(OString("Shape"), pXTextDocument->getTextSelection("text/plain;charset=utf-8", aUsedFormat));
385 comphelper::LibreOfficeKit::setActive(false);
388 void SwTiledRenderingTest::testSetGraphicSelection()
390 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
391 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
392 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
393 SdrObject* pObject = pPage->GetObj(0);
394 pWrtShell->SelectObj(Point(), 0, pObject);
395 // Make sure the rectangle has 8 handles: at each corner and at the center of each edge.
396 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(8), pObject->GetHdlCount());
397 // Take the bottom center one.
398 SdrHdl* pHdl = pObject->GetHdl(6);
399 CPPUNIT_ASSERT_EQUAL((int)SdrHdlKind::Lower, (int)pHdl->GetKind());
400 tools::Rectangle aShapeBefore = pObject->GetSnapRect();
401 // Resize.
402 pXTextDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_START, pHdl->GetPos().getX(), pHdl->GetPos().getY());
403 pXTextDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_END, pHdl->GetPos().getX(), pHdl->GetPos().getY() + 1000);
404 tools::Rectangle aShapeAfter = pObject->GetSnapRect();
405 // Check that a resize happened, but aspect ratio is not kept.
406 CPPUNIT_ASSERT_EQUAL(aShapeBefore.getWidth(), aShapeAfter.getWidth());
407 CPPUNIT_ASSERT_EQUAL(aShapeBefore.getHeight() + 1000, aShapeAfter.getHeight());
410 void SwTiledRenderingTest::testResetSelection()
412 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
413 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
414 // Select one character.
415 pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false);
416 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
417 // We have a text selection.
418 CPPUNIT_ASSERT(pShellCursor->HasMark());
420 pXTextDocument->resetSelection();
421 // We no longer have a text selection.
422 CPPUNIT_ASSERT(!pShellCursor->HasMark());
424 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
425 SdrObject* pObject = pPage->GetObj(0);
426 Point aPoint = pObject->GetSnapRect().Center();
427 // Select the shape.
428 pWrtShell->EnterSelFrameMode(&aPoint);
429 // We have a graphic selection.
430 CPPUNIT_ASSERT(pWrtShell->IsSelFrameMode());
432 pXTextDocument->resetSelection();
433 // We no longer have a graphic selection.
434 CPPUNIT_ASSERT(!pWrtShell->IsSelFrameMode());
437 void lcl_search(bool bBackward)
439 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
441 {"SearchItem.SearchString", uno::makeAny(OUString("shape"))},
442 {"SearchItem.Backward", uno::makeAny(bBackward)}
443 }));
444 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
447 void SwTiledRenderingTest::testSearch()
449 comphelper::LibreOfficeKit::setActive();
451 SwXTextDocument* pXTextDocument = createDoc("search.odt");
452 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
453 pWrtShell->GetSfxViewShell()->registerLibreOfficeKitViewCallback(&SwTiledRenderingTest::callback, this);
454 std::size_t nNode = pWrtShell->getShellCursor(false)->Start()->nNode.GetNode().GetIndex();
456 // First hit, in the second paragraph, before the shape.
457 lcl_search(false);
458 CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
459 std::size_t nActual = pWrtShell->getShellCursor(false)->Start()->nNode.GetNode().GetIndex();
460 CPPUNIT_ASSERT_EQUAL(nNode + 1, nActual);
461 /// Make sure we get search result selection for normal find as well, not only find all.
462 CPPUNIT_ASSERT(!m_aSearchResultSelection.empty());
464 // Next hit, in the shape.
465 lcl_search(false);
466 CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
468 // Next hit, in the shape, still.
469 lcl_search(false);
470 CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
472 // Last hit, in the last paragraph, after the shape.
473 lcl_search(false);
474 CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
475 nActual = pWrtShell->getShellCursor(false)->Start()->nNode.GetNode().GetIndex();
476 CPPUNIT_ASSERT_EQUAL(nNode + 7, nActual);
478 // Now change direction and make sure that the first 2 hits are in the shape, but not the 3rd one.
479 lcl_search(true);
480 CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
481 lcl_search(true);
482 CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
483 lcl_search(true);
484 CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
485 nActual = pWrtShell->getShellCursor(false)->Start()->nNode.GetNode().GetIndex();
486 CPPUNIT_ASSERT_EQUAL(nNode + 1, nActual);
488 comphelper::LibreOfficeKit::setActive(false);
491 void SwTiledRenderingTest::testSearchViewArea()
493 SwXTextDocument* pXTextDocument = createDoc("search.odt");
494 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
495 // Go to the second page, 1-based.
496 pWrtShell->GotoPage(2, false);
497 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
498 // Get the ~top left corner of the second page.
499 Point aPoint = pShellCursor->GetSttPos();
501 // Go back to the first page, search while the cursor is there, but the
502 // visible area is the second page.
503 pWrtShell->GotoPage(1, false);
504 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
506 {"SearchItem.SearchString", uno::makeAny(OUString("Heading"))},
507 {"SearchItem.Backward", uno::makeAny(false)},
508 {"SearchItem.SearchStartPointX", uno::makeAny(static_cast<sal_Int32>(aPoint.getX()))},
509 {"SearchItem.SearchStartPointY", uno::makeAny(static_cast<sal_Int32>(aPoint.getY()))}
510 }));
511 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
512 // This was just "Heading", i.e. SwView::SearchAndWrap() did not search from only the top of the second page.
513 CPPUNIT_ASSERT_EQUAL(OUString("Heading on second page"), pShellCursor->GetPoint()->nNode.GetNode().GetTextNode()->GetText());
516 void SwTiledRenderingTest::testSearchTextFrame()
518 comphelper::LibreOfficeKit::setActive();
520 SwXTextDocument* pXTextDocument = createDoc("search.odt");
521 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
522 pWrtShell->GetSfxViewShell()->registerLibreOfficeKitViewCallback(&SwTiledRenderingTest::callback, this);
523 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
525 {"SearchItem.SearchString", uno::makeAny(OUString("TextFrame"))},
526 {"SearchItem.Backward", uno::makeAny(false)},
527 }));
528 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
529 // This was empty: nothing was highlighted after searching for 'TextFrame'.
530 CPPUNIT_ASSERT(!m_aTextSelection.isEmpty());
532 comphelper::LibreOfficeKit::setActive(false);
535 void SwTiledRenderingTest::testSearchTextFrameWrapAround()
537 SwXTextDocument* pXTextDocument = createDoc("search.odt");
538 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
539 pWrtShell->GetSfxViewShell()->registerLibreOfficeKitViewCallback(&SwTiledRenderingTest::callback, this);
540 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
542 {"SearchItem.SearchString", uno::makeAny(OUString("TextFrame"))},
543 {"SearchItem.Backward", uno::makeAny(false)},
544 }));
545 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
546 CPPUNIT_ASSERT(m_bFound);
547 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
548 // This failed, i.e. the second time 'not found' was reported, instead of wrapping around.
549 CPPUNIT_ASSERT(m_bFound);
552 void SwTiledRenderingTest::testDocumentSizeChanged()
554 comphelper::LibreOfficeKit::setActive();
555 // Get the current document size.
556 SwXTextDocument* pXTextDocument = createDoc("2-pages.odt");
557 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
558 pWrtShell->GetSfxViewShell()->registerLibreOfficeKitViewCallback(&SwTiledRenderingTest::callback, this);
559 Size aSize = pXTextDocument->getDocumentSize();
561 // Delete the second page and see how the size changes.
562 pWrtShell->Down(false);
563 pWrtShell->DelLeft();
564 // Document width should not change, this was 0.
565 CPPUNIT_ASSERT_EQUAL(aSize.getWidth(), m_aDocumentSize.getWidth());
566 // Document height should be smaller now.
567 CPPUNIT_ASSERT(aSize.getHeight() > m_aDocumentSize.getHeight());
568 comphelper::LibreOfficeKit::setActive(false);
571 void SwTiledRenderingTest::testSearchAll()
573 comphelper::LibreOfficeKit::setActive();
575 SwXTextDocument* pXTextDocument = createDoc("search.odt");
576 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
577 pWrtShell->GetSfxViewShell()->registerLibreOfficeKitViewCallback(&SwTiledRenderingTest::callback, this);
578 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
580 {"SearchItem.SearchString", uno::makeAny(OUString("shape"))},
581 {"SearchItem.Backward", uno::makeAny(false)},
582 {"SearchItem.Command", uno::makeAny(static_cast<sal_uInt16>(SvxSearchCmd::FIND_ALL))},
583 }));
584 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
585 // This was 0; should be 2 results in the body text.
586 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), m_aSearchResultSelection.size());
587 // Writer documents are always a single part.
588 CPPUNIT_ASSERT_EQUAL(0, m_aSearchResultPart[0]);
590 comphelper::LibreOfficeKit::setActive(false);
593 void SwTiledRenderingTest::testSearchAllNotifications()
595 comphelper::LibreOfficeKit::setActive();
596 SwXTextDocument* pXTextDocument = createDoc("search.odt");
597 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
598 pWrtShell->GetSfxViewShell()->registerLibreOfficeKitViewCallback(&SwTiledRenderingTest::callback, this);
599 // Reset notification counter before search.
600 m_nSelectionBeforeSearchResult = 0;
601 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
603 {"SearchItem.SearchString", uno::makeAny(OUString("shape"))},
604 {"SearchItem.Backward", uno::makeAny(false)},
605 {"SearchItem.Command", uno::makeAny(static_cast<sal_uInt16>(SvxSearchCmd::FIND_ALL))},
606 }));
607 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
608 Scheduler::ProcessEventsToIdle();
610 // This was 5, make sure that we get no notifications about selection changes during search.
611 CPPUNIT_ASSERT_EQUAL(0, m_nSelectionBeforeSearchResult);
612 // But we do get the selection afterwards.
613 CPPUNIT_ASSERT(m_nSelectionAfterSearchResult > 0);
615 comphelper::LibreOfficeKit::setActive(false);
618 void SwTiledRenderingTest::testPageDownInvalidation()
620 comphelper::LibreOfficeKit::setActive();
622 SwXTextDocument* pXTextDocument = createDoc("pagedown-invalidation.odt");
623 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
625 {".uno:HideWhitespace", uno::makeAny(true)},
626 }));
627 pXTextDocument->initializeForTiledRendering(aPropertyValues);
628 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
629 pWrtShell->GetSfxViewShell()->registerLibreOfficeKitViewCallback(&SwTiledRenderingTest::callback, this);
630 comphelper::dispatchCommand(".uno:PageDown", uno::Sequence<beans::PropertyValue>());
632 // This was 2.
633 CPPUNIT_ASSERT_EQUAL(0, m_nInvalidations);
635 comphelper::LibreOfficeKit::setActive(false);
638 void SwTiledRenderingTest::testPartHash()
640 comphelper::LibreOfficeKit::setActive();
642 SwXTextDocument* pXTextDocument = createDoc("pagedown-invalidation.odt");
643 int nParts = pXTextDocument->getParts();
644 for (int it = 0; it < nParts; it++)
646 CPPUNIT_ASSERT(!pXTextDocument->getPartHash(it).isEmpty());
649 comphelper::LibreOfficeKit::setActive(false);
652 /// A view callback tracks callbacks invoked on one specific view.
653 class ViewCallback
655 public:
656 bool m_bOwnCursorInvalidated;
657 bool m_bOwnCursorAtOrigin;
658 tools::Rectangle m_aOwnCursor;
659 bool m_bViewCursorInvalidated;
660 tools::Rectangle m_aViewCursor;
661 bool m_bOwnSelectionSet;
662 bool m_bViewSelectionSet;
663 OString m_aViewSelection;
664 bool m_bTilesInvalidated;
665 bool m_bViewCursorVisible;
666 bool m_bGraphicViewSelection;
667 bool m_bGraphicSelection;
668 bool m_bViewLock;
669 /// Set if any callback was invoked.
670 bool m_bCalled;
671 /// Redline table size changed payload
672 boost::property_tree::ptree m_aRedlineTableChanged;
673 /// Redline table modified payload
674 boost::property_tree::ptree m_aRedlineTableModified;
676 ViewCallback()
677 : m_bOwnCursorInvalidated(false),
678 m_bOwnCursorAtOrigin(false),
679 m_bViewCursorInvalidated(false),
680 m_bOwnSelectionSet(false),
681 m_bViewSelectionSet(false),
682 m_bTilesInvalidated(false),
683 m_bViewCursorVisible(false),
684 m_bGraphicViewSelection(false),
685 m_bGraphicSelection(false),
686 m_bViewLock(false),
687 m_bCalled(false)
691 static void callback(int nType, const char* pPayload, void* pData)
693 static_cast<ViewCallback*>(pData)->callbackImpl(nType, pPayload);
696 void callbackImpl(int nType, const char* pPayload)
698 OString aPayload(pPayload);
699 m_bCalled = true;
700 switch (nType)
702 case LOK_CALLBACK_INVALIDATE_TILES:
704 m_bTilesInvalidated = true;
706 break;
707 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
709 m_bOwnCursorInvalidated = true;
711 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::fromUtf8(aPayload));
712 if (OString("EMPTY") == pPayload)
713 return;
714 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength());
715 m_aOwnCursor.setX(aSeq[0].toInt32());
716 m_aOwnCursor.setY(aSeq[1].toInt32());
717 m_aOwnCursor.setWidth(aSeq[2].toInt32());
718 m_aOwnCursor.setHeight(aSeq[3].toInt32());
719 if (m_aOwnCursor.getX() == 0 && m_aOwnCursor.getY() == 0)
720 m_bOwnCursorAtOrigin = true;
722 break;
723 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
725 m_bViewCursorInvalidated = true;
726 std::stringstream aStream(pPayload);
727 boost::property_tree::ptree aTree;
728 boost::property_tree::read_json(aStream, aTree);
729 OString aRect = aTree.get_child("rectangle").get_value<std::string>().c_str();
731 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::fromUtf8(aRect));
732 if (OString("EMPTY") == pPayload)
733 return;
734 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength());
735 m_aViewCursor.setX(aSeq[0].toInt32());
736 m_aViewCursor.setY(aSeq[1].toInt32());
737 m_aViewCursor.setWidth(aSeq[2].toInt32());
738 m_aViewCursor.setHeight(aSeq[3].toInt32());
740 break;
741 case LOK_CALLBACK_TEXT_SELECTION:
743 m_bOwnSelectionSet = true;
745 break;
746 case LOK_CALLBACK_TEXT_VIEW_SELECTION:
748 m_bViewSelectionSet = true;
749 m_aViewSelection = aPayload;
751 break;
752 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
754 std::stringstream aStream(pPayload);
755 boost::property_tree::ptree aTree;
756 boost::property_tree::read_json(aStream, aTree);
757 m_bViewCursorVisible = aTree.get_child("visible").get_value<std::string>() == "true";
759 break;
760 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
762 std::stringstream aStream(pPayload);
763 boost::property_tree::ptree aTree;
764 boost::property_tree::read_json(aStream, aTree);
765 m_bGraphicViewSelection = aTree.get_child("selection").get_value<std::string>() != "EMPTY";
767 break;
768 case LOK_CALLBACK_GRAPHIC_SELECTION:
770 m_bGraphicSelection = aPayload != "EMPTY";
772 break;
773 case LOK_CALLBACK_VIEW_LOCK:
775 std::stringstream aStream(pPayload);
776 boost::property_tree::ptree aTree;
777 boost::property_tree::read_json(aStream, aTree);
778 m_bViewLock = aTree.get_child("rectangle").get_value<std::string>() != "EMPTY";
780 break;
781 case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED:
783 m_aRedlineTableChanged.clear();
784 std::stringstream aStream(pPayload);
785 boost::property_tree::read_json(aStream, m_aRedlineTableChanged);
786 m_aRedlineTableChanged = m_aRedlineTableChanged.get_child("redline");
788 break;
789 case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED:
791 m_aRedlineTableModified.clear();
792 std::stringstream aStream(pPayload);
793 boost::property_tree::read_json(aStream, m_aRedlineTableModified);
794 m_aRedlineTableModified = m_aRedlineTableModified.get_child("redline");
796 break;
801 class TestResultListener : public cppu::WeakImplHelper<css::frame::XDispatchResultListener>
803 public:
804 sal_uInt32 m_nDocRepair;
806 TestResultListener() : m_nDocRepair(0)
810 virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& rEvent) override
812 if (rEvent.State == frame::DispatchResultState::SUCCESS)
814 rEvent.Result >>= m_nDocRepair;
818 virtual void SAL_CALL disposing(const css::lang::EventObject&) override
823 void SwTiledRenderingTest::testMissingInvalidation()
825 comphelper::LibreOfficeKit::setActive();
827 // Create two views.
828 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
829 ViewCallback aView1;
830 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
831 int nView1 = SfxLokHelper::getView();
832 SfxLokHelper::createView();
833 ViewCallback aView2;
834 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
835 int nView2 = SfxLokHelper::getView();
837 // First view: put the cursor into the first word.
838 SfxLokHelper::setView(nView1);
839 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
840 pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
842 // Second view: select the first word.
843 SfxLokHelper::setView(nView2);
844 CPPUNIT_ASSERT(pXTextDocument->GetDocShell()->GetWrtShell() != pWrtShell);
845 pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
846 pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
847 pWrtShell->SelWrd();
849 // Now delete the selected word and make sure both views are invalidated.
850 aView1.m_bTilesInvalidated = false;
851 aView2.m_bTilesInvalidated = false;
852 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DELETE);
853 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DELETE);
854 Scheduler::ProcessEventsToIdle();
855 CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
856 CPPUNIT_ASSERT(aView2.m_bTilesInvalidated);
857 mxComponent->dispose();
858 mxComponent.clear();
860 comphelper::LibreOfficeKit::setActive(false);
863 void SwTiledRenderingTest::testViewCursors()
865 comphelper::LibreOfficeKit::setActive();
867 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
868 ViewCallback aView1;
869 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
870 SfxLokHelper::createView();
871 ViewCallback aView2;
872 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
873 CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
874 CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
875 CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
876 // This failed: the cursor position of view1 was only known to view2 once
877 // it changed.
878 CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
880 // Make sure that aView1 gets a view-only selection notification, while
881 // aView2 gets a real selection notification.
882 aView1.m_bOwnSelectionSet = false;
883 aView1.m_bViewSelectionSet = false;
884 aView2.m_bOwnSelectionSet = false;
885 aView2.m_bViewSelectionSet = false;
886 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
887 // Move the cursor into the second word.
888 pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 5, /*bBasicCall=*/false);
889 // Create a selection on the word.
890 pWrtShell->SelWrd();
891 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
892 // Did we indeed manage to select the second word?
893 CPPUNIT_ASSERT_EQUAL(OUString("bbb"), pShellCursor->GetText());
894 CPPUNIT_ASSERT(!aView1.m_bOwnSelectionSet);
895 // This failed, aView1 did not get notification about selection changes in
896 // aView2.
897 CPPUNIT_ASSERT(aView1.m_bViewSelectionSet);
898 CPPUNIT_ASSERT(aView2.m_bOwnSelectionSet);
899 CPPUNIT_ASSERT(!aView2.m_bViewSelectionSet);
900 mxComponent->dispose();
901 mxComponent.clear();
903 comphelper::LibreOfficeKit::setActive(false);
906 void SwTiledRenderingTest::testShapeViewCursors()
908 comphelper::LibreOfficeKit::setActive();
910 // Load a document and create a view, so we have 2 ones.
911 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
912 ViewCallback aView1;
913 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
914 SfxLokHelper::createView();
915 ViewCallback aView2;
916 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
917 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
918 SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
920 // Start shape text in the second view.
921 SdrPage* pPage = pWrtShell2->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
922 SdrObject* pObject = pPage->GetObj(0);
923 SdrView* pView = pWrtShell2->GetDrawView();
924 pWrtShell2->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell2->GetWin());
925 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
926 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
928 // Press a key in the second view, while the first one observes this.
929 aView1.m_bOwnCursorInvalidated = false;
930 aView1.m_bViewCursorInvalidated = false;
931 aView2.m_bOwnCursorInvalidated = false;
932 aView2.m_bViewCursorInvalidated = false;
933 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'y', 0);
934 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'y', 0);
935 // Make sure that aView1 gets a view-only cursor notification, while
936 // aView2 gets a real cursor notification.
937 CPPUNIT_ASSERT(!aView1.m_bOwnCursorInvalidated);
938 CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
939 CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
940 CPPUNIT_ASSERT(!aView2.m_bViewCursorInvalidated);
941 mxComponent->dispose();
942 mxComponent.clear();
944 comphelper::LibreOfficeKit::setActive(false);
947 void SwTiledRenderingTest::testViewCursorVisibility()
949 comphelper::LibreOfficeKit::setActive();
951 // Load a document that has a shape and create two views.
952 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
953 ViewCallback aView1;
954 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
955 SfxLokHelper::createView();
956 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
957 ViewCallback aView2;
958 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
959 // This failed, initially the view cursor in the second view wasn't visible.
960 CPPUNIT_ASSERT(aView2.m_bViewCursorVisible);
962 // Click on the shape in the second view.
963 aView1.m_bViewCursorVisible = true;
964 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
965 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
966 SdrObject* pObject = pPage->GetObj(0);
967 Point aCenter = pObject->GetSnapRect().Center();
968 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
969 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
970 Scheduler::ProcessEventsToIdle();
971 // Make sure the "view/text" cursor of the first view gets a notification.
972 CPPUNIT_ASSERT(!aView1.m_bViewCursorVisible);
973 mxComponent->dispose();
974 mxComponent.clear();
976 comphelper::LibreOfficeKit::setActive(false);
979 void SwTiledRenderingTest::testViewCursorCleanup()
981 comphelper::LibreOfficeKit::setActive();
983 // Load a document that has a shape and create two views.
984 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
985 ViewCallback aView1;
986 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
987 int nView2 = SfxLokHelper::createView();
988 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
989 ViewCallback aView2;
990 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
992 // Click on the shape in the second view.
993 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
994 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
995 SdrObject* pObject = pPage->GetObj(0);
996 Point aCenter = pObject->GetSnapRect().Center();
997 aView1.m_bGraphicViewSelection = false;
998 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
999 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
1000 Scheduler::ProcessEventsToIdle();
1001 // Make sure there is a graphic view selection on the first view.
1002 CPPUNIT_ASSERT(aView1.m_bGraphicViewSelection);
1004 // Now destroy the second view.
1005 SfxLokHelper::destroyView(nView2);
1006 Scheduler::ProcessEventsToIdle();
1007 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), SfxLokHelper::getViewsCount());
1008 // Make sure that the graphic view selection on the first view is cleaned up.
1009 CPPUNIT_ASSERT(!aView1.m_bGraphicViewSelection);
1010 mxComponent->dispose();
1011 mxComponent.clear();
1013 comphelper::LibreOfficeKit::setActive(false);
1016 void SwTiledRenderingTest::testViewLock()
1018 comphelper::LibreOfficeKit::setActive();
1020 // Load a document that has a shape and create two views.
1021 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
1022 ViewCallback aView1;
1023 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
1024 SfxLokHelper::createView();
1025 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1026 ViewCallback aView2;
1027 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
1029 // Begin text edit in the second view and assert that the first gets a lock
1030 // notification.
1031 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1032 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1033 SdrObject* pObject = pPage->GetObj(0);
1034 SdrView* pView = pWrtShell->GetDrawView();
1035 aView1.m_bViewLock = false;
1036 pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
1037 CPPUNIT_ASSERT(aView1.m_bViewLock);
1039 // End text edit in the second view, and assert that the lock is removed in
1040 // the first view.
1041 pWrtShell->EndTextEdit();
1042 CPPUNIT_ASSERT(!aView1.m_bViewLock);
1044 mxComponent->dispose();
1045 mxComponent.clear();
1047 comphelper::LibreOfficeKit::setActive(false);
1050 void SwTiledRenderingTest::testTextEditViewInvalidations()
1052 // Load a document that has a shape and create two views.
1053 comphelper::LibreOfficeKit::setActive();
1054 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
1055 ViewCallback aView1;
1056 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
1057 SfxLokHelper::createView();
1058 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1059 ViewCallback aView2;
1060 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
1062 // Begin text edit in the second view.
1063 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1064 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1065 SdrObject* pObject = pPage->GetObj(0);
1066 SdrView* pView = pWrtShell->GetDrawView();
1067 pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
1068 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1069 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1071 // Assert that both views are invalidated when pressing a key while in text edit.
1072 aView1.m_bTilesInvalidated = false;
1073 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'y', 0);
1074 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'y', 0);
1075 CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
1077 pWrtShell->EndTextEdit();
1078 mxComponent->dispose();
1079 mxComponent.clear();
1080 comphelper::LibreOfficeKit::setActive(false);
1083 void SwTiledRenderingTest::testUndoInvalidations()
1085 // Load a document and create two views.
1086 comphelper::LibreOfficeKit::setActive();
1087 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1088 ViewCallback aView1;
1089 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
1090 int nView1 = SfxLokHelper::getView();
1091 SfxLokHelper::createView();
1092 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1093 ViewCallback aView2;
1094 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
1095 SfxLokHelper::setView(nView1);
1097 // Insert a character the end of the document.
1098 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1099 pWrtShell->EndDoc();
1100 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', 0);
1101 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', 0);
1102 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
1103 CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb.c"), pShellCursor->GetPoint()->nNode.GetNode().GetTextNode()->GetText());
1105 // Undo and assert that both views are invalidated.
1106 aView1.m_bTilesInvalidated = false;
1107 aView2.m_bTilesInvalidated = false;
1108 comphelper::dispatchCommand(".uno:Undo", {});
1109 Scheduler::ProcessEventsToIdle();
1110 CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
1111 // Undo was dispatched on the first view, this second view was not invalidated.
1112 CPPUNIT_ASSERT(aView2.m_bTilesInvalidated);
1114 mxComponent->dispose();
1115 mxComponent.clear();
1116 comphelper::LibreOfficeKit::setActive(false);
1119 void SwTiledRenderingTest::testUndoLimiting()
1121 // Load a document and create two views.
1122 comphelper::LibreOfficeKit::setActive();
1123 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1124 SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
1125 SfxLokHelper::createView();
1126 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1128 // Insert a character the end of the document in the second view.
1129 SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
1130 pWrtShell2->EndDoc();
1131 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', 0);
1132 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', 0);
1133 SwShellCursor* pShellCursor = pWrtShell2->getShellCursor(false);
1134 CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb.c"), pShellCursor->GetPoint()->nNode.GetNode().GetTextNode()->GetText());
1136 // Assert that the first view can't undo, but the second view can.
1137 CPPUNIT_ASSERT(!pWrtShell1->GetLastUndoInfo(nullptr, nullptr, &pWrtShell1->GetView()));
1138 CPPUNIT_ASSERT(pWrtShell2->GetLastUndoInfo(nullptr, nullptr, &pWrtShell2->GetView()));
1140 comphelper::LibreOfficeKit::setActive(false);
1143 void SwTiledRenderingTest::testUndoShapeLimiting()
1145 // Load a document and create a view.
1146 comphelper::LibreOfficeKit::setActive();
1147 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
1148 SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
1149 SfxLokHelper::createView();
1150 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1151 SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
1153 // Start shape text in the second view.
1154 SdrPage* pPage = pWrtShell2->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1155 SdrObject* pObject = pPage->GetObj(0);
1156 SdrView* pView = pWrtShell2->GetDrawView();
1157 pWrtShell2->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell2->GetWin());
1158 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1159 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1161 // Assert that the first view can't and the second view can undo the insertion.
1162 SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
1163 sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
1164 rUndoManager.SetView(&pWrtShell1->GetView());
1165 // This was 1: first view could undo the change of the second view.
1166 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), rUndoManager.GetUndoActionCount());
1167 rUndoManager.SetView(&pWrtShell2->GetView());
1168 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
1170 pWrtShell2->EndTextEdit();
1171 rUndoManager.SetView(nullptr);
1172 comphelper::LibreOfficeKit::setActive(false);
1175 void SwTiledRenderingTest::testUndoDispatch()
1177 // Load a document and create two views.
1178 comphelper::LibreOfficeKit::setActive();
1179 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1180 int nView1 = SfxLokHelper::getView();
1181 SfxLokHelper::createView();
1182 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1183 int nView2 = SfxLokHelper::getView();
1185 // Insert a character in the first view.
1186 SfxLokHelper::setView(nView1);
1187 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', 0);
1188 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', 0);
1190 // Click before the first word in the second view.
1191 SfxLokHelper::setView(nView2);
1192 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1193 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
1194 Point aStart = pShellCursor->GetSttPos();
1195 aStart.setX(aStart.getX() - 1000);
1196 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
1197 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
1198 uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(comphelper::getProcessComponentContext());
1199 uno::Reference<frame::XFrame> xFrame2 = xDesktop->getActiveFrame();
1201 // Now switch back to the first view, and make sure that the active frame is updated.
1202 SfxLokHelper::setView(nView1);
1203 uno::Reference<frame::XFrame> xFrame1 = xDesktop->getActiveFrame();
1204 // This failed: setView() did not update the active frame.
1205 CPPUNIT_ASSERT(xFrame1 != xFrame2);
1207 comphelper::LibreOfficeKit::setActive(false);
1210 void SwTiledRenderingTest::testUndoRepairDispatch()
1212 // Load a document and create two views.
1213 comphelper::LibreOfficeKit::setActive();
1214 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1215 int nView1 = SfxLokHelper::getView();
1216 SfxLokHelper::createView();
1217 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1218 int nView2 = SfxLokHelper::getView();
1220 // Insert a character in the first view.
1221 SfxLokHelper::setView(nView1);
1222 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', 0);
1223 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', 0);
1225 // Assert that by default the second view can't undo the action.
1226 SfxLokHelper::setView(nView2);
1227 SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
1228 sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
1229 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
1230 comphelper::dispatchCommand(".uno:Undo", {});
1231 Scheduler::ProcessEventsToIdle();
1232 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
1234 // But the same is allowed in repair mode.
1235 SfxLokHelper::setView(nView2);
1236 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
1237 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
1239 {"Repair", uno::makeAny(true)}
1240 }));
1241 comphelper::dispatchCommand(".uno:Undo", aPropertyValues);
1242 Scheduler::ProcessEventsToIdle();
1243 // This was 1: repair mode couldn't undo the action, either.
1244 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), rUndoManager.GetUndoActionCount());
1246 comphelper::LibreOfficeKit::setActive(false);
1249 void SwTiledRenderingTest::testShapeTextUndoShells()
1251 // Load a document and create a view.
1252 comphelper::LibreOfficeKit::setActive();
1253 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
1254 sal_Int32 nView1 = SfxLokHelper::getView();
1256 // Begin text edit.
1257 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1258 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1259 SdrObject* pObject = pPage->GetObj(0);
1260 SdrView* pView = pWrtShell->GetDrawView();
1261 pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
1262 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1263 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1265 // Make sure that the undo item remembers who created it.
1266 SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
1267 sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
1268 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
1269 // This was -1: the view shell id for the undo action wasn't known.
1270 CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), rUndoManager.GetUndoAction()->GetViewShellId());
1272 mxComponent->dispose();
1273 mxComponent.clear();
1274 comphelper::LibreOfficeKit::setActive(false);
1277 void SwTiledRenderingTest::testShapeTextUndoGroupShells()
1279 // Load a document and create a view.
1280 comphelper::LibreOfficeKit::setActive();
1281 SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
1282 ViewCallback aView1;
1283 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
1284 sal_Int32 nView1 = SfxLokHelper::getView();
1286 // Begin text edit.
1287 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1288 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1289 SdrObject* pObject = pPage->GetObj(0);
1290 SdrView* pView = pWrtShell->GetDrawView();
1291 pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
1292 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1293 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1294 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::BACKSPACE);
1295 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::BACKSPACE);
1297 // Make sure that the undo item remembers who created it.
1298 SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
1299 sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
1300 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rUndoManager.GetUndoActionCount());
1301 // This was -1: the view shell id for the (top) undo list action wasn't known.
1302 CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), rUndoManager.GetUndoAction()->GetViewShellId());
1304 // Create an editeng text selection in the first view.
1305 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
1306 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1307 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1308 // 0th para, 0th char -> 0th para, 1st char.
1309 ESelection aWordSelection(0, 0, 0, 1);
1310 rEditView.SetSelection(aWordSelection);
1312 // Create a second view, and make sure that the new view sees the same
1313 // cursor position as the old one.
1314 SfxLokHelper::createView();
1315 pXTextDocument->initializeForTiledRendering({});
1316 ViewCallback aView2;
1317 aView2.m_aViewCursor = tools::Rectangle();
1318 aView2.m_bViewSelectionSet = false;
1319 aView2.m_bViewLock = false;
1320 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
1321 // Difference was 935 twips, the new view didn't see the editeng cursor of
1322 // the old one. The new difference should be <1px, but here we deal with twips.
1323 CPPUNIT_ASSERT(std::abs(aView1.m_aOwnCursor.Top() - aView2.m_aViewCursor.Top()) < 10);
1324 // This was false, editeng text selection of the first view wasn't noticed
1325 // by the second view.
1326 CPPUNIT_ASSERT(aView2.m_bViewSelectionSet);
1327 // This was false, the new view wasn't aware of the shape text lock created
1328 // by the old view.
1329 CPPUNIT_ASSERT(aView2.m_bViewLock);
1331 mxComponent->dispose();
1332 mxComponent.clear();
1333 comphelper::LibreOfficeKit::setActive(false);
1336 void SwTiledRenderingTest::testTrackChanges()
1338 // Load a document.
1339 comphelper::LibreOfficeKit::setActive();
1340 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1342 // Turn on trak changes, type "zzz" at the end, and move to the start.
1343 uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
1344 xPropertySet->setPropertyValue("RecordChanges", uno::makeAny(true));
1345 ViewCallback aView;
1346 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1347 pWrtShell->GetSfxViewShell()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView);
1348 pWrtShell->EndDoc();
1349 pWrtShell->Insert("zzz");
1350 pWrtShell->SttDoc();
1352 // Get the redline just created
1353 const SwRedlineTable& rTable = pWrtShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1354 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), rTable.size());
1355 SwRangeRedline* pRedline = rTable[0];
1357 // Reject the change by id, while the cursor does not cover the tracked change.
1358 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
1360 {"RejectTrackedChange", uno::makeAny(static_cast<sal_uInt16>(pRedline->GetId()))}
1361 }));
1362 comphelper::dispatchCommand(".uno:RejectTrackedChange", aPropertyValues);
1363 Scheduler::ProcessEventsToIdle();
1365 // Assert that the reject was performed.
1366 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
1367 // This was 'Aaa bbb.zzz', the change wasn't rejected.
1368 CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb."), pShellCursor->GetPoint()->nNode.GetNode().GetTextNode()->GetText());
1370 comphelper::LibreOfficeKit::setActive(false);
1373 void SwTiledRenderingTest::testTrackChangesCallback()
1375 // Load a document.
1376 comphelper::LibreOfficeKit::setActive();
1377 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1378 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1379 pWrtShell->GetSfxViewShell()->registerLibreOfficeKitViewCallback(&SwTiledRenderingTest::callback, this);
1381 // Turn on track changes and type "x".
1382 uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
1383 xPropertySet->setPropertyValue("RecordChanges", uno::makeAny(true));
1384 m_nRedlineTableSizeChanged = 0;
1385 pWrtShell->Insert("x");
1387 // Assert that we get exactly one notification about the redline insert.
1388 // This was 0, as LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED wasn't sent.
1389 CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableSizeChanged);
1391 CPPUNIT_ASSERT_EQUAL(-1, m_nTrackedChangeIndex);
1392 pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
1393 SfxItemSet aSet(pWrtShell->GetDoc()->GetAttrPool(), svl::Items<FN_REDLINE_ACCEPT_DIRECT, FN_REDLINE_ACCEPT_DIRECT>{});
1394 SfxVoidItem aItem(FN_REDLINE_ACCEPT_DIRECT);
1395 aSet.Put(aItem);
1396 pWrtShell->GetView().GetState(aSet);
1397 // This failed, LOK_CALLBACK_STATE_CHANGED wasn't sent.
1398 CPPUNIT_ASSERT_EQUAL(0, m_nTrackedChangeIndex);
1400 comphelper::LibreOfficeKit::setActive(false);
1403 void SwTiledRenderingTest::testRedlineUpdateCallback()
1405 // Load a document.
1406 comphelper::LibreOfficeKit::setActive();
1407 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1408 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1409 pWrtShell->GetSfxViewShell()->registerLibreOfficeKitViewCallback(&SwTiledRenderingTest::callback, this);
1411 // Turn on track changes, type "xx" and delete the second one.
1412 uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
1413 xPropertySet->setPropertyValue("RecordChanges", uno::makeAny(true));
1414 pWrtShell->Insert("xx");
1415 m_nRedlineTableEntryModified = 0;
1416 pWrtShell->DelLeft();
1418 // Assert that we get exactly one notification about the redline update.
1419 // This was 0, as LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED wasn't sent.
1420 CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableEntryModified);
1422 // Turn off the change tracking mode, make some modification to left of the
1423 // redline so that its position changes
1424 xPropertySet->setPropertyValue("RecordChanges", uno::makeAny(false));
1425 pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
1426 pWrtShell->Insert("This text is left of the redline");
1428 // Position of the redline has changed => Modify callback
1429 CPPUNIT_ASSERT_EQUAL(2, m_nRedlineTableEntryModified);
1431 pWrtShell->DelLeft();
1432 // Deletion also emits Modify callback
1433 CPPUNIT_ASSERT_EQUAL(3, m_nRedlineTableEntryModified);
1435 // Make changes to the right of the redline => no position change in redline
1436 pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 100/*Go enough right */, /*bBasicCall=*/false);
1437 pWrtShell->Insert("This text is right of the redline");
1439 // No Modify callbacks
1440 CPPUNIT_ASSERT_EQUAL(3, m_nRedlineTableEntryModified);
1442 comphelper::LibreOfficeKit::setActive(false);
1445 void SwTiledRenderingTest::testSetViewGraphicSelection()
1447 // Load a document.
1448 comphelper::LibreOfficeKit::setActive();
1449 SwXTextDocument* pXTextDocument = createDoc("frame.odt");
1450 int nView1 = SfxLokHelper::getView();
1451 ViewCallback aView1;
1452 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
1453 // Create a second view, and switch back to the first view.
1454 SfxLokHelper::createView();
1455 pXTextDocument->initializeForTiledRendering({});
1456 SfxLokHelper::setView(nView1);
1458 // Mark the textframe in the first view.
1459 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1460 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1461 SdrObject* pObject = pPage->GetObj(0);
1462 SdrView* pView = pWrtShell->GetDrawView();
1463 pView->MarkObj(pObject, pView->GetSdrPageView());
1464 CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
1466 // Now start to switch to the second view (part of setView()).
1467 pWrtShell->ShellLoseFocus();
1468 // This failed, mark handles were hidden in the first view.
1469 CPPUNIT_ASSERT(!pView->areMarkHandlesHidden());
1471 mxComponent->dispose();
1472 mxComponent.clear();
1473 comphelper::LibreOfficeKit::setActive(false);
1476 void SwTiledRenderingTest::testCreateViewGraphicSelection()
1478 // Load a document.
1479 comphelper::LibreOfficeKit::setActive();
1480 SwXTextDocument* pXTextDocument = createDoc("frame.odt");
1481 ViewCallback aView1;
1482 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
1484 // Mark the textframe in the first view.
1485 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1486 SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
1487 SdrObject* pObject = pPage->GetObj(0);
1488 SdrView* pView = pWrtShell->GetDrawView();
1489 aView1.m_bGraphicSelection = true;
1490 pView->MarkObj(pObject, pView->GetSdrPageView());
1491 pWrtShell->HideCursor();
1492 CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
1494 // Create a second view.
1495 SfxLokHelper::createView();
1496 // This was false, creating a second view cleared the selection of the
1497 // first one.
1498 CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
1500 // Make sure that the hidden text cursor isn't visible in the second view, either.
1501 ViewCallback aView2;
1502 aView2.m_bViewCursorVisible = true;
1503 aView2.m_bGraphicViewSelection = false;
1504 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
1505 // This was true, the second view didn't get the visibility of the text
1506 // cursor of the first view.
1507 CPPUNIT_ASSERT(!aView2.m_bViewCursorVisible);
1508 // This was false, the second view didn't get the graphic selection of the
1509 // first view.
1510 CPPUNIT_ASSERT(aView2.m_bGraphicViewSelection);
1512 mxComponent->dispose();
1513 mxComponent.clear();
1514 comphelper::LibreOfficeKit::setActive(false);
1517 void SwTiledRenderingTest::testCreateViewTextSelection()
1519 // Load a document.
1520 comphelper::LibreOfficeKit::setActive();
1521 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1522 ViewCallback aView1;
1523 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
1525 // Create a text selection:
1526 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1527 // Move the cursor into the second word.
1528 pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 5, /*bBasicCall=*/false);
1529 // Create a selection on the word.
1530 pWrtShell->SelWrd();
1531 SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
1532 // Did we indeed manage to select the second word?
1533 CPPUNIT_ASSERT_EQUAL(OUString("bbb"), pShellCursor->GetText());
1535 // Create a second view.
1536 SfxLokHelper::createView();
1538 // Make sure that the text selection is visible in the second view.
1539 ViewCallback aView2;
1540 aView2.m_bViewSelectionSet = true;
1541 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
1542 // This failed, the second view didn't get the text selection of the first view.
1543 CPPUNIT_ASSERT(!aView2.m_aViewSelection.isEmpty());
1545 mxComponent->dispose();
1546 mxComponent.clear();
1547 comphelper::LibreOfficeKit::setActive(false);
1550 void SwTiledRenderingTest::testRedlineColors()
1552 // Load a document.
1553 comphelper::LibreOfficeKit::setActive();
1554 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1556 // Turn on track changes, type "zzz" at the end.
1557 uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
1558 xPropertySet->setPropertyValue("RecordChanges", uno::makeAny(true));
1559 SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
1560 pWrtShell->EndDoc();
1561 pWrtShell->Insert("zzz");
1563 // Assert that info about exactly one author is returned.
1564 OUString aInfo = pXTextDocument->getTrackedChangeAuthors();
1565 std::stringstream aStream(aInfo.toUtf8().getStr());
1566 boost::property_tree::ptree aTree;
1567 boost::property_tree::read_json(aStream, aTree);
1568 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree.get_child("authors").size());
1570 comphelper::LibreOfficeKit::setActive(false);
1573 void SwTiledRenderingTest::testCommentEndTextEdit()
1575 // Create a document, type a character and remember the cursor position.
1576 comphelper::LibreOfficeKit::setActive();
1577 SwXTextDocument* pXTextDocument = createDoc();
1578 ViewCallback aView1;
1579 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
1580 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1581 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1582 tools::Rectangle aBodyCursor = aView1.m_aOwnCursor;
1584 // Create a comment and type a character there as well.
1585 const int nCtrlAltC = KEY_MOD1 + KEY_MOD2 + 512 + 'c' - 'a';
1586 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', nCtrlAltC);
1587 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', nCtrlAltC);
1588 Scheduler::ProcessEventsToIdle();
1589 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1590 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1592 // End comment text edit by clicking in the body text area, and assert that
1593 // no unexpected cursor callbacks are emitted at origin (top left corner of
1594 // the document).
1595 aView1.m_bOwnCursorAtOrigin = false;
1596 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aBodyCursor.getX(), aBodyCursor.getY(), 1, MOUSE_LEFT, 0);
1597 pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aBodyCursor.getX(), aBodyCursor.getY(), 1, MOUSE_LEFT, 0);
1598 // This failed, the cursor was at 0, 0 at some point during end text edit
1599 // of the comment.
1600 CPPUNIT_ASSERT(!aView1.m_bOwnCursorAtOrigin);
1602 // Hit enter and expect invalidation.
1603 Scheduler::ProcessEventsToIdle();
1604 aView1.m_bTilesInvalidated = false;
1605 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
1606 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
1607 Scheduler::ProcessEventsToIdle();
1608 CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
1610 mxComponent->dispose();
1611 mxComponent.clear();
1612 comphelper::LibreOfficeKit::setActive(false);
1615 void SwTiledRenderingTest::testCursorPosition()
1617 // Load a document and register a callback, should get an own cursor.
1618 comphelper::LibreOfficeKit::setActive();
1619 SwXTextDocument* pXTextDocument = createDoc();
1620 ViewCallback aView1;
1621 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
1623 // Crete a second view, so the first view gets a collaborative cursor.
1624 SfxLokHelper::createView();
1625 pXTextDocument->initializeForTiledRendering({});
1626 ViewCallback aView2;
1627 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
1629 // Make sure the two are exactly the same.
1630 // This failed, own cursor was at '1418, 1418', collaborative cursor was at
1631 // '1425, 1425', due to pixel alignment.
1632 CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor.toString(), aView1.m_aViewCursor.toString());
1634 mxComponent->dispose();
1635 mxComponent.clear();
1636 comphelper::LibreOfficeKit::setActive(false);
1639 void SwTiledRenderingTest::testPaintCallbacks()
1641 // Test that paintTile() never results in callbacks, which can cause a
1642 // paint <-> invalidate loop.
1644 // Load a document and register a callback for the first view.
1645 comphelper::LibreOfficeKit::setActive();
1646 SwXTextDocument* pXTextDocument = createDoc();
1647 ViewCallback aView1;
1648 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
1650 // Create a second view and paint a tile on that second view.
1651 SfxLokHelper::createView();
1652 int nCanvasWidth = 256;
1653 int nCanvasHeight = 256;
1654 std::vector<unsigned char> aBuffer(nCanvasWidth * nCanvasHeight * 4);
1655 ScopedVclPtrInstance<VirtualDevice> pDevice(nullptr, Size(1, 1), DeviceFormat::DEFAULT);
1656 pDevice->SetOutputSizePixelScaleOffsetAndBuffer(Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), aBuffer.data());
1657 // Make sure that painting a tile in the second view doesn't invoke
1658 // callbacks on the first view.
1659 aView1.m_bCalled = false;
1660 pXTextDocument->paintTile(*pDevice.get(), nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0, /*nTilePosY=*/0, /*nTileWidth=*/3840, /*nTileHeight=*/3840);
1661 CPPUNIT_ASSERT(!aView1.m_bCalled);
1663 mxComponent->dispose();
1664 mxComponent.clear();
1665 comphelper::LibreOfficeKit::setActive(false);
1668 void SwTiledRenderingTest::testUndoRepairResult()
1670 // Load a document and create two views.
1671 comphelper::LibreOfficeKit::setActive();
1672 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1673 int nView1 = SfxLokHelper::getView();
1674 SfxLokHelper::createView();
1675 TestResultListener* pResult2 = new TestResultListener();
1676 css::uno::Reference< css::frame::XDispatchResultListener > xListener(static_cast< css::frame::XDispatchResultListener* >(pResult2), css::uno::UNO_QUERY);
1677 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1678 int nView2 = SfxLokHelper::getView();
1680 // Insert a character in the second view.
1681 SfxLokHelper::setView(nView2);
1682 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'b', 0);
1683 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'b', 0);
1685 // Insert a character in the first view.
1686 SfxLokHelper::setView(nView1);
1687 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'a', 0);
1688 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'a', 0);
1690 // Assert that by default the second view can't undo the action.
1691 SfxLokHelper::setView(nView2);
1692 comphelper::dispatchCommand(".uno:Undo", {}, xListener);
1693 Scheduler::ProcessEventsToIdle();
1694 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pResult2->m_nDocRepair);
1696 mxComponent->dispose();
1697 mxComponent.clear();
1698 comphelper::LibreOfficeKit::setActive(false);
1701 void SwTiledRenderingTest::testRedoRepairResult()
1703 // Load a document and create two views.
1704 comphelper::LibreOfficeKit::setActive();
1705 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1706 int nView1 = SfxLokHelper::getView();
1707 SfxLokHelper::createView();
1708 TestResultListener* pResult2 = new TestResultListener();
1709 css::uno::Reference< css::frame::XDispatchResultListener > xListener(static_cast< css::frame::XDispatchResultListener* >(pResult2), css::uno::UNO_QUERY);
1710 pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1711 int nView2 = SfxLokHelper::getView();
1713 // Insert a character in the second view.
1714 SfxLokHelper::setView(nView2);
1715 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'b', 0);
1716 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'b', 0);
1718 // Insert a character in the first view.
1719 SfxLokHelper::setView(nView1);
1720 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'a', 0);
1721 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'a', 0);
1722 comphelper::dispatchCommand(".uno:Undo", {}, xListener);
1723 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(0), pResult2->m_nDocRepair);
1725 // Assert that by default the second view can't redo the action.
1726 SfxLokHelper::setView(nView2);
1727 comphelper::dispatchCommand(".uno:Redo", {}, xListener);
1728 Scheduler::ProcessEventsToIdle();
1729 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pResult2->m_nDocRepair);
1731 mxComponent->dispose();
1732 mxComponent.clear();
1733 comphelper::LibreOfficeKit::setActive(false);
1736 namespace {
1738 void checkUndoRepairStates(SwXTextDocument* pXTextDocument, SwView* pView1, SwView* pView2)
1740 SfxItemSet aItemSet1(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>{});
1741 SfxItemSet aItemSet2(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>{});
1742 // first view, undo enabled
1743 pView1->GetState(aItemSet1);
1744 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet1.GetItemState(SID_UNDO));
1745 const SfxUInt32Item *pUnsetItem = dynamic_cast<const SfxUInt32Item*>(aItemSet1.GetItem(SID_UNDO));
1746 CPPUNIT_ASSERT(!pUnsetItem);
1747 // second view, undo conflict
1748 pView2->GetState(aItemSet2);
1749 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet2.GetItemState(SID_UNDO));
1750 const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item*>(aItemSet2.GetItem(SID_UNDO));
1751 CPPUNIT_ASSERT(pUInt32Item);
1752 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pUInt32Item->GetValue());
1757 void SwTiledRenderingTest::testDisableUndoRepair()
1759 comphelper::LibreOfficeKit::setActive();
1761 // Create two views.
1762 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1763 ViewCallback aView1;
1764 SwView* pView1 = dynamic_cast<SwView*>(SfxViewShell::Current());
1765 CPPUNIT_ASSERT(pView1);
1766 int nView1 = SfxLokHelper::getView();
1767 SfxLokHelper::createView();
1768 ViewCallback aView2;
1769 SwView* pView2 = dynamic_cast<SwView*>(SfxViewShell::Current());
1770 CPPUNIT_ASSERT(pView2);
1771 int nView2 = SfxLokHelper::getView();
1774 SfxItemSet aItemSet1(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>{});
1775 SfxItemSet aItemSet2(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>{});
1776 pView1->GetState(aItemSet1);
1777 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, aItemSet1.GetItemState(SID_UNDO));
1778 pView2->GetState(aItemSet2);
1779 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, aItemSet2.GetItemState(SID_UNDO));
1782 // Insert a character in the first view.
1783 SfxLokHelper::setView(nView1);
1784 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'k', 0);
1785 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'k', 0);
1786 Scheduler::ProcessEventsToIdle();
1787 checkUndoRepairStates(pXTextDocument, pView1, pView2);
1789 // Insert a character in the second view.
1790 SfxLokHelper::setView(nView2);
1791 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'u', 0);
1792 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'u', 0);
1793 Scheduler::ProcessEventsToIdle();
1795 SfxItemSet aItemSet1(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>{});
1796 SfxItemSet aItemSet2(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>{});
1797 // second view, undo enabled
1798 pView2->GetState(aItemSet2);
1799 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet2.GetItemState(SID_UNDO));
1800 const SfxUInt32Item *pUnsetItem = dynamic_cast<const SfxUInt32Item*>(aItemSet2.GetItem(SID_UNDO));
1801 CPPUNIT_ASSERT(!pUnsetItem);
1802 // first view, undo conflict
1803 pView1->GetState(aItemSet1);
1804 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet1.GetItemState(SID_UNDO));
1805 const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item*>(aItemSet1.GetItem(SID_UNDO));
1806 CPPUNIT_ASSERT(pUInt32Item);
1807 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pUInt32Item->GetValue());
1810 // Insert a character in the first view.
1811 SfxLokHelper::setView(nView1);
1812 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'l', 0);
1813 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'l', 0);
1814 Scheduler::ProcessEventsToIdle();
1815 checkUndoRepairStates(pXTextDocument, pView1, pView2);
1817 mxComponent->dispose();
1818 mxComponent.clear();
1819 comphelper::LibreOfficeKit::setActive(false);
1822 void SwTiledRenderingTest::testAllTrackedChanges()
1824 // Load a document.
1825 comphelper::LibreOfficeKit::setActive();
1826 createDoc("dummy.fodt");
1828 uno::Reference<beans::XPropertySet> xPropSet(mxComponent, uno::UNO_QUERY);
1829 xPropSet->setPropertyValue("RecordChanges", uno::makeAny(true));
1831 // view #1
1832 SwView* pView1 = dynamic_cast<SwView*>(SfxViewShell::Current());
1833 CPPUNIT_ASSERT(pView1);
1834 SwWrtShell* pWrtShell1 = pView1->GetWrtShellPtr();
1836 // view #2
1837 SfxLokHelper::createView();
1838 SwView* pView2 = dynamic_cast<SwView*>(SfxViewShell::Current());
1839 CPPUNIT_ASSERT(pView2 && pView1 != pView2);
1840 SwWrtShell* pWrtShell2 = pView2->GetWrtShellPtr();
1841 // Insert text and reject all
1843 pWrtShell1->SttDoc();
1844 pWrtShell1->Insert("hxx");
1846 pWrtShell2->EndDoc();
1847 pWrtShell2->Insert("cxx");
1850 // Get the redline
1851 const SwRedlineTable& rTable = pWrtShell2->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1852 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(2), rTable.size());
1854 SfxVoidItem aItem(FN_REDLINE_REJECT_ALL);
1855 pView1->GetViewFrame()->GetDispatcher()->ExecuteList(FN_REDLINE_REJECT_ALL,
1856 SfxCallMode::SYNCHRON, { &aItem });
1859 // The reject all was performed.
1860 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(0), rTable.size());
1862 SwShellCursor* pShellCursor = pWrtShell1->getShellCursor(false);
1863 CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb."), pShellCursor->GetPoint()->nNode.GetNode().GetTextNode()->GetText());
1866 // Insert text and accept all
1868 pWrtShell1->SttDoc();
1869 pWrtShell1->Insert("hyy");
1871 pWrtShell2->EndDoc();
1872 pWrtShell2->Insert("cyy");
1875 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(2), rTable.size());
1877 SfxVoidItem aItem(FN_REDLINE_ACCEPT_ALL);
1878 pView1->GetViewFrame()->GetDispatcher()->ExecuteList(FN_REDLINE_ACCEPT_ALL,
1879 SfxCallMode::SYNCHRON, { &aItem });
1882 // The accept all was performed
1883 CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(0), rTable.size());
1885 SwShellCursor* pShellCursor = pWrtShell2->getShellCursor(false);
1886 CPPUNIT_ASSERT_EQUAL(OUString("hyyAaa bbb.cyy"), pShellCursor->GetPoint()->nNode.GetNode().GetTextNode()->GetText());
1889 comphelper::LibreOfficeKit::setActive(false);
1892 void SwTiledRenderingTest::testDocumentRepair()
1894 comphelper::LibreOfficeKit::setActive();
1896 // Create two views.
1897 SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
1898 // view #1
1899 SfxViewShell* pView1 = SfxViewShell::Current();
1901 // view #2
1902 SfxLokHelper::createView();
1903 SfxViewShell* pView2 = SfxViewShell::Current();
1904 int nView2 = SfxLokHelper::getView();
1905 CPPUNIT_ASSERT(pView1 != pView2);
1907 std::unique_ptr<SfxPoolItem> pItem1;
1908 std::unique_ptr<SfxPoolItem> pItem2;
1909 pView1->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, pItem1);
1910 pView2->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, pItem2);
1911 CPPUNIT_ASSERT(dynamic_cast< const SfxBoolItem* >(pItem1.get()));
1912 CPPUNIT_ASSERT(dynamic_cast< const SfxBoolItem* >(pItem2.get()));
1913 CPPUNIT_ASSERT_EQUAL(false, dynamic_cast< const SfxBoolItem* >(pItem1.get())->GetValue());
1914 CPPUNIT_ASSERT_EQUAL(false, dynamic_cast< const SfxBoolItem* >(pItem2.get())->GetValue());
1917 // Insert a character in the second view.
1918 SfxLokHelper::setView(nView2);
1919 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'u', 0);
1920 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'u', 0);
1921 Scheduler::ProcessEventsToIdle();
1923 std::unique_ptr<SfxPoolItem> pItem1;
1924 std::unique_ptr<SfxPoolItem> pItem2;
1925 pView1->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, pItem1);
1926 pView2->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, pItem2);
1927 CPPUNIT_ASSERT(dynamic_cast< const SfxBoolItem* >(pItem1.get()));
1928 CPPUNIT_ASSERT(dynamic_cast< const SfxBoolItem* >(pItem2.get()));
1929 CPPUNIT_ASSERT_EQUAL(true, dynamic_cast< const SfxBoolItem* >(pItem1.get())->GetValue());
1930 CPPUNIT_ASSERT_EQUAL(true, dynamic_cast< const SfxBoolItem* >(pItem2.get())->GetValue());
1933 comphelper::LibreOfficeKit::setActive(false);
1937 CPPUNIT_TEST_SUITE_REGISTRATION(SwTiledRenderingTest);
1939 CPPUNIT_PLUGIN_IMPLEMENT();
1941 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */