nss: upgrade to release 3.73
[LibreOffice.git] / sc / qa / unit / tiledrendering / tiledrendering.cxx
blobbb34928fef7da6df586745864abdb32b1968c789
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 <test/bootstrapfixture.hxx>
11 #include <test/helper/transferable.hxx>
12 #include <unotest/macros_test.hxx>
13 #include <test/xmltesttools.hxx>
14 #include <boost/property_tree/json_parser.hpp>
16 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
17 #include <com/sun/star/frame/Desktop.hpp>
18 #include <com/sun/star/frame/DispatchHelper.hpp>
19 #include <com/sun/star/datatransfer/clipboard/SystemClipboard.hpp>
20 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
21 #include <comphelper/dispatchcommand.hxx>
22 #include <comphelper/processfactory.hxx>
23 #include <comphelper/propertysequence.hxx>
24 #include <osl/conditn.hxx>
25 #include <sfx2/dispatch.hxx>
26 #include <sfx2/viewfrm.hxx>
27 #include <svl/stritem.hxx>
29 #include <comphelper/lok.hxx>
30 #include <comphelper/propertyvalue.hxx>
31 #include <sfx2/childwin.hxx>
32 #include <sfx2/lokhelper.hxx>
33 #include <svx/svdpage.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/scheduler.hxx>
36 #include <vcl/vclevent.hxx>
37 #include <vcl/virdev.hxx>
38 #include <vcl/unohelp2.hxx>
39 #include <sc.hrc>
40 #include <comphelper/string.hxx>
41 #include <tools/json_writer.hxx>
42 #include <docoptio.hxx>
44 #include <chrono>
45 #include <cstddef>
47 #include <tabvwsh.hxx>
48 #include <docsh.hxx>
49 #include <document.hxx>
50 #include <docuno.hxx>
51 #include <drwlayer.hxx>
53 using namespace css;
55 static std::ostream& operator<<(std::ostream& os, ViewShellId const & id)
57 os << static_cast<sal_Int32>(id); return os;
60 namespace
63 char const DATA_DIRECTORY[] = "/sc/qa/unit/tiledrendering/data/";
65 class ScTiledRenderingTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools
67 public:
68 ScTiledRenderingTest();
69 virtual void setUp() override;
70 virtual void tearDown() override;
72 void testRowColumnHeaders();
73 void testRowColumnSelections();
74 void testSortAscendingDescending();
75 void testPartHash();
76 void testDocumentSize();
77 void testEmptyColumnSelection();
78 void testViewCursors();
79 void testTextViewSelection();
80 void testDocumentSizeChanged();
81 void testViewLock();
82 void testColRowResize();
83 void testUndoShells();
84 void testCreateViewGraphicSelection();
85 void testTextEditViews();
86 void testTextEditViewInvalidations();
87 void testGraphicInvalidate();
88 void testAutoSum();
89 void testHideColRow();
90 void testInvalidateOnCopyPasteCells();
91 void testInvalidateOnInserRowCol();
92 void testCommentCallback();
93 void testUndoLimiting();
94 void testUndoRepairDispatch();
95 void testInsertGraphicInvalidations();
96 void testDocumentSizeWithTwoViews();
97 void testDisableUndoRepair();
98 void testDocumentRepair();
99 void testLanguageStatus();
100 void testMultiViewCopyPaste();
101 void testIMESupport();
102 void testFilterDlg();
103 void testVbaRangeCopyPaste();
104 void testInvalidationLoop();
105 void testPageDownInvalidation();
106 void testSheetChangeInvalidation();
107 void testInsertDeletePageInvalidation();
108 void testGetRowColumnHeadersInvalidation();
109 void testJumpHorizontallyInvalidation();
110 void testJumpToLastRowInvalidation();
111 void testSheetGeometryDataInvariance();
112 void testSheetGeometryDataCorrectness();
113 void testDeleteCellMultilineContent();
114 void testSpellOnlineParameter();
116 CPPUNIT_TEST_SUITE(ScTiledRenderingTest);
117 CPPUNIT_TEST(testRowColumnHeaders);
118 CPPUNIT_TEST(testRowColumnSelections);
119 CPPUNIT_TEST(testSortAscendingDescending);
120 CPPUNIT_TEST(testPartHash);
121 CPPUNIT_TEST(testDocumentSize);
122 CPPUNIT_TEST(testEmptyColumnSelection);
123 CPPUNIT_TEST(testViewCursors);
124 CPPUNIT_TEST(testTextViewSelection);
125 CPPUNIT_TEST(testDocumentSizeChanged);
126 CPPUNIT_TEST(testViewLock);
127 CPPUNIT_TEST(testColRowResize);
128 CPPUNIT_TEST(testUndoShells);
129 CPPUNIT_TEST(testCreateViewGraphicSelection);
130 CPPUNIT_TEST(testTextEditViews);
131 CPPUNIT_TEST(testTextEditViewInvalidations);
132 CPPUNIT_TEST(testGraphicInvalidate);
133 CPPUNIT_TEST(testAutoSum);
134 CPPUNIT_TEST(testHideColRow);
135 CPPUNIT_TEST(testInvalidateOnCopyPasteCells);
136 CPPUNIT_TEST(testInvalidateOnInserRowCol);
137 CPPUNIT_TEST(testCommentCallback);
138 CPPUNIT_TEST(testUndoLimiting);
139 CPPUNIT_TEST(testUndoRepairDispatch);
140 CPPUNIT_TEST(testInsertGraphicInvalidations);
141 CPPUNIT_TEST(testDocumentSizeWithTwoViews);
142 CPPUNIT_TEST(testDisableUndoRepair);
143 CPPUNIT_TEST(testDocumentRepair);
144 CPPUNIT_TEST(testLanguageStatus);
145 CPPUNIT_TEST(testMultiViewCopyPaste);
146 CPPUNIT_TEST(testIMESupport);
147 CPPUNIT_TEST(testFilterDlg);
148 CPPUNIT_TEST(testVbaRangeCopyPaste);
149 CPPUNIT_TEST(testInvalidationLoop);
150 CPPUNIT_TEST(testPageDownInvalidation);
151 CPPUNIT_TEST(testSheetChangeInvalidation);
152 CPPUNIT_TEST(testInsertDeletePageInvalidation);
153 CPPUNIT_TEST(testGetRowColumnHeadersInvalidation);
154 CPPUNIT_TEST(testJumpHorizontallyInvalidation);
155 CPPUNIT_TEST(testJumpToLastRowInvalidation);
156 CPPUNIT_TEST(testSheetGeometryDataInvariance);
157 CPPUNIT_TEST(testSheetGeometryDataCorrectness);
158 CPPUNIT_TEST(testDeleteCellMultilineContent);
159 CPPUNIT_TEST(testSpellOnlineParameter);
160 CPPUNIT_TEST_SUITE_END();
162 private:
163 ScModelObj* createDoc(const char* pName);
164 static void callback(int nType, const char* pPayload, void* pData);
165 void callbackImpl(int nType, const char* pPayload);
167 /// document size changed callback.
168 osl::Condition m_aDocSizeCondition;
169 Size m_aDocumentSize;
171 uno::Reference<lang::XComponent> mxComponent;
174 ScTiledRenderingTest::ScTiledRenderingTest()
178 void ScTiledRenderingTest::setUp()
180 test::BootstrapFixture::setUp();
182 mxDesktop.set(css::frame::Desktop::create(comphelper::getComponentContext(getMultiServiceFactory())));
185 void ScTiledRenderingTest::tearDown()
187 if (mxComponent.is())
189 ScModelObj* pModelObj = static_cast<ScModelObj*>(mxComponent.get());
190 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
191 if (pDocSh)
193 ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false);
194 if (pViewShell)
196 // The current view is unregistered here, multiple views have to be unregistered
197 // in the test function itself.
198 pViewShell->registerLibreOfficeKitViewCallback(nullptr, nullptr);
201 mxComponent->dispose();
203 comphelper::LibreOfficeKit::setActive(false);
205 test::BootstrapFixture::tearDown();
208 ScModelObj* ScTiledRenderingTest::createDoc(const char* pName)
210 if (mxComponent.is())
211 mxComponent->dispose();
212 mxComponent = loadFromDesktop(m_directories.getURLFromSrc(DATA_DIRECTORY) + OUString::createFromAscii(pName), "com.sun.star.sheet.SpreadsheetDocument");
213 ScModelObj* pModelObj = dynamic_cast<ScModelObj*>(mxComponent.get());
214 CPPUNIT_ASSERT(pModelObj);
215 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
216 return pModelObj;
219 void ScTiledRenderingTest::callback(int nType, const char* pPayload, void* pData)
221 static_cast<ScTiledRenderingTest*>(pData)->callbackImpl(nType, pPayload);
224 /* TODO when needed...
225 static std::vector<OUString> lcl_convertSeparated(const OUString& rString, sal_Unicode nSeparator)
227 std::vector<OUString> aRet;
229 sal_Int32 nIndex = 0;
232 OUString aToken = rString.getToken(0, nSeparator, nIndex);
233 aToken = aToken.trim();
234 if (!aToken.isEmpty())
235 aRet.push_back(aToken);
237 while (nIndex >= 0);
239 return aRet;
242 static void lcl_convertRectangle(const OUString& rString, Rectangle& rRectangle)
244 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(rString);
245 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength());
246 rRectangle.setX(aSeq[0].toInt32());
247 rRectangle.setY(aSeq[1].toInt32());
248 rRectangle.setWidth(aSeq[2].toInt32());
249 rRectangle.setHeight(aSeq[3].toInt32());
253 void ScTiledRenderingTest::callbackImpl(int nType, const char* pPayload)
255 switch (nType)
257 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
259 OString aPayload(pPayload);
260 sal_Int32 nIndex = 0;
261 OString aToken = aPayload.getToken(0, ',', nIndex);
262 m_aDocumentSize.setWidth(aToken.toInt32());
263 aToken = aPayload.getToken(0, ',', nIndex);
264 m_aDocumentSize.setHeight(aToken.toInt32());
265 m_aDocSizeCondition.set();
267 break;
271 void ScTiledRenderingTest::testRowColumnSelections()
273 comphelper::LibreOfficeKit::setActive();
274 ScModelObj* pModelObj = createDoc("select-row-cols.ods");
276 // Select the 5th row with no modifier
277 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
278 { "Row", uno::Any(sal_Int32(5 - 1)) },
279 { "Modifier", uno::Any(sal_uInt16(0)) }
280 }));
281 comphelper::dispatchCommand(".uno:SelectRow", aArgs);
283 // Check if it is selected
284 OString aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
285 OString aExpected("1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\n");
286 CPPUNIT_ASSERT_EQUAL(aExpected, aResult);
288 // Select the 10th row with shift modifier
289 aArgs[0].Name = "Row";
290 aArgs[0].Value <<= static_cast<sal_Int32>(10 - 1);
291 aArgs[1].Name = "Modifier";
292 aArgs[1].Value <<= KEY_SHIFT;
293 comphelper::dispatchCommand(".uno:SelectRow", aArgs);
295 // Check if all the rows from 5th to 10th get selected
296 aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
297 aExpected = "1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\n2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\t22\n3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\t22\t23\n4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\t22\t23\t24\n5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\t22\t23\t24\t25\n6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\t22\t23\t24\t25\t26\n";
298 CPPUNIT_ASSERT_EQUAL(aExpected, aResult);
300 // Select the 10th row with ctrl modifier
301 aArgs[0].Name = "Row";
302 aArgs[0].Value <<= static_cast<sal_Int32>(13 - 1);
303 aArgs[1].Name = "Modifier";
304 aArgs[1].Value <<= KEY_MOD1;
305 comphelper::dispatchCommand(".uno:SelectRow", aArgs);
307 // When we copy this, we don't get anything useful, but we must not crash
308 // (used to happen)
309 aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
310 CPPUNIT_ASSERT_EQUAL(OString(), aResult);
312 // TODO check that we really selected what we wanted here
314 // Select Column 5 with ctrl modifier
315 aArgs[0].Name = "Col";
316 aArgs[0].Value <<= static_cast<sal_Int32>(5 - 1);
317 aArgs[1].Name = "Modifier";
318 aArgs[1].Value <<= KEY_MOD1;
319 comphelper::dispatchCommand(".uno:SelectColumn", aArgs);
321 // When we copy this, we don't get anything useful, but we must not crash
322 // (used to happen)
323 aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
324 CPPUNIT_ASSERT_EQUAL(OString(), aResult);
326 // TODO check that we really selected what we wanted here
328 // Test for deselection of already selected rows
329 // First Deselect Row 13 because copy doesn't work for multiple selections
330 aArgs[0].Name = "Row";
331 aArgs[0].Value <<= static_cast<sal_Int32>(13 - 1);
332 aArgs[1].Name = "Modifier";
333 aArgs[1].Value <<= KEY_MOD1;
334 comphelper::dispatchCommand(".uno:SelectRow", aArgs);
336 // Deselect row 10
337 aArgs[0].Name = "Row";
338 aArgs[0].Value <<= static_cast<sal_Int32>(10 - 1);
339 aArgs[1].Name = "Modifier";
340 aArgs[1].Value <<= KEY_MOD1;
341 comphelper::dispatchCommand(".uno:SelectRow", aArgs);
343 // Click at row 6 holding shift
344 aArgs[0].Name = "Row";
345 aArgs[0].Value <<= static_cast<sal_Int32>(6 - 1);
346 aArgs[1].Name = "Modifier";
347 aArgs[1].Value <<= KEY_SHIFT;
348 comphelper::dispatchCommand(".uno:SelectRow", aArgs);
350 // only row 5 should remain selected
351 aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
352 aExpected = "1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\n";
353 CPPUNIT_ASSERT_EQUAL(aExpected, aResult);
356 void ScTiledRenderingTest::testSortAscendingDescending()
358 comphelper::LibreOfficeKit::setActive();
359 ScModelObj* pModelObj = createDoc("sort-range.ods");
360 ScDocument* pDoc = pModelObj->GetDocument();
362 // select the values in the first column
363 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, 551, 129, 1, MOUSE_LEFT, 0);
364 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEMOVE, 820, 1336, 1, MOUSE_LEFT, 0);
365 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, 820, 1359, 1, MOUSE_LEFT, 0);
366 Scheduler::ProcessEventsToIdle();
368 // sort ascending
369 uno::Sequence<beans::PropertyValue> aArgs;
370 comphelper::dispatchCommand(".uno:SortAscending", aArgs);
372 // check it's sorted
373 for (SCROW r = 0; r < 6; ++r)
375 CPPUNIT_ASSERT_EQUAL(double(r + 1), pDoc->GetValue(ScAddress(0, r, 0)));
378 // sort descending
379 comphelper::dispatchCommand(".uno:SortDescending", aArgs);
381 // check it's sorted
382 for (SCROW r = 0; r < 6; ++r)
384 CPPUNIT_ASSERT_EQUAL(double(6 - r), pDoc->GetValue(ScAddress(0, r, 0)));
387 // nothing else was sorted
388 CPPUNIT_ASSERT_EQUAL(double(1), pDoc->GetValue(ScAddress(1, 0, 0)));
389 CPPUNIT_ASSERT_EQUAL(double(3), pDoc->GetValue(ScAddress(1, 1, 0)));
390 CPPUNIT_ASSERT_EQUAL(double(2), pDoc->GetValue(ScAddress(1, 2, 0)));
393 void ScTiledRenderingTest::testPartHash()
395 comphelper::LibreOfficeKit::setActive();
396 ScModelObj* pModelObj = createDoc("sort-range.ods");
398 int nParts = pModelObj->getParts();
399 for (int it = 0; it < nParts; it++)
401 CPPUNIT_ASSERT(!pModelObj->getPartHash(it).isEmpty());
404 // check part that it does not exists
405 CPPUNIT_ASSERT(pModelObj->getPartHash(100).isEmpty());
408 void ScTiledRenderingTest::testDocumentSize()
410 comphelper::LibreOfficeKit::setActive();
411 ScModelObj* pModelObj = createDoc("sort-range.ods");
412 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
413 CPPUNIT_ASSERT(pDocSh);
415 ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false);
416 CPPUNIT_ASSERT(pViewShell);
418 pViewShell->registerLibreOfficeKitViewCallback(&ScTiledRenderingTest::callback, this);
420 // check initial document size
421 Size aDocSize = pModelObj->getDocumentSize();
422 CPPUNIT_ASSERT(aDocSize.Width() > 0);
423 CPPUNIT_ASSERT(aDocSize.Height() > 0);
425 // Set cursor column
426 pViewShell->SetCursor(100, 0);
427 // 2 seconds
428 osl::Condition::Result aResult = m_aDocSizeCondition.wait(std::chrono::seconds(2));
429 CPPUNIT_ASSERT_EQUAL(osl::Condition::result_ok, aResult);
431 // Set cursor row
432 pViewShell->SetCursor(0, 100);
433 // 2 seconds
434 aResult = m_aDocSizeCondition.wait(std::chrono::seconds(2));
435 CPPUNIT_ASSERT_EQUAL(osl::Condition::result_ok, aResult);
438 void ScTiledRenderingTest::testEmptyColumnSelection()
440 comphelper::LibreOfficeKit::setActive();
441 ScModelObj* pModelObj = createDoc("select-row-cols.ods");
443 // Select empty column, 1000
444 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
445 { "Col", uno::Any(sal_Int32(1000 - 1)) },
446 { "Modifier", uno::Any(sal_uInt16(0)) }
447 }));
448 comphelper::dispatchCommand(".uno:SelectColumn", aArgs);
450 // should be an empty string
451 CPPUNIT_ASSERT_EQUAL(OString(), apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8"));
454 /// A view callback tracks callbacks invoked on one specific view.
455 class ViewCallback final
457 SfxViewShell* mpViewShell;
458 int mnView;
459 public:
460 bool m_bOwnCursorInvalidated;
461 bool m_bViewCursorInvalidated;
462 bool m_bTextViewSelectionInvalidated;
463 bool m_bGraphicSelection;
464 bool m_bGraphicViewSelection;
465 bool m_bFullInvalidateTiles;
466 bool m_bInvalidateTiles;
467 std::vector<tools::Rectangle> m_aInvalidations;
468 std::vector<int> m_aInvalidationsParts;
469 bool m_bViewLock;
470 OString m_sCellFormula;
471 boost::property_tree::ptree m_aCommentCallbackResult;
472 OString m_sInvalidateHeader;
474 ViewCallback(bool bDeleteListenerOnDestruct=true)
475 : m_bOwnCursorInvalidated(false),
476 m_bViewCursorInvalidated(false),
477 m_bTextViewSelectionInvalidated(false),
478 m_bGraphicSelection(false),
479 m_bGraphicViewSelection(false),
480 m_bFullInvalidateTiles(false),
481 m_bInvalidateTiles(false),
482 m_bViewLock(false)
484 mpViewShell = SfxViewShell::Current();
485 mpViewShell->registerLibreOfficeKitViewCallback(&ViewCallback::callback, this);
486 mnView = SfxLokHelper::getView();
487 if (!bDeleteListenerOnDestruct)
488 mpViewShell = nullptr;
491 ~ViewCallback()
493 if (mpViewShell)
495 SfxLokHelper::setView(mnView);
496 mpViewShell->registerLibreOfficeKitViewCallback(nullptr, nullptr);
500 static void callback(int nType, const char* pPayload, void* pData)
502 static_cast<ViewCallback*>(pData)->callbackImpl(nType, pPayload);
505 void callbackImpl(int nType, const char* pPayload)
507 switch (nType)
509 case LOK_CALLBACK_CELL_CURSOR:
511 m_bOwnCursorInvalidated = true;
513 break;
514 case LOK_CALLBACK_CELL_VIEW_CURSOR:
516 m_bViewCursorInvalidated = true;
518 break;
519 case LOK_CALLBACK_TEXT_VIEW_SELECTION:
521 m_bTextViewSelectionInvalidated = true;
523 break;
524 case LOK_CALLBACK_VIEW_LOCK:
526 std::stringstream aStream(pPayload);
527 boost::property_tree::ptree aTree;
528 boost::property_tree::read_json(aStream, aTree);
529 m_bViewLock = aTree.get_child("rectangle").get_value<std::string>() != "EMPTY";
531 break;
532 case LOK_CALLBACK_GRAPHIC_SELECTION:
534 m_bGraphicSelection = true;
536 break;
537 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
539 m_bGraphicViewSelection = true;
541 break;
542 case LOK_CALLBACK_INVALIDATE_TILES:
544 OString text(pPayload);
545 if (text.startsWith("EMPTY"))
547 m_bFullInvalidateTiles = true;
549 else
551 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload));
552 CPPUNIT_ASSERT(aSeq.getLength() == 4 || aSeq.getLength() == 5);
553 tools::Rectangle aInvalidationRect;
554 aInvalidationRect.setX(aSeq[0].toInt32());
555 aInvalidationRect.setY(aSeq[1].toInt32());
556 aInvalidationRect.setWidth(aSeq[2].toInt32());
557 aInvalidationRect.setHeight(aSeq[3].toInt32());
558 m_aInvalidations.push_back(aInvalidationRect);
559 if (aSeq.getLength() == 5)
560 m_aInvalidationsParts.push_back(aSeq[4].toInt32());
561 m_bInvalidateTiles = true;
564 break;
565 case LOK_CALLBACK_CELL_FORMULA:
567 m_sCellFormula = pPayload;
569 break;
570 case LOK_CALLBACK_COMMENT:
572 m_aCommentCallbackResult.clear();
573 std::stringstream aStream(pPayload);
574 boost::property_tree::read_json(aStream, m_aCommentCallbackResult);
575 m_aCommentCallbackResult = m_aCommentCallbackResult.get_child("comment");
577 break;
578 case LOK_CALLBACK_INVALIDATE_HEADER:
580 m_sInvalidateHeader = pPayload;
587 void ScTiledRenderingTest::testViewCursors()
589 comphelper::LibreOfficeKit::setActive();
591 ScModelObj* pModelObj = createDoc("select-row-cols.ods");
592 ViewCallback aView1;
593 SfxLokHelper::createView();
594 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
595 ViewCallback aView2(/*bDeleteListenerOnDestruct*/false);
596 // This was false, the new view did not get the view (cell) cursor of the old view.
597 CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
598 CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
599 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN);
600 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN);
601 Scheduler::ProcessEventsToIdle();
602 SfxLokHelper::destroyView(SfxLokHelper::getView());
603 CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
606 void ScTiledRenderingTest::testTextViewSelection()
608 comphelper::LibreOfficeKit::setActive();
610 // Create two views, and leave the second one current.
611 ScModelObj* pModelObj = createDoc("select-row-cols.ods");
612 ViewCallback aView1;
613 SfxLokHelper::createView();
614 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
615 ViewCallback aView2;
617 // Create a selection on two cells in the second view, that's a text selection in LOK terms.
618 aView1.m_bTextViewSelectionInvalidated = false;
619 dispatchCommand(mxComponent, ".uno:GoRightSel", {});
620 Scheduler::ProcessEventsToIdle();
621 // Make sure the first view got its notification.
622 CPPUNIT_ASSERT(aView1.m_bTextViewSelectionInvalidated);
625 void ScTiledRenderingTest::testDocumentSizeChanged()
627 comphelper::LibreOfficeKit::setActive();
629 // Load a document that doesn't have much content.
630 createDoc("small.ods");
631 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ScTiledRenderingTest::callback, this);
633 // Go to the A30 cell -- that will extend the document size.
634 uno::Sequence<beans::PropertyValue> aPropertyValues =
636 comphelper::makePropertyValue("ToPoint", OUString("$A$30")),
638 dispatchCommand(mxComponent, ".uno:GoToCell", aPropertyValues);
639 Scheduler::ProcessEventsToIdle();
640 // Assert that the size in the payload is not 0.
641 CPPUNIT_ASSERT(m_aDocumentSize.getWidth() > 0);
642 CPPUNIT_ASSERT(m_aDocumentSize.getHeight() > 0);
645 void ScTiledRenderingTest::testViewLock()
647 comphelper::LibreOfficeKit::setActive();
649 // Load a document that has a shape and create two views.
650 ScModelObj* pModelObj = createDoc("shape.ods");
651 ViewCallback aView1;
652 SfxLokHelper::createView();
653 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
654 ViewCallback aView2;
656 // Begin text edit in the second view and assert that the first gets a lock
657 // notification.
658 const ScViewData* pViewData = ScDocShell::GetViewData();
659 ScTabViewShell* pViewShell = pViewData->GetViewShell();
660 CPPUNIT_ASSERT(pViewShell);
661 SdrModel* pDrawModel = pViewData->GetDocument().GetDrawLayer();
662 SdrPage* pDrawPage = pDrawModel->GetPage(0);
663 SdrObject* pObject = pDrawPage->GetObj(0);
664 SdrView* pView = pViewShell->GetScDrawView();
665 aView1.m_bViewLock = false;
666 pView->SdrBeginTextEdit(pObject);
667 CPPUNIT_ASSERT(aView1.m_bViewLock);
669 // End text edit in the second view, and assert that the lock is removed in
670 // the first view.
671 pView->SdrEndTextEdit();
672 CPPUNIT_ASSERT(!aView1.m_bViewLock);
675 void ScTiledRenderingTest::testColRowResize()
677 comphelper::LibreOfficeKit::setActive();
678 ScModelObj* pModelObj = createDoc("sort-range.ods");
679 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
680 CPPUNIT_ASSERT(pDocSh);
682 ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false);
683 CPPUNIT_ASSERT(pViewShell);
685 pViewShell->registerLibreOfficeKitViewCallback(&ScTiledRenderingTest::callback, this);
687 ScDocument& rDoc = pDocSh->GetDocument();
689 // Col 3, Tab 0
690 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
691 { "ColumnWidth", uno::Any(sal_uInt16(4000)) }, // 4cm
692 { "Column", uno::Any(sal_Int16(3)) }
693 }));
694 comphelper::dispatchCommand(".uno:ColumnWidth", aArgs);
696 sal_uInt16 nWidth = rDoc.GetColWidth(static_cast<SCCOL>(2), static_cast<SCTAB>(0), false) * HMM_PER_TWIPS;
697 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(4000), nWidth);
699 // Row 5, Tab 0
700 uno::Sequence<beans::PropertyValue> aArgs2( comphelper::InitPropertySequence({
701 { "RowHeight", uno::Any(sal_uInt16(2000)) },
702 { "Row", uno::Any(sal_Int16(5)) },
703 }));
704 comphelper::dispatchCommand(".uno:RowHeight", aArgs2);
706 sal_uInt16 nHeight = rDoc.GetRowHeight(static_cast<SCROW>(4), static_cast<SCTAB>(0), false) * HMM_PER_TWIPS;
707 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(2000), nHeight);
710 void ScTiledRenderingTest::testUndoShells()
712 comphelper::LibreOfficeKit::setActive();
713 ScModelObj* pModelObj = createDoc("small.ods");
714 // Clear the currently selected cell.
715 comphelper::dispatchCommand(".uno:ClearContents", {});
717 auto pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
718 CPPUNIT_ASSERT(pDocShell);
719 ScDocument& rDoc = pDocShell->GetDocument();
720 SfxUndoManager* pUndoManager = rDoc.GetUndoManager();
721 CPPUNIT_ASSERT(pUndoManager);
722 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pUndoManager->GetUndoActionCount());
723 sal_Int32 nView1 = SfxLokHelper::getView();
724 // This was -1: ScSimpleUndo did not remember what view shell created it.
725 CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), pUndoManager->GetUndoAction()->GetViewShellId());
728 bool lcl_hasEditView(const ScViewData& rViewData)
730 bool bResult = false;
731 for (unsigned int i=0; i<4; i++)
733 bResult = rViewData.HasEditView( static_cast<ScSplitPos>(i) );
734 if (bResult) break;
736 return bResult;
739 void ScTiledRenderingTest::testTextEditViews()
741 comphelper::LibreOfficeKit::setActive();
743 ScModelObj* pModelObj = createDoc("small.ods");
744 CPPUNIT_ASSERT(pModelObj);
745 ScViewData* pViewData = ScDocShell::GetViewData();
746 CPPUNIT_ASSERT(pViewData);
748 // view #1
749 ViewCallback aView1;
750 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
752 // text edit a cell in view #1
753 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
754 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
755 Scheduler::ProcessEventsToIdle();
756 CPPUNIT_ASSERT(lcl_hasEditView(*pViewData));
758 // view #2
759 SfxLokHelper::createView();
760 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
761 ViewCallback aView2;
763 // move cell cursor i view #2
764 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN);
765 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN);
766 Scheduler::ProcessEventsToIdle();
768 // check that text edit view in view #1 has not be killed
769 CPPUNIT_ASSERT(lcl_hasEditView(*pViewData));
772 void ScTiledRenderingTest::testTextEditViewInvalidations()
774 comphelper::LibreOfficeKit::setActive();
776 ScModelObj* pModelObj = createDoc("small.ods");
777 CPPUNIT_ASSERT(pModelObj);
778 ScViewData* pViewData = ScDocShell::GetViewData();
779 CPPUNIT_ASSERT(pViewData);
781 // view #1
782 int nView1 = SfxLokHelper::getView();
783 ViewCallback aView1;
784 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
786 // view #2
787 SfxLokHelper::createView();
788 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
789 ViewCallback aView2;
791 // text edit a cell in view #1
792 SfxLokHelper::setView(nView1);
793 aView2.m_bInvalidateTiles = false;
794 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
795 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
796 Scheduler::ProcessEventsToIdle();
797 CPPUNIT_ASSERT(lcl_hasEditView(*pViewData));
798 CPPUNIT_ASSERT(aView2.m_bInvalidateTiles);
800 // text edit a cell in view #1 until
801 // we can be sure we are out of the initial tile
802 for (int i = 0; i < 40; ++i)
804 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
805 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
807 Scheduler::ProcessEventsToIdle();
809 // text edit a cell in view #1 inside the new tile and
810 // check that view #2 receive a tile invalidate message
811 aView2.m_bInvalidateTiles = false;
812 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
813 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
814 Scheduler::ProcessEventsToIdle();
815 CPPUNIT_ASSERT(aView2.m_bInvalidateTiles);
817 // view #3
818 SfxLokHelper::createView();
819 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
820 ViewCallback aView3;
822 // text edit a cell in view #1
823 SfxLokHelper::setView(nView1);
824 aView3.m_bInvalidateTiles = false;
825 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'y', 0);
826 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'y', 0);
827 Scheduler::ProcessEventsToIdle();
828 CPPUNIT_ASSERT(aView3.m_bInvalidateTiles);
831 void ScTiledRenderingTest::testCreateViewGraphicSelection()
833 // Load a document
834 comphelper::LibreOfficeKit::setActive();
836 // Load a document that has a shape and create two views.
837 ScModelObj* pModelObj = createDoc("shape.ods");
838 ViewCallback aView1;
840 // Mark the graphic in the first view.
841 const ScViewData* pViewData = ScDocShell::GetViewData();
842 ScTabViewShell* pViewShell = pViewData->GetViewShell();
843 CPPUNIT_ASSERT(pViewShell);
844 SdrModel* pDrawModel = pViewData->GetDocument().GetDrawLayer();
845 SdrPage* pDrawPage = pDrawModel->GetPage(0);
846 SdrObject* pObject = pDrawPage->GetObj(0);
847 SdrView* pView = pViewShell->GetScDrawView();
848 aView1.m_bGraphicSelection = false;
849 aView1.m_bGraphicViewSelection = false;
850 pView->MarkObj(pObject, pView->GetSdrPageView());
851 CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
853 // Create a second view.
854 int nView1 = SfxLokHelper::getView();
855 SfxLokHelper::createView();
856 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
857 ViewCallback aView2;
858 CPPUNIT_ASSERT(aView2.m_bGraphicViewSelection);
859 CPPUNIT_ASSERT(aView1.m_bGraphicViewSelection);
861 SfxLokHelper::setView(nView1);
862 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
865 void ScTiledRenderingTest::testGraphicInvalidate()
867 // Load a document
868 comphelper::LibreOfficeKit::setActive();
870 // Load a document that has a shape and create two views.
871 ScModelObj* pModelObj = createDoc("shape.ods");
872 ViewCallback aView;
874 // Click to select graphic
875 aView.m_bGraphicSelection = false;
876 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, /*x=*/ 1,/*y=*/ 1,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
877 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, /*x=*/ 1, /*y=*/ 1, /*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
878 Scheduler::ProcessEventsToIdle();
879 CPPUNIT_ASSERT(aView.m_bGraphicSelection);
881 // Drag Drop graphic
882 aView.m_bGraphicSelection = false;
883 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, /*x=*/ 1,/*y=*/ 1,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
884 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEMOVE, /*x=*/ 1,/*y=*/ 10,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
885 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, /*x=*/ 1, /*y=*/ 10, /*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
886 Scheduler::ProcessEventsToIdle();
887 CPPUNIT_ASSERT(!aView.m_bFullInvalidateTiles);
889 // Check again
890 Scheduler::ProcessEventsToIdle();
891 CPPUNIT_ASSERT(!aView.m_bFullInvalidateTiles);
894 void ScTiledRenderingTest::testAutoSum()
896 // Load a document
897 comphelper::LibreOfficeKit::setActive();
898 createDoc("small.ods");
900 ViewCallback aView;
902 uno::Sequence<beans::PropertyValue> aArgs;
903 comphelper::dispatchCommand(".uno:AutoSum", aArgs);
904 Scheduler::ProcessEventsToIdle();
905 CPPUNIT_ASSERT(aView.m_sCellFormula.startsWith("=SUM("));
908 void ScTiledRenderingTest::testHideColRow()
910 // Load a document
911 comphelper::LibreOfficeKit::setActive();
912 createDoc("small.ods");
914 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
915 { "Col", uno::Any(sal_Int32(2 - 1)) },
916 { "Modifier", uno::Any(KEY_SHIFT) }
917 }));
918 comphelper::dispatchCommand(".uno:SelectColumn", aArgs);
920 uno::Sequence<beans::PropertyValue> aArgs2( comphelper::InitPropertySequence({
921 { "Col", uno::Any(sal_Int32(3 - 1)) },
922 { "Modifier", uno::Any(sal_uInt16(0)) }
923 }));
925 comphelper::dispatchCommand(".uno:SelectColumn", aArgs2);
926 Scheduler::ProcessEventsToIdle();
929 SCCOL nOldCurX = ScDocShell::GetViewData()->GetCurX();
930 SCROW nOldCurY = ScDocShell::GetViewData()->GetCurY();
932 uno::Sequence<beans::PropertyValue> aArgs;
933 comphelper::dispatchCommand(".uno:HideColumn", aArgs);
934 Scheduler::ProcessEventsToIdle();
937 SCCOL nNewCurX = ScDocShell::GetViewData()->GetCurX();
938 SCROW nNewCurY = ScDocShell::GetViewData()->GetCurY();
939 CPPUNIT_ASSERT(nNewCurX > nOldCurX);
940 CPPUNIT_ASSERT_EQUAL(nOldCurY, nNewCurY);
942 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
943 { "Row", uno::Any(sal_Int32(6 - 1)) },
944 { "Modifier", uno::Any(KEY_SHIFT) }
945 }));
946 comphelper::dispatchCommand(".uno:SelectRow", aArgs);
948 uno::Sequence<beans::PropertyValue> aArgs2( comphelper::InitPropertySequence({
949 { "Row", uno::Any(sal_Int32(7 - 1)) },
950 { "Modifier", uno::Any(sal_uInt16(0)) }
951 }));
952 comphelper::dispatchCommand(".uno:SelectRow", aArgs2);
953 Scheduler::ProcessEventsToIdle();
956 nOldCurX = ScDocShell::GetViewData()->GetCurX();
957 nOldCurY = ScDocShell::GetViewData()->GetCurY();
959 uno::Sequence<beans::PropertyValue> aArgs;
960 comphelper::dispatchCommand(".uno:HideRow", aArgs);
961 Scheduler::ProcessEventsToIdle();
963 nNewCurX = ScDocShell::GetViewData()->GetCurX();
964 nNewCurY = ScDocShell::GetViewData()->GetCurY();
965 CPPUNIT_ASSERT(nNewCurY > nOldCurY);
966 CPPUNIT_ASSERT_EQUAL(nOldCurX, nNewCurX);
969 void ScTiledRenderingTest::testInvalidateOnCopyPasteCells()
971 // Load a document
972 comphelper::LibreOfficeKit::setActive();
973 ScModelObj* pModelObj = createDoc("small.ods");
974 CPPUNIT_ASSERT(pModelObj);
976 // view
977 ViewCallback aView;
979 uno::Sequence<beans::PropertyValue> aArgs;
980 // select and copy cells
981 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
982 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
983 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_SHIFT);
984 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_SHIFT);
985 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT | KEY_SHIFT);
986 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT | KEY_SHIFT);
987 Scheduler::ProcessEventsToIdle();
988 comphelper::dispatchCommand(".uno:Copy", aArgs);
990 // move to destination cell
991 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
992 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
993 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_MOD1);
994 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_MOD1);
995 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_UP);
996 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_UP);
997 Scheduler::ProcessEventsToIdle();
999 // paste cells
1000 aView.m_bInvalidateTiles = false;
1001 comphelper::dispatchCommand(".uno:Paste", aArgs);
1002 Scheduler::ProcessEventsToIdle();
1003 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1006 void ScTiledRenderingTest::testInvalidateOnInserRowCol()
1008 // Load a document
1009 comphelper::LibreOfficeKit::setActive();
1010 ScModelObj* pModelObj = createDoc("small.ods");
1011 CPPUNIT_ASSERT(pModelObj);
1013 // view
1014 ViewCallback aView;
1016 uno::Sequence<beans::PropertyValue> aArgs;
1017 // move downward
1018 for (int i = 0; i < 200; ++i)
1020 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
1021 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
1023 Scheduler::ProcessEventsToIdle();
1025 // insert row
1026 aView.m_bInvalidateTiles = false;
1027 aView.m_aInvalidations.clear();
1028 comphelper::dispatchCommand(".uno:InsertRows", aArgs);
1029 Scheduler::ProcessEventsToIdle();
1030 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1031 CPPUNIT_ASSERT_EQUAL(size_t(2), aView.m_aInvalidations.size());
1032 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(-75, 50985, 32212230, 63990), aView.m_aInvalidations[0]);
1034 // move on the right
1035 for (int i = 0; i < 200; ++i)
1037 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT);
1038 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT);
1040 Scheduler::ProcessEventsToIdle();
1042 // insert column
1043 aView.m_bInvalidateTiles = false;
1044 aView.m_aInvalidations.clear();
1045 comphelper::dispatchCommand(".uno:InsertColumns", aArgs);
1046 Scheduler::ProcessEventsToIdle();
1047 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1048 CPPUNIT_ASSERT_EQUAL(size_t(2), aView.m_aInvalidations.size());
1049 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(253650, -15, 32212230, 63990), aView.m_aInvalidations[0]);
1052 void ScTiledRenderingTest::testCommentCallback()
1054 // Load a document
1055 comphelper::LibreOfficeKit::setActive();
1056 // Comments callback are emitted only if tiled annotations are off
1057 comphelper::LibreOfficeKit::setTiledAnnotations(false);
1060 ScModelObj* pModelObj = createDoc("small.ods");
1061 ViewCallback aView1;
1062 int nView1 = SfxLokHelper::getView();
1064 // Create a 2nd view
1065 SfxLokHelper::createView();
1066 pModelObj->initializeForTiledRendering({});
1067 ViewCallback aView2;
1069 SfxLokHelper::setView(nView1);
1071 // Add a new comment
1072 uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
1074 {"Text", uno::makeAny(OUString("Comment"))},
1075 {"Author", uno::makeAny(OUString("LOK User1"))},
1076 }));
1077 comphelper::dispatchCommand(".uno:InsertAnnotation", aArgs);
1078 Scheduler::ProcessEventsToIdle();
1080 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
1081 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1082 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
1083 CPPUNIT_ASSERT_EQUAL(std::string("1"), aView1.m_aCommentCallbackResult.get<std::string>("id"));
1084 CPPUNIT_ASSERT_EQUAL(std::string("1"), aView2.m_aCommentCallbackResult.get<std::string>("id"));
1085 CPPUNIT_ASSERT_EQUAL(std::string("0"), aView1.m_aCommentCallbackResult.get<std::string>("tab"));
1086 CPPUNIT_ASSERT_EQUAL(std::string("0"), aView2.m_aCommentCallbackResult.get<std::string>("tab"));
1087 CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), aView1.m_aCommentCallbackResult.get<std::string>("author"));
1088 CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), aView2.m_aCommentCallbackResult.get<std::string>("author"));
1089 CPPUNIT_ASSERT_EQUAL(std::string("Comment"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
1090 CPPUNIT_ASSERT_EQUAL(std::string("Comment"), aView2.m_aCommentCallbackResult.get<std::string>("text"));
1091 CPPUNIT_ASSERT_EQUAL(std::string("0, 255, 1274, 254"), aView1.m_aCommentCallbackResult.get<std::string>("cellPos"));
1092 CPPUNIT_ASSERT_EQUAL(std::string("0, 255, 1274, 254"), aView2.m_aCommentCallbackResult.get<std::string>("cellPos"));
1094 std::string aCommentId = aView1.m_aCommentCallbackResult.get<std::string>("id");
1096 // Edit a comment
1097 // Select some random cell, we should be able to edit the cell note without
1098 // selecting the cell
1099 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
1100 if (pTabViewShell)
1101 pTabViewShell->SetCursor(3, 100);
1102 aArgs = comphelper::InitPropertySequence(
1104 {"Id", uno::makeAny(OUString::createFromAscii(aCommentId.c_str()))},
1105 {"Text", uno::makeAny(OUString("Edited comment"))},
1106 {"Author", uno::makeAny(OUString("LOK User2"))},
1108 comphelper::dispatchCommand(".uno:EditAnnotation", aArgs);
1109 Scheduler::ProcessEventsToIdle();
1111 // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action
1112 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1113 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
1114 CPPUNIT_ASSERT_EQUAL(aCommentId, aView1.m_aCommentCallbackResult.get<std::string>("id"));
1115 CPPUNIT_ASSERT_EQUAL(aCommentId, aView2.m_aCommentCallbackResult.get<std::string>("id"));
1116 CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), aView1.m_aCommentCallbackResult.get<std::string>("author"));
1117 CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), aView2.m_aCommentCallbackResult.get<std::string>("author"));
1118 CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
1119 CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView2.m_aCommentCallbackResult.get<std::string>("text"));
1120 CPPUNIT_ASSERT_EQUAL(std::string("0, 255, 1274, 254"), aView1.m_aCommentCallbackResult.get<std::string>("cellPos"));
1121 CPPUNIT_ASSERT_EQUAL(std::string("0, 255, 1274, 254"), aView2.m_aCommentCallbackResult.get<std::string>("cellPos"));
1123 // Delete the comment
1124 if (pTabViewShell)
1125 pTabViewShell->SetCursor(4, 43);
1126 aArgs = comphelper::InitPropertySequence(
1128 {"Id", uno::makeAny(OUString::createFromAscii(aCommentId.c_str()))}
1130 comphelper::dispatchCommand(".uno:DeleteNote", aArgs);
1131 Scheduler::ProcessEventsToIdle();
1133 // We received a LOK_CALLBACK_COMMENT callback with comment 'Remove' action
1134 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1135 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
1136 CPPUNIT_ASSERT_EQUAL(aCommentId, aView1.m_aCommentCallbackResult.get<std::string>("id"));
1137 CPPUNIT_ASSERT_EQUAL(aCommentId, aView2.m_aCommentCallbackResult.get<std::string>("id"));
1139 comphelper::LibreOfficeKit::setTiledAnnotations(true);
1142 void ScTiledRenderingTest::testUndoLimiting()
1144 comphelper::LibreOfficeKit::setActive();
1146 ScModelObj* pModelObj = createDoc("small.ods");
1147 CPPUNIT_ASSERT(pModelObj);
1148 ScDocument* pDoc = pModelObj->GetDocument();
1149 CPPUNIT_ASSERT(pDoc);
1150 SfxUndoManager* pUndoManager = pDoc->GetUndoManager();
1151 CPPUNIT_ASSERT(pUndoManager);
1153 // view #1
1154 int nView1 = SfxLokHelper::getView();
1155 ViewCallback aView1;
1157 // view #2
1158 SfxLokHelper::createView();
1159 int nView2 = SfxLokHelper::getView();
1160 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1161 ViewCallback aView2;
1163 // text edit a cell in view #1
1164 SfxLokHelper::setView(nView1);
1165 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1166 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1167 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1168 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1169 Scheduler::ProcessEventsToIdle();
1171 // check that undo action count in not 0
1172 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
1174 // try to execute undo in view #2
1175 SfxLokHelper::setView(nView2);
1176 comphelper::dispatchCommand(".uno:Undo", {});
1177 Scheduler::ProcessEventsToIdle();
1178 // check that undo has not been executed on view #2
1179 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
1181 // try to execute undo in view #1
1182 SfxLokHelper::setView(nView1);
1183 comphelper::dispatchCommand(".uno:Undo", {});
1184 Scheduler::ProcessEventsToIdle();
1185 // check that undo has been executed on view #1
1186 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetUndoActionCount());
1188 // check that redo action count in not 0
1189 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetRedoActionCount());
1191 // try to execute redo in view #2
1192 SfxLokHelper::setView(nView2);
1193 comphelper::dispatchCommand(".uno:Redo", {});
1194 Scheduler::ProcessEventsToIdle();
1195 // check that redo has not been executed on view #2
1196 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetRedoActionCount());
1198 // try to execute redo in view #1
1199 SfxLokHelper::setView(nView1);
1200 comphelper::dispatchCommand(".uno:Redo", {});
1201 Scheduler::ProcessEventsToIdle();
1202 // check that redo has been executed on view #1
1203 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetRedoActionCount());
1206 void ScTiledRenderingTest::testUndoRepairDispatch()
1208 comphelper::LibreOfficeKit::setActive();
1210 ScModelObj* pModelObj = createDoc("small.ods");
1211 CPPUNIT_ASSERT(pModelObj);
1212 ScDocument* pDoc = pModelObj->GetDocument();
1213 CPPUNIT_ASSERT(pDoc);
1214 SfxUndoManager* pUndoManager = pDoc->GetUndoManager();
1215 CPPUNIT_ASSERT(pUndoManager);
1217 // view #1
1218 int nView1 = SfxLokHelper::getView();
1219 ViewCallback aView1;
1221 // view #2
1222 SfxLokHelper::createView();
1223 int nView2 = SfxLokHelper::getView();
1224 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1225 ViewCallback aView2;
1227 // text edit a cell in view #1
1228 SfxLokHelper::setView(nView1);
1229 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1230 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1231 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1232 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1233 Scheduler::ProcessEventsToIdle();
1235 // check that undo action count in not 0
1236 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
1238 // try to execute undo in view #2
1239 SfxLokHelper::setView(nView2);
1240 comphelper::dispatchCommand(".uno:Undo", {});
1241 Scheduler::ProcessEventsToIdle();
1242 // check that undo has not been executed on view #2
1243 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
1245 // try to execute undo in view #2 in repair mode
1246 SfxLokHelper::setView(nView2);
1247 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
1249 {"Repair", uno::makeAny(true)}
1250 }));
1251 comphelper::dispatchCommand(".uno:Undo", aPropertyValues);
1252 Scheduler::ProcessEventsToIdle();
1253 // check that undo has been executed on view #2 in repair mode
1254 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetUndoActionCount());
1257 void ScTiledRenderingTest::testInsertGraphicInvalidations()
1259 comphelper::LibreOfficeKit::setActive();
1261 ScModelObj* pModelObj = createDoc("small.ods");
1262 CPPUNIT_ASSERT(pModelObj);
1263 ScViewData* pViewData = ScDocShell::GetViewData();
1264 CPPUNIT_ASSERT(pViewData);
1266 // view
1267 ViewCallback aView;
1269 // we need to paint a tile in the view for triggering the tile invalidation solution
1270 int nCanvasWidth = 256;
1271 int nCanvasHeight = 256;
1272 std::vector<unsigned char> aBuffer(nCanvasWidth * nCanvasHeight * 4);
1273 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::DEFAULT);
1274 pDevice->SetOutputSizePixelScaleOffsetAndBuffer(Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), aBuffer.data());
1275 pModelObj->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0, /*nTilePosY=*/0, /*nTileWidth=*/3840, /*nTileHeight=*/3840);
1276 Scheduler::ProcessEventsToIdle();
1278 // insert an image in view and see if both views are invalidated
1279 aView.m_bInvalidateTiles = false;
1280 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
1281 { "FileName", uno::Any(m_directories.getURLFromSrc(DATA_DIRECTORY) + "smile.png") }
1282 }));
1283 comphelper::dispatchCommand(".uno:InsertGraphic", aArgs);
1284 Scheduler::ProcessEventsToIdle();
1285 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1287 // undo image insertion in view and see if both views are invalidated
1288 aView.m_bInvalidateTiles = false;
1289 uno::Sequence<beans::PropertyValue> aArgs2;
1290 comphelper::dispatchCommand(".uno:Undo", aArgs2);
1291 Scheduler::ProcessEventsToIdle();
1292 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1295 void ScTiledRenderingTest::testDocumentSizeWithTwoViews()
1297 comphelper::LibreOfficeKit::setActive();
1299 // Open a document that has the cursor far away & paint a tile
1300 ScModelObj* pModelObj = createDoc("cursor-away.ods");
1302 // Set the visible area, and press page down
1303 pModelObj->setClientVisibleArea(tools::Rectangle(750, 1861, 20583, 6997));
1304 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN);
1305 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN);
1306 Scheduler::ProcessEventsToIdle();
1308 int nCanvasWidth = 256;
1309 int nCanvasHeight = 256;
1310 std::vector<unsigned char> aBuffer1(nCanvasWidth * nCanvasHeight * 4);
1311 ScopedVclPtrInstance<VirtualDevice> pDevice1(DeviceFormat::DEFAULT);
1312 pDevice1->SetOutputSizePixelScaleOffsetAndBuffer(Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), aBuffer1.data());
1313 pModelObj->paintTile(*pDevice1, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0, /*nTilePosY=*/291840, /*nTileWidth=*/3840, /*nTileHeight=*/3840);
1314 Scheduler::ProcessEventsToIdle();
1316 // Create a new view
1317 int nView1 = SfxLokHelper::getView();
1318 SfxLokHelper::createView();
1320 std::vector<unsigned char> aBuffer2(nCanvasWidth * nCanvasHeight * 4);
1321 ScopedVclPtrInstance<VirtualDevice> pDevice2(DeviceFormat::DEFAULT);
1322 pDevice2->SetOutputSizePixelScaleOffsetAndBuffer(Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), aBuffer2.data());
1323 pModelObj->paintTile(*pDevice2, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0, /*nTilePosY=*/291840, /*nTileWidth=*/3840, /*nTileHeight=*/3840);
1324 Scheduler::ProcessEventsToIdle();
1326 // Check that the tiles actually have the same content
1327 for (size_t i = 0; i < aBuffer1.size(); ++i)
1328 CPPUNIT_ASSERT_EQUAL(aBuffer1[i], aBuffer2[i]);
1330 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
1331 SfxLokHelper::setView(nView1);
1332 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
1335 void ScTiledRenderingTest::testDisableUndoRepair()
1337 comphelper::LibreOfficeKit::setActive();
1338 ScModelObj* pModelObj = createDoc("cursor-away.ods");
1339 CPPUNIT_ASSERT(pModelObj);
1341 // view #1
1342 int nView1 = SfxLokHelper::getView();
1343 SfxViewShell* pView1 = SfxViewShell::Current();
1345 // view #2
1346 SfxLokHelper::createView();
1347 int nView2 = SfxLokHelper::getView();
1348 SfxViewShell* pView2 = SfxViewShell::Current();
1349 CPPUNIT_ASSERT(pView1 != pView2);
1351 SfxItemSet aSet1(pView1->GetPool(), svl::Items<SID_UNDO, SID_UNDO>{});
1352 SfxItemSet aSet2(pView2->GetPool(), svl::Items<SID_UNDO, SID_UNDO>{});
1353 pView1->GetSlotState(SID_UNDO, nullptr, &aSet1);
1354 pView2->GetSlotState(SID_UNDO, nullptr, &aSet2);
1355 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, aSet1.GetItemState(SID_UNDO));
1356 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, aSet2.GetItemState(SID_UNDO));
1359 // text edit a cell in view #1
1360 SfxLokHelper::setView(nView1);
1361 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'h', 0);
1362 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'h', 0);
1363 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1364 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1365 Scheduler::ProcessEventsToIdle();
1367 SfxItemSet aSet1(pView1->GetPool(), svl::Items<SID_UNDO, SID_UNDO>{});
1368 SfxItemSet aSet2(pView2->GetPool(), svl::Items<SID_UNDO, SID_UNDO>{});
1369 pView1->GetSlotState(SID_UNDO, nullptr, &aSet1);
1370 pView2->GetSlotState(SID_UNDO, nullptr, &aSet2);
1371 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet1.GetItemState(SID_UNDO));
1372 CPPUNIT_ASSERT(dynamic_cast< const SfxStringItem* >(aSet1.GetItem(SID_UNDO)));
1373 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet2.GetItemState(SID_UNDO));
1374 CPPUNIT_ASSERT(dynamic_cast< const SfxUInt32Item* >(aSet2.GetItem(SID_UNDO)));
1375 const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item*>(aSet2.GetItem(SID_UNDO));
1376 CPPUNIT_ASSERT(pUInt32Item);
1377 CPPUNIT_ASSERT_EQUAL(static_cast< sal_uInt32 >(SID_REPAIRPACKAGE), pUInt32Item->GetValue());
1380 // text edit a cell in view #2
1381 SfxLokHelper::setView(nView2);
1382 pModelObj->setPart(1);
1383 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', 0);
1384 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', 0);
1385 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1386 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1387 Scheduler::ProcessEventsToIdle();
1389 SfxItemSet aSet1(pView1->GetPool(), svl::Items<SID_UNDO, SID_UNDO>{});
1390 SfxItemSet aSet2(pView2->GetPool(), svl::Items<SID_UNDO, SID_UNDO>{});
1391 pView1->GetSlotState(SID_UNDO, nullptr, &aSet1);
1392 pView2->GetSlotState(SID_UNDO, nullptr, &aSet2);
1393 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet1.GetItemState(SID_UNDO));
1394 const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item*>(aSet1.GetItem(SID_UNDO));
1395 CPPUNIT_ASSERT(pUInt32Item);
1396 CPPUNIT_ASSERT_EQUAL(static_cast< sal_uInt32 >(SID_REPAIRPACKAGE), pUInt32Item->GetValue());
1397 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet2.GetItemState(SID_UNDO));
1398 CPPUNIT_ASSERT(dynamic_cast< const SfxStringItem* >(aSet2.GetItem(SID_UNDO)));
1401 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
1402 SfxLokHelper::setView(nView1);
1403 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
1406 void ScTiledRenderingTest::testDocumentRepair()
1408 comphelper::LibreOfficeKit::setActive();
1410 // Create two views.
1411 ScModelObj* pModelObj = createDoc("cursor-away.ods");
1412 CPPUNIT_ASSERT(pModelObj);
1414 // view #1
1415 SfxViewShell* pView1 = SfxViewShell::Current();
1417 // view #2
1418 int nView1 = SfxLokHelper::getView();
1419 SfxLokHelper::createView();
1420 SfxViewShell* pView2 = SfxViewShell::Current();
1421 int nView2 = SfxLokHelper::getView();
1422 CPPUNIT_ASSERT(pView1 != pView2);
1424 std::unique_ptr<SfxPoolItem> xItem1;
1425 std::unique_ptr<SfxPoolItem> xItem2;
1426 pView1->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, xItem1);
1427 pView2->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, xItem2);
1428 const SfxBoolItem* pItem1 = dynamic_cast< const SfxBoolItem* >(xItem1.get());
1429 const SfxBoolItem* pItem2 = dynamic_cast< const SfxBoolItem* >(xItem2.get());
1430 CPPUNIT_ASSERT(pItem1);
1431 CPPUNIT_ASSERT(pItem2);
1432 CPPUNIT_ASSERT_EQUAL(false, pItem1->GetValue());
1433 CPPUNIT_ASSERT_EQUAL(false, pItem2->GetValue());
1436 // Insert a character in the second view.
1437 SfxLokHelper::setView(nView2);
1438 pModelObj->setPart(1);
1439 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', 0);
1440 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', 0);
1441 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1442 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1443 Scheduler::ProcessEventsToIdle();
1445 std::unique_ptr<SfxPoolItem> xItem1;
1446 std::unique_ptr<SfxPoolItem> xItem2;
1447 pView1->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, xItem1);
1448 pView2->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, xItem2);
1449 const SfxBoolItem* pItem1 = dynamic_cast< const SfxBoolItem* >(xItem1.get());
1450 const SfxBoolItem* pItem2 = dynamic_cast< const SfxBoolItem* >(xItem2.get());
1451 CPPUNIT_ASSERT(pItem1);
1452 CPPUNIT_ASSERT(pItem2);
1453 CPPUNIT_ASSERT_EQUAL(true, pItem1->GetValue());
1454 CPPUNIT_ASSERT_EQUAL(true, pItem2->GetValue());
1457 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
1458 SfxLokHelper::setView(nView1);
1459 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
1462 void ScTiledRenderingTest::testLanguageStatus()
1464 comphelper::LibreOfficeKit::setActive();
1465 ScModelObj* pModelObj = createDoc("small.ods");
1466 CPPUNIT_ASSERT(pModelObj);
1467 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
1468 CPPUNIT_ASSERT(pDocSh);
1470 // view #1
1471 SfxViewShell* pView1 = SfxViewShell::Current();
1473 // view #2
1474 int nView1 = SfxLokHelper::getView();
1475 SfxLokHelper::createView();
1476 SfxViewShell* pView2 = SfxViewShell::Current();
1477 CPPUNIT_ASSERT(pView1 != pView2);
1479 std::unique_ptr<SfxPoolItem> xItem1;
1480 std::unique_ptr<SfxPoolItem> xItem2;
1481 pView1->GetViewFrame()->GetBindings().QueryState(SID_LANGUAGE_STATUS, xItem1);
1482 pView2->GetViewFrame()->GetBindings().QueryState(SID_LANGUAGE_STATUS, xItem2);
1483 const SfxStringItem* pItem1 = dynamic_cast<const SfxStringItem*>(xItem1.get());
1484 const SfxStringItem* pItem2 = dynamic_cast<const SfxStringItem*>(xItem2.get());
1485 CPPUNIT_ASSERT(pItem1);
1486 CPPUNIT_ASSERT(pItem2);
1487 CPPUNIT_ASSERT(!pItem1->GetValue().isEmpty());
1488 CPPUNIT_ASSERT(!pItem2->GetValue().isEmpty());
1492 SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Default_Spanish (Bolivia)");
1493 pView1->GetViewFrame()->GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS,
1494 SfxCallMode::SYNCHRON, { &aLangString });
1498 std::unique_ptr<SfxPoolItem> xItem1;
1499 std::unique_ptr<SfxPoolItem> xItem2;
1500 pView1->GetViewFrame()->GetBindings().QueryState(SID_LANGUAGE_STATUS, xItem1);
1501 pView2->GetViewFrame()->GetBindings().QueryState(SID_LANGUAGE_STATUS, xItem2);
1502 const SfxStringItem* pItem1 = dynamic_cast<const SfxStringItem*>(xItem1.get());
1503 const SfxStringItem* pItem2 = dynamic_cast<const SfxStringItem*>(xItem2.get());
1504 CPPUNIT_ASSERT(pItem1);
1505 CPPUNIT_ASSERT(pItem2);
1506 const OUString aLangBolivia("Spanish (Bolivia);es-BO");
1507 CPPUNIT_ASSERT_EQUAL(aLangBolivia, pItem1->GetValue());
1508 CPPUNIT_ASSERT_EQUAL(aLangBolivia, pItem2->GetValue());
1511 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
1512 SfxLokHelper::setView(nView1);
1513 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
1516 void ScTiledRenderingTest::testMultiViewCopyPaste()
1518 comphelper::LibreOfficeKit::setActive();
1520 ScModelObj* pModelObj = createDoc("empty.ods");
1521 ScDocument* pDoc = pModelObj->GetDocument();
1522 CPPUNIT_ASSERT(pDoc);
1524 pDoc->SetString(ScAddress(0, 0, 0), "TestCopy1");
1525 pDoc->SetString(ScAddress(1, 0, 0), "TestCopy2");
1527 // view #1
1528 ScTabViewShell* pView1 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
1529 CPPUNIT_ASSERT(pView1);
1530 // emulate clipboard
1531 pView1->GetViewData().GetActiveWin()->SetClipboard(css::datatransfer::clipboard::SystemClipboard::create(comphelper::getProcessComponentContext()));
1533 // view #2
1534 int nView1 = SfxLokHelper::getView();
1535 SfxLokHelper::createView();
1536 ScTabViewShell* pView2 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
1537 // emulate clipboard
1538 pView2->GetViewData().GetActiveWin()->SetClipboard(css::datatransfer::clipboard::SystemClipboard::create(comphelper::getProcessComponentContext()));
1539 CPPUNIT_ASSERT(pView2);
1540 CPPUNIT_ASSERT(pView1 != pView2);
1541 CPPUNIT_ASSERT(pView1->GetViewData().GetActiveWin()->GetClipboard() != pView2->GetViewData().GetActiveWin()->GetClipboard());
1543 // copy text view 1
1544 pView1->SetCursor(0, 0);
1545 pView1->GetViewFrame()->GetBindings().Execute(SID_COPY);
1547 // copy text view 2
1548 pView2->SetCursor(1, 0);
1549 pView2->GetViewFrame()->GetBindings().Execute(SID_COPY);
1551 // paste text view 1
1552 pView1->SetCursor(0, 1);
1553 pView1->GetViewFrame()->GetBindings().Execute(SID_PASTE);
1555 // paste text view 2
1556 pView2->SetCursor(1, 1);
1557 pView2->GetViewFrame()->GetBindings().Execute(SID_PASTE);
1559 CPPUNIT_ASSERT_EQUAL(OUString("TestCopy1"), pDoc->GetString(ScAddress(0, 1, 0)));
1560 CPPUNIT_ASSERT_EQUAL(OUString("TestCopy2"), pDoc->GetString(ScAddress(1, 1, 0)));
1562 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
1563 SfxLokHelper::setView(nView1);
1564 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
1567 void ScTiledRenderingTest::testIMESupport()
1569 comphelper::LibreOfficeKit::setActive();
1571 ScModelObj* pModelObj = createDoc("empty.ods");
1572 VclPtr<vcl::Window> pDocWindow = pModelObj->getDocWindow();
1573 ScDocument* pDoc = pModelObj->GetDocument();
1575 ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
1576 CPPUNIT_ASSERT(pView);
1578 pView->SetCursor(0, 0);
1579 // sequence of chinese IME compositions when 'nihao' is typed in an IME
1580 const std::vector<OString> aUtf8Inputs{ "年", "你", "你好", "你哈", "你好", "你好" };
1581 std::vector<OUString> aInputs;
1582 std::transform(aUtf8Inputs.begin(), aUtf8Inputs.end(),
1583 std::back_inserter(aInputs), [](OString aInput) {
1584 return OUString::fromUtf8(aInput);
1586 for (const auto& aInput: aInputs)
1588 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, aInput);
1590 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
1592 // commit the string to the cell
1593 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1594 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1595 Scheduler::ProcessEventsToIdle();
1597 CPPUNIT_ASSERT_EQUAL(aInputs[aInputs.size() - 1], pDoc->GetString(ScAddress(0, 0, 0)));
1600 void ScTiledRenderingTest::testFilterDlg()
1602 comphelper::LibreOfficeKit::setActive();
1604 createDoc("empty.ods");
1606 // view #1
1607 SfxViewShell* pView1 = SfxViewShell::Current();
1608 int nView1 = SfxLokHelper::getView();
1610 // view #2
1611 SfxLokHelper::createView();
1612 SfxViewShell* pView2 = SfxViewShell::Current();
1613 CPPUNIT_ASSERT(pView1 != pView2);
1615 pView2->GetViewFrame()->GetDispatcher()->Execute(SID_FILTER,
1616 SfxCallMode::SLOT|SfxCallMode::RECORD);
1619 Scheduler::ProcessEventsToIdle();
1620 SfxChildWindow* pRefWindow = pView2->GetViewFrame()->GetChildWindow(SID_FILTER);
1621 CPPUNIT_ASSERT(pRefWindow);
1623 // switch to view 1
1624 SfxLokHelper::setView(nView1);
1625 CPPUNIT_ASSERT_EQUAL(true, pView2->GetViewFrame()->GetDispatcher()->IsLocked());
1626 CPPUNIT_ASSERT_EQUAL(false, pView1->GetViewFrame()->GetDispatcher()->IsLocked());
1628 pRefWindow->GetController()->response(RET_CANCEL);
1630 CPPUNIT_ASSERT_EQUAL(false, pView2->GetViewFrame()->GetDispatcher()->IsLocked());
1631 CPPUNIT_ASSERT_EQUAL(false, pView1->GetViewFrame()->GetDispatcher()->IsLocked());
1633 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
1634 SfxLokHelper::setView(nView1);
1635 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
1638 void ScTiledRenderingTest::testSpellOnlineParameter()
1640 ScModelObj* pModelObj = createDoc("empty.ods");
1641 ScDocument* pDoc = pModelObj->GetDocument();
1642 bool bSet = pDoc->GetDocOptions().IsAutoSpell();
1644 uno::Sequence<beans::PropertyValue> params =
1646 comphelper::makePropertyValue("Enable", uno::makeAny(!bSet)),
1648 dispatchCommand(mxComponent, ".uno:SpellOnline", params);
1649 CPPUNIT_ASSERT_EQUAL(!bSet, pDoc->GetDocOptions().IsAutoSpell());
1651 // set the same state as now and we don't expect any change (no-toggle)
1652 params =
1654 comphelper::makePropertyValue("Enable", uno::makeAny(!bSet)),
1656 dispatchCommand(mxComponent, ".uno:SpellOnline", params);
1657 CPPUNIT_ASSERT_EQUAL(!bSet, pDoc->GetDocOptions().IsAutoSpell());
1660 void ScTiledRenderingTest::testVbaRangeCopyPaste()
1662 comphelper::LibreOfficeKit::setActive();
1663 ScModelObj* pModelObj = createDoc("RangeCopyPaste.ods");
1664 ScDocShell* pDocShell = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
1665 CPPUNIT_ASSERT(pDocShell);
1667 uno::Any aRet;
1668 uno::Sequence< uno::Any > aOutParam;
1669 uno::Sequence< uno::Any > aParams;
1670 uno::Sequence< sal_Int16 > aOutParamIndex;
1672 SfxObjectShell::CallXScript(
1673 mxComponent,
1674 "vnd.sun.Star.script:Standard.Module1.Test_RangeCopyPaste?language=Basic&location=document",
1675 aParams, aRet, aOutParamIndex, aOutParam);
1677 CPPUNIT_ASSERT(!pDocShell->GetClipData().is());
1680 void ScTiledRenderingTest::testInvalidationLoop()
1682 // Load the document with a form control.
1683 createDoc("invalidation-loop.fods");
1684 // Without the accompanying fix in place, this test would have never returned due to an infinite
1685 // invalidation loop between ScGridWindow::Paint() and vcl::Window::ImplPosSizeWindow().
1686 Scheduler::ProcessEventsToIdle();
1689 void ScTiledRenderingTest::testPageDownInvalidation()
1691 comphelper::LibreOfficeKit::setActive();
1693 ScModelObj* pModelObj = createDoc("empty.ods");
1694 ScViewData* pViewData = ScDocShell::GetViewData();
1695 CPPUNIT_ASSERT(pViewData);
1697 int nView1 = SfxLokHelper::getView();
1698 ViewCallback aView1;
1699 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
1701 SfxLokHelper::setView(nView1);
1702 aView1.m_bInvalidateTiles = false;
1703 aView1.m_aInvalidations.clear();
1704 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, awt::Key::PAGEDOWN, 0);
1705 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, awt::Key::PAGEDOWN, 0);
1706 Scheduler::ProcessEventsToIdle();
1707 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1708 CPPUNIT_ASSERT_EQUAL(size_t(3), aView1.m_aInvalidations.size());
1709 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(15, 15, 1230, 225), aView1.m_aInvalidations[0]);
1712 void ScTiledRenderingTest::testSheetChangeInvalidation()
1714 comphelper::LibreOfficeKit::setActive();
1716 ScModelObj* pModelObj = createDoc("two_sheets.ods");
1717 ScViewData* pViewData = ScDocShell::GetViewData();
1718 CPPUNIT_ASSERT(pViewData);
1720 int nView1 = SfxLokHelper::getView();
1721 ViewCallback aView1;
1722 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
1724 SfxLokHelper::setView(nView1);
1725 aView1.m_bInvalidateTiles = false;
1726 aView1.m_aInvalidations.clear();
1727 aView1.m_aInvalidationsParts.clear();
1728 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN | KEY_MOD1);
1729 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN | KEY_MOD1);
1730 Scheduler::ProcessEventsToIdle();
1731 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1732 CPPUNIT_ASSERT_EQUAL(size_t(2), aView1.m_aInvalidations.size());
1733 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 0, 1310720, 268435456), aView1.m_aInvalidations[0]);
1734 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 0, 1000000000, 1000000000), aView1.m_aInvalidations[1]);
1735 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidationsParts.size());
1736 CPPUNIT_ASSERT_EQUAL(pModelObj->getPart(), aView1.m_aInvalidationsParts[0]);
1739 void ScTiledRenderingTest::testInsertDeletePageInvalidation()
1741 comphelper::LibreOfficeKit::setActive();
1743 ScModelObj* pModelObj = createDoc("insert_delete_sheet.ods");
1744 // the document has 1 sheet
1745 CPPUNIT_ASSERT_EQUAL(1, pModelObj->getParts());
1746 ScViewData* pViewData = ScDocShell::GetViewData();
1747 CPPUNIT_ASSERT(pViewData);
1749 int nView1 = SfxLokHelper::getView();
1750 ViewCallback aView1;
1751 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
1753 SfxLokHelper::setView(nView1);
1754 aView1.m_bInvalidateTiles = false;
1755 aView1.m_aInvalidations.clear();
1757 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
1758 { "Name", uno::Any(OUString("")) },
1759 { "Index", uno::Any(sal_Int32(1)) }
1760 }));
1761 comphelper::dispatchCommand(".uno:Insert", aArgs);
1762 Scheduler::ProcessEventsToIdle();
1763 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1764 CPPUNIT_ASSERT_EQUAL(size_t(6), aView1.m_aInvalidations.size());
1765 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 0, 1000000000, 1000000000), aView1.m_aInvalidations[0]);
1766 CPPUNIT_ASSERT_EQUAL(2, pModelObj->getParts());
1768 // Delete sheet
1769 aView1.m_bInvalidateTiles = false;
1770 aView1.m_aInvalidations.clear();
1771 uno::Sequence<beans::PropertyValue> aArgs2( comphelper::InitPropertySequence({
1772 { "Index", uno::Any(sal_Int32(1)) }
1773 }));
1774 comphelper::dispatchCommand(".uno:Remove", aArgs2);
1775 Scheduler::ProcessEventsToIdle();
1776 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1777 CPPUNIT_ASSERT_EQUAL(size_t(5), aView1.m_aInvalidations.size());
1778 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 0, 1000000000, 1000000000), aView1.m_aInvalidations[0]);
1779 CPPUNIT_ASSERT_EQUAL(1, pModelObj->getParts());
1782 void ScTiledRenderingTest::testGetRowColumnHeadersInvalidation()
1784 comphelper::LibreOfficeKit::setActive();
1786 ScModelObj* pModelObj = createDoc("empty.ods");
1787 ScViewData* pViewData = ScDocShell::GetViewData();
1788 CPPUNIT_ASSERT(pViewData);
1790 int nView1 = SfxLokHelper::getView();
1791 ViewCallback aView1;
1792 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
1794 SfxLokHelper::setView(nView1);
1795 aView1.m_bInvalidateTiles = false;
1796 aView1.m_aInvalidations.clear();
1797 tools::JsonWriter aJsonWriter1;
1798 pModelObj->getRowColumnHeaders(tools::Rectangle(0, 15, 19650, 5400), aJsonWriter1);
1799 free(aJsonWriter1.extractData());
1800 Scheduler::ProcessEventsToIdle();
1801 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1802 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
1803 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(26775, 0, 49725, 13005), aView1.m_aInvalidations[0]);
1805 // Extend area top-to-bottom
1806 aView1.m_bInvalidateTiles = false;
1807 aView1.m_aInvalidations.clear();
1808 tools::JsonWriter aJsonWriter2;
1809 pModelObj->getRowColumnHeaders(tools::Rectangle(0, 5400, 19650, 9800), aJsonWriter2);
1810 free(aJsonWriter2.extractData());
1811 Scheduler::ProcessEventsToIdle();
1812 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1813 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
1814 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 13005, 49725, 19380), aView1.m_aInvalidations[0]);
1816 // Extend area left-to-right
1817 aView1.m_bInvalidateTiles = false;
1818 aView1.m_aInvalidations.clear();
1819 tools::JsonWriter aJsonWriter3;
1820 pModelObj->getRowColumnHeaders(tools::Rectangle(5400, 5400, 25050, 9800), aJsonWriter3);
1821 free(aJsonWriter3.extractData());
1822 Scheduler::ProcessEventsToIdle();
1823 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1824 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
1825 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(49725, 0, 75225, 19380), aView1.m_aInvalidations[0]);
1828 void ScTiledRenderingTest::testJumpHorizontallyInvalidation()
1830 comphelper::LibreOfficeKit::setActive();
1832 ScModelObj* pModelObj = createDoc("empty.ods");
1833 ScViewData* pViewData = ScDocShell::GetViewData();
1834 CPPUNIT_ASSERT(pViewData);
1836 int nView1 = SfxLokHelper::getView();
1837 ViewCallback aView1;
1838 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
1840 SfxLokHelper::setView(nView1);
1841 aView1.m_bInvalidateTiles = false;
1842 aView1.m_aInvalidations.clear();
1843 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN | KEY_MOD2);
1844 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN | KEY_MOD2);
1845 Scheduler::ProcessEventsToIdle();
1846 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN | KEY_MOD2);
1847 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN | KEY_MOD2);
1848 Scheduler::ProcessEventsToIdle();
1849 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1850 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
1851 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(26775, 0, 39525, 13005), aView1.m_aInvalidations[0]);
1854 void ScTiledRenderingTest::testJumpToLastRowInvalidation()
1856 comphelper::LibreOfficeKit::setActive();
1858 ScModelObj* pModelObj = createDoc("empty.ods");
1859 ScViewData* pViewData = ScDocShell::GetViewData();
1860 CPPUNIT_ASSERT(pViewData);
1862 int nView1 = SfxLokHelper::getView();
1863 ViewCallback aView1;
1864 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
1866 SfxLokHelper::setView(nView1);
1867 aView1.m_bInvalidateTiles = false;
1868 aView1.m_aInvalidations.clear();
1869 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN | KEY_MOD1);
1870 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN | KEY_MOD1);
1871 Scheduler::ProcessEventsToIdle();
1872 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1873 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
1874 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 13005, 26775, 127500255), aView1.m_aInvalidations[0]);
1877 // We need to ensure that views are not perterbed by rendering (!?) hmm ...
1878 void ScTiledRenderingTest::testRowColumnHeaders()
1880 comphelper::LibreOfficeKit::setActive();
1882 ScModelObj* pModelObj = createDoc("empty.ods");
1883 ScViewData* pViewData = ScDocShell::GetViewData();
1884 CPPUNIT_ASSERT(pViewData);
1886 // view #1
1887 ViewCallback aView1;
1888 int nView1 = SfxLokHelper::getView();
1889 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
1890 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
1892 // view #2
1893 SfxLokHelper::createView();
1894 int nView2 = SfxLokHelper::getView();
1895 ViewCallback aView2;
1896 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1897 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
1899 // ViewRowColumnHeaders test
1900 SfxLokHelper::setView(nView1);
1901 tools::JsonWriter aJsonWriter1;
1902 pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter1);
1903 OString aHeaders1 = aJsonWriter1.extractAsOString();
1905 SfxLokHelper::setView(nView2);
1906 // 50% zoom
1907 pModelObj->setClientVisibleArea(tools::Rectangle(0, 0, 22474, 47333));
1908 pModelObj->setClientZoom(256, 256, 6636, 6636);
1909 tools::JsonWriter aJsonWriter2;
1910 pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter2);
1911 OString aHeaders2 = aJsonWriter2.extractAsOString();
1913 // Check vs. view #1
1914 SfxLokHelper::setView(nView1);
1915 tools::JsonWriter aJsonWriter3;
1916 pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter3);
1917 OString aHeaders1_2 = aJsonWriter3.extractAsOString();
1918 CPPUNIT_ASSERT_EQUAL(aHeaders1, aHeaders1_2);
1920 // Check vs. view #2
1921 SfxLokHelper::setView(nView2);
1922 tools::JsonWriter aJsonWriter4;
1923 pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter4);
1924 OString aHeaders2_2 = aJsonWriter4.extractAsOString();
1925 CPPUNIT_ASSERT_EQUAL(aHeaders2, aHeaders2_2);
1927 SfxLokHelper::setView(nView1);
1928 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
1929 SfxLokHelper::setView(nView2);
1930 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
1933 // Helper structs for setup and testing of ScModelObj::getSheetGeometryData()
1934 struct SpanEntry
1936 size_t nVal;
1937 SCCOLROW nEnd;
1940 struct SheetDimData
1942 typedef std::vector<SpanEntry> SpanList;
1943 SpanList aSizes;
1944 SpanList aHidden;
1945 SpanList aFiltered;
1946 // TODO: Add group info too to test.
1948 void setDataToDoc(ScDocument* pDoc, bool bCol) const
1950 SCCOLROW nStart = 0;
1951 // sizes
1952 for (const auto& rSpan : aSizes)
1954 if (bCol)
1956 for (SCCOLROW nIdx = nStart; nIdx <= rSpan.nEnd; ++nIdx)
1957 pDoc->SetColWidthOnly(nIdx, 0, rSpan.nVal);
1959 else
1960 pDoc->SetRowHeightOnly(nStart, rSpan.nEnd, 0, rSpan.nVal);
1962 nStart = rSpan.nEnd + 1;
1965 nStart = 0;
1966 // hidden
1967 for (const auto& rSpan : aHidden)
1969 if (bCol)
1970 pDoc->SetColHidden(nStart, rSpan.nEnd, 0, !!rSpan.nVal);
1971 else
1972 pDoc->SetRowHidden(nStart, rSpan.nEnd, 0, !!rSpan.nVal);
1974 nStart = rSpan.nEnd + 1;
1977 // There is no ScDocument interface to set ScTable::mpFilteredCols
1978 // It seems ScTable::mpFilteredCols is not really used !?
1979 if (bCol)
1980 return;
1982 nStart = 0;
1983 // filtered
1984 for (const auto& rSpan : aFiltered)
1986 pDoc->SetRowFiltered(nStart, rSpan.nEnd, 0, !!rSpan.nVal);
1987 nStart = rSpan.nEnd + 1;
1991 void testPropertyTree(const boost::property_tree::ptree& rTree, bool bCol) const
1993 struct SpanListWithKey
1995 OString aKey;
1996 const SpanList& rSpanList;
1999 const SpanListWithKey aPairList[] = {
2000 { "sizes", aSizes },
2001 { "hidden", aHidden },
2002 { "filtered", aFiltered }
2005 for (const auto& rEntry : aPairList)
2007 // There is no ScDocument interface to set ScTable::mpFilteredCols
2008 // It seems ScTable::mpFilteredCols is not really used !?
2009 if (bCol && rEntry.aKey == "filtered")
2010 continue;
2012 bool bBooleanValue = rEntry.aKey != "sizes";
2013 OString aExpectedEncoding;
2014 bool bFirst = true;
2015 for (const auto& rSpan : rEntry.rSpanList)
2017 size_t nVal = rSpan.nVal;
2018 if (bBooleanValue && bFirst)
2019 nVal = static_cast<size_t>(!!nVal);
2020 if (!bBooleanValue || bFirst)
2021 aExpectedEncoding += OString::number(nVal) + ":";
2022 aExpectedEncoding += OString::number(rSpan.nEnd) + " ";
2023 bFirst = false;
2026 // Get the tree's value for the property key ("sizes"/"hidden"/"filtered").
2027 OString aTreeValue = rTree.get<std::string>(rEntry.aKey.getStr()).c_str();
2029 CPPUNIT_ASSERT_EQUAL(aExpectedEncoding, aTreeValue);
2034 class SheetGeometryData
2036 SheetDimData aCols;
2037 SheetDimData aRows;
2039 public:
2041 SheetGeometryData()
2043 aCols = {
2044 // width spans
2045 { { STD_COL_WIDTH, MAXCOL } },
2046 // hidden spans
2047 { { 0, MAXCOL } },
2048 // filtered spans
2049 { { 0, MAXCOL } }
2052 aRows = {
2053 // height spans
2054 { { ScGlobal::nStdRowHeight, MAXROW } },
2055 // hidden spans
2056 { { 0, MAXROW } },
2057 // filtered spans
2058 { { 0, MAXROW } }
2062 SheetGeometryData(const SheetDimData& rCols, const SheetDimData& rRows) :
2063 aCols(rCols), aRows(rRows)
2066 void setDataToDoc(ScDocument* pDoc) const
2068 aCols.setDataToDoc(pDoc, true);
2069 aRows.setDataToDoc(pDoc, false);
2072 void parseTest(const OString& rJSON) const
2074 // Assumes all flags passed to getSheetGeometryData() are true.
2075 boost::property_tree::ptree aTree;
2076 std::stringstream aStream(rJSON.getStr());
2077 boost::property_tree::read_json(aStream, aTree);
2079 CPPUNIT_ASSERT_EQUAL(OString(".uno:SheetGeometryData"), OString(aTree.get<std::string>("commandName").c_str()));
2081 aCols.testPropertyTree(aTree.get_child("columns"), true);
2082 aRows.testPropertyTree(aTree.get_child("rows"), false);
2086 // getSheetGeometryData() should return the exact same message
2087 // irrespective of client zoom and view-area. Switching views
2088 // should also not alter it.
2089 void ScTiledRenderingTest::testSheetGeometryDataInvariance()
2091 comphelper::LibreOfficeKit::setActive();
2093 ScModelObj* pModelObj = createDoc("empty.ods");
2094 const SheetGeometryData aSGData(
2095 // cols
2097 // width spans
2099 { STD_COL_WIDTH, 20 },
2100 { 2*STD_COL_WIDTH, 26 },
2101 { STD_COL_WIDTH, MAXCOL }
2104 // hidden spans
2106 { 0, 5 },
2107 { 1, 12 },
2108 { 0, MAXCOL }
2111 // filtered spans
2113 { 0, 50 },
2114 { 1, 59 },
2115 { 0, MAXCOL }
2119 // rows
2121 // height spans
2123 { 300, 50 },
2124 { 600, 65 },
2125 { 300, MAXROW }
2128 // hidden spans
2130 { 1, 100 },
2131 { 0, 500 },
2132 { 1, 578 },
2133 { 0, MAXROW }
2136 // filtered spans
2138 { 0, 150 },
2139 { 1, 159 },
2140 { 0, MAXROW }
2145 ScDocument* pDoc = pModelObj->GetDocument();
2147 ScViewData* pViewData = ScDocShell::GetViewData();
2148 CPPUNIT_ASSERT(pViewData);
2150 // view #1
2151 ViewCallback aView1;
2152 int nView1 = SfxLokHelper::getView();
2153 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
2155 // view #2
2156 SfxLokHelper::createView();
2157 int nView2 = SfxLokHelper::getView();
2158 ViewCallback aView2;
2159 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
2160 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
2162 // Try with the default empty document once (nIdx = 0) and then with sheet geometry settings (nIdx = 1)
2163 for (size_t nIdx = 0; nIdx < 2; ++nIdx)
2165 if (nIdx)
2166 aSGData.setDataToDoc(pDoc);
2168 SfxLokHelper::setView(nView1);
2169 OString aGeomStr1 = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2170 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2172 SfxLokHelper::setView(nView2);
2173 pModelObj->setClientVisibleArea(tools::Rectangle(0, 0, 22474, 47333));
2174 pModelObj->setClientZoom(256, 256, 6636, 6636);
2175 OString aGeomStr2 = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2176 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2178 // Check vs. view #1
2179 SfxLokHelper::setView(nView1);
2180 OString aGeomStr1_2 = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2181 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2182 CPPUNIT_ASSERT_EQUAL(aGeomStr1, aGeomStr1_2);
2184 // Check vs. view #2
2185 SfxLokHelper::setView(nView2);
2186 OString aGeomStr2_2 = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2187 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2188 CPPUNIT_ASSERT_EQUAL(aGeomStr2, aGeomStr2_2);
2191 SfxLokHelper::setView(nView1);
2192 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
2193 SfxLokHelper::setView(nView2);
2194 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
2197 void ScTiledRenderingTest::testSheetGeometryDataCorrectness()
2199 comphelper::LibreOfficeKit::setActive();
2201 ScModelObj* pModelObj = createDoc("empty.ods");
2202 const SheetGeometryData aSGData(
2203 // cols
2205 // width spans
2207 { STD_COL_WIDTH, 20 },
2208 { 2*STD_COL_WIDTH, 26 },
2209 { STD_COL_WIDTH, MAXCOL }
2212 // hidden spans
2214 { 0, 5 },
2215 { 1, 12 },
2216 { 0, MAXCOL }
2219 // filtered spans
2221 { 0, 50 },
2222 { 1, 59 },
2223 { 0, MAXCOL }
2227 // rows
2229 // height spans
2231 { 300, 50 },
2232 { 600, 65 },
2233 { 300, MAXROW }
2236 // hidden spans
2238 { 1, 100 },
2239 { 0, 500 },
2240 { 1, 578 },
2241 { 0, MAXROW }
2244 // filtered spans
2246 { 0, 150 },
2247 { 1, 159 },
2248 { 0, MAXROW }
2253 ScDocument* pDoc = pModelObj->GetDocument();
2255 ScViewData* pViewData = ScDocShell::GetViewData();
2256 CPPUNIT_ASSERT(pViewData);
2258 // view #1
2259 ViewCallback aView1;
2260 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
2262 // with the default empty sheet and test the JSON encoding.
2263 OString aGeomDefaultStr = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2264 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2265 SheetGeometryData().parseTest(aGeomDefaultStr);
2267 // Apply geometry settings to the sheet and then test the resulting JSON encoding.
2268 aSGData.setDataToDoc(pDoc);
2269 OString aGeomStr = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2270 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2271 aSGData.parseTest(aGeomStr);
2273 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
2276 void ScTiledRenderingTest::testDeleteCellMultilineContent()
2278 comphelper::LibreOfficeKit::setActive();
2280 ScModelObj* pModelObj = createDoc("multiline.ods");
2281 CPPUNIT_ASSERT(pModelObj);
2282 ScViewData* pViewData = ScDocShell::GetViewData();
2283 CPPUNIT_ASSERT(pViewData);
2284 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
2285 CPPUNIT_ASSERT(pDocSh);
2287 // view #1
2288 ViewCallback aView1;
2289 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
2290 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
2292 aView1.m_sInvalidateHeader = "";
2293 ScDocument& rDoc = pDocSh->GetDocument();
2294 sal_uInt16 nRow1Height = rDoc.GetRowHeight(static_cast<SCROW>(0), static_cast<SCTAB>(0), false);
2296 // delete multiline cell content in view #1
2297 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN);
2298 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN);
2299 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DELETE);
2300 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DELETE);
2301 Scheduler::ProcessEventsToIdle();
2303 // check if the row header has been invalidated and if the involved row is of the expected height
2304 CPPUNIT_ASSERT_EQUAL(OString("row"), aView1.m_sInvalidateHeader);
2305 sal_uInt16 nRow2Height = rDoc.GetRowHeight(static_cast<SCROW>(0), static_cast<SCTAB>(0), false);
2306 CPPUNIT_ASSERT_EQUAL(nRow1Height, nRow2Height);
2307 SfxViewShell::Current()->registerLibreOfficeKitViewCallback(nullptr, nullptr);
2312 CPPUNIT_TEST_SUITE_REGISTRATION(ScTiledRenderingTest);
2314 CPPUNIT_PLUGIN_IMPLEMENT();
2316 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */