calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / sc / qa / unit / tiledrendering / tiledrendering.cxx
blob7d89cb787c85405a4f41170c11598e157f71f7aa
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/unoapixml_test.hxx>
11 #include <test/helper/transferable.hxx>
12 #include <cppunit/tools/StringHelper.h>
13 #include <boost/property_tree/json_parser.hpp>
15 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
16 #include <com/sun/star/frame/Desktop.hpp>
17 #include <com/sun/star/frame/DispatchHelper.hpp>
18 #include <com/sun/star/datatransfer/clipboard/LokClipboard.hpp>
19 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
20 #include <comphelper/processfactory.hxx>
21 #include <comphelper/propertysequence.hxx>
22 #include <osl/conditn.hxx>
23 #include <sfx2/dispatch.hxx>
24 #include <sfx2/viewfrm.hxx>
25 #include <svl/stritem.hxx>
27 #include <comphelper/lok.hxx>
28 #include <comphelper/propertyvalue.hxx>
29 #include <sfx2/childwin.hxx>
30 #include <sfx2/lokhelper.hxx>
31 #include <svx/svdpage.hxx>
32 #include <vcl/scheduler.hxx>
33 #include <vcl/vclevent.hxx>
34 #include <vcl/virdev.hxx>
35 #include <sc.hrc>
36 #include <comphelper/string.hxx>
37 #include <tools/json_writer.hxx>
38 #include <docoptio.hxx>
39 #include <postit.hxx>
40 #include <test/lokcallback.hxx>
41 #include <osl/file.hxx>
42 #include <unotools/tempfile.hxx>
44 #include <chrono>
45 #include <cstddef>
47 #include <attrib.hxx>
48 #include <scitems.hxx>
49 #include <tabvwsh.hxx>
50 #include <docsh.hxx>
51 #include <document.hxx>
52 #include <docuno.hxx>
53 #include <drwlayer.hxx>
54 #include <editutil.hxx>
55 #include <undomanager.hxx>
57 using namespace css;
59 static std::ostream& operator<<(std::ostream& os, ViewShellId const & id)
61 os << static_cast<sal_Int32>(id); return os;
64 // for passing data to testInvalidateOnTextEditWithDifferentZoomLevels
65 struct ColRowZoom
67 SCCOL col;
68 SCROW row;
69 int zoom;
72 CPPUNIT_NS_BEGIN
73 namespace StringHelper
75 // used by CPPUNIT_TEST_PARAMETERIZED for testInvalidateOnTextEditWithDifferentZoomLevels
76 template<>
77 inline std::string toString(const ColRowZoom& item)
79 std::ostringstream ss;
80 ss << "zoom level: " << item.zoom << ", "
81 << "col: " << item.col << ", "
82 << "row: " << item.row;
83 return ss.str();
87 CPPUNIT_NS_END
89 namespace
92 class ScTiledRenderingTest : public UnoApiXmlTest
94 public:
95 ScTiledRenderingTest();
96 virtual void setUp() override;
97 virtual void tearDown() override;
99 void testRowColumnHeaders();
100 void testRowColumnSelections();
101 void testPartHash();
102 void testDocumentSize();
103 void testEmptyColumnSelection();
104 void testViewCursors();
105 void testTextViewSelection();
106 void testDocumentSizeChanged();
107 void testViewLock();
108 void testColRowResize();
109 void testUndoShells();
110 void testCreateViewGraphicSelection();
111 void testTextEditViews();
112 void testTextEditViewInvalidations();
113 void testGraphicInvalidate();
114 void testAutoSum();
115 void testHideColRow();
116 void testInvalidateOnCopyPasteCells();
117 void testInvalidateOnInserRowCol();
118 void testCommentCallback();
119 void testUndoLimiting();
120 void testUndoRepairDispatch();
121 void testInsertGraphicInvalidations();
122 void testDocumentSizeWithTwoViews();
123 void testDisableUndoRepair();
124 void testDocumentRepair();
125 void testLanguageStatus();
126 void testMultiViewCopyPaste();
127 void testIMESupport();
128 void testFilterDlg();
129 void testVbaRangeCopyPaste();
130 void testInvalidationLoop();
131 void testPageDownInvalidation();
132 void testSheetChangeInvalidation();
133 void testInsertDeletePageInvalidation();
134 void testGetRowColumnHeadersInvalidation();
135 void testJumpHorizontallyInvalidation();
136 void testJumpToLastRowInvalidation();
137 void testSheetGeometryDataInvariance();
138 void testSheetGeometryDataCorrectness();
139 void testDeleteCellMultilineContent();
140 void testFunctionDlg();
141 void testSpellOnlineParameter();
142 void testSpellOnlineRenderParameter();
143 void testPasteIntoWrapTextCell();
144 void testSortAscendingDescending();
145 void testAutoInputStringBlock();
146 void testAutoInputExactMatch();
147 void testMoveShapeHandle();
148 void testEditCursorBounds();
149 void testTextSelectionBounds();
150 void testSheetViewDataCrash();
151 void testTextBoxInsert();
152 void testCommentCellCopyPaste();
153 void testInvalidEntrySave();
154 void testUndoReordering();
155 void testUndoReorderingRedo();
156 void testUndoReorderingMulti();
157 void testGetViewRenderState();
158 void testInvalidateOnTextEditWithDifferentZoomLevels(const ColRowZoom& rData);
160 CPPUNIT_TEST_SUITE(ScTiledRenderingTest);
161 CPPUNIT_TEST(testRowColumnHeaders);
162 CPPUNIT_TEST(testRowColumnSelections);
163 CPPUNIT_TEST(testPartHash);
164 CPPUNIT_TEST(testDocumentSize);
165 CPPUNIT_TEST(testEmptyColumnSelection);
166 CPPUNIT_TEST(testViewCursors);
167 CPPUNIT_TEST(testTextViewSelection);
168 CPPUNIT_TEST(testDocumentSizeChanged);
169 CPPUNIT_TEST(testViewLock);
170 CPPUNIT_TEST(testColRowResize);
171 CPPUNIT_TEST(testUndoShells);
172 CPPUNIT_TEST(testCreateViewGraphicSelection);
173 CPPUNIT_TEST(testTextEditViews);
174 CPPUNIT_TEST(testTextEditViewInvalidations);
175 CPPUNIT_TEST(testGraphicInvalidate);
176 CPPUNIT_TEST(testAutoSum);
177 CPPUNIT_TEST(testHideColRow);
178 CPPUNIT_TEST(testInvalidateOnCopyPasteCells);
179 CPPUNIT_TEST(testInvalidateOnInserRowCol);
180 CPPUNIT_TEST(testCommentCallback);
181 CPPUNIT_TEST(testUndoLimiting);
182 CPPUNIT_TEST(testUndoRepairDispatch);
183 CPPUNIT_TEST(testInsertGraphicInvalidations);
184 CPPUNIT_TEST(testDocumentSizeWithTwoViews);
185 CPPUNIT_TEST(testDisableUndoRepair);
186 CPPUNIT_TEST(testDocumentRepair);
187 CPPUNIT_TEST(testLanguageStatus);
188 CPPUNIT_TEST(testMultiViewCopyPaste);
189 CPPUNIT_TEST(testIMESupport);
190 CPPUNIT_TEST(testFilterDlg);
191 CPPUNIT_TEST(testVbaRangeCopyPaste);
192 CPPUNIT_TEST(testInvalidationLoop);
193 CPPUNIT_TEST(testPageDownInvalidation);
194 CPPUNIT_TEST(testSheetChangeInvalidation);
195 CPPUNIT_TEST(testInsertDeletePageInvalidation);
196 CPPUNIT_TEST(testGetRowColumnHeadersInvalidation);
197 CPPUNIT_TEST(testJumpHorizontallyInvalidation);
198 CPPUNIT_TEST(testJumpToLastRowInvalidation);
199 CPPUNIT_TEST(testSheetGeometryDataInvariance);
200 CPPUNIT_TEST(testSheetGeometryDataCorrectness);
201 CPPUNIT_TEST(testDeleteCellMultilineContent);
202 CPPUNIT_TEST(testFunctionDlg);
203 CPPUNIT_TEST(testSpellOnlineParameter);
204 CPPUNIT_TEST(testSpellOnlineRenderParameter);
205 CPPUNIT_TEST(testPasteIntoWrapTextCell);
206 CPPUNIT_TEST(testSortAscendingDescending);
207 CPPUNIT_TEST(testAutoInputStringBlock);
208 CPPUNIT_TEST(testAutoInputExactMatch);
209 CPPUNIT_TEST(testMoveShapeHandle);
210 CPPUNIT_TEST(testEditCursorBounds);
211 CPPUNIT_TEST(testTextSelectionBounds);
212 CPPUNIT_TEST(testSheetViewDataCrash);
213 CPPUNIT_TEST(testTextBoxInsert);
214 CPPUNIT_TEST(testCommentCellCopyPaste);
215 CPPUNIT_TEST(testInvalidEntrySave);
216 CPPUNIT_TEST(testUndoReordering);
217 CPPUNIT_TEST(testUndoReorderingRedo);
218 CPPUNIT_TEST(testUndoReorderingMulti);
219 CPPUNIT_TEST(testGetViewRenderState);
220 CPPUNIT_TEST_PARAMETERIZED(testInvalidateOnTextEditWithDifferentZoomLevels,
221 std::initializer_list<ColRowZoom>
223 // zoom level 120%
224 {0, 999, 1}, {99, 0, 1},
225 // zoom level 40%
226 {0, 999, -5}, {99, 0, -5}
228 CPPUNIT_TEST_SUITE_END();
230 private:
231 ScModelObj* createDoc(const char* pName);
232 void setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell);
233 static void callback(int nType, const char* pPayload, void* pData);
234 void callbackImpl(int nType, const char* pPayload);
236 /// document size changed callback.
237 osl::Condition m_aDocSizeCondition;
238 Size m_aDocumentSize;
240 TestLokCallbackWrapper m_callbackWrapper;
243 ScTiledRenderingTest::ScTiledRenderingTest()
244 : UnoApiXmlTest("/sc/qa/unit/tiledrendering/data/"),
245 m_callbackWrapper(&callback, this)
249 void ScTiledRenderingTest::setUp()
251 UnoApiXmlTest::setUp();
253 comphelper::LibreOfficeKit::setActive(true);
256 void ScTiledRenderingTest::tearDown()
258 if (mxComponent.is())
260 mxComponent->dispose();
261 mxComponent.clear();
264 m_callbackWrapper.clear();
265 comphelper::LibreOfficeKit::setActive(false);
267 UnoApiXmlTest::tearDown();
270 ScModelObj* ScTiledRenderingTest::createDoc(const char* pName)
272 loadFromURL(OUString::createFromAscii(pName));
274 ScModelObj* pModelObj = dynamic_cast<ScModelObj*>(mxComponent.get());
275 CPPUNIT_ASSERT(pModelObj);
276 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
277 return pModelObj;
280 void ScTiledRenderingTest::setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell)
282 pViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
283 m_callbackWrapper.setLOKViewId(SfxLokHelper::getView(pViewShell));
286 void ScTiledRenderingTest::callback(int nType, const char* pPayload, void* pData)
288 static_cast<ScTiledRenderingTest*>(pData)->callbackImpl(nType, pPayload);
291 /* TODO when needed...
292 static std::vector<OUString> lcl_convertSeparated(const OUString& rString, sal_Unicode nSeparator)
294 std::vector<OUString> aRet;
296 sal_Int32 nIndex = 0;
299 OUString aToken = rString.getToken(0, nSeparator, nIndex);
300 aToken = aToken.trim();
301 if (!aToken.isEmpty())
302 aRet.push_back(aToken);
304 while (nIndex >= 0);
306 return aRet;
309 static void lcl_convertRectangle(const OUString& rString, Rectangle& rRectangle)
311 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(rString);
312 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength());
313 rRectangle.SetLeft(aSeq[0].toInt32());
314 rRectangle.SetTop(aSeq[1].toInt32());
315 rRectangle.setWidth(aSeq[2].toInt32());
316 rRectangle.setHeight(aSeq[3].toInt32());
320 void ScTiledRenderingTest::callbackImpl(int nType, const char* pPayload)
322 switch (nType)
324 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
326 OString aPayload(pPayload);
327 sal_Int32 nIndex = 0;
328 OString aToken = aPayload.getToken(0, ',', nIndex);
329 m_aDocumentSize.setWidth(aToken.toInt32());
330 aToken = aPayload.getToken(0, ',', nIndex);
331 m_aDocumentSize.setHeight(aToken.toInt32());
332 m_aDocSizeCondition.set();
334 break;
338 void ScTiledRenderingTest::testRowColumnSelections()
340 ScModelObj* pModelObj = createDoc("select-row-cols.ods");
342 // Select the 5th row with no modifier
343 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
344 { "Row", uno::Any(sal_Int32(5 - 1)) },
345 { "Modifier", uno::Any(sal_uInt16(0)) }
346 }));
347 dispatchCommand(mxComponent, ".uno:SelectRow", aArgs);
349 // Check if it is selected
350 OString aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
351 OString aExpected("1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\n");
352 CPPUNIT_ASSERT_EQUAL(aExpected, aResult);
354 // Select the 10th row with shift modifier
355 aArgs = comphelper::InitPropertySequence({ { "Row", uno::Any(static_cast<sal_Int32>(10 - 1)) },
356 { "Modifier", uno::Any(KEY_SHIFT) } });
357 dispatchCommand(mxComponent, ".uno:SelectRow", aArgs);
359 // Check if all the rows from 5th to 10th get selected
360 aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
361 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";
362 CPPUNIT_ASSERT_EQUAL(aExpected, aResult);
364 // Select the 10th row with ctrl modifier
365 aArgs = comphelper::InitPropertySequence({ { "Row", uno::Any(static_cast<sal_Int32>(13 - 1)) },
366 { "Modifier", uno::Any(KEY_MOD1) } });
367 dispatchCommand(mxComponent, ".uno:SelectRow", aArgs);
369 // When we copy this, we don't get anything useful, but we must not crash
370 // (used to happen)
371 aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
372 CPPUNIT_ASSERT_EQUAL(OString(), aResult);
374 // TODO check that we really selected what we wanted here
376 // Select Column 5 with ctrl modifier
377 aArgs = comphelper::InitPropertySequence({ { "Col", uno::Any(static_cast<sal_Int32>(5 - 1)) },
378 { "Modifier", uno::Any(KEY_MOD1) } });
379 dispatchCommand(mxComponent, ".uno:SelectColumn", aArgs);
381 // When we copy this, we don't get anything useful, but we must not crash
382 // (used to happen)
383 aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
384 CPPUNIT_ASSERT_EQUAL(OString(), aResult);
386 // TODO check that we really selected what we wanted here
388 // Test for deselection of already selected rows
389 // First Deselect Row 13 because copy doesn't work for multiple selections
390 aArgs = comphelper::InitPropertySequence({ { "Row", uno::Any(static_cast<sal_Int32>(13 - 1)) },
391 { "Modifier", uno::Any(KEY_MOD1) } });
392 dispatchCommand(mxComponent, ".uno:SelectRow", aArgs);
394 // Deselect row 10
395 aArgs = comphelper::InitPropertySequence({ { "Row", uno::Any(static_cast<sal_Int32>(10 - 1)) },
396 { "Modifier", uno::Any(KEY_MOD1) } });
397 dispatchCommand(mxComponent, ".uno:SelectRow", aArgs);
399 // Click at row 6 holding shift
400 aArgs = comphelper::InitPropertySequence({ { "Row", uno::Any(static_cast<sal_Int32>(6 - 1)) },
401 { "Modifier", uno::Any(KEY_SHIFT) } });
402 dispatchCommand(mxComponent, ".uno:SelectRow", aArgs);
404 // only row 5 should remain selected
405 aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
406 aExpected = "1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\n";
407 CPPUNIT_ASSERT_EQUAL(aExpected, aResult);
410 void ScTiledRenderingTest::testPartHash()
412 ScModelObj* pModelObj = createDoc("sort-range.ods");
414 int nParts = pModelObj->getParts();
415 for (int it = 0; it < nParts; it++)
417 CPPUNIT_ASSERT(!pModelObj->getPartHash(it).isEmpty());
420 // check part that it does not exists
421 CPPUNIT_ASSERT(pModelObj->getPartHash(100).isEmpty());
424 void ScTiledRenderingTest::testDocumentSize()
426 ScModelObj* pModelObj = createDoc("sort-range.ods");
427 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
428 CPPUNIT_ASSERT(pDocSh);
430 ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false);
431 CPPUNIT_ASSERT(pViewShell);
433 setupLibreOfficeKitViewCallback(pViewShell);
435 // check initial document size
436 Size aDocSize = pModelObj->getDocumentSize();
437 CPPUNIT_ASSERT(aDocSize.Width() > 0);
438 CPPUNIT_ASSERT(aDocSize.Height() > 0);
440 // Set cursor column
441 pViewShell->SetCursor(100, 0);
442 // 2 seconds
443 osl::Condition::Result aResult = m_aDocSizeCondition.wait(std::chrono::seconds(2));
444 CPPUNIT_ASSERT_EQUAL(osl::Condition::result_ok, aResult);
446 // Set cursor row
447 pViewShell->SetCursor(0, 100);
448 // 2 seconds
449 aResult = m_aDocSizeCondition.wait(std::chrono::seconds(2));
450 CPPUNIT_ASSERT_EQUAL(osl::Condition::result_ok, aResult);
453 void ScTiledRenderingTest::testEmptyColumnSelection()
455 ScModelObj* pModelObj = createDoc("select-row-cols.ods");
457 // Select empty column, 1000
458 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
459 { "Col", uno::Any(sal_Int32(1000 - 1)) },
460 { "Modifier", uno::Any(sal_uInt16(0)) }
461 }));
462 dispatchCommand(mxComponent, ".uno:SelectColumn", aArgs);
464 // should be an empty string
465 CPPUNIT_ASSERT_EQUAL(OString(), apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8"));
468 struct EditCursorMessage final {
469 tools::Rectangle m_aRelRect;
470 Point m_aRefPoint;
472 void clear()
474 m_aRelRect.SetEmpty();
475 m_aRefPoint = Point(-1, -1);
478 bool empty()
480 return m_aRelRect.IsEmpty() &&
481 m_aRefPoint.X() == -1 &&
482 m_aRefPoint.Y() == -1;
485 void parseMessage(const char* pMessage)
487 clear();
488 if (!pMessage || !comphelper::LibreOfficeKit::isCompatFlagSet(
489 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs) ||
490 !comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
491 return;
493 std::stringstream aStream(pMessage);
494 boost::property_tree::ptree aTree;
495 boost::property_tree::read_json(aStream, aTree);
496 std::string aVal;
497 boost::property_tree::ptree::const_assoc_iterator it = aTree.find("refpoint");
498 if (it != aTree.not_found())
499 aVal = aTree.get_child("refpoint").get_value<std::string>();
500 else
501 return; // happens in testTextBoxInsert test
503 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(aVal.c_str()));
504 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aSeq.getLength());
505 m_aRefPoint.setX(aSeq[0].toInt32());
506 m_aRefPoint.setY(aSeq[1].toInt32());
508 aVal = aTree.get_child("relrect").get_value<std::string>();
509 aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(aVal.c_str()));
510 CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aSeq.getLength());
511 m_aRelRect.SetLeft(aSeq[0].toInt32());
512 m_aRelRect.SetTop(aSeq[1].toInt32());
513 m_aRelRect.setWidth(aSeq[2].toInt32());
514 m_aRelRect.setHeight(aSeq[3].toInt32());
517 tools::Rectangle getBounds()
519 tools::Rectangle aBounds = m_aRelRect;
520 aBounds.Move(m_aRefPoint.X(), m_aRefPoint.Y());
521 return aBounds;
525 struct TextSelectionMessage
527 std::vector<tools::Rectangle> m_aRelRects;
528 Point m_aRefPoint;
530 void clear() {
531 m_aRefPoint.setX(0);
532 m_aRefPoint.setY(0);
533 m_aRelRects.clear();
536 bool empty() {
537 return m_aRelRects.empty();
540 void parseMessage(const char* pMessage)
542 clear();
543 if (!pMessage)
544 return;
546 std::string aStr(pMessage);
547 if (aStr.find(",") == std::string::npos)
548 return;
550 size_t nRefDelimStart = aStr.find("::");
551 std::string aRectListString = (nRefDelimStart == std::string::npos) ? aStr : aStr.substr(0, nRefDelimStart);
552 std::string aRefPointString = (nRefDelimStart == std::string::npos) ?
553 std::string("0, 0") :
554 aStr.substr(nRefDelimStart + 2, aStr.length() - 2 - nRefDelimStart);
555 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(aRefPointString.c_str()));
556 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aSeq.getLength());
557 m_aRefPoint.setX(aSeq[0].toInt32());
558 m_aRefPoint.setY(aSeq[1].toInt32());
560 size_t nStart = 0;
561 size_t nEnd = aRectListString.find(";");
562 if (nEnd == std::string::npos)
563 nEnd = aRectListString.length();
566 std::string aRectString = aRectListString.substr(nStart, nEnd - nStart);
568 aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(aRectString.c_str()));
569 CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aSeq.getLength());
570 tools::Rectangle aRect;
571 aRect.SetLeft(aSeq[0].toInt32());
572 aRect.SetTop(aSeq[1].toInt32());
573 aRect.setWidth(aSeq[2].toInt32());
574 aRect.setHeight(aSeq[3].toInt32());
576 m_aRelRects.push_back(aRect);
579 nStart = nEnd + 1;
580 nEnd = aRectListString.find(";", nStart);
582 while(nEnd != std::string::npos);
585 tools::Rectangle getBounds(size_t nIndex)
587 if (nIndex >= m_aRelRects.size())
588 return tools::Rectangle();
590 tools::Rectangle aBounds = m_aRelRects[nIndex];
591 aBounds.Move(m_aRefPoint.X(), m_aRefPoint.Y());
592 return aBounds;
597 /// A view callback tracks callbacks invoked on one specific view.
598 class ViewCallback final
600 SfxViewShell* mpViewShell;
601 int mnView;
602 public:
603 bool m_bOwnCursorInvalidated;
604 bool m_bViewCursorInvalidated;
605 bool m_bTextViewSelectionInvalidated;
606 bool m_bGraphicSelection;
607 bool m_bGraphicViewSelection;
608 bool m_bFullInvalidateTiles;
609 bool m_bInvalidateTiles;
610 std::vector<tools::Rectangle> m_aInvalidations;
611 tools::Rectangle m_aCellCursorBounds;
612 std::vector<int> m_aInvalidationsParts;
613 std::vector<int> m_aInvalidationsMode;
614 bool m_bViewLock;
615 OString m_sCellFormula;
616 boost::property_tree::ptree m_aCommentCallbackResult;
617 EditCursorMessage m_aInvalidateCursorResult;
618 TextSelectionMessage m_aTextSelectionResult;
619 OString m_sInvalidateHeader;
620 OString m_sInvalidateSheetGeometry;
621 OString m_ShapeSelection;
622 TestLokCallbackWrapper m_callbackWrapper;
624 ViewCallback(bool bDeleteListenerOnDestruct=true)
625 : m_bOwnCursorInvalidated(false),
626 m_bViewCursorInvalidated(false),
627 m_bTextViewSelectionInvalidated(false),
628 m_bGraphicSelection(false),
629 m_bGraphicViewSelection(false),
630 m_bFullInvalidateTiles(false),
631 m_bInvalidateTiles(false),
632 m_bViewLock(false),
633 m_callbackWrapper(&callback, this)
635 mpViewShell = SfxViewShell::Current();
636 mpViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
637 mnView = SfxLokHelper::getView();
638 m_callbackWrapper.setLOKViewId( mnView );
639 if (!bDeleteListenerOnDestruct)
640 mpViewShell = nullptr;
643 ~ViewCallback()
645 if (mpViewShell)
647 SfxLokHelper::setView(mnView);
648 mpViewShell->setLibreOfficeKitViewCallback(nullptr);
652 static void callback(int nType, const char* pPayload, void* pData)
654 static_cast<ViewCallback*>(pData)->callbackImpl(nType, pPayload);
657 void callbackImpl(int nType, const char* pPayload)
659 switch (nType)
661 case LOK_CALLBACK_CELL_CURSOR:
663 m_bOwnCursorInvalidated = true;
664 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload));
665 m_aCellCursorBounds = tools::Rectangle();
666 if (aSeq.getLength() == 6) {
667 m_aCellCursorBounds.SetLeft(aSeq[0].toInt32());
668 m_aCellCursorBounds.SetTop(aSeq[1].toInt32());
669 m_aCellCursorBounds.setWidth(aSeq[2].toInt32());
670 m_aCellCursorBounds.setHeight(aSeq[3].toInt32());
673 break;
674 case LOK_CALLBACK_CELL_VIEW_CURSOR:
676 m_bViewCursorInvalidated = true;
678 break;
679 case LOK_CALLBACK_TEXT_VIEW_SELECTION:
681 m_bTextViewSelectionInvalidated = true;
683 break;
684 case LOK_CALLBACK_VIEW_LOCK:
686 std::stringstream aStream(pPayload);
687 boost::property_tree::ptree aTree;
688 boost::property_tree::read_json(aStream, aTree);
689 m_bViewLock = aTree.get_child("rectangle").get_value<std::string>() != "EMPTY";
691 break;
692 case LOK_CALLBACK_GRAPHIC_SELECTION:
694 m_bGraphicSelection = true;
695 m_ShapeSelection = OString(pPayload);
697 break;
698 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
700 m_bGraphicViewSelection = true;
702 break;
703 case LOK_CALLBACK_INVALIDATE_TILES:
705 OString text(pPayload);
706 if (text.startsWith("EMPTY"))
708 m_bFullInvalidateTiles = true;
710 else
712 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload));
713 CPPUNIT_ASSERT(aSeq.getLength() == 4 || aSeq.getLength() == 6);
714 tools::Rectangle aInvalidationRect;
715 aInvalidationRect.SetLeft(aSeq[0].toInt32());
716 aInvalidationRect.SetTop(aSeq[1].toInt32());
717 aInvalidationRect.setWidth(aSeq[2].toInt32());
718 aInvalidationRect.setHeight(aSeq[3].toInt32());
719 m_aInvalidations.push_back(aInvalidationRect);
720 if (aSeq.getLength() == 6)
722 m_aInvalidationsParts.push_back(aSeq[4].toInt32());
723 m_aInvalidationsMode.push_back(aSeq[5].toInt32());
725 m_bInvalidateTiles = true;
728 break;
729 case LOK_CALLBACK_CELL_FORMULA:
731 m_sCellFormula = pPayload;
733 break;
734 case LOK_CALLBACK_COMMENT:
736 m_aCommentCallbackResult.clear();
737 std::stringstream aStream(pPayload);
738 boost::property_tree::read_json(aStream, m_aCommentCallbackResult);
739 m_aCommentCallbackResult = m_aCommentCallbackResult.get_child("comment");
741 break;
742 case LOK_CALLBACK_INVALIDATE_HEADER:
744 m_sInvalidateHeader = pPayload;
746 break;
747 case LOK_CALLBACK_INVALIDATE_SHEET_GEOMETRY:
749 m_sInvalidateSheetGeometry = pPayload;
751 break;
752 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
754 m_aInvalidateCursorResult.parseMessage(pPayload);
756 break;
757 case LOK_CALLBACK_TEXT_SELECTION:
759 m_aTextSelectionResult.parseMessage(pPayload);
766 void ScTiledRenderingTest::testViewCursors()
768 ScModelObj* pModelObj = createDoc("select-row-cols.ods");
769 ViewCallback aView1;
770 SfxLokHelper::createView();
771 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
772 ViewCallback aView2(/*bDeleteListenerOnDestruct*/false);
773 // This was false, the new view did not get the view (cell) cursor of the old view.
774 CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
775 CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
776 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN);
777 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN);
778 Scheduler::ProcessEventsToIdle();
779 SfxLokHelper::destroyView(SfxLokHelper::getView());
780 CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
783 void ScTiledRenderingTest::testSpellOnlineRenderParameter()
785 ScModelObj* pModelObj = createDoc("empty.ods");
786 ScDocument* pDoc = pModelObj->GetDocument();
787 bool bSet = pDoc->GetDocOptions().IsAutoSpell();
789 uno::Sequence<beans::PropertyValue> aPropertyValues =
791 comphelper::makePropertyValue(".uno:SpellOnline", uno::Any(!bSet)),
793 pModelObj->initializeForTiledRendering(aPropertyValues);
795 CPPUNIT_ASSERT_EQUAL(!bSet, pDoc->GetDocOptions().IsAutoSpell());
798 void ScTiledRenderingTest::testTextViewSelection()
800 // Create two views, and leave the second one current.
801 ScModelObj* pModelObj = createDoc("select-row-cols.ods");
802 ViewCallback aView1;
803 SfxLokHelper::createView();
804 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
805 ViewCallback aView2;
807 // Create a selection on two cells in the second view, that's a text selection in LOK terms.
808 aView1.m_bTextViewSelectionInvalidated = false;
809 dispatchCommand(mxComponent, ".uno:GoRightSel", {});
810 Scheduler::ProcessEventsToIdle();
811 // Make sure the first view got its notification.
812 CPPUNIT_ASSERT(aView1.m_bTextViewSelectionInvalidated);
815 void ScTiledRenderingTest::testDocumentSizeChanged()
817 // Load a document that doesn't have much content.
818 createDoc("small.ods");
819 setupLibreOfficeKitViewCallback(SfxViewShell::Current());
821 // Go to the A30 cell -- that will extend the document size.
822 uno::Sequence<beans::PropertyValue> aPropertyValues =
824 comphelper::makePropertyValue("ToPoint", OUString("$A$30")),
826 dispatchCommand(mxComponent, ".uno:GoToCell", aPropertyValues);
827 Scheduler::ProcessEventsToIdle();
828 // Assert that the size in the payload is not 0.
829 CPPUNIT_ASSERT(m_aDocumentSize.getWidth() > 0);
830 CPPUNIT_ASSERT(m_aDocumentSize.getHeight() > 0);
833 void ScTiledRenderingTest::testViewLock()
835 // Load a document that has a shape and create two views.
836 ScModelObj* pModelObj = createDoc("shape.ods");
837 ViewCallback aView1;
838 SfxLokHelper::createView();
839 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
840 ViewCallback aView2;
842 // Begin text edit in the second view and assert that the first gets a lock
843 // notification.
844 const ScViewData* pViewData = ScDocShell::GetViewData();
845 ScTabViewShell* pViewShell = pViewData->GetViewShell();
846 CPPUNIT_ASSERT(pViewShell);
847 SdrModel* pDrawModel = pViewData->GetDocument().GetDrawLayer();
848 SdrPage* pDrawPage = pDrawModel->GetPage(0);
849 SdrObject* pObject = pDrawPage->GetObj(0);
850 SdrView* pView = pViewShell->GetScDrawView();
851 aView1.m_bViewLock = false;
852 pView->SdrBeginTextEdit(pObject);
853 CPPUNIT_ASSERT(aView1.m_bViewLock);
855 // End text edit in the second view, and assert that the lock is removed in
856 // the first view.
857 pView->SdrEndTextEdit();
858 CPPUNIT_ASSERT(!aView1.m_bViewLock);
861 void lcl_extractHandleParameters(std::string_view selection, sal_uInt32& id, sal_uInt32& x, sal_uInt32& y)
863 OString extraInfo( selection.substr(selection.find("{")) );
864 std::stringstream aStream(extraInfo.getStr());
865 boost::property_tree::ptree aTree;
866 boost::property_tree::read_json(aStream, aTree);
867 boost::property_tree::ptree
868 handle0 = aTree
869 .get_child("handles")
870 .get_child("kinds")
871 .get_child("rectangle")
872 .get_child("1")
873 .begin()->second;
874 id = handle0.get_child("id").get_value<int>();
875 x = handle0.get_child("point").get_child("x").get_value<int>();
876 y = handle0.get_child("point").get_child("y").get_value<int>();
879 void ScTiledRenderingTest::testMoveShapeHandle()
881 ScModelObj* pModelObj = createDoc("shape.ods");
882 ViewCallback aView1;
883 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, /*x=*/ 1,/*y=*/ 1,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
884 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, /*x=*/ 1, /*y=*/ 1, /*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
885 Scheduler::ProcessEventsToIdle();
887 CPPUNIT_ASSERT(!aView1.m_ShapeSelection.isEmpty());
889 sal_uInt32 id, x, y;
890 lcl_extractHandleParameters(aView1.m_ShapeSelection, id, x ,y);
891 sal_uInt32 oldX = x;
892 sal_uInt32 oldY = y;
893 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
895 {"HandleNum", uno::Any(id)},
896 {"NewPosX", uno::Any(x+1)},
897 {"NewPosY", uno::Any(y+1)}
898 }));
899 dispatchCommand(mxComponent, ".uno:MoveShapeHandle", aPropertyValues);
900 Scheduler::ProcessEventsToIdle();
901 CPPUNIT_ASSERT(!aView1.m_ShapeSelection.isEmpty());
902 lcl_extractHandleParameters(aView1.m_ShapeSelection, id, x ,y);
903 CPPUNIT_ASSERT_EQUAL(x-1, oldX);
904 CPPUNIT_ASSERT_EQUAL(y-1, oldY);
908 void ScTiledRenderingTest::testColRowResize()
910 ScModelObj* pModelObj = createDoc("sort-range.ods");
911 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
912 CPPUNIT_ASSERT(pDocSh);
914 ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false);
915 CPPUNIT_ASSERT(pViewShell);
917 setupLibreOfficeKitViewCallback(pViewShell);
919 ScDocument& rDoc = pDocSh->GetDocument();
921 // Col 3, Tab 0
922 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
923 { "ColumnWidth", uno::Any(sal_uInt16(4000)) }, // 4cm
924 { "Column", uno::Any(sal_Int16(3)) }
925 }));
926 dispatchCommand(mxComponent, ".uno:ColumnWidth", aArgs);
928 sal_uInt16 nWidth = o3tl::convert(rDoc.GetColWidth(static_cast<SCCOL>(2), static_cast<SCTAB>(0), false), o3tl::Length::twip, o3tl::Length::mm100);
929 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(4001), nWidth);
931 // Row 5, Tab 0
932 uno::Sequence<beans::PropertyValue> aArgs2( comphelper::InitPropertySequence({
933 { "RowHeight", uno::Any(sal_uInt16(2000)) },
934 { "Row", uno::Any(sal_Int16(5)) },
935 }));
936 dispatchCommand(mxComponent, ".uno:RowHeight", aArgs2);
938 sal_uInt16 nHeight = o3tl::convert(rDoc.GetRowHeight(static_cast<SCROW>(4), static_cast<SCTAB>(0), false), o3tl::Length::twip, o3tl::Length::mm100);
939 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(2000), nHeight);
942 void ScTiledRenderingTest::testUndoShells()
944 ScModelObj* pModelObj = createDoc("small.ods");
945 // Clear the currently selected cell.
946 dispatchCommand(mxComponent, ".uno:ClearContents", {});
948 auto pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
949 CPPUNIT_ASSERT(pDocShell);
950 ScDocument& rDoc = pDocShell->GetDocument();
951 ScUndoManager* pUndoManager = rDoc.GetUndoManager();
952 CPPUNIT_ASSERT(pUndoManager);
953 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pUndoManager->GetUndoActionCount());
954 sal_Int32 nView1 = SfxLokHelper::getView();
955 // This was -1: ScSimpleUndo did not remember what view shell created it.
956 CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), pUndoManager->GetUndoAction()->GetViewShellId());
959 bool lcl_hasEditView(const ScViewData& rViewData)
961 bool bResult = false;
962 for (unsigned int i=0; i<4; i++)
964 bResult = rViewData.HasEditView( static_cast<ScSplitPos>(i) );
965 if (bResult) break;
967 return bResult;
970 void ScTiledRenderingTest::testTextEditViews()
972 ScModelObj* pModelObj = createDoc("small.ods");
973 CPPUNIT_ASSERT(pModelObj);
974 ScViewData* pViewData = ScDocShell::GetViewData();
975 CPPUNIT_ASSERT(pViewData);
977 // view #1
978 ViewCallback aView1;
979 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
981 // text edit a cell in view #1
982 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
983 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
984 Scheduler::ProcessEventsToIdle();
985 CPPUNIT_ASSERT(lcl_hasEditView(*pViewData));
987 // view #2
988 SfxLokHelper::createView();
989 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
990 ViewCallback aView2;
992 // move cell cursor i view #2
993 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN);
994 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN);
995 Scheduler::ProcessEventsToIdle();
997 // check that text edit view in view #1 has not be killed
998 CPPUNIT_ASSERT(lcl_hasEditView(*pViewData));
1001 void ScTiledRenderingTest::testTextEditViewInvalidations()
1003 ScModelObj* pModelObj = createDoc("small.ods");
1004 CPPUNIT_ASSERT(pModelObj);
1005 ScViewData* pViewData = ScDocShell::GetViewData();
1006 CPPUNIT_ASSERT(pViewData);
1008 // view #1
1009 int nView1 = SfxLokHelper::getView();
1010 ViewCallback aView1;
1011 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
1013 // view #2
1014 SfxLokHelper::createView();
1015 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1016 ViewCallback aView2;
1018 // text edit a cell in view #1
1019 SfxLokHelper::setView(nView1);
1020 aView2.m_bInvalidateTiles = false;
1021 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1022 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1023 Scheduler::ProcessEventsToIdle();
1024 CPPUNIT_ASSERT(lcl_hasEditView(*pViewData));
1025 CPPUNIT_ASSERT(aView2.m_bInvalidateTiles);
1027 // text edit a cell in view #1 until
1028 // we can be sure we are out of the initial tile
1029 for (int i = 0; i < 40; ++i)
1031 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1032 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1034 Scheduler::ProcessEventsToIdle();
1036 // text edit a cell in view #1 inside the new tile and
1037 // check that view #2 receive a tile invalidate message
1038 aView2.m_bInvalidateTiles = false;
1039 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1040 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1041 Scheduler::ProcessEventsToIdle();
1042 CPPUNIT_ASSERT(aView2.m_bInvalidateTiles);
1044 // view #3
1045 SfxLokHelper::createView();
1046 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1047 ViewCallback aView3;
1049 // text edit a cell in view #1
1050 SfxLokHelper::setView(nView1);
1051 aView3.m_bInvalidateTiles = false;
1052 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'y', 0);
1053 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'y', 0);
1054 Scheduler::ProcessEventsToIdle();
1055 CPPUNIT_ASSERT(aView3.m_bInvalidateTiles);
1058 void ScTiledRenderingTest::testCreateViewGraphicSelection()
1060 // Load a document that has a shape and create two views.
1061 ScModelObj* pModelObj = createDoc("shape.ods");
1062 ViewCallback aView1;
1064 // Mark the graphic in the first view.
1065 const ScViewData* pViewData = ScDocShell::GetViewData();
1066 ScTabViewShell* pViewShell = pViewData->GetViewShell();
1067 CPPUNIT_ASSERT(pViewShell);
1068 SdrModel* pDrawModel = pViewData->GetDocument().GetDrawLayer();
1069 SdrPage* pDrawPage = pDrawModel->GetPage(0);
1070 SdrObject* pObject = pDrawPage->GetObj(0);
1071 SdrView* pView = pViewShell->GetScDrawView();
1072 aView1.m_bGraphicSelection = false;
1073 aView1.m_bGraphicViewSelection = false;
1074 pView->MarkObj(pObject, pView->GetSdrPageView());
1075 CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
1077 // Create a second view.
1078 int nView1 = SfxLokHelper::getView();
1079 SfxLokHelper::createView();
1080 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1081 ViewCallback aView2;
1082 CPPUNIT_ASSERT(aView2.m_bGraphicViewSelection);
1083 CPPUNIT_ASSERT(aView1.m_bGraphicViewSelection);
1085 SfxLokHelper::setView(nView1);
1086 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1089 void ScTiledRenderingTest::testGraphicInvalidate()
1091 // Load a document that has a shape and create two views.
1092 ScModelObj* pModelObj = createDoc("shape.ods");
1093 ViewCallback aView;
1095 // Click to select graphic
1096 aView.m_bGraphicSelection = false;
1097 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, /*x=*/ 1,/*y=*/ 1,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
1098 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, /*x=*/ 1, /*y=*/ 1, /*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
1099 Scheduler::ProcessEventsToIdle();
1100 CPPUNIT_ASSERT(aView.m_bGraphicSelection);
1102 // Drag Drop graphic
1103 aView.m_bGraphicSelection = false;
1104 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, /*x=*/ 1,/*y=*/ 1,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
1105 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEMOVE, /*x=*/ 1,/*y=*/ 10,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
1106 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, /*x=*/ 1, /*y=*/ 10, /*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
1107 Scheduler::ProcessEventsToIdle();
1108 CPPUNIT_ASSERT(!aView.m_bFullInvalidateTiles);
1110 // Check again
1111 Scheduler::ProcessEventsToIdle();
1112 CPPUNIT_ASSERT(!aView.m_bFullInvalidateTiles);
1115 void ScTiledRenderingTest::testAutoSum()
1117 createDoc("small.ods");
1119 ViewCallback aView;
1121 uno::Sequence<beans::PropertyValue> aArgs;
1122 dispatchCommand(mxComponent, ".uno:AutoSum", aArgs);
1123 Scheduler::ProcessEventsToIdle();
1124 CPPUNIT_ASSERT(aView.m_sCellFormula.startsWith("=SUM("));
1127 void ScTiledRenderingTest::testHideColRow()
1129 createDoc("small.ods");
1131 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
1132 { "Col", uno::Any(sal_Int32(2 - 1)) },
1133 { "Modifier", uno::Any(KEY_SHIFT) }
1134 }));
1135 dispatchCommand(mxComponent, ".uno:SelectColumn", aArgs);
1137 uno::Sequence<beans::PropertyValue> aArgs2( comphelper::InitPropertySequence({
1138 { "Col", uno::Any(sal_Int32(3 - 1)) },
1139 { "Modifier", uno::Any(sal_uInt16(0)) }
1140 }));
1142 dispatchCommand(mxComponent, ".uno:SelectColumn", aArgs2);
1143 Scheduler::ProcessEventsToIdle();
1146 SCCOL nOldCurX = ScDocShell::GetViewData()->GetCurX();
1147 SCROW nOldCurY = ScDocShell::GetViewData()->GetCurY();
1149 uno::Sequence<beans::PropertyValue> aArgs;
1150 dispatchCommand(mxComponent, ".uno:HideColumn", aArgs);
1151 Scheduler::ProcessEventsToIdle();
1154 SCCOL nNewCurX = ScDocShell::GetViewData()->GetCurX();
1155 SCROW nNewCurY = ScDocShell::GetViewData()->GetCurY();
1156 CPPUNIT_ASSERT(nNewCurX > nOldCurX);
1157 CPPUNIT_ASSERT_EQUAL(nOldCurY, nNewCurY);
1159 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
1160 { "Row", uno::Any(sal_Int32(6 - 1)) },
1161 { "Modifier", uno::Any(KEY_SHIFT) }
1162 }));
1163 dispatchCommand(mxComponent, ".uno:SelectRow", aArgs);
1165 uno::Sequence<beans::PropertyValue> aArgs2( comphelper::InitPropertySequence({
1166 { "Row", uno::Any(sal_Int32(7 - 1)) },
1167 { "Modifier", uno::Any(sal_uInt16(0)) }
1168 }));
1169 dispatchCommand(mxComponent, ".uno:SelectRow", aArgs2);
1170 Scheduler::ProcessEventsToIdle();
1173 nOldCurX = ScDocShell::GetViewData()->GetCurX();
1174 nOldCurY = ScDocShell::GetViewData()->GetCurY();
1176 uno::Sequence<beans::PropertyValue> aArgs;
1177 dispatchCommand(mxComponent, ".uno:HideRow", aArgs);
1178 Scheduler::ProcessEventsToIdle();
1180 nNewCurX = ScDocShell::GetViewData()->GetCurX();
1181 nNewCurY = ScDocShell::GetViewData()->GetCurY();
1182 CPPUNIT_ASSERT(nNewCurY > nOldCurY);
1183 CPPUNIT_ASSERT_EQUAL(nOldCurX, nNewCurX);
1186 void ScTiledRenderingTest::testInvalidateOnCopyPasteCells()
1188 ScModelObj* pModelObj = createDoc("small.ods");
1189 CPPUNIT_ASSERT(pModelObj);
1191 // view
1192 ViewCallback aView;
1194 uno::Sequence<beans::PropertyValue> aArgs;
1195 // select and copy cells
1196 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
1197 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
1198 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_SHIFT);
1199 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_SHIFT);
1200 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT | KEY_SHIFT);
1201 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT | KEY_SHIFT);
1202 Scheduler::ProcessEventsToIdle();
1203 dispatchCommand(mxComponent, ".uno:Copy", aArgs);
1205 // move to destination cell
1206 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
1207 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
1208 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_MOD1);
1209 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_MOD1);
1210 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_UP);
1211 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_UP);
1212 Scheduler::ProcessEventsToIdle();
1214 // paste cells
1215 aView.m_bInvalidateTiles = false;
1216 dispatchCommand(mxComponent, ".uno:Paste", aArgs);
1217 Scheduler::ProcessEventsToIdle();
1218 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1221 void ScTiledRenderingTest::testInvalidateOnInserRowCol()
1223 ScModelObj* pModelObj = createDoc("small.ods");
1224 CPPUNIT_ASSERT(pModelObj);
1226 // view
1227 ViewCallback aView;
1229 uno::Sequence<beans::PropertyValue> aArgs;
1230 // move downward
1231 for (int i = 0; i < 200; ++i)
1233 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
1234 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
1236 Scheduler::ProcessEventsToIdle();
1238 // insert row
1239 aView.m_bInvalidateTiles = false;
1240 aView.m_aInvalidations.clear();
1241 dispatchCommand(mxComponent, ".uno:InsertRows", aArgs);
1242 Scheduler::ProcessEventsToIdle();
1243 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1244 CPPUNIT_ASSERT_EQUAL(size_t(1), aView.m_aInvalidations.size());
1245 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(-75, 51240, 32212230, 63990), aView.m_aInvalidations[0]);
1247 // move on the right
1248 for (int i = 0; i < 200; ++i)
1250 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT);
1251 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT);
1253 Scheduler::ProcessEventsToIdle();
1255 // insert column
1256 aView.m_bInvalidateTiles = false;
1257 aView.m_aInvalidations.clear();
1258 dispatchCommand(mxComponent, ".uno:InsertColumns", aArgs);
1259 Scheduler::ProcessEventsToIdle();
1260 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1261 CPPUNIT_ASSERT_EQUAL(size_t(1), aView.m_aInvalidations.size());
1262 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(254925, -15, 32212230, 63990), aView.m_aInvalidations[0]);
1265 void ScTiledRenderingTest::testCommentCallback()
1267 // Comments callback are emitted only if tiled annotations are off
1268 comphelper::LibreOfficeKit::setTiledAnnotations(false);
1270 // FIXME: Hack because previous tests do not destroy ScDocument(with annotations) on exit (?).
1271 ScPostIt::mnLastPostItId = 1;
1274 ScModelObj* pModelObj = createDoc("small.ods");
1275 ViewCallback aView1;
1276 int nView1 = SfxLokHelper::getView();
1278 // Create a 2nd view
1279 SfxLokHelper::createView();
1280 pModelObj->initializeForTiledRendering({});
1281 ViewCallback aView2;
1283 SfxLokHelper::setView(nView1);
1285 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
1286 if (pTabViewShell)
1287 pTabViewShell->SetCursor(4, 4);
1289 // Add a new comment
1290 uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
1292 {"Text", uno::Any(OUString("Comment"))},
1293 {"Author", uno::Any(OUString("LOK User1"))},
1294 }));
1295 dispatchCommand(mxComponent, ".uno:InsertAnnotation", aArgs);
1296 Scheduler::ProcessEventsToIdle();
1298 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
1299 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1300 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
1301 CPPUNIT_ASSERT_EQUAL(std::string("1"), aView1.m_aCommentCallbackResult.get<std::string>("id"));
1302 CPPUNIT_ASSERT_EQUAL(std::string("1"), aView2.m_aCommentCallbackResult.get<std::string>("id"));
1303 CPPUNIT_ASSERT_EQUAL(std::string("0"), aView1.m_aCommentCallbackResult.get<std::string>("tab"));
1304 CPPUNIT_ASSERT_EQUAL(std::string("0"), aView2.m_aCommentCallbackResult.get<std::string>("tab"));
1305 CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), aView1.m_aCommentCallbackResult.get<std::string>("author"));
1306 CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), aView2.m_aCommentCallbackResult.get<std::string>("author"));
1307 CPPUNIT_ASSERT_EQUAL(std::string("Comment"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
1308 CPPUNIT_ASSERT_EQUAL(std::string("Comment"), aView2.m_aCommentCallbackResult.get<std::string>("text"));
1309 CPPUNIT_ASSERT_EQUAL(std::string("4 4 4 4"), aView1.m_aCommentCallbackResult.get<std::string>("cellRange"));
1310 CPPUNIT_ASSERT_EQUAL(std::string("4 4 4 4"), aView2.m_aCommentCallbackResult.get<std::string>("cellRange"));
1312 // Ensure deleting rows updates comments
1313 if (pTabViewShell)
1314 pTabViewShell->SetCursor(2, 2);
1316 dispatchCommand(mxComponent, ".uno:DeleteRows", {});
1317 Scheduler::ProcessEventsToIdle();
1318 CPPUNIT_ASSERT_EQUAL(std::string("4 3 4 3"), aView1.m_aCommentCallbackResult.get<std::string>("cellRange"));
1319 CPPUNIT_ASSERT_EQUAL(std::string("4 3 4 3"), aView2.m_aCommentCallbackResult.get<std::string>("cellRange"));
1321 // Ensure deleting columns updates comments
1322 if (pTabViewShell)
1323 pTabViewShell->SetCursor(2, 2);
1325 dispatchCommand(mxComponent, ".uno:DeleteColumns", {});
1326 Scheduler::ProcessEventsToIdle();
1327 CPPUNIT_ASSERT_EQUAL(std::string("3 3 3 3"), aView1.m_aCommentCallbackResult.get<std::string>("cellRange"));
1328 CPPUNIT_ASSERT_EQUAL(std::string("3 3 3 3"), aView2.m_aCommentCallbackResult.get<std::string>("cellRange"));
1330 std::string aCommentId = aView1.m_aCommentCallbackResult.get<std::string>("id");
1332 // Edit a comment
1333 // Select some random cell, we should be able to edit the cell note without
1334 // selecting the cell
1335 if (pTabViewShell)
1336 pTabViewShell->SetCursor(3, 100);
1337 aArgs = comphelper::InitPropertySequence(
1339 {"Id", uno::Any(OUString::createFromAscii(aCommentId.c_str()))},
1340 {"Text", uno::Any(OUString("Edited comment"))},
1341 {"Author", uno::Any(OUString("LOK User2"))},
1343 dispatchCommand(mxComponent, ".uno:EditAnnotation", aArgs);
1344 Scheduler::ProcessEventsToIdle();
1346 // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action
1347 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1348 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
1349 CPPUNIT_ASSERT_EQUAL(aCommentId, aView1.m_aCommentCallbackResult.get<std::string>("id"));
1350 CPPUNIT_ASSERT_EQUAL(aCommentId, aView2.m_aCommentCallbackResult.get<std::string>("id"));
1351 CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), aView1.m_aCommentCallbackResult.get<std::string>("author"));
1352 CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), aView2.m_aCommentCallbackResult.get<std::string>("author"));
1353 CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
1354 CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView2.m_aCommentCallbackResult.get<std::string>("text"));
1355 CPPUNIT_ASSERT_EQUAL(std::string("3 3 3 3"), aView1.m_aCommentCallbackResult.get<std::string>("cellRange"));
1356 CPPUNIT_ASSERT_EQUAL(std::string("3 3 3 3"), aView2.m_aCommentCallbackResult.get<std::string>("cellRange"));
1358 // Delete the comment
1359 if (pTabViewShell)
1360 pTabViewShell->SetCursor(4, 43);
1361 aArgs = comphelper::InitPropertySequence(
1363 {"Id", uno::Any(OUString::createFromAscii(aCommentId.c_str()))}
1365 dispatchCommand(mxComponent, ".uno:DeleteNote", aArgs);
1366 Scheduler::ProcessEventsToIdle();
1368 // We received a LOK_CALLBACK_COMMENT callback with comment 'Remove' action
1369 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1370 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
1371 CPPUNIT_ASSERT_EQUAL(aCommentId, aView1.m_aCommentCallbackResult.get<std::string>("id"));
1372 CPPUNIT_ASSERT_EQUAL(aCommentId, aView2.m_aCommentCallbackResult.get<std::string>("id"));
1374 comphelper::LibreOfficeKit::setTiledAnnotations(true);
1377 void ScTiledRenderingTest::testUndoLimiting()
1379 ScModelObj* pModelObj = createDoc("small.ods");
1380 CPPUNIT_ASSERT(pModelObj);
1381 ScDocument* pDoc = pModelObj->GetDocument();
1382 CPPUNIT_ASSERT(pDoc);
1383 ScUndoManager* pUndoManager = pDoc->GetUndoManager();
1384 CPPUNIT_ASSERT(pUndoManager);
1386 // view #1
1387 int nView1 = SfxLokHelper::getView();
1388 ViewCallback aView1;
1390 // view #2
1391 SfxLokHelper::createView();
1392 int nView2 = SfxLokHelper::getView();
1393 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1394 ViewCallback aView2;
1396 // text edit a cell in view #1
1397 SfxLokHelper::setView(nView1);
1398 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1399 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1400 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1401 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1402 Scheduler::ProcessEventsToIdle();
1404 // check that undo action count in not 0
1405 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
1407 // try to execute undo in view #2
1408 SfxLokHelper::setView(nView2);
1409 dispatchCommand(mxComponent, ".uno:Undo", {});
1410 Scheduler::ProcessEventsToIdle();
1411 // check that undo has not been executed on view #2
1412 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
1414 // try to execute undo in view #1
1415 SfxLokHelper::setView(nView1);
1416 dispatchCommand(mxComponent, ".uno:Undo", {});
1417 Scheduler::ProcessEventsToIdle();
1418 // check that undo has been executed on view #1
1419 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetUndoActionCount());
1421 // check that redo action count in not 0
1422 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetRedoActionCount());
1424 // try to execute redo in view #2
1425 SfxLokHelper::setView(nView2);
1426 dispatchCommand(mxComponent, ".uno:Redo", {});
1427 Scheduler::ProcessEventsToIdle();
1428 // check that redo has not been executed on view #2
1429 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetRedoActionCount());
1431 // try to execute redo in view #1
1432 SfxLokHelper::setView(nView1);
1433 dispatchCommand(mxComponent, ".uno:Redo", {});
1434 Scheduler::ProcessEventsToIdle();
1435 // check that redo has been executed on view #1
1436 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetRedoActionCount());
1439 void ScTiledRenderingTest::testUndoRepairDispatch()
1441 ScModelObj* pModelObj = createDoc("small.ods");
1442 CPPUNIT_ASSERT(pModelObj);
1443 ScDocument* pDoc = pModelObj->GetDocument();
1444 CPPUNIT_ASSERT(pDoc);
1445 ScUndoManager* pUndoManager = pDoc->GetUndoManager();
1446 CPPUNIT_ASSERT(pUndoManager);
1448 // view #1
1449 int nView1 = SfxLokHelper::getView();
1450 ViewCallback aView1;
1452 // view #2
1453 SfxLokHelper::createView();
1454 int nView2 = SfxLokHelper::getView();
1455 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1456 ViewCallback aView2;
1458 // text edit a cell in view #1
1459 SfxLokHelper::setView(nView1);
1460 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1461 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1462 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1463 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1464 Scheduler::ProcessEventsToIdle();
1466 // check that undo action count in not 0
1467 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
1469 // try to execute undo in view #2
1470 SfxLokHelper::setView(nView2);
1471 dispatchCommand(mxComponent, ".uno:Undo", {});
1472 Scheduler::ProcessEventsToIdle();
1473 // check that undo has not been executed on view #2
1474 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
1476 // try to execute undo in view #2 in repair mode
1477 SfxLokHelper::setView(nView2);
1478 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
1480 {"Repair", uno::Any(true)}
1481 }));
1482 dispatchCommand(mxComponent, ".uno:Undo", aPropertyValues);
1483 Scheduler::ProcessEventsToIdle();
1484 // check that undo has been executed on view #2 in repair mode
1485 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetUndoActionCount());
1488 void ScTiledRenderingTest::testInsertGraphicInvalidations()
1490 ScModelObj* pModelObj = createDoc("small.ods");
1491 CPPUNIT_ASSERT(pModelObj);
1492 ScViewData* pViewData = ScDocShell::GetViewData();
1493 CPPUNIT_ASSERT(pViewData);
1495 // view
1496 ViewCallback aView;
1498 // we need to paint a tile in the view for triggering the tile invalidation solution
1499 int nCanvasWidth = 256;
1500 int nCanvasHeight = 256;
1501 std::vector<unsigned char> aBuffer(nCanvasWidth * nCanvasHeight * 4);
1502 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::DEFAULT);
1503 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), aBuffer.data());
1504 pModelObj->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0, /*nTilePosY=*/0, /*nTileWidth=*/3840, /*nTileHeight=*/3840);
1505 Scheduler::ProcessEventsToIdle();
1507 // insert an image in view and see if both views are invalidated
1508 aView.m_bInvalidateTiles = false;
1509 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
1510 { "FileName", uno::Any(createFileURL(u"smile.png")) }
1511 }));
1512 dispatchCommand(mxComponent, ".uno:InsertGraphic", aArgs);
1513 Scheduler::ProcessEventsToIdle();
1514 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1516 // undo image insertion in view and see if both views are invalidated
1517 aView.m_bInvalidateTiles = false;
1518 uno::Sequence<beans::PropertyValue> aArgs2;
1519 dispatchCommand(mxComponent, ".uno:Undo", aArgs2);
1520 Scheduler::ProcessEventsToIdle();
1521 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1524 void ScTiledRenderingTest::testDocumentSizeWithTwoViews()
1526 // Open a document that has the cursor far away & paint a tile
1527 ScModelObj* pModelObj = createDoc("cursor-away.ods");
1529 // Set the visible area, and press page down
1530 pModelObj->setClientVisibleArea(tools::Rectangle(750, 1861, 20583, 6997));
1531 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN);
1532 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN);
1533 Scheduler::ProcessEventsToIdle();
1535 int nCanvasWidth = 256;
1536 int nCanvasHeight = 256;
1537 std::vector<unsigned char> aBuffer1(nCanvasWidth * nCanvasHeight * 4);
1538 ScopedVclPtrInstance<VirtualDevice> pDevice1(DeviceFormat::DEFAULT);
1539 pDevice1->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), aBuffer1.data());
1540 pModelObj->paintTile(*pDevice1, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0, /*nTilePosY=*/291840, /*nTileWidth=*/3840, /*nTileHeight=*/3840);
1541 Scheduler::ProcessEventsToIdle();
1543 // Create a new view
1544 int nView1 = SfxLokHelper::getView();
1545 SfxLokHelper::createView();
1547 std::vector<unsigned char> aBuffer2(nCanvasWidth * nCanvasHeight * 4);
1548 ScopedVclPtrInstance<VirtualDevice> pDevice2(DeviceFormat::DEFAULT);
1549 pDevice2->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), aBuffer2.data());
1550 pModelObj->paintTile(*pDevice2, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0, /*nTilePosY=*/291840, /*nTileWidth=*/3840, /*nTileHeight=*/3840);
1551 Scheduler::ProcessEventsToIdle();
1553 // Check that the tiles actually have the same content
1554 for (size_t i = 0; i < aBuffer1.size(); ++i)
1555 CPPUNIT_ASSERT_EQUAL(aBuffer1[i], aBuffer2[i]);
1557 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1558 SfxLokHelper::setView(nView1);
1559 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1562 void ScTiledRenderingTest::testDisableUndoRepair()
1564 ScModelObj* pModelObj = createDoc("cursor-away.ods");
1565 CPPUNIT_ASSERT(pModelObj);
1567 // view #1
1568 int nView1 = SfxLokHelper::getView();
1569 SfxViewShell* pView1 = SfxViewShell::Current();
1571 // view #2
1572 SfxLokHelper::createView();
1573 int nView2 = SfxLokHelper::getView();
1574 SfxViewShell* pView2 = SfxViewShell::Current();
1575 CPPUNIT_ASSERT(pView1 != pView2);
1577 // both views have UNDO disabled
1579 SfxItemSet aSet1(pView1->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
1580 SfxItemSet aSet2(pView2->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
1581 pView1->GetSlotState(SID_UNDO, nullptr, &aSet1);
1582 pView2->GetSlotState(SID_UNDO, nullptr, &aSet2);
1583 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, aSet1.GetItemState(SID_UNDO));
1584 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, aSet2.GetItemState(SID_UNDO));
1587 // text edit a cell in view #1
1588 SfxLokHelper::setView(nView1);
1589 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'h', 0);
1590 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'h', 0);
1591 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1592 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1593 Scheduler::ProcessEventsToIdle();
1594 // view1 has UNDO enabled, view2 is in UNDO-repair
1596 SfxItemSet aSet1(pView1->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
1597 SfxItemSet aSet2(pView2->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
1598 pView1->GetSlotState(SID_UNDO, nullptr, &aSet1);
1599 pView2->GetSlotState(SID_UNDO, nullptr, &aSet2);
1600 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet1.GetItemState(SID_UNDO));
1601 CPPUNIT_ASSERT(dynamic_cast< const SfxStringItem* >(aSet1.GetItem(SID_UNDO)));
1602 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet2.GetItemState(SID_UNDO));
1603 CPPUNIT_ASSERT(dynamic_cast< const SfxUInt32Item* >(aSet2.GetItem(SID_UNDO)));
1604 const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item*>(aSet2.GetItem(SID_UNDO));
1605 CPPUNIT_ASSERT(pUInt32Item);
1606 CPPUNIT_ASSERT_EQUAL(static_cast< sal_uInt32 >(SID_REPAIRPACKAGE), pUInt32Item->GetValue());
1609 // text edit a cell in view #2
1610 SfxLokHelper::setView(nView2);
1611 pModelObj->setPart(1);
1612 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', 0);
1613 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', 0);
1614 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1615 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1616 // both views have UNDO enabled
1617 Scheduler::ProcessEventsToIdle();
1619 SfxItemSet aSet1(pView1->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
1620 SfxItemSet aSet2(pView2->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
1621 pView1->GetSlotState(SID_UNDO, nullptr, &aSet1);
1622 pView2->GetSlotState(SID_UNDO, nullptr, &aSet2);
1623 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet1.GetItemState(SID_UNDO));
1624 CPPUNIT_ASSERT(dynamic_cast< const SfxStringItem* >(aSet1.GetItem(SID_UNDO)));
1625 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet2.GetItemState(SID_UNDO));
1626 CPPUNIT_ASSERT(dynamic_cast< const SfxStringItem* >(aSet2.GetItem(SID_UNDO)));
1629 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1630 SfxLokHelper::setView(nView1);
1631 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1634 void ScTiledRenderingTest::testDocumentRepair()
1636 // Create two views.
1637 ScModelObj* pModelObj = createDoc("cursor-away.ods");
1638 CPPUNIT_ASSERT(pModelObj);
1640 // view #1
1641 SfxViewShell* pView1 = SfxViewShell::Current();
1643 // view #2
1644 int nView1 = SfxLokHelper::getView();
1645 SfxLokHelper::createView();
1646 SfxViewShell* pView2 = SfxViewShell::Current();
1647 int nView2 = SfxLokHelper::getView();
1648 CPPUNIT_ASSERT(pView1 != pView2);
1650 std::unique_ptr<SfxBoolItem> pItem1;
1651 std::unique_ptr<SfxBoolItem> pItem2;
1652 pView1->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, pItem1);
1653 pView2->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, pItem2);
1654 CPPUNIT_ASSERT(pItem1);
1655 CPPUNIT_ASSERT(pItem2);
1656 CPPUNIT_ASSERT_EQUAL(false, pItem1->GetValue());
1657 CPPUNIT_ASSERT_EQUAL(false, pItem2->GetValue());
1660 // Insert a character in the second view.
1661 SfxLokHelper::setView(nView2);
1662 pModelObj->setPart(1);
1663 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', 0);
1664 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', 0);
1665 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1666 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1667 Scheduler::ProcessEventsToIdle();
1669 std::unique_ptr<SfxBoolItem> pItem1;
1670 std::unique_ptr<SfxBoolItem> pItem2;
1671 pView1->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, pItem1);
1672 pView2->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, pItem2);
1673 CPPUNIT_ASSERT(pItem1);
1674 CPPUNIT_ASSERT(pItem2);
1675 CPPUNIT_ASSERT_EQUAL(true, pItem1->GetValue());
1676 CPPUNIT_ASSERT_EQUAL(true, pItem2->GetValue());
1679 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1680 SfxLokHelper::setView(nView1);
1681 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1684 void ScTiledRenderingTest::testLanguageStatus()
1686 ScModelObj* pModelObj = createDoc("small.ods");
1687 CPPUNIT_ASSERT(pModelObj);
1688 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
1689 CPPUNIT_ASSERT(pDocSh);
1691 // view #1
1692 SfxViewShell* pView1 = SfxViewShell::Current();
1694 // view #2
1695 int nView1 = SfxLokHelper::getView();
1696 SfxLokHelper::createView();
1697 SfxViewShell* pView2 = SfxViewShell::Current();
1698 CPPUNIT_ASSERT(pView1 != pView2);
1700 std::unique_ptr<SfxPoolItem> xItem1;
1701 std::unique_ptr<SfxPoolItem> xItem2;
1702 pView1->GetViewFrame()->GetBindings().QueryState(SID_LANGUAGE_STATUS, xItem1);
1703 pView2->GetViewFrame()->GetBindings().QueryState(SID_LANGUAGE_STATUS, xItem2);
1704 const SfxStringItem* pItem1 = dynamic_cast<const SfxStringItem*>(xItem1.get());
1705 const SfxStringItem* pItem2 = dynamic_cast<const SfxStringItem*>(xItem2.get());
1706 CPPUNIT_ASSERT(pItem1);
1707 CPPUNIT_ASSERT(pItem2);
1708 CPPUNIT_ASSERT(!pItem1->GetValue().isEmpty());
1709 CPPUNIT_ASSERT(!pItem2->GetValue().isEmpty());
1713 SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Default_Spanish (Bolivia)");
1714 pView1->GetViewFrame()->GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS,
1715 SfxCallMode::SYNCHRON, { &aLangString });
1719 std::unique_ptr<SfxPoolItem> xItem1;
1720 std::unique_ptr<SfxPoolItem> xItem2;
1721 pView1->GetViewFrame()->GetBindings().QueryState(SID_LANGUAGE_STATUS, xItem1);
1722 pView2->GetViewFrame()->GetBindings().QueryState(SID_LANGUAGE_STATUS, xItem2);
1723 const SfxStringItem* pItem1 = dynamic_cast<const SfxStringItem*>(xItem1.get());
1724 const SfxStringItem* pItem2 = dynamic_cast<const SfxStringItem*>(xItem2.get());
1725 CPPUNIT_ASSERT(pItem1);
1726 CPPUNIT_ASSERT(pItem2);
1727 const OUString aLangBolivia("Spanish (Bolivia);es-BO");
1728 CPPUNIT_ASSERT_EQUAL(aLangBolivia, pItem1->GetValue());
1729 CPPUNIT_ASSERT_EQUAL(aLangBolivia, pItem2->GetValue());
1732 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1733 SfxLokHelper::setView(nView1);
1734 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1737 void ScTiledRenderingTest::testMultiViewCopyPaste()
1739 ScModelObj* pModelObj = createDoc("empty.ods");
1740 ScDocument* pDoc = pModelObj->GetDocument();
1741 CPPUNIT_ASSERT(pDoc);
1743 pDoc->SetString(ScAddress(0, 0, 0), "TestCopy1");
1744 pDoc->SetString(ScAddress(1, 0, 0), "TestCopy2");
1746 // view #1
1747 ScTabViewShell* pView1 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
1748 CPPUNIT_ASSERT(pView1);
1749 // emulate clipboard
1750 pView1->GetViewData().GetActiveWin()->SetClipboard(css::datatransfer::clipboard::LokClipboard::create(comphelper::getProcessComponentContext()));
1752 // view #2
1753 int nView1 = SfxLokHelper::getView();
1754 SfxLokHelper::createView();
1755 ScTabViewShell* pView2 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
1756 // emulate clipboard
1757 pView2->GetViewData().GetActiveWin()->SetClipboard(css::datatransfer::clipboard::LokClipboard::create(comphelper::getProcessComponentContext()));
1758 CPPUNIT_ASSERT(pView2);
1759 CPPUNIT_ASSERT(pView1 != pView2);
1760 CPPUNIT_ASSERT(pView1->GetViewData().GetActiveWin()->GetClipboard() != pView2->GetViewData().GetActiveWin()->GetClipboard());
1762 // copy text view 1
1763 pView1->SetCursor(0, 0);
1764 pView1->GetViewFrame()->GetBindings().Execute(SID_COPY);
1766 // copy text view 2
1767 pView2->SetCursor(1, 0);
1768 pView2->GetViewFrame()->GetBindings().Execute(SID_COPY);
1770 // paste text view 1
1771 pView1->SetCursor(0, 1);
1772 pView1->GetViewFrame()->GetBindings().Execute(SID_PASTE);
1774 // paste text view 2
1775 pView2->SetCursor(1, 1);
1776 pView2->GetViewFrame()->GetBindings().Execute(SID_PASTE);
1778 CPPUNIT_ASSERT_EQUAL(OUString("TestCopy1"), pDoc->GetString(ScAddress(0, 1, 0)));
1779 CPPUNIT_ASSERT_EQUAL(OUString("TestCopy2"), pDoc->GetString(ScAddress(1, 1, 0)));
1781 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1782 SfxLokHelper::setView(nView1);
1783 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1786 void ScTiledRenderingTest::testIMESupport()
1788 ScModelObj* pModelObj = createDoc("empty.ods");
1789 VclPtr<vcl::Window> pDocWindow = pModelObj->getDocWindow();
1790 ScDocument* pDoc = pModelObj->GetDocument();
1792 ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
1793 CPPUNIT_ASSERT(pView);
1795 pView->SetCursor(0, 0);
1796 // sequence of chinese IME compositions when 'nihao' is typed in an IME
1797 const std::vector<OString> aUtf8Inputs{ "年", "你", "你好", "你哈", "你好", "你好" };
1798 std::vector<OUString> aInputs;
1799 std::transform(aUtf8Inputs.begin(), aUtf8Inputs.end(),
1800 std::back_inserter(aInputs), [](OString aInput) {
1801 return OUString::fromUtf8(aInput);
1803 for (const auto& aInput: aInputs)
1805 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, aInput);
1807 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
1809 // commit the string to the cell
1810 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1811 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1812 Scheduler::ProcessEventsToIdle();
1814 CPPUNIT_ASSERT_EQUAL(aInputs[aInputs.size() - 1], pDoc->GetString(ScAddress(0, 0, 0)));
1817 void ScTiledRenderingTest::testFilterDlg()
1819 createDoc("empty.ods");
1821 // view #1
1822 SfxViewShell* pView1 = SfxViewShell::Current();
1823 int nView1 = SfxLokHelper::getView();
1825 // view #2
1826 SfxLokHelper::createView();
1827 SfxViewShell* pView2 = SfxViewShell::Current();
1828 CPPUNIT_ASSERT(pView1 != pView2);
1830 pView2->GetViewFrame()->GetDispatcher()->Execute(SID_FILTER,
1831 SfxCallMode::SLOT|SfxCallMode::RECORD);
1834 Scheduler::ProcessEventsToIdle();
1835 SfxChildWindow* pRefWindow = pView2->GetViewFrame()->GetChildWindow(SID_FILTER);
1836 CPPUNIT_ASSERT(pRefWindow);
1838 // switch to view 1
1839 SfxLokHelper::setView(nView1);
1840 CPPUNIT_ASSERT_EQUAL(true, pView2->GetViewFrame()->GetDispatcher()->IsLocked());
1841 CPPUNIT_ASSERT_EQUAL(false, pView1->GetViewFrame()->GetDispatcher()->IsLocked());
1843 pRefWindow->GetController()->response(RET_CANCEL);
1845 CPPUNIT_ASSERT_EQUAL(false, pView2->GetViewFrame()->GetDispatcher()->IsLocked());
1846 CPPUNIT_ASSERT_EQUAL(false, pView1->GetViewFrame()->GetDispatcher()->IsLocked());
1848 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1849 SfxLokHelper::setView(nView1);
1850 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1853 void ScTiledRenderingTest::testFunctionDlg()
1855 createDoc("empty.ods");
1857 // view #1
1858 SfxViewShell* pView1 = SfxViewShell::Current();
1859 int nView1 = SfxLokHelper::getView();
1861 pView1->GetViewFrame()->GetDispatcher()->Execute(SID_OPENDLG_FUNCTION,
1862 SfxCallMode::SLOT|SfxCallMode::RECORD);
1864 Scheduler::ProcessEventsToIdle();
1865 SfxChildWindow* pRefWindow = pView1->GetViewFrame()->GetChildWindow(SID_OPENDLG_FUNCTION);
1866 CPPUNIT_ASSERT(pRefWindow);
1868 // view #2
1869 int nView2 = SfxLokHelper::createView();
1870 SfxViewShell* pView2 = SfxViewShell::Current();
1871 CPPUNIT_ASSERT(pView1 != pView2);
1873 // check locking
1874 CPPUNIT_ASSERT_EQUAL(true, pView1->GetViewFrame()->GetDispatcher()->IsLocked());
1875 CPPUNIT_ASSERT_EQUAL(false, pView2->GetViewFrame()->GetDispatcher()->IsLocked());
1877 SfxLokHelper::setView(nView1);
1878 pRefWindow->GetController()->response(RET_CANCEL);
1880 CPPUNIT_ASSERT_EQUAL(false, pView1->GetViewFrame()->GetDispatcher()->IsLocked());
1881 CPPUNIT_ASSERT_EQUAL(false, pView2->GetViewFrame()->GetDispatcher()->IsLocked());
1883 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1884 SfxLokHelper::setView(nView2);
1885 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1888 void ScTiledRenderingTest::testSpellOnlineParameter()
1890 ScModelObj* pModelObj = createDoc("empty.ods");
1891 ScDocument* pDoc = pModelObj->GetDocument();
1892 bool bSet = pDoc->GetDocOptions().IsAutoSpell();
1894 uno::Sequence<beans::PropertyValue> params =
1896 comphelper::makePropertyValue("Enable", uno::Any(!bSet)),
1898 dispatchCommand(mxComponent, ".uno:SpellOnline", params);
1899 CPPUNIT_ASSERT_EQUAL(!bSet, pDoc->GetDocOptions().IsAutoSpell());
1901 // set the same state as now and we don't expect any change (no-toggle)
1902 params =
1904 comphelper::makePropertyValue("Enable", uno::Any(!bSet)),
1906 dispatchCommand(mxComponent, ".uno:SpellOnline", params);
1907 CPPUNIT_ASSERT_EQUAL(!bSet, pDoc->GetDocOptions().IsAutoSpell());
1910 void ScTiledRenderingTest::testVbaRangeCopyPaste()
1912 ScModelObj* pModelObj = createDoc("RangeCopyPaste.ods");
1913 ScDocShell* pDocShell = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
1914 CPPUNIT_ASSERT(pDocShell);
1916 uno::Any aRet;
1917 uno::Sequence< uno::Any > aOutParam;
1918 uno::Sequence< uno::Any > aParams;
1919 uno::Sequence< sal_Int16 > aOutParamIndex;
1921 SfxObjectShell::CallXScript(
1922 mxComponent,
1923 "vnd.sun.Star.script:Standard.Module1.Test_RangeCopyPaste?language=Basic&location=document",
1924 aParams, aRet, aOutParamIndex, aOutParam);
1926 CPPUNIT_ASSERT(!pDocShell->GetClipData().is());
1929 void ScTiledRenderingTest::testInvalidationLoop()
1931 // Load the document with a form control.
1932 createDoc("invalidation-loop.fods");
1933 // Without the accompanying fix in place, this test would have never returned due to an infinite
1934 // invalidation loop between ScGridWindow::Paint() and vcl::Window::ImplPosSizeWindow().
1935 Scheduler::ProcessEventsToIdle();
1938 void ScTiledRenderingTest::testPageDownInvalidation()
1940 ScModelObj* pModelObj = createDoc("empty.ods");
1941 ScViewData* pViewData = ScDocShell::GetViewData();
1942 CPPUNIT_ASSERT(pViewData);
1944 int nView1 = SfxLokHelper::getView();
1945 ViewCallback aView1;
1946 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
1948 SfxLokHelper::setView(nView1);
1949 aView1.m_bInvalidateTiles = false;
1950 aView1.m_aInvalidations.clear();
1951 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, awt::Key::PAGEDOWN, 0);
1952 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, awt::Key::PAGEDOWN, 0);
1953 Scheduler::ProcessEventsToIdle();
1954 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1955 CPPUNIT_ASSERT_EQUAL(size_t(3), aView1.m_aInvalidations.size());
1956 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(15, 15, 1230, 225), aView1.m_aInvalidations[0]);
1959 void ScTiledRenderingTest::testSheetChangeInvalidation()
1961 const bool oldPartInInvalidation = comphelper::LibreOfficeKit::isPartInInvalidation();
1962 comphelper::LibreOfficeKit::setPartInInvalidation(true);
1964 ScModelObj* pModelObj = createDoc("two_sheets.ods");
1965 ScDocument* pDoc = pModelObj->GetDocument();
1966 ScViewData* pViewData = ScDocShell::GetViewData();
1967 CPPUNIT_ASSERT(pViewData);
1969 int nView1 = SfxLokHelper::getView();
1970 ViewCallback aView1;
1971 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
1973 SfxLokHelper::setView(nView1);
1974 aView1.m_bInvalidateTiles = false;
1975 aView1.m_aInvalidations.clear();
1976 aView1.m_aInvalidationsParts.clear();
1977 aView1.m_aInvalidationsMode.clear();
1978 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN | KEY_MOD1);
1979 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN | KEY_MOD1);
1980 Scheduler::ProcessEventsToIdle();
1981 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1982 CPPUNIT_ASSERT_EQUAL(size_t(2), aView1.m_aInvalidations.size());
1983 const ScSheetLimits& rLimits = pDoc->GetSheetLimits();
1984 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 0, 1280 * rLimits.GetMaxColCount(),
1985 256 * rLimits.GetMaxRowCount()),
1986 aView1.m_aInvalidations[0]);
1987 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 0, 1000000000, 1000000000), aView1.m_aInvalidations[1]);
1988 CPPUNIT_ASSERT_EQUAL(size_t(2), aView1.m_aInvalidationsParts.size());
1989 CPPUNIT_ASSERT_EQUAL(pModelObj->getPart(), aView1.m_aInvalidationsParts[0]);
1990 CPPUNIT_ASSERT_EQUAL(pModelObj->getPart(), aView1.m_aInvalidationsParts[1]);
1991 CPPUNIT_ASSERT_EQUAL(size_t(2), aView1.m_aInvalidationsMode.size());
1992 CPPUNIT_ASSERT_EQUAL(pModelObj->getEditMode(), aView1.m_aInvalidationsMode[0]);
1993 CPPUNIT_ASSERT_EQUAL(pModelObj->getEditMode(), aView1.m_aInvalidationsMode[1]);
1994 comphelper::LibreOfficeKit::setPartInInvalidation(oldPartInInvalidation);
1997 void ScTiledRenderingTest::testInsertDeletePageInvalidation()
1999 ScModelObj* pModelObj = createDoc("insert_delete_sheet.ods");
2000 // the document has 1 sheet
2001 CPPUNIT_ASSERT_EQUAL(1, pModelObj->getParts());
2002 ScViewData* pViewData = ScDocShell::GetViewData();
2003 CPPUNIT_ASSERT(pViewData);
2005 int nView1 = SfxLokHelper::getView();
2006 ViewCallback aView1;
2007 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
2009 SfxLokHelper::setView(nView1);
2010 aView1.m_bInvalidateTiles = false;
2011 aView1.m_aInvalidations.clear();
2013 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
2014 { "Name", uno::Any(OUString("")) },
2015 { "Index", uno::Any(sal_Int32(1)) }
2016 }));
2017 dispatchCommand(mxComponent, ".uno:Insert", aArgs);
2018 Scheduler::ProcessEventsToIdle();
2019 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
2020 CPPUNIT_ASSERT_EQUAL(size_t(6), aView1.m_aInvalidations.size());
2021 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 0, 1000000000, 1000000000), aView1.m_aInvalidations[0]);
2022 CPPUNIT_ASSERT_EQUAL(2, pModelObj->getParts());
2024 // Delete sheet
2025 aView1.m_bInvalidateTiles = false;
2026 aView1.m_aInvalidations.clear();
2027 uno::Sequence<beans::PropertyValue> aArgs2( comphelper::InitPropertySequence({
2028 { "Index", uno::Any(sal_Int32(1)) }
2029 }));
2030 dispatchCommand(mxComponent, ".uno:Remove", aArgs2);
2031 Scheduler::ProcessEventsToIdle();
2032 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
2033 CPPUNIT_ASSERT_EQUAL(size_t(5), aView1.m_aInvalidations.size());
2034 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 0, 1000000000, 1000000000), aView1.m_aInvalidations[0]);
2035 CPPUNIT_ASSERT_EQUAL(1, pModelObj->getParts());
2038 void ScTiledRenderingTest::testGetRowColumnHeadersInvalidation()
2040 ScModelObj* pModelObj = createDoc("empty.ods");
2041 ScViewData* pViewData = ScDocShell::GetViewData();
2042 CPPUNIT_ASSERT(pViewData);
2044 int nView1 = SfxLokHelper::getView();
2045 ViewCallback aView1;
2046 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
2048 SfxLokHelper::setView(nView1);
2049 aView1.m_bInvalidateTiles = false;
2050 aView1.m_aInvalidations.clear();
2051 tools::JsonWriter aJsonWriter1;
2052 pModelObj->getRowColumnHeaders(tools::Rectangle(0, 15, 19650, 5400), aJsonWriter1);
2053 free(aJsonWriter1.extractData());
2054 Scheduler::ProcessEventsToIdle();
2055 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
2056 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
2057 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(26775, 0, 49725, 13005), aView1.m_aInvalidations[0]);
2059 // Extend area top-to-bottom
2060 aView1.m_bInvalidateTiles = false;
2061 aView1.m_aInvalidations.clear();
2062 tools::JsonWriter aJsonWriter2;
2063 pModelObj->getRowColumnHeaders(tools::Rectangle(0, 5400, 19650, 9800), aJsonWriter2);
2064 free(aJsonWriter2.extractData());
2065 Scheduler::ProcessEventsToIdle();
2066 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
2067 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
2068 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 13005, 49725, 19380), aView1.m_aInvalidations[0]);
2070 // Extend area left-to-right
2071 aView1.m_bInvalidateTiles = false;
2072 aView1.m_aInvalidations.clear();
2073 tools::JsonWriter aJsonWriter3;
2074 pModelObj->getRowColumnHeaders(tools::Rectangle(5400, 5400, 25050, 9800), aJsonWriter3);
2075 free(aJsonWriter3.extractData());
2076 Scheduler::ProcessEventsToIdle();
2077 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
2078 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
2079 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(49725, 0, 75225, 19380), aView1.m_aInvalidations[0]);
2082 void ScTiledRenderingTest::testJumpHorizontallyInvalidation()
2084 ScModelObj* pModelObj = createDoc("empty.ods");
2085 ScViewData* pViewData = ScDocShell::GetViewData();
2086 CPPUNIT_ASSERT(pViewData);
2088 int nView1 = SfxLokHelper::getView();
2089 ViewCallback aView1;
2090 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
2092 SfxLokHelper::setView(nView1);
2093 aView1.m_bInvalidateTiles = false;
2094 aView1.m_aInvalidations.clear();
2095 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN | KEY_MOD2);
2096 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN | KEY_MOD2);
2097 Scheduler::ProcessEventsToIdle();
2098 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN | KEY_MOD2);
2099 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN | KEY_MOD2);
2100 Scheduler::ProcessEventsToIdle();
2101 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
2102 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
2103 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(26775, 0, 39525, 13005), aView1.m_aInvalidations[0]);
2106 void ScTiledRenderingTest::testJumpToLastRowInvalidation()
2108 ScModelObj* pModelObj = createDoc("empty.ods");
2109 ScViewData* pViewData = ScDocShell::GetViewData();
2110 CPPUNIT_ASSERT(pViewData);
2112 int nView1 = SfxLokHelper::getView();
2113 ViewCallback aView1;
2114 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
2116 SfxLokHelper::setView(nView1);
2117 aView1.m_bInvalidateTiles = false;
2118 aView1.m_aInvalidations.clear();
2119 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN | KEY_MOD1);
2120 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN | KEY_MOD1);
2121 Scheduler::ProcessEventsToIdle();
2122 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
2123 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
2124 // 261375 because we limit how far we jump into empty space in online, 267386880 if we don't limit
2125 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 13005, 26775, 261375), aView1.m_aInvalidations[0]);
2128 // We need to ensure that views are not perterbed by rendering (!?) hmm ...
2129 void ScTiledRenderingTest::testRowColumnHeaders()
2131 ScModelObj* pModelObj = createDoc("empty.ods");
2132 ScViewData* pViewData = ScDocShell::GetViewData();
2133 CPPUNIT_ASSERT(pViewData);
2135 // view #1
2136 ViewCallback aView1;
2137 int nView1 = SfxLokHelper::getView();
2138 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
2140 // view #2
2141 SfxLokHelper::createView();
2142 int nView2 = SfxLokHelper::getView();
2143 ViewCallback aView2;
2144 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
2146 // ViewRowColumnHeaders test
2147 SfxLokHelper::setView(nView1);
2148 tools::JsonWriter aJsonWriter1;
2149 pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter1);
2150 OString aHeaders1 = aJsonWriter1.extractAsOString();
2152 SfxLokHelper::setView(nView2);
2153 // 50% zoom
2154 pModelObj->setClientVisibleArea(tools::Rectangle(0, 0, 22474, 47333));
2155 pModelObj->setClientZoom(256, 256, 6636, 6636);
2156 tools::JsonWriter aJsonWriter2;
2157 pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter2);
2158 OString aHeaders2 = aJsonWriter2.extractAsOString();
2160 // Check vs. view #1
2161 SfxLokHelper::setView(nView1);
2162 tools::JsonWriter aJsonWriter3;
2163 pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter3);
2164 OString aHeaders1_2 = aJsonWriter3.extractAsOString();
2165 CPPUNIT_ASSERT_EQUAL(aHeaders1, aHeaders1_2);
2167 // Check vs. view #2
2168 SfxLokHelper::setView(nView2);
2169 tools::JsonWriter aJsonWriter4;
2170 pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter4);
2171 OString aHeaders2_2 = aJsonWriter4.extractAsOString();
2172 CPPUNIT_ASSERT_EQUAL(aHeaders2, aHeaders2_2);
2174 SfxLokHelper::setView(nView1);
2175 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2176 SfxLokHelper::setView(nView2);
2177 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2180 // Helper structs for setup and testing of ScModelObj::getSheetGeometryData()
2181 struct SpanEntry
2183 size_t nVal;
2184 SCCOLROW nEnd;
2187 struct SheetDimData
2189 typedef std::vector<SpanEntry> SpanList;
2190 SpanList aSizes;
2191 SpanList aHidden;
2192 SpanList aFiltered;
2193 // TODO: Add group info too to test.
2195 void setDataToDoc(ScDocument* pDoc, bool bCol) const
2197 SCCOLROW nStart = 0;
2198 // sizes
2199 for (const auto& rSpan : aSizes)
2201 if (bCol)
2203 for (SCCOLROW nIdx = nStart; nIdx <= rSpan.nEnd; ++nIdx)
2204 pDoc->SetColWidthOnly(nIdx, 0, rSpan.nVal);
2206 else
2207 pDoc->SetRowHeightOnly(nStart, rSpan.nEnd, 0, rSpan.nVal);
2209 nStart = rSpan.nEnd + 1;
2212 nStart = 0;
2213 // hidden
2214 for (const auto& rSpan : aHidden)
2216 if (bCol)
2217 pDoc->SetColHidden(nStart, rSpan.nEnd, 0, !!rSpan.nVal);
2218 else
2219 pDoc->SetRowHidden(nStart, rSpan.nEnd, 0, !!rSpan.nVal);
2221 nStart = rSpan.nEnd + 1;
2224 // There is no ScDocument interface to set ScTable::mpFilteredCols
2225 // It seems ScTable::mpFilteredCols is not really used !?
2226 if (bCol)
2227 return;
2229 nStart = 0;
2230 // filtered
2231 for (const auto& rSpan : aFiltered)
2233 pDoc->SetRowFiltered(nStart, rSpan.nEnd, 0, !!rSpan.nVal);
2234 nStart = rSpan.nEnd + 1;
2238 void testPropertyTree(const boost::property_tree::ptree& rTree, bool bCol) const
2240 struct SpanListWithKey
2242 OString aKey;
2243 const SpanList& rSpanList;
2246 const SpanListWithKey aPairList[] = {
2247 { "sizes", aSizes },
2248 { "hidden", aHidden },
2249 { "filtered", aFiltered }
2252 for (const auto& rEntry : aPairList)
2254 // There is no ScDocument interface to set ScTable::mpFilteredCols
2255 // It seems ScTable::mpFilteredCols is not really used !?
2256 if (bCol && rEntry.aKey == "filtered")
2257 continue;
2259 bool bBooleanValue = rEntry.aKey != "sizes";
2260 OString aExpectedEncoding;
2261 bool bFirst = true;
2262 for (const auto& rSpan : rEntry.rSpanList)
2264 size_t nVal = rSpan.nVal;
2265 if (bBooleanValue && bFirst)
2266 nVal = static_cast<size_t>(!!nVal);
2267 if (!bBooleanValue || bFirst)
2268 aExpectedEncoding += OString::number(nVal) + ":";
2269 aExpectedEncoding += OString::number(rSpan.nEnd) + " ";
2270 bFirst = false;
2273 // Get the tree's value for the property key ("sizes"/"hidden"/"filtered").
2274 OString aTreeValue = rTree.get<std::string>(rEntry.aKey.getStr()).c_str();
2276 CPPUNIT_ASSERT_EQUAL(aExpectedEncoding, aTreeValue);
2281 class SheetGeometryData
2283 SheetDimData aCols;
2284 SheetDimData aRows;
2286 public:
2288 SheetGeometryData(const SheetDimData& rCols, const SheetDimData& rRows) :
2289 aCols(rCols), aRows(rRows)
2292 void setDataToDoc(ScDocument* pDoc) const
2294 aCols.setDataToDoc(pDoc, true);
2295 aRows.setDataToDoc(pDoc, false);
2298 void parseTest(const OString& rJSON) const
2300 // Assumes all flags passed to getSheetGeometryData() are true.
2301 boost::property_tree::ptree aTree;
2302 std::stringstream aStream(rJSON.getStr());
2303 boost::property_tree::read_json(aStream, aTree);
2305 CPPUNIT_ASSERT_EQUAL(OString(".uno:SheetGeometryData"), OString(aTree.get<std::string>("commandName").c_str()));
2307 aCols.testPropertyTree(aTree.get_child("columns"), true);
2308 aRows.testPropertyTree(aTree.get_child("rows"), false);
2312 // getSheetGeometryData() should return the exact same message
2313 // irrespective of client zoom and view-area. Switching views
2314 // should also not alter it.
2315 void ScTiledRenderingTest::testSheetGeometryDataInvariance()
2317 ScModelObj* pModelObj = createDoc("empty.ods");
2318 ScDocument* pDoc = pModelObj->GetDocument();
2319 const SheetGeometryData aSGData(
2320 // cols
2322 // width spans
2324 { STD_COL_WIDTH, 20 },
2325 { 2*STD_COL_WIDTH, 26 },
2326 { STD_COL_WIDTH, pDoc->MaxCol() }
2329 // hidden spans
2331 { 0, 5 },
2332 { 1, 12 },
2333 { 0, pDoc->MaxCol() }
2336 // filtered spans
2338 { 0, 50 },
2339 { 1, 59 },
2340 { 0, pDoc->MaxCol() }
2344 // rows
2346 // height spans
2348 { 300, 50 },
2349 { 600, 65 },
2350 { 300, pDoc->MaxRow() }
2353 // hidden spans
2355 { 1, 100 },
2356 { 0, 500 },
2357 { 1, 578 },
2358 { 0, pDoc->MaxRow() }
2361 // filtered spans
2363 { 0, 150 },
2364 { 1, 159 },
2365 { 0, pDoc->MaxRow() }
2370 ScViewData* pViewData = ScDocShell::GetViewData();
2371 CPPUNIT_ASSERT(pViewData);
2373 // view #1
2374 ViewCallback aView1;
2375 int nView1 = SfxLokHelper::getView();
2377 // view #2
2378 SfxLokHelper::createView();
2379 int nView2 = SfxLokHelper::getView();
2380 ViewCallback aView2;
2381 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
2383 // Try with the default empty document once (nIdx = 0) and then with sheet geometry settings (nIdx = 1)
2384 for (size_t nIdx = 0; nIdx < 2; ++nIdx)
2386 if (nIdx)
2387 aSGData.setDataToDoc(pDoc);
2389 SfxLokHelper::setView(nView1);
2390 OString aGeomStr1 = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2391 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2393 SfxLokHelper::setView(nView2);
2394 pModelObj->setClientVisibleArea(tools::Rectangle(0, 0, 22474, 47333));
2395 pModelObj->setClientZoom(256, 256, 6636, 6636);
2396 OString aGeomStr2 = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2397 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2399 // Check vs. view #1
2400 SfxLokHelper::setView(nView1);
2401 OString aGeomStr1_2 = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2402 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2403 CPPUNIT_ASSERT_EQUAL(aGeomStr1, aGeomStr1_2);
2405 // Check vs. view #2
2406 SfxLokHelper::setView(nView2);
2407 OString aGeomStr2_2 = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2408 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2409 CPPUNIT_ASSERT_EQUAL(aGeomStr2, aGeomStr2_2);
2412 SfxLokHelper::setView(nView1);
2413 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2414 SfxLokHelper::setView(nView2);
2415 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2418 void ScTiledRenderingTest::testSheetGeometryDataCorrectness()
2420 ScModelObj* pModelObj = createDoc("empty.ods");
2421 ScDocument* pDoc = pModelObj->GetDocument();
2422 const SheetGeometryData aDefaultSGData(
2423 // cols
2425 // width spans
2426 { { STD_COL_WIDTH, pDoc->MaxCol() } },
2427 // hidden spans
2428 { { 0, pDoc->MaxCol() } },
2429 // filtered spans
2430 { { 0, pDoc->MaxCol() } }
2432 // rows
2434 // height spans
2435 { { ScGlobal::nStdRowHeight, pDoc->MaxRow() } },
2436 // hidden spans
2437 { { 0, pDoc->MaxRow() } },
2438 // filtered spans
2439 { { 0, pDoc->MaxRow() } }
2443 const SheetGeometryData aSGData(
2444 // cols
2446 // width spans
2448 { STD_COL_WIDTH, 20 },
2449 { 2*STD_COL_WIDTH, 26 },
2450 { STD_COL_WIDTH, pDoc->MaxCol() }
2453 // hidden spans
2455 { 0, 5 },
2456 { 1, 12 },
2457 { 0, pDoc->MaxCol() }
2460 // filtered spans
2462 { 0, 50 },
2463 { 1, 59 },
2464 { 0, pDoc->MaxCol() }
2468 // rows
2470 // height spans
2472 { 300, 50 },
2473 { 600, 65 },
2474 { 300, pDoc->MaxRow() }
2477 // hidden spans
2479 { 1, 100 },
2480 { 0, 500 },
2481 { 1, 578 },
2482 { 0, pDoc->MaxRow() }
2485 // filtered spans
2487 { 0, 150 },
2488 { 1, 159 },
2489 { 0, pDoc->MaxRow() }
2494 ScViewData* pViewData = ScDocShell::GetViewData();
2495 CPPUNIT_ASSERT(pViewData);
2497 // view #1
2498 ViewCallback aView1;
2500 // with the default empty sheet and test the JSON encoding.
2501 OString aGeomDefaultStr = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2502 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2503 aDefaultSGData.parseTest(aGeomDefaultStr);
2505 // Apply geometry settings to the sheet and then test the resulting JSON encoding.
2506 aSGData.setDataToDoc(pDoc);
2507 OString aGeomStr = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2508 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2509 aSGData.parseTest(aGeomStr);
2511 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2514 void ScTiledRenderingTest::testDeleteCellMultilineContent()
2516 ScModelObj* pModelObj = createDoc("multiline.ods");
2517 CPPUNIT_ASSERT(pModelObj);
2518 ScViewData* pViewData = ScDocShell::GetViewData();
2519 CPPUNIT_ASSERT(pViewData);
2520 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
2521 CPPUNIT_ASSERT(pDocSh);
2523 // view #1
2524 ViewCallback aView1;
2525 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
2527 aView1.m_sInvalidateHeader = "";
2528 ScDocument& rDoc = pDocSh->GetDocument();
2529 sal_uInt16 nRow1Height = rDoc.GetRowHeight(static_cast<SCROW>(0), static_cast<SCTAB>(0), false);
2531 // delete multiline cell content in view #1
2532 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN);
2533 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN);
2534 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DELETE);
2535 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DELETE);
2536 Scheduler::ProcessEventsToIdle();
2538 // check if the row header has been invalidated and if the involved row is of the expected height
2539 CPPUNIT_ASSERT_EQUAL(OString("row"), aView1.m_sInvalidateHeader);
2540 sal_uInt16 nRow2Height = rDoc.GetRowHeight(static_cast<SCROW>(0), static_cast<SCTAB>(0), false);
2541 CPPUNIT_ASSERT_EQUAL(nRow1Height, nRow2Height);
2542 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2545 void ScTiledRenderingTest::testPasteIntoWrapTextCell()
2547 comphelper::LibreOfficeKit::setCompatFlag(
2548 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
2550 ScModelObj* pModelObj = createDoc("empty.ods");
2551 CPPUNIT_ASSERT(pModelObj);
2552 ScDocument* pDoc = pModelObj->GetDocument();
2554 // Set Wrap text in A3
2555 pDoc->ApplyAttr(0, 2, 0, ScLineBreakCell(true));
2556 const ScLineBreakCell* pItem = pDoc->GetAttr(0, 2, 0, ATTR_LINEBREAK);
2557 CPPUNIT_ASSERT(pItem->GetValue());
2559 ScViewData* pViewData = ScDocShell::GetViewData();
2560 CPPUNIT_ASSERT(pViewData);
2562 ViewCallback aView;
2563 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
2565 ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2566 CPPUNIT_ASSERT(pView);
2568 // create source text in A1
2569 OUString sCopyContent("Very long text to copy");
2570 pDoc->SetString(0, 0, 0, sCopyContent);
2572 // copy A1
2573 pView->SetCursor(0, 0);
2574 Scheduler::ProcessEventsToIdle();
2575 pView->GetViewFrame()->GetBindings().Execute(SID_COPY);
2576 Scheduler::ProcessEventsToIdle();
2578 // verify clipboard
2579 uno::Reference<datatransfer::clipboard::XClipboard> xClipboard1 = pView->GetViewData().GetActiveWin()->GetClipboard();
2580 uno::Reference< datatransfer::XTransferable > xDataObj =
2581 xClipboard1->getContents();
2582 datatransfer::DataFlavor aFlavor;
2583 SotExchange::GetFormatDataFlavor(SotClipboardFormatId::STRING, aFlavor);
2584 uno::Any aData = xDataObj->getTransferData(aFlavor);
2585 OUString aTmpText;
2586 aData >>= aTmpText;
2587 CPPUNIT_ASSERT_EQUAL(sCopyContent, aTmpText.trim());
2589 // Go to A2 and paste.
2590 pView->SetCursor(0, 1);
2591 Scheduler::ProcessEventsToIdle();
2592 aView.m_sInvalidateSheetGeometry = "";
2593 pView->GetViewFrame()->GetBindings().Execute(SID_PASTE);
2594 Scheduler::ProcessEventsToIdle();
2596 CPPUNIT_ASSERT_EQUAL(sCopyContent, pDoc->GetString(0, 1, 0));
2597 CPPUNIT_ASSERT_EQUAL(OString("rows sizes"), aView.m_sInvalidateSheetGeometry);
2599 // create new source text in A2
2600 OUString sCopyContent2("Very long text to copy 2");
2601 pDoc->SetString(0, 1, 0, sCopyContent2);
2602 Scheduler::ProcessEventsToIdle();
2604 // cut from A2
2605 pView->GetViewFrame()->GetBindings().Execute(SID_CUT);
2606 Scheduler::ProcessEventsToIdle();
2608 // verify clipboard
2609 uno::Reference<datatransfer::clipboard::XClipboard> xClipboard2
2610 = pView->GetViewData().GetActiveWin()->GetClipboard();
2611 xDataObj = xClipboard2->getContents();
2612 SotExchange::GetFormatDataFlavor(SotClipboardFormatId::STRING, aFlavor);
2613 aData = xDataObj->getTransferData(aFlavor);
2614 aData >>= aTmpText;
2615 CPPUNIT_ASSERT_EQUAL(xClipboard1, xClipboard2);
2616 CPPUNIT_ASSERT_EQUAL(sCopyContent2, aTmpText.trim());
2618 // Go to A3 and paste.
2619 pView->SetCursor(0, 2);
2620 Scheduler::ProcessEventsToIdle();
2621 aView.m_sInvalidateSheetGeometry = "";
2622 pView->GetViewFrame()->GetBindings().Execute(SID_PASTE);
2623 Scheduler::ProcessEventsToIdle();
2625 // SG invalidations for all
2626 CPPUNIT_ASSERT_EQUAL(sCopyContent2, pDoc->GetString(0, 1, 0));
2627 CPPUNIT_ASSERT_EQUAL(OString("all"), aView.m_sInvalidateSheetGeometry);
2629 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2632 void ScTiledRenderingTest::testSortAscendingDescending()
2634 comphelper::LibreOfficeKit::setCompatFlag(
2635 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
2636 ScModelObj* pModelObj = createDoc("sort-range.ods");
2637 ScDocument* pDoc = pModelObj->GetDocument();
2639 ViewCallback aView;
2641 // select the values in the first column
2642 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, 551, 129, 1, MOUSE_LEFT, 0);
2643 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEMOVE, 820, 1336, 1, MOUSE_LEFT, 0);
2644 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, 820, 1359, 1, MOUSE_LEFT, 0);
2645 Scheduler::ProcessEventsToIdle();
2646 aView.m_sInvalidateSheetGeometry = "";
2648 // sort ascending
2649 uno::Sequence<beans::PropertyValue> aArgs;
2650 dispatchCommand(mxComponent, ".uno:SortAscending", aArgs);
2652 // check it's sorted
2653 for (SCROW r = 0; r < 6; ++r)
2655 CPPUNIT_ASSERT_EQUAL(double(r + 1), pDoc->GetValue(ScAddress(0, r, 0)));
2658 Scheduler::ProcessEventsToIdle();
2659 CPPUNIT_ASSERT_EQUAL(OString("rows"), aView.m_sInvalidateSheetGeometry);
2661 aView.m_sInvalidateSheetGeometry = "";
2662 // sort descending
2663 dispatchCommand(mxComponent, ".uno:SortDescending", aArgs);
2665 // check it's sorted
2666 for (SCROW r = 0; r < 6; ++r)
2668 CPPUNIT_ASSERT_EQUAL(double(6 - r), pDoc->GetValue(ScAddress(0, r, 0)));
2671 // nothing else was sorted
2672 CPPUNIT_ASSERT_EQUAL(double(1), pDoc->GetValue(ScAddress(1, 0, 0)));
2673 CPPUNIT_ASSERT_EQUAL(double(3), pDoc->GetValue(ScAddress(1, 1, 0)));
2674 CPPUNIT_ASSERT_EQUAL(double(2), pDoc->GetValue(ScAddress(1, 2, 0)));
2676 Scheduler::ProcessEventsToIdle();
2677 CPPUNIT_ASSERT_EQUAL(OString("rows"), aView.m_sInvalidateSheetGeometry);
2680 void lcl_typeCharsInCell(const std::string& aStr, SCCOL nCol, SCROW nRow, ScTabViewShell* pView,
2681 ScModelObj* pModelObj, bool bInEdit = false, bool bCommit = true)
2683 if (!bInEdit)
2684 pView->SetCursor(nCol, nRow);
2686 for (const char& cChar : aStr)
2688 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, cChar, 0);
2689 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, cChar, 0);
2690 Scheduler::ProcessEventsToIdle();
2693 if (bCommit)
2695 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
2696 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
2697 Scheduler::ProcessEventsToIdle();
2701 void ScTiledRenderingTest::testAutoInputStringBlock()
2703 ScModelObj* pModelObj = createDoc("empty.ods");
2704 CPPUNIT_ASSERT(pModelObj);
2705 ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2706 CPPUNIT_ASSERT(pView);
2707 ScDocument* pDoc = pModelObj->GetDocument();
2709 pDoc->SetString(ScAddress(0, 3, 0), "ABC"); // A4
2710 pDoc->SetString(ScAddress(0, 4, 0), "BAC"); // A5
2711 ScFieldEditEngine& rEE = pDoc->GetEditEngine();
2712 rEE.SetText("XYZ");
2713 pDoc->SetEditText(ScAddress(0, 5, 0), rEE.CreateTextObject()); // A6
2714 pDoc->SetValue(ScAddress(0, 6, 0), 123);
2715 pDoc->SetString(ScAddress(0, 7, 0), "ZZZ"); // A8
2717 ScAddress aA1(0, 0, 0);
2718 lcl_typeCharsInCell("X", aA1.Col(), aA1.Row(), pView, pModelObj); // Type 'X' in A1
2719 CPPUNIT_ASSERT_EQUAL_MESSAGE("A1 should autocomplete", OUString("XYZ"), pDoc->GetString(aA1));
2721 ScAddress aA3(0, 2, 0); // Adjacent to the string "superblock" A4:A8
2722 lcl_typeCharsInCell("X", aA3.Col(), aA3.Row(), pView, pModelObj); // Type 'X' in A3
2723 CPPUNIT_ASSERT_EQUAL_MESSAGE("A3 should autocomplete", OUString("XYZ"), pDoc->GetString(aA3));
2725 ScAddress aA9(0, 8, 0); // Adjacent to the string "superblock" A4:A8
2726 lcl_typeCharsInCell("X", aA9.Col(), aA9.Row(), pView, pModelObj); // Type 'X' in A9
2727 CPPUNIT_ASSERT_EQUAL_MESSAGE("A9 should autocomplete", OUString("XYZ"), pDoc->GetString(aA9));
2729 ScAddress aA11(0, 10, 0);
2730 lcl_typeCharsInCell("X", aA11.Col(), aA11.Row(), pView, pModelObj); // Type 'X' in A11
2731 CPPUNIT_ASSERT_EQUAL_MESSAGE("A11 should autocomplete", OUString("XYZ"), pDoc->GetString(aA11));
2734 void ScTiledRenderingTest::testAutoInputExactMatch()
2736 ScModelObj* pModelObj = createDoc("empty.ods");
2737 CPPUNIT_ASSERT(pModelObj);
2738 ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2739 CPPUNIT_ASSERT(pView);
2740 ScDocument* pDoc = pModelObj->GetDocument();
2742 pDoc->SetString(ScAddress(0, 1, 0), "Simple"); // A2
2743 pDoc->SetString(ScAddress(0, 2, 0), "Simple"); // A3
2744 pDoc->SetString(ScAddress(0, 3, 0), "Sing"); // A4
2745 ScFieldEditEngine& rEE = pDoc->GetEditEngine();
2746 rEE.SetText("Case");
2747 pDoc->SetEditText(ScAddress(0, 4, 0), rEE.CreateTextObject()); // A5
2748 pDoc->SetString(ScAddress(0, 5, 0), "Time"); // A6
2749 pDoc->SetString(ScAddress(0, 6, 0), "Castle"); // A7
2751 ScAddress aA8(0, 7, 0);
2752 lcl_typeCharsInCell("S", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "S" in A8
2753 // Should show the partial completion "i".
2754 CPPUNIT_ASSERT_EQUAL_MESSAGE("1: A8 should have partial completion Si", OUString("Si"), pDoc->GetString(aA8));
2756 lcl_typeCharsInCell("Si", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "Si" in A8
2757 // Should not show any suggestions.
2758 CPPUNIT_ASSERT_EQUAL_MESSAGE("2: A8 should not show suggestions", OUString("Si"), pDoc->GetString(aA8));
2760 lcl_typeCharsInCell("Sim", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "Sim" in A8
2761 // Should autocomplete to "Simple" which is the only match.
2762 CPPUNIT_ASSERT_EQUAL_MESSAGE("3: A8 should autocomplete", OUString("Simple"), pDoc->GetString(aA8));
2764 lcl_typeCharsInCell("Sin", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "Sin" in A8
2765 // Should autocomplete to "Sing" which is the only match.
2766 CPPUNIT_ASSERT_EQUAL_MESSAGE("4: A8 should autocomplete", OUString("Sing"), pDoc->GetString(aA8));
2768 lcl_typeCharsInCell("C", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "C" in A8
2769 // Should show the partial completion "as".
2770 CPPUNIT_ASSERT_EQUAL_MESSAGE("5: A8 should have partial completion Cas", OUString("Cas"), pDoc->GetString(aA8));
2772 lcl_typeCharsInCell("Cast", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "Cast" in A8
2773 // Should autocomplete to "Castle" which is the only match.
2774 CPPUNIT_ASSERT_EQUAL_MESSAGE("6: A8 should autocomplete", OUString("Castle"), pDoc->GetString(aA8));
2776 lcl_typeCharsInCell("T", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "T" in A8
2777 // Should autocomplete to "Time" which is the only match.
2778 CPPUNIT_ASSERT_EQUAL_MESSAGE("7: A8 should autocomplete", OUString("Time"), pDoc->GetString(aA8));
2781 void ScTiledRenderingTest::testEditCursorBounds()
2783 comphelper::LibreOfficeKit::setCompatFlag(
2784 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
2785 ScModelObj* pModelObj = createDoc("empty.ods");
2786 ScDocument* pDoc = pModelObj->GetDocument();
2788 ViewCallback aView;
2789 ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2790 CPPUNIT_ASSERT(pView);
2791 comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
2793 // ~170% zoom.
2794 pModelObj->setClientZoom(256, 256, 2222, 2222);
2795 pModelObj->setClientVisibleArea(tools::Rectangle(7725, 379832, 16240, 6449));
2796 Scheduler::ProcessEventsToIdle();
2798 constexpr SCCOL nCol = 5;
2799 constexpr SCROW nRow = 2048;
2800 pDoc->SetValue(ScAddress(nCol, nRow, 0), 123);
2802 aView.m_bOwnCursorInvalidated = false;
2803 // Obtain the cell bounds via cursor.
2804 pView->SetCursor(nCol, nRow);
2805 Scheduler::ProcessEventsToIdle();
2807 CPPUNIT_ASSERT(aView.m_bOwnCursorInvalidated);
2808 CPPUNIT_ASSERT(!aView.m_aCellCursorBounds.IsEmpty());
2809 tools::Rectangle aCellBounds(aView.m_aCellCursorBounds);
2811 aView.m_aInvalidateCursorResult.clear();
2812 // Enter edit mode in the same cell.
2813 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::F2);
2814 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::F2);
2815 Scheduler::ProcessEventsToIdle();
2817 CPPUNIT_ASSERT(!aView.m_aInvalidateCursorResult.empty());
2818 CPPUNIT_ASSERT_MESSAGE("Edit cursor must be in cell bounds!",
2819 aCellBounds.Contains(aView.m_aInvalidateCursorResult.getBounds()));
2821 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2824 void ScTiledRenderingTest::testTextSelectionBounds()
2826 comphelper::LibreOfficeKit::setCompatFlag(
2827 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
2828 ScModelObj* pModelObj = createDoc("empty.ods");
2829 ScDocument* pDoc = pModelObj->GetDocument();
2831 ViewCallback aView;
2832 ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2833 CPPUNIT_ASSERT(pView);
2834 comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
2836 // ~170% zoom.
2837 pModelObj->setClientZoom(256, 256, 2222, 2222);
2838 pModelObj->setClientVisibleArea(tools::Rectangle(7725, 379832, 16240, 6449));
2839 Scheduler::ProcessEventsToIdle();
2841 constexpr SCCOL nCol = 5;
2842 constexpr SCROW nRow = 2048;
2843 pDoc->SetValue(ScAddress(nCol, nRow, 0), 123);
2845 aView.m_bOwnCursorInvalidated = false;
2846 // Obtain the cell bounds via cursor.
2847 pView->SetCursor(nCol, nRow);
2848 Scheduler::ProcessEventsToIdle();
2850 CPPUNIT_ASSERT(aView.m_bOwnCursorInvalidated);
2851 CPPUNIT_ASSERT(!aView.m_aCellCursorBounds.IsEmpty());
2852 tools::Rectangle aCellBounds(aView.m_aCellCursorBounds);
2854 aView.m_aTextSelectionResult.clear();
2855 // Enter edit mode in the same cell and select all text.
2856 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::F2);
2857 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::F2);
2858 Scheduler::ProcessEventsToIdle();
2860 // CTRL + A
2861 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_MOD1 | awt::Key::A);
2862 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_MOD1 | awt::Key::A);
2863 Scheduler::ProcessEventsToIdle();
2865 CPPUNIT_ASSERT(!aView.m_aTextSelectionResult.empty());
2866 CPPUNIT_ASSERT_MESSAGE("Text selections must be in cell bounds!",
2867 !aCellBounds.Intersection(aView.m_aTextSelectionResult.getBounds(0)).IsEmpty());
2869 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2872 void ScTiledRenderingTest::testSheetViewDataCrash()
2874 ScModelObj* pModelObj = createDoc("empty.ods");
2876 // view #1
2877 int nView1 = SfxLokHelper::getView();
2878 SfxLokHelper::setView(nView1);
2880 // Imitate online while creating a new sheet on empty.ods.
2881 uno::Sequence<beans::PropertyValue> aArgs(
2882 comphelper::InitPropertySequence({
2883 { "Name", uno::Any(OUString("NewSheet")) },
2884 { "Index", uno::Any(sal_Int32(2)) }
2885 }));
2886 dispatchCommand(mxComponent, ".uno:Insert", aArgs);
2887 Scheduler::ProcessEventsToIdle();
2888 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN | KEY_MOD1);
2889 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN | KEY_MOD1);
2890 Scheduler::ProcessEventsToIdle();
2891 ScTabViewShell* pView1 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2892 CPPUNIT_ASSERT(pView1);
2894 // view #2
2895 SfxLokHelper::createView();
2896 ScTabViewShell* pView2 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2897 CPPUNIT_ASSERT(pView2);
2898 Scheduler::ProcessEventsToIdle();
2900 SfxLokHelper::setView(nView1);
2901 // Delete a range.
2902 pView1->SetCursor(1, 1);
2903 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN | KEY_SHIFT);
2904 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN | KEY_SHIFT);
2905 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DELETE);
2906 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DELETE);
2907 // It will crash at this point without the fix.
2908 Scheduler::ProcessEventsToIdle();
2911 void ScTiledRenderingTest::testTextBoxInsert()
2913 createDoc("empty.ods");
2914 ViewCallback aView1;
2916 // insert textbox
2917 uno::Sequence<beans::PropertyValue> aArgs(
2918 comphelper::InitPropertySequence({
2919 { "CreateDirectly", uno::Any(true) }
2920 }));
2921 dispatchCommand(mxComponent, ".uno:DrawText", aArgs);
2922 Scheduler::ProcessEventsToIdle();
2924 // check if we have textbox selected
2925 CPPUNIT_ASSERT(!aView1.m_ShapeSelection.isEmpty());
2926 CPPUNIT_ASSERT(aView1.m_ShapeSelection != "EMPTY");
2928 Scheduler::ProcessEventsToIdle();
2931 void ScTiledRenderingTest::testCommentCellCopyPaste()
2933 // Comments callback are emitted only if tiled annotations are off
2934 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2936 // FIXME: Hack because previous tests do not destroy ScDocument(with annotations) on exit (?).
2937 ScPostIt::mnLastPostItId = 1;
2940 ScModelObj* pModelObj = createDoc("empty.ods");
2941 ViewCallback aView;
2942 int nView = SfxLokHelper::getView();
2944 SfxLokHelper::setView(nView);
2946 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2947 CPPUNIT_ASSERT(pTabViewShell);
2949 lcl_typeCharsInCell("ABC", 0, 0, pTabViewShell, pModelObj); // Type "ABC" in A1
2951 pTabViewShell->SetCursor(1, 1);
2953 // Add a new comment
2954 uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
2956 {"Text", uno::Any(OUString("LOK Comment Cell B2"))},
2957 {"Author", uno::Any(OUString("LOK Client"))},
2958 }));
2959 dispatchCommand(mxComponent, ".uno:InsertAnnotation", aArgs);
2960 Scheduler::ProcessEventsToIdle();
2962 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
2963 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView.m_aCommentCallbackResult.get<std::string>("action"));
2964 CPPUNIT_ASSERT_EQUAL(std::string("1"), aView.m_aCommentCallbackResult.get<std::string>("id"));
2965 CPPUNIT_ASSERT_EQUAL(std::string("0"), aView.m_aCommentCallbackResult.get<std::string>("tab"));
2966 CPPUNIT_ASSERT_EQUAL(std::string("LOK Client"), aView.m_aCommentCallbackResult.get<std::string>("author"));
2967 CPPUNIT_ASSERT_EQUAL(std::string("LOK Comment Cell B2"), aView.m_aCommentCallbackResult.get<std::string>("text"));
2969 uno::Sequence<beans::PropertyValue> aCopyPasteArgs;
2971 // We need separate tests for single cell copy-paste and cell-range copy-paste
2972 // since they hit different code paths in ScColumn methods.
2974 // Single cell(with comment) copy paste test
2976 dispatchCommand(mxComponent, ".uno:Copy", aCopyPasteArgs);
2977 Scheduler::ProcessEventsToIdle();
2979 pTabViewShell->SetCursor(1, 49);
2980 Scheduler::ProcessEventsToIdle();
2981 dispatchCommand(mxComponent, ".uno:Paste", aCopyPasteArgs); // Paste to cell B50
2982 Scheduler::ProcessEventsToIdle();
2984 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
2985 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView.m_aCommentCallbackResult.get<std::string>("action"));
2986 // Without the fix the id will be "1".
2987 CPPUNIT_ASSERT_EQUAL(std::string("2"), aView.m_aCommentCallbackResult.get<std::string>("id"));
2988 CPPUNIT_ASSERT_EQUAL(std::string("0"), aView.m_aCommentCallbackResult.get<std::string>("tab"));
2989 CPPUNIT_ASSERT_EQUAL(std::string("LOK Client"), aView.m_aCommentCallbackResult.get<std::string>("author"));
2990 CPPUNIT_ASSERT_EQUAL(std::string("LOK Comment Cell B2"), aView.m_aCommentCallbackResult.get<std::string>("text"));
2993 // Cell range (with a comment) copy paste test
2995 // Select range A1:C3
2996 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
2997 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
2998 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_SHIFT);
2999 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_SHIFT);
3000 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_SHIFT);
3001 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_SHIFT);
3002 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT | KEY_SHIFT);
3003 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT | KEY_SHIFT);
3004 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT | KEY_SHIFT);
3005 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT | KEY_SHIFT);
3006 Scheduler::ProcessEventsToIdle();
3008 dispatchCommand(mxComponent, ".uno:Copy", aCopyPasteArgs);
3009 Scheduler::ProcessEventsToIdle();
3011 pTabViewShell->SetCursor(3, 49);
3012 Scheduler::ProcessEventsToIdle();
3013 dispatchCommand(mxComponent, ".uno:Paste", aCopyPasteArgs); // Paste to cell D50
3014 Scheduler::ProcessEventsToIdle();
3016 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
3017 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView.m_aCommentCallbackResult.get<std::string>("action"));
3018 // Without the fix the id will be "1".
3019 CPPUNIT_ASSERT_EQUAL(std::string("3"), aView.m_aCommentCallbackResult.get<std::string>("id"));
3020 CPPUNIT_ASSERT_EQUAL(std::string("0"), aView.m_aCommentCallbackResult.get<std::string>("tab"));
3021 CPPUNIT_ASSERT_EQUAL(std::string("LOK Client"), aView.m_aCommentCallbackResult.get<std::string>("author"));
3022 CPPUNIT_ASSERT_EQUAL(std::string("LOK Comment Cell B2"), aView.m_aCommentCallbackResult.get<std::string>("text"));
3025 comphelper::LibreOfficeKit::setTiledAnnotations(true);
3028 void ScTiledRenderingTest::testInvalidEntrySave()
3030 loadFromURL(u"validity.xlsx");
3032 // .uno:Save modifies the original file, make a copy first
3033 saveAndReload("Calc Office Open XML");
3034 ScModelObj* pModelObj = dynamic_cast<ScModelObj*>(mxComponent.get());
3035 CPPUNIT_ASSERT(pModelObj);
3036 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
3037 const ScDocument* pDoc = pModelObj->GetDocument();
3038 ViewCallback aView;
3039 int nView = SfxLokHelper::getView();
3041 SfxLokHelper::setView(nView);
3043 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
3044 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
3045 CPPUNIT_ASSERT(pTabViewShell);
3047 // Type partial date "7/8" of "7/8/2013" that
3048 // the validation cell at A8 can accept
3049 lcl_typeCharsInCell("7/8", 0, 7, pTabViewShell, pModelObj,
3050 false /* bInEdit */, false /* bCommit */); // Type "7/8" in A8
3052 uno::Sequence<beans::PropertyValue> aArgs;
3053 dispatchCommand(mxComponent, ".uno:Save", aArgs);
3054 Scheduler::ProcessEventsToIdle();
3056 CPPUNIT_ASSERT_MESSAGE("Should not be marked modified after save", !pDocSh->IsModified());
3058 // Complete the date in A8 by appending "/2013" and commit.
3059 lcl_typeCharsInCell("/2013", 0, 7, pTabViewShell, pModelObj,
3060 true /* bInEdit */, true /* bCommit */);
3062 // This would hang if the date entered "7/8/2013" is not acceptable.
3063 Scheduler::ProcessEventsToIdle();
3065 // Ensure that the correct date is recorded in the document.
3066 CPPUNIT_ASSERT_EQUAL(double(41463), pDoc->GetValue(ScAddress(0, 7, 0)));
3069 void ScTiledRenderingTest::testUndoReordering()
3071 ScModelObj* pModelObj = createDoc("small.ods");
3072 CPPUNIT_ASSERT(pModelObj);
3073 ScDocument* pDoc = pModelObj->GetDocument();
3074 CPPUNIT_ASSERT(pDoc);
3075 ScUndoManager* pUndoManager = pDoc->GetUndoManager();
3076 CPPUNIT_ASSERT(pUndoManager);
3078 // view #1
3079 int nView1 = SfxLokHelper::getView();
3080 ViewCallback aView1;
3082 // view #2
3083 SfxLokHelper::createView();
3084 int nView2 = SfxLokHelper::getView();
3085 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
3086 ViewCallback aView2;
3088 // text edit a cell in view #1
3089 SfxLokHelper::setView(nView1);
3090 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
3091 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
3092 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3093 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3094 Scheduler::ProcessEventsToIdle();
3096 // check that undo action count is not 0
3097 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
3099 // text edit a different cell in view #2
3100 SfxLokHelper::setView(nView2);
3101 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
3102 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
3103 Scheduler::ProcessEventsToIdle();
3104 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
3105 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
3106 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
3107 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
3108 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3109 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3110 Scheduler::ProcessEventsToIdle();
3112 // check that undo action count is not 1
3113 CPPUNIT_ASSERT_EQUAL(std::size_t(2), pUndoManager->GetUndoActionCount());
3115 // try to execute undo in view #1
3116 SfxLokHelper::setView(nView1);
3117 dispatchCommand(mxComponent, ".uno:Undo", {});
3118 Scheduler::ProcessEventsToIdle();
3119 // check that undo has been executed on view #1
3120 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
3122 // try to execute undo in view #2
3123 SfxLokHelper::setView(nView2);
3124 dispatchCommand(mxComponent, ".uno:Undo", {});
3125 Scheduler::ProcessEventsToIdle();
3126 // check that undo has been executed on view #2
3127 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetUndoActionCount());
3130 void ScTiledRenderingTest::testUndoReorderingRedo()
3132 ScModelObj* pModelObj = createDoc("empty.ods");
3133 CPPUNIT_ASSERT(pModelObj);
3134 ScDocument* pDoc = pModelObj->GetDocument();
3135 CPPUNIT_ASSERT(pDoc);
3136 ScUndoManager* pUndoManager = pDoc->GetUndoManager();
3137 CPPUNIT_ASSERT(pUndoManager);
3138 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetUndoActionCount());
3140 // view #1
3141 int nView1 = SfxLokHelper::getView();
3142 SfxViewShell* pView1 = SfxViewShell::Current();
3143 ViewCallback aView1;
3145 // view #2
3146 SfxLokHelper::createView();
3147 int nView2 = SfxLokHelper::getView();
3148 SfxViewShell* pView2 = SfxViewShell::Current();
3149 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
3150 ViewCallback aView2;
3152 // text edit a cell in view #1
3153 SfxLokHelper::setView(nView1);
3154 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
3155 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
3156 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
3157 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
3158 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3159 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3160 Scheduler::ProcessEventsToIdle();
3161 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
3163 // text edit another cell in view #1
3164 SfxLokHelper::setView(nView1);
3165 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'y', 0);
3166 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'y', 0);
3167 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'y', 0);
3168 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'y', 0);
3169 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3170 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3171 Scheduler::ProcessEventsToIdle();
3172 CPPUNIT_ASSERT_EQUAL(std::size_t(2), pUndoManager->GetUndoActionCount());
3173 CPPUNIT_ASSERT_EQUAL(OUString("xx"), pDoc->GetString(ScAddress(0, 0, 0)));
3174 CPPUNIT_ASSERT_EQUAL(OUString("yy"), pDoc->GetString(ScAddress(0, 1, 0)));
3176 // text edit a different cell in view #2
3177 SfxLokHelper::setView(nView2);
3178 ScTabViewShell* pViewShell2 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
3179 pViewShell2->SetCursor(0, 2);
3180 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'C', 0);
3181 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'C', 0);
3182 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'C', 0);
3183 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'C', 0);
3184 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3185 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3186 Scheduler::ProcessEventsToIdle();
3187 CPPUNIT_ASSERT_EQUAL(std::size_t(3), pUndoManager->GetUndoActionCount());
3188 CPPUNIT_ASSERT_EQUAL(OUString("xx"), pDoc->GetString(ScAddress(0, 0, 0)));
3189 CPPUNIT_ASSERT_EQUAL(OUString("yy"), pDoc->GetString(ScAddress(0, 1, 0)));
3190 CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
3192 // View 1 presses undo, and the second cell is erased
3193 SfxLokHelper::setView(nView1);
3194 dispatchCommand(mxComponent, ".uno:Undo", {});
3195 Scheduler::ProcessEventsToIdle();
3196 CPPUNIT_ASSERT_EQUAL(std::size_t(2), pUndoManager->GetUndoActionCount());
3197 CPPUNIT_ASSERT_EQUAL(OUString("xx"), pDoc->GetString(ScAddress(0, 0, 0)));
3198 CPPUNIT_ASSERT_EQUAL(OUString(""), pDoc->GetString(ScAddress(0, 1, 0)));
3199 CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
3201 // Verify that the UNDO buttons/actions are still enabled
3203 SfxItemSet aSet1(pView1->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
3204 SfxItemSet aSet2(pView2->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
3205 pView1->GetSlotState(SID_UNDO, nullptr, &aSet1);
3206 pView2->GetSlotState(SID_UNDO, nullptr, &aSet2);
3207 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet1.GetItemState(SID_UNDO));
3208 CPPUNIT_ASSERT(dynamic_cast< const SfxStringItem* >(aSet1.GetItem(SID_UNDO)));
3209 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet2.GetItemState(SID_UNDO));
3210 CPPUNIT_ASSERT(dynamic_cast< const SfxStringItem* >(aSet2.GetItem(SID_UNDO)));
3213 // View 1 presses undo again, and the first cell is erased
3214 dispatchCommand(mxComponent, ".uno:Undo", {});
3215 Scheduler::ProcessEventsToIdle();
3216 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
3217 CPPUNIT_ASSERT_EQUAL(OUString(""), pDoc->GetString(ScAddress(0, 0, 0)));
3218 CPPUNIT_ASSERT_EQUAL(OUString(""), pDoc->GetString(ScAddress(0, 1, 0)));
3219 CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
3222 void ScTiledRenderingTest::testUndoReorderingMulti()
3224 ScModelObj* pModelObj = createDoc("empty.ods");
3225 CPPUNIT_ASSERT(pModelObj);
3226 ScDocument* pDoc = pModelObj->GetDocument();
3227 CPPUNIT_ASSERT(pDoc);
3228 ScUndoManager* pUndoManager = pDoc->GetUndoManager();
3229 CPPUNIT_ASSERT(pUndoManager);
3230 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetUndoActionCount());
3232 // view #1
3233 int nView1 = SfxLokHelper::getView();
3234 ViewCallback aView1;
3236 // view #2
3237 SfxLokHelper::createView();
3238 int nView2 = SfxLokHelper::getView();
3239 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
3240 ViewCallback aView2;
3242 // text edit a cell in view #1
3243 SfxLokHelper::setView(nView1);
3244 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
3245 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
3246 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
3247 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
3248 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3249 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3250 Scheduler::ProcessEventsToIdle();
3251 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
3253 // text edit a different cell in view #2
3254 SfxLokHelper::setView(nView2);
3255 ScTabViewShell* pView2 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
3256 pView2->SetCursor(0, 2);
3257 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'C', 0);
3258 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'C', 0);
3259 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'C', 0);
3260 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'C', 0);
3261 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3262 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3263 Scheduler::ProcessEventsToIdle();
3264 CPPUNIT_ASSERT_EQUAL(std::size_t(2), pUndoManager->GetUndoActionCount());
3265 CPPUNIT_ASSERT_EQUAL(OUString("xx"), pDoc->GetString(ScAddress(0, 0, 0)));
3266 CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
3268 // and another cell in view #2
3269 pView2->SetCursor(0, 3);
3270 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'D', 0);
3271 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'D', 0);
3272 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'D', 0);
3273 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'D', 0);
3274 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3275 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3276 Scheduler::ProcessEventsToIdle();
3277 CPPUNIT_ASSERT_EQUAL(std::size_t(3), pUndoManager->GetUndoActionCount());
3278 CPPUNIT_ASSERT_EQUAL(OUString("xx"), pDoc->GetString(ScAddress(0, 0, 0)));
3279 CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
3280 CPPUNIT_ASSERT_EQUAL(OUString("DD"), pDoc->GetString(ScAddress(0, 3, 0)));
3282 // View 1 presses undo
3283 SfxLokHelper::setView(nView1);
3284 dispatchCommand(mxComponent, ".uno:Undo", {});
3285 Scheduler::ProcessEventsToIdle();
3286 CPPUNIT_ASSERT_EQUAL(std::size_t(2), pUndoManager->GetUndoActionCount());
3287 CPPUNIT_ASSERT_EQUAL(OUString(""), pDoc->GetString(ScAddress(0, 0, 0)));
3288 CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
3289 CPPUNIT_ASSERT_EQUAL(OUString("DD"), pDoc->GetString(ScAddress(0, 3, 0)));
3292 void ScTiledRenderingTest::testGetViewRenderState()
3294 // Add a pair of schemes, last added is the default
3295 svtools::EditableColorConfig aColorConfig;
3296 aColorConfig.AddScheme(u"Dark");
3297 aColorConfig.AddScheme(u"Light");
3299 ScModelObj* pModelObj = createDoc("empty.ods");
3300 int nFirstViewId = SfxLokHelper::getView();
3301 ViewCallback aView1;
3303 CPPUNIT_ASSERT_EQUAL(OString(";Default"), pModelObj->getViewRenderState());
3304 // Create a second view
3305 SfxLokHelper::createView();
3306 ViewCallback aView2;
3307 CPPUNIT_ASSERT_EQUAL(OString(";Default"), pModelObj->getViewRenderState());
3308 // Set second view to dark scheme
3310 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
3312 { "NewTheme", uno::Any(OUString("Dark")) },
3315 dispatchCommand(mxComponent, ".uno:ChangeTheme", aPropertyValues);
3317 CPPUNIT_ASSERT_EQUAL(OString(";Dark"), pModelObj->getViewRenderState());
3319 // Switch back to first view and make sure it's the same
3320 SfxLokHelper::setView(nFirstViewId);
3321 CPPUNIT_ASSERT_EQUAL(OString(";Default"), pModelObj->getViewRenderState());
3325 * testInvalidateOnTextEditWithDifferentZoomLevels
3326 * steps:
3327 * set view 1 zoom to the passed zoom level
3328 * in view 1 type a char at the passed cell address
3329 * store invalidation rectangle
3330 * exit from in place editing (press esc)
3331 * create view 2 (keep 100% zoom)
3332 * go to the same cell address used in view 1
3333 * type a char into the cell
3334 * get invalidation rectangle for view 1
3335 * check if the invalidation rectangle is equal to the one stored previously
3337 void ScTiledRenderingTest::testInvalidateOnTextEditWithDifferentZoomLevels(const ColRowZoom& rData)
3339 ScModelObj* pModelObj = createDoc("empty.ods");
3340 CPPUNIT_ASSERT(pModelObj);
3341 ScDocument* pDoc = pModelObj->GetDocument();
3342 CPPUNIT_ASSERT(pDoc);
3344 OUString sZoomUnoCmd = ".uno:ZoomPlus";
3345 int nZoomLevel = rData.zoom;
3346 if (nZoomLevel < 0)
3348 nZoomLevel = -nZoomLevel;
3349 sZoomUnoCmd = ".uno:ZoomMinus";
3352 // view #1
3353 ViewCallback aView1;
3354 // set zoom level
3355 for (int i = 0; i < nZoomLevel; ++i)
3356 dispatchCommand(mxComponent, sZoomUnoCmd, {});
3357 Scheduler::ProcessEventsToIdle();
3359 auto* pTabViewShell1 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
3360 CPPUNIT_ASSERT(pTabViewShell1);
3362 // enable in place editing in view 1
3363 auto& rInvalidations = aView1.m_aInvalidations;
3364 pTabViewShell1->SetCursor(rData.col, rData.row);
3365 Scheduler::ProcessEventsToIdle();
3366 aView1.m_bInvalidateTiles = false;
3367 rInvalidations.clear();
3368 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
3369 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
3370 Scheduler::ProcessEventsToIdle();
3371 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
3372 CPPUNIT_ASSERT(!rInvalidations.empty());
3373 tools::Rectangle aInvRect1 = rInvalidations[0];
3375 // end editing
3376 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::ESCAPE);
3377 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::ESCAPE);
3378 Scheduler::ProcessEventsToIdle();
3380 // view #2
3381 SfxLokHelper::createView();
3382 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
3383 ViewCallback aView2;
3384 Scheduler::ProcessEventsToIdle();
3386 auto* pTabViewShell2 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
3387 CPPUNIT_ASSERT(pTabViewShell2);
3388 pTabViewShell2->SetCursor(rData.col, rData.row);
3389 Scheduler::ProcessEventsToIdle();
3391 // text edit in view #2
3392 aView1.m_bInvalidateTiles = false;
3393 rInvalidations.clear();
3394 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
3395 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
3396 Scheduler::ProcessEventsToIdle();
3397 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
3398 CPPUNIT_ASSERT(!rInvalidations.empty());
3399 tools::Rectangle aInvRect2 = rInvalidations[0];
3401 CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalidation rectangle is wrong.", aInvRect1, aInvRect2);
3406 CPPUNIT_TEST_SUITE_REGISTRATION(ScTiledRenderingTest);
3408 CPPUNIT_PLUGIN_IMPLEMENT();
3410 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */