Version 7.5.1.1, tag libreoffice-7.5.1.1
[LibreOffice.git] / sc / qa / unit / tiledrendering / tiledrendering.cxx
blob8ca176d8d6cbce9c4e3716f38c14e84beb1122f0
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 <boost/property_tree/json_parser.hpp>
14 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
15 #include <com/sun/star/frame/Desktop.hpp>
16 #include <com/sun/star/datatransfer/clipboard/SystemClipboard.hpp>
17 #include <comphelper/dispatchcommand.hxx>
18 #include <comphelper/processfactory.hxx>
19 #include <comphelper/propertysequence.hxx>
20 #include <osl/conditn.hxx>
21 #include <sfx2/dispatch.hxx>
22 #include <sfx2/viewfrm.hxx>
23 #include <svl/stritem.hxx>
25 #include <comphelper/lok.hxx>
26 #include <comphelper/propertyvalue.hxx>
27 #include <sfx2/childwin.hxx>
28 #include <sfx2/lokhelper.hxx>
29 #include <svx/svdpage.hxx>
30 #include <vcl/scheduler.hxx>
31 #include <vcl/vclevent.hxx>
32 #include <vcl/virdev.hxx>
33 #include <sc.hrc>
34 #include <comphelper/string.hxx>
35 #include <tools/json_writer.hxx>
36 #include <docoptio.hxx>
37 #include <postit.hxx>
38 #include <test/lokcallback.hxx>
39 #include <osl/file.hxx>
40 #include <unotools/tempfile.hxx>
42 #include <chrono>
43 #include <cstddef>
45 #include <attrib.hxx>
46 #include <scitems.hxx>
47 #include <tabvwsh.hxx>
48 #include <docsh.hxx>
49 #include <document.hxx>
50 #include <docuno.hxx>
51 #include <drwlayer.hxx>
52 #include <editutil.hxx>
53 #include <undomanager.hxx>
55 using namespace css;
57 static std::ostream& operator<<(std::ostream& os, ViewShellId const & id)
59 os << static_cast<sal_Int32>(id); return os;
62 namespace
65 class ScTiledRenderingTest : public UnoApiXmlTest
67 public:
68 ScTiledRenderingTest();
69 virtual void setUp() override;
70 virtual void tearDown() override;
72 void testRowColumnHeaders();
73 void testRowColumnSelections();
74 void testPartHash();
75 void testDocumentSize();
76 void testEmptyColumnSelection();
77 void testViewCursors();
78 void testTextViewSelection();
79 void testDocumentSizeChanged();
80 void testViewLock();
81 void testColRowResize();
82 void testUndoShells();
83 void testCreateViewGraphicSelection();
84 void testTextEditViews();
85 void testTextEditViewInvalidations();
86 void testGraphicInvalidate();
87 void testAutoSum();
88 void testHideColRow();
89 void testInvalidateOnCopyPasteCells();
90 void testInvalidateOnInserRowCol();
91 void testCommentCallback();
92 void testUndoLimiting();
93 void testUndoRepairDispatch();
94 void testInsertGraphicInvalidations();
95 void testDocumentSizeWithTwoViews();
96 void testDisableUndoRepair();
97 void testDocumentRepair();
98 void testLanguageStatus();
99 void testMultiViewCopyPaste();
100 void testIMESupport();
101 void testFilterDlg();
102 void testVbaRangeCopyPaste();
103 void testInvalidationLoop();
104 void testPageDownInvalidation();
105 void testSheetChangeInvalidation();
106 void testInsertDeletePageInvalidation();
107 void testGetRowColumnHeadersInvalidation();
108 void testJumpHorizontallyInvalidation();
109 void testJumpToLastRowInvalidation();
110 void testSheetGeometryDataInvariance();
111 void testSheetGeometryDataCorrectness();
112 void testDeleteCellMultilineContent();
113 void testFunctionDlg();
114 void testSpellOnlineParameter();
115 void testSpellOnlineRenderParameter();
116 void testPasteIntoWrapTextCell();
117 void testSortAscendingDescending();
118 void testAutoInputStringBlock();
119 void testAutoInputExactMatch();
120 void testMoveShapeHandle();
121 void testEditCursorBounds();
122 void testTextSelectionBounds();
123 void testSheetViewDataCrash();
124 void testTextBoxInsert();
125 void testCommentCellCopyPaste();
126 void testInvalidEntrySave();
127 void testUndoReordering();
128 void testUndoReorderingRedo();
129 void testUndoReorderingMulti();
131 CPPUNIT_TEST_SUITE(ScTiledRenderingTest);
132 CPPUNIT_TEST(testRowColumnHeaders);
133 CPPUNIT_TEST(testRowColumnSelections);
134 CPPUNIT_TEST(testPartHash);
135 CPPUNIT_TEST(testDocumentSize);
136 CPPUNIT_TEST(testEmptyColumnSelection);
137 CPPUNIT_TEST(testViewCursors);
138 CPPUNIT_TEST(testTextViewSelection);
139 CPPUNIT_TEST(testDocumentSizeChanged);
140 CPPUNIT_TEST(testViewLock);
141 CPPUNIT_TEST(testColRowResize);
142 CPPUNIT_TEST(testUndoShells);
143 CPPUNIT_TEST(testCreateViewGraphicSelection);
144 CPPUNIT_TEST(testTextEditViews);
145 CPPUNIT_TEST(testTextEditViewInvalidations);
146 CPPUNIT_TEST(testGraphicInvalidate);
147 CPPUNIT_TEST(testAutoSum);
148 CPPUNIT_TEST(testHideColRow);
149 CPPUNIT_TEST(testInvalidateOnCopyPasteCells);
150 CPPUNIT_TEST(testInvalidateOnInserRowCol);
151 CPPUNIT_TEST(testCommentCallback);
152 CPPUNIT_TEST(testUndoLimiting);
153 CPPUNIT_TEST(testUndoRepairDispatch);
154 CPPUNIT_TEST(testInsertGraphicInvalidations);
155 CPPUNIT_TEST(testDocumentSizeWithTwoViews);
156 CPPUNIT_TEST(testDisableUndoRepair);
157 CPPUNIT_TEST(testDocumentRepair);
158 CPPUNIT_TEST(testLanguageStatus);
159 CPPUNIT_TEST(testMultiViewCopyPaste);
160 CPPUNIT_TEST(testIMESupport);
161 CPPUNIT_TEST(testFilterDlg);
162 CPPUNIT_TEST(testVbaRangeCopyPaste);
163 CPPUNIT_TEST(testInvalidationLoop);
164 CPPUNIT_TEST(testPageDownInvalidation);
165 CPPUNIT_TEST(testSheetChangeInvalidation);
166 CPPUNIT_TEST(testInsertDeletePageInvalidation);
167 CPPUNIT_TEST(testGetRowColumnHeadersInvalidation);
168 CPPUNIT_TEST(testJumpHorizontallyInvalidation);
169 CPPUNIT_TEST(testJumpToLastRowInvalidation);
170 CPPUNIT_TEST(testSheetGeometryDataInvariance);
171 CPPUNIT_TEST(testSheetGeometryDataCorrectness);
172 CPPUNIT_TEST(testDeleteCellMultilineContent);
173 CPPUNIT_TEST(testFunctionDlg);
174 CPPUNIT_TEST(testSpellOnlineParameter);
175 CPPUNIT_TEST(testSpellOnlineRenderParameter);
176 CPPUNIT_TEST(testPasteIntoWrapTextCell);
177 CPPUNIT_TEST(testSortAscendingDescending);
178 CPPUNIT_TEST(testAutoInputStringBlock);
179 CPPUNIT_TEST(testAutoInputExactMatch);
180 CPPUNIT_TEST(testMoveShapeHandle);
181 CPPUNIT_TEST(testEditCursorBounds);
182 CPPUNIT_TEST(testTextSelectionBounds);
183 CPPUNIT_TEST(testSheetViewDataCrash);
184 CPPUNIT_TEST(testTextBoxInsert);
185 CPPUNIT_TEST(testCommentCellCopyPaste);
186 CPPUNIT_TEST(testInvalidEntrySave);
187 CPPUNIT_TEST(testUndoReordering);
188 CPPUNIT_TEST(testUndoReorderingRedo);
189 CPPUNIT_TEST(testUndoReorderingMulti);
190 CPPUNIT_TEST_SUITE_END();
192 private:
193 ScModelObj* createDoc(const char* pName);
194 void setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell);
195 static void callback(int nType, const char* pPayload, void* pData);
196 void callbackImpl(int nType, const char* pPayload);
198 /// document size changed callback.
199 osl::Condition m_aDocSizeCondition;
200 Size m_aDocumentSize;
202 TestLokCallbackWrapper m_callbackWrapper;
205 ScTiledRenderingTest::ScTiledRenderingTest()
206 : UnoApiXmlTest("/sc/qa/unit/tiledrendering/data/"),
207 m_callbackWrapper(&callback, this)
211 void ScTiledRenderingTest::setUp()
213 UnoApiXmlTest::setUp();
215 comphelper::LibreOfficeKit::setActive(true);
218 void ScTiledRenderingTest::tearDown()
220 if (mxComponent.is())
222 mxComponent->dispose();
223 mxComponent.clear();
226 m_callbackWrapper.clear();
227 comphelper::LibreOfficeKit::setActive(false);
229 UnoApiXmlTest::tearDown();
232 ScModelObj* ScTiledRenderingTest::createDoc(const char* pName)
234 loadFromURL(OUString::createFromAscii(pName));
236 ScModelObj* pModelObj = dynamic_cast<ScModelObj*>(mxComponent.get());
237 CPPUNIT_ASSERT(pModelObj);
238 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
239 return pModelObj;
242 void ScTiledRenderingTest::setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell)
244 pViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
245 m_callbackWrapper.setLOKViewId(SfxLokHelper::getView(pViewShell));
248 void ScTiledRenderingTest::callback(int nType, const char* pPayload, void* pData)
250 static_cast<ScTiledRenderingTest*>(pData)->callbackImpl(nType, pPayload);
253 /* TODO when needed...
254 static std::vector<OUString> lcl_convertSeparated(const OUString& rString, sal_Unicode nSeparator)
256 std::vector<OUString> aRet;
258 sal_Int32 nIndex = 0;
261 OUString aToken = rString.getToken(0, nSeparator, nIndex);
262 aToken = aToken.trim();
263 if (!aToken.isEmpty())
264 aRet.push_back(aToken);
266 while (nIndex >= 0);
268 return aRet;
271 static void lcl_convertRectangle(const OUString& rString, Rectangle& rRectangle)
273 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(rString);
274 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength());
275 rRectangle.SetLeft(aSeq[0].toInt32());
276 rRectangle.SetTop(aSeq[1].toInt32());
277 rRectangle.setWidth(aSeq[2].toInt32());
278 rRectangle.setHeight(aSeq[3].toInt32());
282 void ScTiledRenderingTest::callbackImpl(int nType, const char* pPayload)
284 switch (nType)
286 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
288 OString aPayload(pPayload);
289 sal_Int32 nIndex = 0;
290 OString aToken = aPayload.getToken(0, ',', nIndex);
291 m_aDocumentSize.setWidth(aToken.toInt32());
292 aToken = aPayload.getToken(0, ',', nIndex);
293 m_aDocumentSize.setHeight(aToken.toInt32());
294 m_aDocSizeCondition.set();
296 break;
300 void ScTiledRenderingTest::testRowColumnSelections()
302 ScModelObj* pModelObj = createDoc("select-row-cols.ods");
304 // Select the 5th row with no modifier
305 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
306 { "Row", uno::Any(sal_Int32(5 - 1)) },
307 { "Modifier", uno::Any(sal_uInt16(0)) }
308 }));
309 comphelper::dispatchCommand(".uno:SelectRow", aArgs);
311 // Check if it is selected
312 OString aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
313 OString aExpected("1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\n");
314 CPPUNIT_ASSERT_EQUAL(aExpected, aResult);
316 // Select the 10th row with shift modifier
317 aArgs = comphelper::InitPropertySequence({ { "Row", uno::Any(static_cast<sal_Int32>(10 - 1)) },
318 { "Modifier", uno::Any(KEY_SHIFT) } });
319 comphelper::dispatchCommand(".uno:SelectRow", aArgs);
321 // Check if all the rows from 5th to 10th get selected
322 aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
323 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";
324 CPPUNIT_ASSERT_EQUAL(aExpected, aResult);
326 // Select the 10th row with ctrl modifier
327 aArgs = comphelper::InitPropertySequence({ { "Row", uno::Any(static_cast<sal_Int32>(13 - 1)) },
328 { "Modifier", uno::Any(KEY_MOD1) } });
329 comphelper::dispatchCommand(".uno:SelectRow", aArgs);
331 // When we copy this, we don't get anything useful, but we must not crash
332 // (used to happen)
333 aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
334 CPPUNIT_ASSERT_EQUAL(OString(), aResult);
336 // TODO check that we really selected what we wanted here
338 // Select Column 5 with ctrl modifier
339 aArgs = comphelper::InitPropertySequence({ { "Col", uno::Any(static_cast<sal_Int32>(5 - 1)) },
340 { "Modifier", uno::Any(KEY_MOD1) } });
341 comphelper::dispatchCommand(".uno:SelectColumn", aArgs);
343 // When we copy this, we don't get anything useful, but we must not crash
344 // (used to happen)
345 aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
346 CPPUNIT_ASSERT_EQUAL(OString(), aResult);
348 // TODO check that we really selected what we wanted here
350 // Test for deselection of already selected rows
351 // First Deselect Row 13 because copy doesn't work for multiple selections
352 aArgs = comphelper::InitPropertySequence({ { "Row", uno::Any(static_cast<sal_Int32>(13 - 1)) },
353 { "Modifier", uno::Any(KEY_MOD1) } });
354 comphelper::dispatchCommand(".uno:SelectRow", aArgs);
356 // Deselect row 10
357 aArgs = comphelper::InitPropertySequence({ { "Row", uno::Any(static_cast<sal_Int32>(10 - 1)) },
358 { "Modifier", uno::Any(KEY_MOD1) } });
359 comphelper::dispatchCommand(".uno:SelectRow", aArgs);
361 // Click at row 6 holding shift
362 aArgs = comphelper::InitPropertySequence({ { "Row", uno::Any(static_cast<sal_Int32>(6 - 1)) },
363 { "Modifier", uno::Any(KEY_SHIFT) } });
364 comphelper::dispatchCommand(".uno:SelectRow", aArgs);
366 // only row 5 should remain selected
367 aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
368 aExpected = "1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\n";
369 CPPUNIT_ASSERT_EQUAL(aExpected, aResult);
372 void ScTiledRenderingTest::testPartHash()
374 ScModelObj* pModelObj = createDoc("sort-range.ods");
376 int nParts = pModelObj->getParts();
377 for (int it = 0; it < nParts; it++)
379 CPPUNIT_ASSERT(!pModelObj->getPartHash(it).isEmpty());
382 // check part that it does not exists
383 CPPUNIT_ASSERT(pModelObj->getPartHash(100).isEmpty());
386 void ScTiledRenderingTest::testDocumentSize()
388 ScModelObj* pModelObj = createDoc("sort-range.ods");
389 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
390 CPPUNIT_ASSERT(pDocSh);
392 ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false);
393 CPPUNIT_ASSERT(pViewShell);
395 setupLibreOfficeKitViewCallback(pViewShell);
397 // check initial document size
398 Size aDocSize = pModelObj->getDocumentSize();
399 CPPUNIT_ASSERT(aDocSize.Width() > 0);
400 CPPUNIT_ASSERT(aDocSize.Height() > 0);
402 // Set cursor column
403 pViewShell->SetCursor(100, 0);
404 // 2 seconds
405 osl::Condition::Result aResult = m_aDocSizeCondition.wait(std::chrono::seconds(2));
406 CPPUNIT_ASSERT_EQUAL(osl::Condition::result_ok, aResult);
408 // Set cursor row
409 pViewShell->SetCursor(0, 100);
410 // 2 seconds
411 aResult = m_aDocSizeCondition.wait(std::chrono::seconds(2));
412 CPPUNIT_ASSERT_EQUAL(osl::Condition::result_ok, aResult);
415 void ScTiledRenderingTest::testEmptyColumnSelection()
417 ScModelObj* pModelObj = createDoc("select-row-cols.ods");
419 // Select empty column, 1000
420 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
421 { "Col", uno::Any(sal_Int32(1000 - 1)) },
422 { "Modifier", uno::Any(sal_uInt16(0)) }
423 }));
424 comphelper::dispatchCommand(".uno:SelectColumn", aArgs);
426 // should be an empty string
427 CPPUNIT_ASSERT_EQUAL(OString(), apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8"));
430 struct EditCursorMessage final {
431 tools::Rectangle m_aRelRect;
432 Point m_aRefPoint;
434 void clear()
436 m_aRelRect.SetEmpty();
437 m_aRefPoint = Point(-1, -1);
440 bool empty()
442 return m_aRelRect.IsEmpty() &&
443 m_aRefPoint.X() == -1 &&
444 m_aRefPoint.Y() == -1;
447 void parseMessage(const char* pMessage)
449 clear();
450 if (!pMessage || !comphelper::LibreOfficeKit::isCompatFlagSet(
451 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs) ||
452 !comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
453 return;
455 std::stringstream aStream(pMessage);
456 boost::property_tree::ptree aTree;
457 boost::property_tree::read_json(aStream, aTree);
458 std::string aVal;
459 boost::property_tree::ptree::const_assoc_iterator it = aTree.find("refpoint");
460 if (it != aTree.not_found())
461 aVal = aTree.get_child("refpoint").get_value<std::string>();
462 else
463 return; // happens in testTextBoxInsert test
465 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(aVal.c_str()));
466 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aSeq.getLength());
467 m_aRefPoint.setX(aSeq[0].toInt32());
468 m_aRefPoint.setY(aSeq[1].toInt32());
470 aVal = aTree.get_child("relrect").get_value<std::string>();
471 aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(aVal.c_str()));
472 CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aSeq.getLength());
473 m_aRelRect.SetLeft(aSeq[0].toInt32());
474 m_aRelRect.SetTop(aSeq[1].toInt32());
475 m_aRelRect.setWidth(aSeq[2].toInt32());
476 m_aRelRect.setHeight(aSeq[3].toInt32());
479 tools::Rectangle getBounds()
481 tools::Rectangle aBounds = m_aRelRect;
482 aBounds.Move(m_aRefPoint.X(), m_aRefPoint.Y());
483 return aBounds;
487 struct TextSelectionMessage
489 std::vector<tools::Rectangle> m_aRelRects;
490 Point m_aRefPoint;
492 void clear() {
493 m_aRefPoint.setX(0);
494 m_aRefPoint.setY(0);
495 m_aRelRects.clear();
498 bool empty() {
499 return m_aRelRects.empty();
502 void parseMessage(const char* pMessage)
504 clear();
505 if (!pMessage)
506 return;
508 std::string aStr(pMessage);
509 if (aStr.find(",") == std::string::npos)
510 return;
512 size_t nRefDelimStart = aStr.find("::");
513 std::string aRectListString = (nRefDelimStart == std::string::npos) ? aStr : aStr.substr(0, nRefDelimStart);
514 std::string aRefPointString = (nRefDelimStart == std::string::npos) ?
515 std::string("0, 0") :
516 aStr.substr(nRefDelimStart + 2, aStr.length() - 2 - nRefDelimStart);
517 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(aRefPointString.c_str()));
518 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aSeq.getLength());
519 m_aRefPoint.setX(aSeq[0].toInt32());
520 m_aRefPoint.setY(aSeq[1].toInt32());
522 size_t nStart = 0;
523 size_t nEnd = aRectListString.find(";");
524 if (nEnd == std::string::npos)
525 nEnd = aRectListString.length();
528 std::string aRectString = aRectListString.substr(nStart, nEnd - nStart);
530 aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(aRectString.c_str()));
531 CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aSeq.getLength());
532 tools::Rectangle aRect;
533 aRect.SetLeft(aSeq[0].toInt32());
534 aRect.SetTop(aSeq[1].toInt32());
535 aRect.setWidth(aSeq[2].toInt32());
536 aRect.setHeight(aSeq[3].toInt32());
538 m_aRelRects.push_back(aRect);
541 nStart = nEnd + 1;
542 nEnd = aRectListString.find(";", nStart);
544 while(nEnd != std::string::npos);
547 tools::Rectangle getBounds(size_t nIndex)
549 if (nIndex >= m_aRelRects.size())
550 return tools::Rectangle();
552 tools::Rectangle aBounds = m_aRelRects[nIndex];
553 aBounds.Move(m_aRefPoint.X(), m_aRefPoint.Y());
554 return aBounds;
559 /// A view callback tracks callbacks invoked on one specific view.
560 class ViewCallback final
562 SfxViewShell* mpViewShell;
563 int mnView;
564 public:
565 bool m_bOwnCursorInvalidated;
566 bool m_bViewCursorInvalidated;
567 bool m_bTextViewSelectionInvalidated;
568 bool m_bGraphicSelection;
569 bool m_bGraphicViewSelection;
570 bool m_bFullInvalidateTiles;
571 bool m_bInvalidateTiles;
572 std::vector<tools::Rectangle> m_aInvalidations;
573 tools::Rectangle m_aCellCursorBounds;
574 std::vector<int> m_aInvalidationsParts;
575 std::vector<int> m_aInvalidationsMode;
576 bool m_bViewLock;
577 OString m_sCellFormula;
578 boost::property_tree::ptree m_aCommentCallbackResult;
579 EditCursorMessage m_aInvalidateCursorResult;
580 TextSelectionMessage m_aTextSelectionResult;
581 OString m_sInvalidateHeader;
582 OString m_sInvalidateSheetGeometry;
583 OString m_ShapeSelection;
584 TestLokCallbackWrapper m_callbackWrapper;
586 ViewCallback(bool bDeleteListenerOnDestruct=true)
587 : m_bOwnCursorInvalidated(false),
588 m_bViewCursorInvalidated(false),
589 m_bTextViewSelectionInvalidated(false),
590 m_bGraphicSelection(false),
591 m_bGraphicViewSelection(false),
592 m_bFullInvalidateTiles(false),
593 m_bInvalidateTiles(false),
594 m_bViewLock(false),
595 m_callbackWrapper(&callback, this)
597 mpViewShell = SfxViewShell::Current();
598 mpViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
599 mnView = SfxLokHelper::getView();
600 m_callbackWrapper.setLOKViewId( mnView );
601 if (!bDeleteListenerOnDestruct)
602 mpViewShell = nullptr;
605 ~ViewCallback()
607 if (mpViewShell)
609 SfxLokHelper::setView(mnView);
610 mpViewShell->setLibreOfficeKitViewCallback(nullptr);
614 static void callback(int nType, const char* pPayload, void* pData)
616 static_cast<ViewCallback*>(pData)->callbackImpl(nType, pPayload);
619 void callbackImpl(int nType, const char* pPayload)
621 switch (nType)
623 case LOK_CALLBACK_CELL_CURSOR:
625 m_bOwnCursorInvalidated = true;
626 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload));
627 m_aCellCursorBounds = tools::Rectangle();
628 if (aSeq.getLength() == 6) {
629 m_aCellCursorBounds.SetLeft(aSeq[0].toInt32());
630 m_aCellCursorBounds.SetTop(aSeq[1].toInt32());
631 m_aCellCursorBounds.setWidth(aSeq[2].toInt32());
632 m_aCellCursorBounds.setHeight(aSeq[3].toInt32());
635 break;
636 case LOK_CALLBACK_CELL_VIEW_CURSOR:
638 m_bViewCursorInvalidated = true;
640 break;
641 case LOK_CALLBACK_TEXT_VIEW_SELECTION:
643 m_bTextViewSelectionInvalidated = true;
645 break;
646 case LOK_CALLBACK_VIEW_LOCK:
648 std::stringstream aStream(pPayload);
649 boost::property_tree::ptree aTree;
650 boost::property_tree::read_json(aStream, aTree);
651 m_bViewLock = aTree.get_child("rectangle").get_value<std::string>() != "EMPTY";
653 break;
654 case LOK_CALLBACK_GRAPHIC_SELECTION:
656 m_bGraphicSelection = true;
657 m_ShapeSelection = OString(pPayload);
659 break;
660 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
662 m_bGraphicViewSelection = true;
664 break;
665 case LOK_CALLBACK_INVALIDATE_TILES:
667 OString text(pPayload);
668 if (text.startsWith("EMPTY"))
670 m_bFullInvalidateTiles = true;
672 else
674 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload));
675 CPPUNIT_ASSERT(aSeq.getLength() == 4 || aSeq.getLength() == 6);
676 tools::Rectangle aInvalidationRect;
677 aInvalidationRect.SetLeft(aSeq[0].toInt32());
678 aInvalidationRect.SetTop(aSeq[1].toInt32());
679 aInvalidationRect.setWidth(aSeq[2].toInt32());
680 aInvalidationRect.setHeight(aSeq[3].toInt32());
681 m_aInvalidations.push_back(aInvalidationRect);
682 if (aSeq.getLength() == 6)
684 m_aInvalidationsParts.push_back(aSeq[4].toInt32());
685 m_aInvalidationsMode.push_back(aSeq[5].toInt32());
687 m_bInvalidateTiles = true;
690 break;
691 case LOK_CALLBACK_CELL_FORMULA:
693 m_sCellFormula = pPayload;
695 break;
696 case LOK_CALLBACK_COMMENT:
698 m_aCommentCallbackResult.clear();
699 std::stringstream aStream(pPayload);
700 boost::property_tree::read_json(aStream, m_aCommentCallbackResult);
701 m_aCommentCallbackResult = m_aCommentCallbackResult.get_child("comment");
703 break;
704 case LOK_CALLBACK_INVALIDATE_HEADER:
706 m_sInvalidateHeader = pPayload;
708 break;
709 case LOK_CALLBACK_INVALIDATE_SHEET_GEOMETRY:
711 m_sInvalidateSheetGeometry = pPayload;
713 break;
714 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
716 m_aInvalidateCursorResult.parseMessage(pPayload);
718 break;
719 case LOK_CALLBACK_TEXT_SELECTION:
721 m_aTextSelectionResult.parseMessage(pPayload);
728 void ScTiledRenderingTest::testViewCursors()
730 ScModelObj* pModelObj = createDoc("select-row-cols.ods");
731 ViewCallback aView1;
732 SfxLokHelper::createView();
733 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
734 ViewCallback aView2(/*bDeleteListenerOnDestruct*/false);
735 // This was false, the new view did not get the view (cell) cursor of the old view.
736 CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
737 CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
738 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN);
739 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN);
740 Scheduler::ProcessEventsToIdle();
741 SfxLokHelper::destroyView(SfxLokHelper::getView());
742 CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
745 void ScTiledRenderingTest::testSpellOnlineRenderParameter()
747 ScModelObj* pModelObj = createDoc("empty.ods");
748 ScDocument* pDoc = pModelObj->GetDocument();
749 bool bSet = pDoc->GetDocOptions().IsAutoSpell();
751 uno::Sequence<beans::PropertyValue> aPropertyValues =
753 comphelper::makePropertyValue(".uno:SpellOnline", uno::Any(!bSet)),
755 pModelObj->initializeForTiledRendering(aPropertyValues);
757 CPPUNIT_ASSERT_EQUAL(!bSet, pDoc->GetDocOptions().IsAutoSpell());
760 void ScTiledRenderingTest::testTextViewSelection()
762 // Create two views, and leave the second one current.
763 ScModelObj* pModelObj = createDoc("select-row-cols.ods");
764 ViewCallback aView1;
765 SfxLokHelper::createView();
766 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
767 ViewCallback aView2;
769 // Create a selection on two cells in the second view, that's a text selection in LOK terms.
770 aView1.m_bTextViewSelectionInvalidated = false;
771 dispatchCommand(mxComponent, ".uno:GoRightSel", {});
772 Scheduler::ProcessEventsToIdle();
773 // Make sure the first view got its notification.
774 CPPUNIT_ASSERT(aView1.m_bTextViewSelectionInvalidated);
777 void ScTiledRenderingTest::testDocumentSizeChanged()
779 // Load a document that doesn't have much content.
780 createDoc("small.ods");
781 setupLibreOfficeKitViewCallback(SfxViewShell::Current());
783 // Go to the A30 cell -- that will extend the document size.
784 uno::Sequence<beans::PropertyValue> aPropertyValues =
786 comphelper::makePropertyValue("ToPoint", OUString("$A$30")),
788 dispatchCommand(mxComponent, ".uno:GoToCell", aPropertyValues);
789 Scheduler::ProcessEventsToIdle();
790 // Assert that the size in the payload is not 0.
791 CPPUNIT_ASSERT(m_aDocumentSize.getWidth() > 0);
792 CPPUNIT_ASSERT(m_aDocumentSize.getHeight() > 0);
795 void ScTiledRenderingTest::testViewLock()
797 // Load a document that has a shape and create two views.
798 ScModelObj* pModelObj = createDoc("shape.ods");
799 ViewCallback aView1;
800 SfxLokHelper::createView();
801 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
802 ViewCallback aView2;
804 // Begin text edit in the second view and assert that the first gets a lock
805 // notification.
806 const ScViewData* pViewData = ScDocShell::GetViewData();
807 ScTabViewShell* pViewShell = pViewData->GetViewShell();
808 CPPUNIT_ASSERT(pViewShell);
809 SdrModel* pDrawModel = pViewData->GetDocument().GetDrawLayer();
810 SdrPage* pDrawPage = pDrawModel->GetPage(0);
811 SdrObject* pObject = pDrawPage->GetObj(0);
812 SdrView* pView = pViewShell->GetScDrawView();
813 aView1.m_bViewLock = false;
814 pView->SdrBeginTextEdit(pObject);
815 CPPUNIT_ASSERT(aView1.m_bViewLock);
817 // End text edit in the second view, and assert that the lock is removed in
818 // the first view.
819 pView->SdrEndTextEdit();
820 CPPUNIT_ASSERT(!aView1.m_bViewLock);
823 void lcl_extractHandleParameters(std::string_view selection, sal_uInt32& id, sal_uInt32& x, sal_uInt32& y)
825 OString extraInfo( selection.substr(selection.find("{")) );
826 std::stringstream aStream(extraInfo.getStr());
827 boost::property_tree::ptree aTree;
828 boost::property_tree::read_json(aStream, aTree);
829 boost::property_tree::ptree
830 handle0 = aTree
831 .get_child("handles")
832 .get_child("kinds")
833 .get_child("rectangle")
834 .get_child("1")
835 .begin()->second;
836 id = handle0.get_child("id").get_value<int>();
837 x = handle0.get_child("point").get_child("x").get_value<int>();
838 y = handle0.get_child("point").get_child("y").get_value<int>();
841 void ScTiledRenderingTest::testMoveShapeHandle()
843 ScModelObj* pModelObj = createDoc("shape.ods");
844 ViewCallback aView1;
845 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, /*x=*/ 1,/*y=*/ 1,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
846 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, /*x=*/ 1, /*y=*/ 1, /*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
847 Scheduler::ProcessEventsToIdle();
849 CPPUNIT_ASSERT(!aView1.m_ShapeSelection.isEmpty());
851 sal_uInt32 id, x, y;
852 lcl_extractHandleParameters(aView1.m_ShapeSelection, id, x ,y);
853 sal_uInt32 oldX = x;
854 sal_uInt32 oldY = y;
855 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
857 {"HandleNum", uno::Any(id)},
858 {"NewPosX", uno::Any(x+1)},
859 {"NewPosY", uno::Any(y+1)}
860 }));
861 comphelper::dispatchCommand(".uno:MoveShapeHandle", aPropertyValues);
862 Scheduler::ProcessEventsToIdle();
863 CPPUNIT_ASSERT(!aView1.m_ShapeSelection.isEmpty());
864 lcl_extractHandleParameters(aView1.m_ShapeSelection, id, x ,y);
865 CPPUNIT_ASSERT_EQUAL(x-1, oldX);
866 CPPUNIT_ASSERT_EQUAL(y-1, oldY);
870 void ScTiledRenderingTest::testColRowResize()
872 ScModelObj* pModelObj = createDoc("sort-range.ods");
873 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
874 CPPUNIT_ASSERT(pDocSh);
876 ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false);
877 CPPUNIT_ASSERT(pViewShell);
879 setupLibreOfficeKitViewCallback(pViewShell);
881 ScDocument& rDoc = pDocSh->GetDocument();
883 // Col 3, Tab 0
884 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
885 { "ColumnWidth", uno::Any(sal_uInt16(4000)) }, // 4cm
886 { "Column", uno::Any(sal_Int16(3)) }
887 }));
888 comphelper::dispatchCommand(".uno:ColumnWidth", aArgs);
890 sal_uInt16 nWidth = o3tl::convert(rDoc.GetColWidth(static_cast<SCCOL>(2), static_cast<SCTAB>(0), false), o3tl::Length::twip, o3tl::Length::mm100);
891 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(4001), nWidth);
893 // Row 5, Tab 0
894 uno::Sequence<beans::PropertyValue> aArgs2( comphelper::InitPropertySequence({
895 { "RowHeight", uno::Any(sal_uInt16(2000)) },
896 { "Row", uno::Any(sal_Int16(5)) },
897 }));
898 comphelper::dispatchCommand(".uno:RowHeight", aArgs2);
900 sal_uInt16 nHeight = o3tl::convert(rDoc.GetRowHeight(static_cast<SCROW>(4), static_cast<SCTAB>(0), false), o3tl::Length::twip, o3tl::Length::mm100);
901 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(2000), nHeight);
904 void ScTiledRenderingTest::testUndoShells()
906 ScModelObj* pModelObj = createDoc("small.ods");
907 // Clear the currently selected cell.
908 comphelper::dispatchCommand(".uno:ClearContents", {});
910 auto pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
911 CPPUNIT_ASSERT(pDocShell);
912 ScDocument& rDoc = pDocShell->GetDocument();
913 ScUndoManager* pUndoManager = rDoc.GetUndoManager();
914 CPPUNIT_ASSERT(pUndoManager);
915 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pUndoManager->GetUndoActionCount());
916 sal_Int32 nView1 = SfxLokHelper::getView();
917 // This was -1: ScSimpleUndo did not remember what view shell created it.
918 CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), pUndoManager->GetUndoAction()->GetViewShellId());
921 bool lcl_hasEditView(const ScViewData& rViewData)
923 bool bResult = false;
924 for (unsigned int i=0; i<4; i++)
926 bResult = rViewData.HasEditView( static_cast<ScSplitPos>(i) );
927 if (bResult) break;
929 return bResult;
932 void ScTiledRenderingTest::testTextEditViews()
934 ScModelObj* pModelObj = createDoc("small.ods");
935 CPPUNIT_ASSERT(pModelObj);
936 ScViewData* pViewData = ScDocShell::GetViewData();
937 CPPUNIT_ASSERT(pViewData);
939 // view #1
940 ViewCallback aView1;
941 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
943 // text edit a cell in view #1
944 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
945 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
946 Scheduler::ProcessEventsToIdle();
947 CPPUNIT_ASSERT(lcl_hasEditView(*pViewData));
949 // view #2
950 SfxLokHelper::createView();
951 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
952 ViewCallback aView2;
954 // move cell cursor i view #2
955 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN);
956 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN);
957 Scheduler::ProcessEventsToIdle();
959 // check that text edit view in view #1 has not be killed
960 CPPUNIT_ASSERT(lcl_hasEditView(*pViewData));
963 void ScTiledRenderingTest::testTextEditViewInvalidations()
965 ScModelObj* pModelObj = createDoc("small.ods");
966 CPPUNIT_ASSERT(pModelObj);
967 ScViewData* pViewData = ScDocShell::GetViewData();
968 CPPUNIT_ASSERT(pViewData);
970 // view #1
971 int nView1 = SfxLokHelper::getView();
972 ViewCallback aView1;
973 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
975 // view #2
976 SfxLokHelper::createView();
977 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
978 ViewCallback aView2;
980 // text edit a cell in view #1
981 SfxLokHelper::setView(nView1);
982 aView2.m_bInvalidateTiles = false;
983 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
984 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
985 Scheduler::ProcessEventsToIdle();
986 CPPUNIT_ASSERT(lcl_hasEditView(*pViewData));
987 CPPUNIT_ASSERT(aView2.m_bInvalidateTiles);
989 // text edit a cell in view #1 until
990 // we can be sure we are out of the initial tile
991 for (int i = 0; i < 40; ++i)
993 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
994 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
996 Scheduler::ProcessEventsToIdle();
998 // text edit a cell in view #1 inside the new tile and
999 // check that view #2 receive a tile invalidate message
1000 aView2.m_bInvalidateTiles = false;
1001 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1002 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1003 Scheduler::ProcessEventsToIdle();
1004 CPPUNIT_ASSERT(aView2.m_bInvalidateTiles);
1006 // view #3
1007 SfxLokHelper::createView();
1008 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1009 ViewCallback aView3;
1011 // text edit a cell in view #1
1012 SfxLokHelper::setView(nView1);
1013 aView3.m_bInvalidateTiles = false;
1014 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'y', 0);
1015 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'y', 0);
1016 Scheduler::ProcessEventsToIdle();
1017 CPPUNIT_ASSERT(aView3.m_bInvalidateTiles);
1020 void ScTiledRenderingTest::testCreateViewGraphicSelection()
1022 // Load a document that has a shape and create two views.
1023 ScModelObj* pModelObj = createDoc("shape.ods");
1024 ViewCallback aView1;
1026 // Mark the graphic in the first view.
1027 const ScViewData* pViewData = ScDocShell::GetViewData();
1028 ScTabViewShell* pViewShell = pViewData->GetViewShell();
1029 CPPUNIT_ASSERT(pViewShell);
1030 SdrModel* pDrawModel = pViewData->GetDocument().GetDrawLayer();
1031 SdrPage* pDrawPage = pDrawModel->GetPage(0);
1032 SdrObject* pObject = pDrawPage->GetObj(0);
1033 SdrView* pView = pViewShell->GetScDrawView();
1034 aView1.m_bGraphicSelection = false;
1035 aView1.m_bGraphicViewSelection = false;
1036 pView->MarkObj(pObject, pView->GetSdrPageView());
1037 CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
1039 // Create a second view.
1040 int nView1 = SfxLokHelper::getView();
1041 SfxLokHelper::createView();
1042 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1043 ViewCallback aView2;
1044 CPPUNIT_ASSERT(aView2.m_bGraphicViewSelection);
1045 CPPUNIT_ASSERT(aView1.m_bGraphicViewSelection);
1047 SfxLokHelper::setView(nView1);
1048 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1051 void ScTiledRenderingTest::testGraphicInvalidate()
1053 // Load a document that has a shape and create two views.
1054 ScModelObj* pModelObj = createDoc("shape.ods");
1055 ViewCallback aView;
1057 // Click to select graphic
1058 aView.m_bGraphicSelection = false;
1059 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, /*x=*/ 1,/*y=*/ 1,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
1060 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, /*x=*/ 1, /*y=*/ 1, /*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
1061 Scheduler::ProcessEventsToIdle();
1062 CPPUNIT_ASSERT(aView.m_bGraphicSelection);
1064 // Drag Drop graphic
1065 aView.m_bGraphicSelection = false;
1066 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, /*x=*/ 1,/*y=*/ 1,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
1067 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEMOVE, /*x=*/ 1,/*y=*/ 10,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
1068 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, /*x=*/ 1, /*y=*/ 10, /*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0);
1069 Scheduler::ProcessEventsToIdle();
1070 CPPUNIT_ASSERT(!aView.m_bFullInvalidateTiles);
1072 // Check again
1073 Scheduler::ProcessEventsToIdle();
1074 CPPUNIT_ASSERT(!aView.m_bFullInvalidateTiles);
1077 void ScTiledRenderingTest::testAutoSum()
1079 createDoc("small.ods");
1081 ViewCallback aView;
1083 uno::Sequence<beans::PropertyValue> aArgs;
1084 comphelper::dispatchCommand(".uno:AutoSum", aArgs);
1085 Scheduler::ProcessEventsToIdle();
1086 CPPUNIT_ASSERT(aView.m_sCellFormula.startsWith("=SUM("));
1089 void ScTiledRenderingTest::testHideColRow()
1091 createDoc("small.ods");
1093 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
1094 { "Col", uno::Any(sal_Int32(2 - 1)) },
1095 { "Modifier", uno::Any(KEY_SHIFT) }
1096 }));
1097 comphelper::dispatchCommand(".uno:SelectColumn", aArgs);
1099 uno::Sequence<beans::PropertyValue> aArgs2( comphelper::InitPropertySequence({
1100 { "Col", uno::Any(sal_Int32(3 - 1)) },
1101 { "Modifier", uno::Any(sal_uInt16(0)) }
1102 }));
1104 comphelper::dispatchCommand(".uno:SelectColumn", aArgs2);
1105 Scheduler::ProcessEventsToIdle();
1108 SCCOL nOldCurX = ScDocShell::GetViewData()->GetCurX();
1109 SCROW nOldCurY = ScDocShell::GetViewData()->GetCurY();
1111 uno::Sequence<beans::PropertyValue> aArgs;
1112 comphelper::dispatchCommand(".uno:HideColumn", aArgs);
1113 Scheduler::ProcessEventsToIdle();
1116 SCCOL nNewCurX = ScDocShell::GetViewData()->GetCurX();
1117 SCROW nNewCurY = ScDocShell::GetViewData()->GetCurY();
1118 CPPUNIT_ASSERT(nNewCurX > nOldCurX);
1119 CPPUNIT_ASSERT_EQUAL(nOldCurY, nNewCurY);
1121 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
1122 { "Row", uno::Any(sal_Int32(6 - 1)) },
1123 { "Modifier", uno::Any(KEY_SHIFT) }
1124 }));
1125 comphelper::dispatchCommand(".uno:SelectRow", aArgs);
1127 uno::Sequence<beans::PropertyValue> aArgs2( comphelper::InitPropertySequence({
1128 { "Row", uno::Any(sal_Int32(7 - 1)) },
1129 { "Modifier", uno::Any(sal_uInt16(0)) }
1130 }));
1131 comphelper::dispatchCommand(".uno:SelectRow", aArgs2);
1132 Scheduler::ProcessEventsToIdle();
1135 nOldCurX = ScDocShell::GetViewData()->GetCurX();
1136 nOldCurY = ScDocShell::GetViewData()->GetCurY();
1138 uno::Sequence<beans::PropertyValue> aArgs;
1139 comphelper::dispatchCommand(".uno:HideRow", aArgs);
1140 Scheduler::ProcessEventsToIdle();
1142 nNewCurX = ScDocShell::GetViewData()->GetCurX();
1143 nNewCurY = ScDocShell::GetViewData()->GetCurY();
1144 CPPUNIT_ASSERT(nNewCurY > nOldCurY);
1145 CPPUNIT_ASSERT_EQUAL(nOldCurX, nNewCurX);
1148 void ScTiledRenderingTest::testInvalidateOnCopyPasteCells()
1150 ScModelObj* pModelObj = createDoc("small.ods");
1151 CPPUNIT_ASSERT(pModelObj);
1153 // view
1154 ViewCallback aView;
1156 uno::Sequence<beans::PropertyValue> aArgs;
1157 // select and copy cells
1158 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
1159 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
1160 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_SHIFT);
1161 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_SHIFT);
1162 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT | KEY_SHIFT);
1163 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT | KEY_SHIFT);
1164 Scheduler::ProcessEventsToIdle();
1165 comphelper::dispatchCommand(".uno:Copy", aArgs);
1167 // move to destination cell
1168 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
1169 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
1170 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_MOD1);
1171 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_MOD1);
1172 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_UP);
1173 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_UP);
1174 Scheduler::ProcessEventsToIdle();
1176 // paste cells
1177 aView.m_bInvalidateTiles = false;
1178 comphelper::dispatchCommand(".uno:Paste", aArgs);
1179 Scheduler::ProcessEventsToIdle();
1180 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1183 void ScTiledRenderingTest::testInvalidateOnInserRowCol()
1185 ScModelObj* pModelObj = createDoc("small.ods");
1186 CPPUNIT_ASSERT(pModelObj);
1188 // view
1189 ViewCallback aView;
1191 uno::Sequence<beans::PropertyValue> aArgs;
1192 // move downward
1193 for (int i = 0; i < 200; ++i)
1195 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
1196 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
1198 Scheduler::ProcessEventsToIdle();
1200 // insert row
1201 aView.m_bInvalidateTiles = false;
1202 aView.m_aInvalidations.clear();
1203 comphelper::dispatchCommand(".uno:InsertRows", aArgs);
1204 Scheduler::ProcessEventsToIdle();
1205 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1206 CPPUNIT_ASSERT_EQUAL(size_t(2), aView.m_aInvalidations.size());
1207 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(-75, 51240, 32212230, 63990), aView.m_aInvalidations[0]);
1209 // move on the right
1210 for (int i = 0; i < 200; ++i)
1212 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT);
1213 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT);
1215 Scheduler::ProcessEventsToIdle();
1217 // insert column
1218 aView.m_bInvalidateTiles = false;
1219 aView.m_aInvalidations.clear();
1220 comphelper::dispatchCommand(".uno:InsertColumns", aArgs);
1221 Scheduler::ProcessEventsToIdle();
1222 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1223 CPPUNIT_ASSERT_EQUAL(size_t(2), aView.m_aInvalidations.size());
1224 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(254925, -15, 32212230, 63990), aView.m_aInvalidations[0]);
1227 void ScTiledRenderingTest::testCommentCallback()
1229 // Comments callback are emitted only if tiled annotations are off
1230 comphelper::LibreOfficeKit::setTiledAnnotations(false);
1233 ScModelObj* pModelObj = createDoc("small.ods");
1234 ViewCallback aView1;
1235 int nView1 = SfxLokHelper::getView();
1237 // Create a 2nd view
1238 SfxLokHelper::createView();
1239 pModelObj->initializeForTiledRendering({});
1240 ViewCallback aView2;
1242 SfxLokHelper::setView(nView1);
1244 // Add a new comment
1245 uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
1247 {"Text", uno::Any(OUString("Comment"))},
1248 {"Author", uno::Any(OUString("LOK User1"))},
1249 }));
1250 comphelper::dispatchCommand(".uno:InsertAnnotation", aArgs);
1251 Scheduler::ProcessEventsToIdle();
1253 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
1254 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1255 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
1256 CPPUNIT_ASSERT_EQUAL(std::string("1"), aView1.m_aCommentCallbackResult.get<std::string>("id"));
1257 CPPUNIT_ASSERT_EQUAL(std::string("1"), aView2.m_aCommentCallbackResult.get<std::string>("id"));
1258 CPPUNIT_ASSERT_EQUAL(std::string("0"), aView1.m_aCommentCallbackResult.get<std::string>("tab"));
1259 CPPUNIT_ASSERT_EQUAL(std::string("0"), aView2.m_aCommentCallbackResult.get<std::string>("tab"));
1260 CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), aView1.m_aCommentCallbackResult.get<std::string>("author"));
1261 CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), aView2.m_aCommentCallbackResult.get<std::string>("author"));
1262 CPPUNIT_ASSERT_EQUAL(std::string("Comment"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
1263 CPPUNIT_ASSERT_EQUAL(std::string("Comment"), aView2.m_aCommentCallbackResult.get<std::string>("text"));
1264 CPPUNIT_ASSERT_EQUAL(std::string("0, 255, 1274, 254"), aView1.m_aCommentCallbackResult.get<std::string>("cellPos"));
1265 CPPUNIT_ASSERT_EQUAL(std::string("0, 255, 1274, 254"), aView2.m_aCommentCallbackResult.get<std::string>("cellPos"));
1267 std::string aCommentId = aView1.m_aCommentCallbackResult.get<std::string>("id");
1269 // Edit a comment
1270 // Select some random cell, we should be able to edit the cell note without
1271 // selecting the cell
1272 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
1273 if (pTabViewShell)
1274 pTabViewShell->SetCursor(3, 100);
1275 aArgs = comphelper::InitPropertySequence(
1277 {"Id", uno::Any(OUString::createFromAscii(aCommentId.c_str()))},
1278 {"Text", uno::Any(OUString("Edited comment"))},
1279 {"Author", uno::Any(OUString("LOK User2"))},
1281 comphelper::dispatchCommand(".uno:EditAnnotation", aArgs);
1282 Scheduler::ProcessEventsToIdle();
1284 // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action
1285 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1286 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
1287 CPPUNIT_ASSERT_EQUAL(aCommentId, aView1.m_aCommentCallbackResult.get<std::string>("id"));
1288 CPPUNIT_ASSERT_EQUAL(aCommentId, aView2.m_aCommentCallbackResult.get<std::string>("id"));
1289 CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), aView1.m_aCommentCallbackResult.get<std::string>("author"));
1290 CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), aView2.m_aCommentCallbackResult.get<std::string>("author"));
1291 CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
1292 CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView2.m_aCommentCallbackResult.get<std::string>("text"));
1293 CPPUNIT_ASSERT_EQUAL(std::string("0, 255, 1274, 254"), aView1.m_aCommentCallbackResult.get<std::string>("cellPos"));
1294 CPPUNIT_ASSERT_EQUAL(std::string("0, 255, 1274, 254"), aView2.m_aCommentCallbackResult.get<std::string>("cellPos"));
1296 // Delete the comment
1297 if (pTabViewShell)
1298 pTabViewShell->SetCursor(4, 43);
1299 aArgs = comphelper::InitPropertySequence(
1301 {"Id", uno::Any(OUString::createFromAscii(aCommentId.c_str()))}
1303 comphelper::dispatchCommand(".uno:DeleteNote", aArgs);
1304 Scheduler::ProcessEventsToIdle();
1306 // We received a LOK_CALLBACK_COMMENT callback with comment 'Remove' action
1307 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1308 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
1309 CPPUNIT_ASSERT_EQUAL(aCommentId, aView1.m_aCommentCallbackResult.get<std::string>("id"));
1310 CPPUNIT_ASSERT_EQUAL(aCommentId, aView2.m_aCommentCallbackResult.get<std::string>("id"));
1312 comphelper::LibreOfficeKit::setTiledAnnotations(true);
1315 void ScTiledRenderingTest::testUndoLimiting()
1317 ScModelObj* pModelObj = createDoc("small.ods");
1318 CPPUNIT_ASSERT(pModelObj);
1319 ScDocument* pDoc = pModelObj->GetDocument();
1320 CPPUNIT_ASSERT(pDoc);
1321 ScUndoManager* pUndoManager = pDoc->GetUndoManager();
1322 CPPUNIT_ASSERT(pUndoManager);
1324 // view #1
1325 int nView1 = SfxLokHelper::getView();
1326 ViewCallback aView1;
1328 // view #2
1329 SfxLokHelper::createView();
1330 int nView2 = SfxLokHelper::getView();
1331 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1332 ViewCallback aView2;
1334 // text edit a cell in view #1
1335 SfxLokHelper::setView(nView1);
1336 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1337 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1338 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1339 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1340 Scheduler::ProcessEventsToIdle();
1342 // check that undo action count in not 0
1343 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
1345 // try to execute undo in view #2
1346 SfxLokHelper::setView(nView2);
1347 comphelper::dispatchCommand(".uno:Undo", {});
1348 Scheduler::ProcessEventsToIdle();
1349 // check that undo has not been executed on view #2
1350 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
1352 // try to execute undo in view #1
1353 SfxLokHelper::setView(nView1);
1354 comphelper::dispatchCommand(".uno:Undo", {});
1355 Scheduler::ProcessEventsToIdle();
1356 // check that undo has been executed on view #1
1357 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetUndoActionCount());
1359 // check that redo action count in not 0
1360 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetRedoActionCount());
1362 // try to execute redo in view #2
1363 SfxLokHelper::setView(nView2);
1364 comphelper::dispatchCommand(".uno:Redo", {});
1365 Scheduler::ProcessEventsToIdle();
1366 // check that redo has not been executed on view #2
1367 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetRedoActionCount());
1369 // try to execute redo in view #1
1370 SfxLokHelper::setView(nView1);
1371 comphelper::dispatchCommand(".uno:Redo", {});
1372 Scheduler::ProcessEventsToIdle();
1373 // check that redo has been executed on view #1
1374 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetRedoActionCount());
1377 void ScTiledRenderingTest::testUndoRepairDispatch()
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 comphelper::dispatchCommand(".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 #2 in repair mode
1415 SfxLokHelper::setView(nView2);
1416 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
1418 {"Repair", uno::Any(true)}
1419 }));
1420 comphelper::dispatchCommand(".uno:Undo", aPropertyValues);
1421 Scheduler::ProcessEventsToIdle();
1422 // check that undo has been executed on view #2 in repair mode
1423 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetUndoActionCount());
1426 void ScTiledRenderingTest::testInsertGraphicInvalidations()
1428 ScModelObj* pModelObj = createDoc("small.ods");
1429 CPPUNIT_ASSERT(pModelObj);
1430 ScViewData* pViewData = ScDocShell::GetViewData();
1431 CPPUNIT_ASSERT(pViewData);
1433 // view
1434 ViewCallback aView;
1436 // we need to paint a tile in the view for triggering the tile invalidation solution
1437 int nCanvasWidth = 256;
1438 int nCanvasHeight = 256;
1439 std::vector<unsigned char> aBuffer(nCanvasWidth * nCanvasHeight * 4);
1440 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::DEFAULT);
1441 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), aBuffer.data());
1442 pModelObj->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0, /*nTilePosY=*/0, /*nTileWidth=*/3840, /*nTileHeight=*/3840);
1443 Scheduler::ProcessEventsToIdle();
1445 // insert an image in view and see if both views are invalidated
1446 aView.m_bInvalidateTiles = false;
1447 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
1448 { "FileName", uno::Any(createFileURL(u"smile.png")) }
1449 }));
1450 comphelper::dispatchCommand(".uno:InsertGraphic", aArgs);
1451 Scheduler::ProcessEventsToIdle();
1452 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1454 // undo image insertion in view and see if both views are invalidated
1455 aView.m_bInvalidateTiles = false;
1456 uno::Sequence<beans::PropertyValue> aArgs2;
1457 comphelper::dispatchCommand(".uno:Undo", aArgs2);
1458 Scheduler::ProcessEventsToIdle();
1459 CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
1462 void ScTiledRenderingTest::testDocumentSizeWithTwoViews()
1464 // Open a document that has the cursor far away & paint a tile
1465 ScModelObj* pModelObj = createDoc("cursor-away.ods");
1467 // Set the visible area, and press page down
1468 pModelObj->setClientVisibleArea(tools::Rectangle(750, 1861, 20583, 6997));
1469 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN);
1470 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN);
1471 Scheduler::ProcessEventsToIdle();
1473 int nCanvasWidth = 256;
1474 int nCanvasHeight = 256;
1475 std::vector<unsigned char> aBuffer1(nCanvasWidth * nCanvasHeight * 4);
1476 ScopedVclPtrInstance<VirtualDevice> pDevice1(DeviceFormat::DEFAULT);
1477 pDevice1->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), aBuffer1.data());
1478 pModelObj->paintTile(*pDevice1, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0, /*nTilePosY=*/291840, /*nTileWidth=*/3840, /*nTileHeight=*/3840);
1479 Scheduler::ProcessEventsToIdle();
1481 // Create a new view
1482 int nView1 = SfxLokHelper::getView();
1483 SfxLokHelper::createView();
1485 std::vector<unsigned char> aBuffer2(nCanvasWidth * nCanvasHeight * 4);
1486 ScopedVclPtrInstance<VirtualDevice> pDevice2(DeviceFormat::DEFAULT);
1487 pDevice2->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), aBuffer2.data());
1488 pModelObj->paintTile(*pDevice2, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0, /*nTilePosY=*/291840, /*nTileWidth=*/3840, /*nTileHeight=*/3840);
1489 Scheduler::ProcessEventsToIdle();
1491 // Check that the tiles actually have the same content
1492 for (size_t i = 0; i < aBuffer1.size(); ++i)
1493 CPPUNIT_ASSERT_EQUAL(aBuffer1[i], aBuffer2[i]);
1495 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1496 SfxLokHelper::setView(nView1);
1497 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1500 void ScTiledRenderingTest::testDisableUndoRepair()
1502 ScModelObj* pModelObj = createDoc("cursor-away.ods");
1503 CPPUNIT_ASSERT(pModelObj);
1505 // view #1
1506 int nView1 = SfxLokHelper::getView();
1507 SfxViewShell* pView1 = SfxViewShell::Current();
1509 // view #2
1510 SfxLokHelper::createView();
1511 int nView2 = SfxLokHelper::getView();
1512 SfxViewShell* pView2 = SfxViewShell::Current();
1513 CPPUNIT_ASSERT(pView1 != pView2);
1515 // both views have UNDO disabled
1517 SfxItemSet aSet1(pView1->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
1518 SfxItemSet aSet2(pView2->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
1519 pView1->GetSlotState(SID_UNDO, nullptr, &aSet1);
1520 pView2->GetSlotState(SID_UNDO, nullptr, &aSet2);
1521 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, aSet1.GetItemState(SID_UNDO));
1522 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, aSet2.GetItemState(SID_UNDO));
1525 // text edit a cell in view #1
1526 SfxLokHelper::setView(nView1);
1527 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'h', 0);
1528 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'h', 0);
1529 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1530 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1531 Scheduler::ProcessEventsToIdle();
1532 // view1 has UNDO enabled, view2 is in UNDO-repair
1534 SfxItemSet aSet1(pView1->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
1535 SfxItemSet aSet2(pView2->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
1536 pView1->GetSlotState(SID_UNDO, nullptr, &aSet1);
1537 pView2->GetSlotState(SID_UNDO, nullptr, &aSet2);
1538 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet1.GetItemState(SID_UNDO));
1539 CPPUNIT_ASSERT(dynamic_cast< const SfxStringItem* >(aSet1.GetItem(SID_UNDO)));
1540 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet2.GetItemState(SID_UNDO));
1541 CPPUNIT_ASSERT(dynamic_cast< const SfxUInt32Item* >(aSet2.GetItem(SID_UNDO)));
1542 const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item*>(aSet2.GetItem(SID_UNDO));
1543 CPPUNIT_ASSERT(pUInt32Item);
1544 CPPUNIT_ASSERT_EQUAL(static_cast< sal_uInt32 >(SID_REPAIRPACKAGE), pUInt32Item->GetValue());
1547 // text edit a cell in view #2
1548 SfxLokHelper::setView(nView2);
1549 pModelObj->setPart(1);
1550 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', 0);
1551 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', 0);
1552 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1553 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1554 // both views have UNDO enabled
1555 Scheduler::ProcessEventsToIdle();
1557 SfxItemSet aSet1(pView1->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
1558 SfxItemSet aSet2(pView2->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
1559 pView1->GetSlotState(SID_UNDO, nullptr, &aSet1);
1560 pView2->GetSlotState(SID_UNDO, nullptr, &aSet2);
1561 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet1.GetItemState(SID_UNDO));
1562 CPPUNIT_ASSERT(dynamic_cast< const SfxStringItem* >(aSet1.GetItem(SID_UNDO)));
1563 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet2.GetItemState(SID_UNDO));
1564 CPPUNIT_ASSERT(dynamic_cast< const SfxStringItem* >(aSet2.GetItem(SID_UNDO)));
1567 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1568 SfxLokHelper::setView(nView1);
1569 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1572 void ScTiledRenderingTest::testDocumentRepair()
1574 // Create two views.
1575 ScModelObj* pModelObj = createDoc("cursor-away.ods");
1576 CPPUNIT_ASSERT(pModelObj);
1578 // view #1
1579 SfxViewShell* pView1 = SfxViewShell::Current();
1581 // view #2
1582 int nView1 = SfxLokHelper::getView();
1583 SfxLokHelper::createView();
1584 SfxViewShell* pView2 = SfxViewShell::Current();
1585 int nView2 = SfxLokHelper::getView();
1586 CPPUNIT_ASSERT(pView1 != pView2);
1588 std::unique_ptr<SfxBoolItem> pItem1;
1589 std::unique_ptr<SfxBoolItem> pItem2;
1590 pView1->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, pItem1);
1591 pView2->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, pItem2);
1592 CPPUNIT_ASSERT(pItem1);
1593 CPPUNIT_ASSERT(pItem2);
1594 CPPUNIT_ASSERT_EQUAL(false, pItem1->GetValue());
1595 CPPUNIT_ASSERT_EQUAL(false, pItem2->GetValue());
1598 // Insert a character in the second view.
1599 SfxLokHelper::setView(nView2);
1600 pModelObj->setPart(1);
1601 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', 0);
1602 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', 0);
1603 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1604 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1605 Scheduler::ProcessEventsToIdle();
1607 std::unique_ptr<SfxBoolItem> pItem1;
1608 std::unique_ptr<SfxBoolItem> pItem2;
1609 pView1->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, pItem1);
1610 pView2->GetViewFrame()->GetBindings().QueryState(SID_DOC_REPAIR, pItem2);
1611 CPPUNIT_ASSERT(pItem1);
1612 CPPUNIT_ASSERT(pItem2);
1613 CPPUNIT_ASSERT_EQUAL(true, pItem1->GetValue());
1614 CPPUNIT_ASSERT_EQUAL(true, pItem2->GetValue());
1617 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1618 SfxLokHelper::setView(nView1);
1619 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1622 void ScTiledRenderingTest::testLanguageStatus()
1624 ScModelObj* pModelObj = createDoc("small.ods");
1625 CPPUNIT_ASSERT(pModelObj);
1626 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
1627 CPPUNIT_ASSERT(pDocSh);
1629 // view #1
1630 SfxViewShell* pView1 = SfxViewShell::Current();
1632 // view #2
1633 int nView1 = SfxLokHelper::getView();
1634 SfxLokHelper::createView();
1635 SfxViewShell* pView2 = SfxViewShell::Current();
1636 CPPUNIT_ASSERT(pView1 != pView2);
1638 std::unique_ptr<SfxPoolItem> xItem1;
1639 std::unique_ptr<SfxPoolItem> xItem2;
1640 pView1->GetViewFrame()->GetBindings().QueryState(SID_LANGUAGE_STATUS, xItem1);
1641 pView2->GetViewFrame()->GetBindings().QueryState(SID_LANGUAGE_STATUS, xItem2);
1642 const SfxStringItem* pItem1 = dynamic_cast<const SfxStringItem*>(xItem1.get());
1643 const SfxStringItem* pItem2 = dynamic_cast<const SfxStringItem*>(xItem2.get());
1644 CPPUNIT_ASSERT(pItem1);
1645 CPPUNIT_ASSERT(pItem2);
1646 CPPUNIT_ASSERT(!pItem1->GetValue().isEmpty());
1647 CPPUNIT_ASSERT(!pItem2->GetValue().isEmpty());
1651 SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Default_Spanish (Bolivia)");
1652 pView1->GetViewFrame()->GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS,
1653 SfxCallMode::SYNCHRON, { &aLangString });
1657 std::unique_ptr<SfxPoolItem> xItem1;
1658 std::unique_ptr<SfxPoolItem> xItem2;
1659 pView1->GetViewFrame()->GetBindings().QueryState(SID_LANGUAGE_STATUS, xItem1);
1660 pView2->GetViewFrame()->GetBindings().QueryState(SID_LANGUAGE_STATUS, xItem2);
1661 const SfxStringItem* pItem1 = dynamic_cast<const SfxStringItem*>(xItem1.get());
1662 const SfxStringItem* pItem2 = dynamic_cast<const SfxStringItem*>(xItem2.get());
1663 CPPUNIT_ASSERT(pItem1);
1664 CPPUNIT_ASSERT(pItem2);
1665 const OUString aLangBolivia("Spanish (Bolivia);es-BO");
1666 CPPUNIT_ASSERT_EQUAL(aLangBolivia, pItem1->GetValue());
1667 CPPUNIT_ASSERT_EQUAL(aLangBolivia, pItem2->GetValue());
1670 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1671 SfxLokHelper::setView(nView1);
1672 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1675 void ScTiledRenderingTest::testMultiViewCopyPaste()
1677 ScModelObj* pModelObj = createDoc("empty.ods");
1678 ScDocument* pDoc = pModelObj->GetDocument();
1679 CPPUNIT_ASSERT(pDoc);
1681 pDoc->SetString(ScAddress(0, 0, 0), "TestCopy1");
1682 pDoc->SetString(ScAddress(1, 0, 0), "TestCopy2");
1684 // view #1
1685 ScTabViewShell* pView1 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
1686 CPPUNIT_ASSERT(pView1);
1687 // emulate clipboard
1688 pView1->GetViewData().GetActiveWin()->SetClipboard(css::datatransfer::clipboard::SystemClipboard::create(comphelper::getProcessComponentContext()));
1690 // view #2
1691 int nView1 = SfxLokHelper::getView();
1692 SfxLokHelper::createView();
1693 ScTabViewShell* pView2 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
1694 // emulate clipboard
1695 pView2->GetViewData().GetActiveWin()->SetClipboard(css::datatransfer::clipboard::SystemClipboard::create(comphelper::getProcessComponentContext()));
1696 CPPUNIT_ASSERT(pView2);
1697 CPPUNIT_ASSERT(pView1 != pView2);
1698 CPPUNIT_ASSERT(pView1->GetViewData().GetActiveWin()->GetClipboard() != pView2->GetViewData().GetActiveWin()->GetClipboard());
1700 // copy text view 1
1701 pView1->SetCursor(0, 0);
1702 pView1->GetViewFrame()->GetBindings().Execute(SID_COPY);
1704 // copy text view 2
1705 pView2->SetCursor(1, 0);
1706 pView2->GetViewFrame()->GetBindings().Execute(SID_COPY);
1708 // paste text view 1
1709 pView1->SetCursor(0, 1);
1710 pView1->GetViewFrame()->GetBindings().Execute(SID_PASTE);
1712 // paste text view 2
1713 pView2->SetCursor(1, 1);
1714 pView2->GetViewFrame()->GetBindings().Execute(SID_PASTE);
1716 CPPUNIT_ASSERT_EQUAL(OUString("TestCopy1"), pDoc->GetString(ScAddress(0, 1, 0)));
1717 CPPUNIT_ASSERT_EQUAL(OUString("TestCopy2"), pDoc->GetString(ScAddress(1, 1, 0)));
1719 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1720 SfxLokHelper::setView(nView1);
1721 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1724 void ScTiledRenderingTest::testIMESupport()
1726 ScModelObj* pModelObj = createDoc("empty.ods");
1727 VclPtr<vcl::Window> pDocWindow = pModelObj->getDocWindow();
1728 ScDocument* pDoc = pModelObj->GetDocument();
1730 ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
1731 CPPUNIT_ASSERT(pView);
1733 pView->SetCursor(0, 0);
1734 // sequence of chinese IME compositions when 'nihao' is typed in an IME
1735 const std::vector<OString> aUtf8Inputs{ "年", "你", "你好", "你哈", "你好", "你好" };
1736 std::vector<OUString> aInputs;
1737 std::transform(aUtf8Inputs.begin(), aUtf8Inputs.end(),
1738 std::back_inserter(aInputs), [](OString aInput) {
1739 return OUString::fromUtf8(aInput);
1741 for (const auto& aInput: aInputs)
1743 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, aInput);
1745 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
1747 // commit the string to the cell
1748 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
1749 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
1750 Scheduler::ProcessEventsToIdle();
1752 CPPUNIT_ASSERT_EQUAL(aInputs[aInputs.size() - 1], pDoc->GetString(ScAddress(0, 0, 0)));
1755 void ScTiledRenderingTest::testFilterDlg()
1757 createDoc("empty.ods");
1759 // view #1
1760 SfxViewShell* pView1 = SfxViewShell::Current();
1761 int nView1 = SfxLokHelper::getView();
1763 // view #2
1764 SfxLokHelper::createView();
1765 SfxViewShell* pView2 = SfxViewShell::Current();
1766 CPPUNIT_ASSERT(pView1 != pView2);
1768 pView2->GetViewFrame()->GetDispatcher()->Execute(SID_FILTER,
1769 SfxCallMode::SLOT|SfxCallMode::RECORD);
1772 Scheduler::ProcessEventsToIdle();
1773 SfxChildWindow* pRefWindow = pView2->GetViewFrame()->GetChildWindow(SID_FILTER);
1774 CPPUNIT_ASSERT(pRefWindow);
1776 // switch to view 1
1777 SfxLokHelper::setView(nView1);
1778 CPPUNIT_ASSERT_EQUAL(true, pView2->GetViewFrame()->GetDispatcher()->IsLocked());
1779 CPPUNIT_ASSERT_EQUAL(false, pView1->GetViewFrame()->GetDispatcher()->IsLocked());
1781 pRefWindow->GetController()->response(RET_CANCEL);
1783 CPPUNIT_ASSERT_EQUAL(false, pView2->GetViewFrame()->GetDispatcher()->IsLocked());
1784 CPPUNIT_ASSERT_EQUAL(false, pView1->GetViewFrame()->GetDispatcher()->IsLocked());
1786 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1787 SfxLokHelper::setView(nView1);
1788 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1791 void ScTiledRenderingTest::testFunctionDlg()
1793 createDoc("empty.ods");
1795 // view #1
1796 SfxViewShell* pView1 = SfxViewShell::Current();
1797 int nView1 = SfxLokHelper::getView();
1799 pView1->GetViewFrame()->GetDispatcher()->Execute(SID_OPENDLG_FUNCTION,
1800 SfxCallMode::SLOT|SfxCallMode::RECORD);
1802 Scheduler::ProcessEventsToIdle();
1803 SfxChildWindow* pRefWindow = pView1->GetViewFrame()->GetChildWindow(SID_OPENDLG_FUNCTION);
1804 CPPUNIT_ASSERT(pRefWindow);
1806 // view #2
1807 int nView2 = SfxLokHelper::createView();
1808 SfxViewShell* pView2 = SfxViewShell::Current();
1809 CPPUNIT_ASSERT(pView1 != pView2);
1811 // check locking
1812 CPPUNIT_ASSERT_EQUAL(true, pView1->GetViewFrame()->GetDispatcher()->IsLocked());
1813 CPPUNIT_ASSERT_EQUAL(false, pView2->GetViewFrame()->GetDispatcher()->IsLocked());
1815 SfxLokHelper::setView(nView1);
1816 pRefWindow->GetController()->response(RET_CANCEL);
1818 CPPUNIT_ASSERT_EQUAL(false, pView1->GetViewFrame()->GetDispatcher()->IsLocked());
1819 CPPUNIT_ASSERT_EQUAL(false, pView2->GetViewFrame()->GetDispatcher()->IsLocked());
1821 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1822 SfxLokHelper::setView(nView2);
1823 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
1826 void ScTiledRenderingTest::testSpellOnlineParameter()
1828 ScModelObj* pModelObj = createDoc("empty.ods");
1829 ScDocument* pDoc = pModelObj->GetDocument();
1830 bool bSet = pDoc->GetDocOptions().IsAutoSpell();
1832 uno::Sequence<beans::PropertyValue> params =
1834 comphelper::makePropertyValue("Enable", uno::Any(!bSet)),
1836 dispatchCommand(mxComponent, ".uno:SpellOnline", params);
1837 CPPUNIT_ASSERT_EQUAL(!bSet, pDoc->GetDocOptions().IsAutoSpell());
1839 // set the same state as now and we don't expect any change (no-toggle)
1840 params =
1842 comphelper::makePropertyValue("Enable", uno::Any(!bSet)),
1844 dispatchCommand(mxComponent, ".uno:SpellOnline", params);
1845 CPPUNIT_ASSERT_EQUAL(!bSet, pDoc->GetDocOptions().IsAutoSpell());
1848 void ScTiledRenderingTest::testVbaRangeCopyPaste()
1850 ScModelObj* pModelObj = createDoc("RangeCopyPaste.ods");
1851 ScDocShell* pDocShell = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
1852 CPPUNIT_ASSERT(pDocShell);
1854 uno::Any aRet;
1855 uno::Sequence< uno::Any > aOutParam;
1856 uno::Sequence< uno::Any > aParams;
1857 uno::Sequence< sal_Int16 > aOutParamIndex;
1859 SfxObjectShell::CallXScript(
1860 mxComponent,
1861 "vnd.sun.Star.script:Standard.Module1.Test_RangeCopyPaste?language=Basic&location=document",
1862 aParams, aRet, aOutParamIndex, aOutParam);
1864 CPPUNIT_ASSERT(!pDocShell->GetClipData().is());
1867 void ScTiledRenderingTest::testInvalidationLoop()
1869 // Load the document with a form control.
1870 createDoc("invalidation-loop.fods");
1871 // Without the accompanying fix in place, this test would have never returned due to an infinite
1872 // invalidation loop between ScGridWindow::Paint() and vcl::Window::ImplPosSizeWindow().
1873 Scheduler::ProcessEventsToIdle();
1876 void ScTiledRenderingTest::testPageDownInvalidation()
1878 ScModelObj* pModelObj = createDoc("empty.ods");
1879 ScViewData* pViewData = ScDocShell::GetViewData();
1880 CPPUNIT_ASSERT(pViewData);
1882 int nView1 = SfxLokHelper::getView();
1883 ViewCallback aView1;
1884 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
1886 SfxLokHelper::setView(nView1);
1887 aView1.m_bInvalidateTiles = false;
1888 aView1.m_aInvalidations.clear();
1889 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, awt::Key::PAGEDOWN, 0);
1890 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, awt::Key::PAGEDOWN, 0);
1891 Scheduler::ProcessEventsToIdle();
1892 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1893 CPPUNIT_ASSERT_EQUAL(size_t(3), aView1.m_aInvalidations.size());
1894 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(15, 15, 1230, 225), aView1.m_aInvalidations[0]);
1897 void ScTiledRenderingTest::testSheetChangeInvalidation()
1899 const bool oldPartInInvalidation = comphelper::LibreOfficeKit::isPartInInvalidation();
1900 comphelper::LibreOfficeKit::setPartInInvalidation(true);
1902 ScModelObj* pModelObj = createDoc("two_sheets.ods");
1903 ScDocument* pDoc = pModelObj->GetDocument();
1904 ScViewData* pViewData = ScDocShell::GetViewData();
1905 CPPUNIT_ASSERT(pViewData);
1907 int nView1 = SfxLokHelper::getView();
1908 ViewCallback aView1;
1909 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
1911 SfxLokHelper::setView(nView1);
1912 aView1.m_bInvalidateTiles = false;
1913 aView1.m_aInvalidations.clear();
1914 aView1.m_aInvalidationsParts.clear();
1915 aView1.m_aInvalidationsMode.clear();
1916 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN | KEY_MOD1);
1917 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN | KEY_MOD1);
1918 Scheduler::ProcessEventsToIdle();
1919 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1920 CPPUNIT_ASSERT_EQUAL(size_t(2), aView1.m_aInvalidations.size());
1921 const ScSheetLimits& rLimits = pDoc->GetSheetLimits();
1922 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 0, 1280 * rLimits.GetMaxColCount(),
1923 256 * rLimits.GetMaxRowCount()),
1924 aView1.m_aInvalidations[0]);
1925 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 0, 1000000000, 1000000000), aView1.m_aInvalidations[1]);
1926 CPPUNIT_ASSERT_EQUAL(size_t(2), aView1.m_aInvalidationsParts.size());
1927 CPPUNIT_ASSERT_EQUAL(pModelObj->getPart(), aView1.m_aInvalidationsParts[0]);
1928 CPPUNIT_ASSERT_EQUAL(pModelObj->getPart(), aView1.m_aInvalidationsParts[1]);
1929 CPPUNIT_ASSERT_EQUAL(size_t(2), aView1.m_aInvalidationsMode.size());
1930 CPPUNIT_ASSERT_EQUAL(pModelObj->getEditMode(), aView1.m_aInvalidationsMode[0]);
1931 CPPUNIT_ASSERT_EQUAL(pModelObj->getEditMode(), aView1.m_aInvalidationsMode[1]);
1932 comphelper::LibreOfficeKit::setPartInInvalidation(oldPartInInvalidation);
1935 void ScTiledRenderingTest::testInsertDeletePageInvalidation()
1937 ScModelObj* pModelObj = createDoc("insert_delete_sheet.ods");
1938 // the document has 1 sheet
1939 CPPUNIT_ASSERT_EQUAL(1, pModelObj->getParts());
1940 ScViewData* pViewData = ScDocShell::GetViewData();
1941 CPPUNIT_ASSERT(pViewData);
1943 int nView1 = SfxLokHelper::getView();
1944 ViewCallback aView1;
1945 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
1947 SfxLokHelper::setView(nView1);
1948 aView1.m_bInvalidateTiles = false;
1949 aView1.m_aInvalidations.clear();
1951 uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
1952 { "Name", uno::Any(OUString("")) },
1953 { "Index", uno::Any(sal_Int32(1)) }
1954 }));
1955 comphelper::dispatchCommand(".uno:Insert", aArgs);
1956 Scheduler::ProcessEventsToIdle();
1957 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1958 CPPUNIT_ASSERT_EQUAL(size_t(6), aView1.m_aInvalidations.size());
1959 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 0, 1000000000, 1000000000), aView1.m_aInvalidations[0]);
1960 CPPUNIT_ASSERT_EQUAL(2, pModelObj->getParts());
1962 // Delete sheet
1963 aView1.m_bInvalidateTiles = false;
1964 aView1.m_aInvalidations.clear();
1965 uno::Sequence<beans::PropertyValue> aArgs2( comphelper::InitPropertySequence({
1966 { "Index", uno::Any(sal_Int32(1)) }
1967 }));
1968 comphelper::dispatchCommand(".uno:Remove", aArgs2);
1969 Scheduler::ProcessEventsToIdle();
1970 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1971 CPPUNIT_ASSERT_EQUAL(size_t(5), aView1.m_aInvalidations.size());
1972 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 0, 1000000000, 1000000000), aView1.m_aInvalidations[0]);
1973 CPPUNIT_ASSERT_EQUAL(1, pModelObj->getParts());
1976 void ScTiledRenderingTest::testGetRowColumnHeadersInvalidation()
1978 ScModelObj* pModelObj = createDoc("empty.ods");
1979 ScViewData* pViewData = ScDocShell::GetViewData();
1980 CPPUNIT_ASSERT(pViewData);
1982 int nView1 = SfxLokHelper::getView();
1983 ViewCallback aView1;
1984 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
1986 SfxLokHelper::setView(nView1);
1987 aView1.m_bInvalidateTiles = false;
1988 aView1.m_aInvalidations.clear();
1989 tools::JsonWriter aJsonWriter1;
1990 pModelObj->getRowColumnHeaders(tools::Rectangle(0, 15, 19650, 5400), aJsonWriter1);
1991 free(aJsonWriter1.extractData());
1992 Scheduler::ProcessEventsToIdle();
1993 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
1994 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
1995 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(26775, 0, 49725, 13005), aView1.m_aInvalidations[0]);
1997 // Extend area top-to-bottom
1998 aView1.m_bInvalidateTiles = false;
1999 aView1.m_aInvalidations.clear();
2000 tools::JsonWriter aJsonWriter2;
2001 pModelObj->getRowColumnHeaders(tools::Rectangle(0, 5400, 19650, 9800), aJsonWriter2);
2002 free(aJsonWriter2.extractData());
2003 Scheduler::ProcessEventsToIdle();
2004 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
2005 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
2006 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 13005, 49725, 19380), aView1.m_aInvalidations[0]);
2008 // Extend area left-to-right
2009 aView1.m_bInvalidateTiles = false;
2010 aView1.m_aInvalidations.clear();
2011 tools::JsonWriter aJsonWriter3;
2012 pModelObj->getRowColumnHeaders(tools::Rectangle(5400, 5400, 25050, 9800), aJsonWriter3);
2013 free(aJsonWriter3.extractData());
2014 Scheduler::ProcessEventsToIdle();
2015 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
2016 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
2017 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(49725, 0, 75225, 19380), aView1.m_aInvalidations[0]);
2020 void ScTiledRenderingTest::testJumpHorizontallyInvalidation()
2022 ScModelObj* pModelObj = createDoc("empty.ods");
2023 ScViewData* pViewData = ScDocShell::GetViewData();
2024 CPPUNIT_ASSERT(pViewData);
2026 int nView1 = SfxLokHelper::getView();
2027 ViewCallback aView1;
2028 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
2030 SfxLokHelper::setView(nView1);
2031 aView1.m_bInvalidateTiles = false;
2032 aView1.m_aInvalidations.clear();
2033 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN | KEY_MOD2);
2034 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN | KEY_MOD2);
2035 Scheduler::ProcessEventsToIdle();
2036 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN | KEY_MOD2);
2037 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN | KEY_MOD2);
2038 Scheduler::ProcessEventsToIdle();
2039 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
2040 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
2041 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(26775, 0, 39525, 13005), aView1.m_aInvalidations[0]);
2044 void ScTiledRenderingTest::testJumpToLastRowInvalidation()
2046 ScModelObj* pModelObj = createDoc("empty.ods");
2047 ScViewData* pViewData = ScDocShell::GetViewData();
2048 CPPUNIT_ASSERT(pViewData);
2050 int nView1 = SfxLokHelper::getView();
2051 ViewCallback aView1;
2052 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
2054 SfxLokHelper::setView(nView1);
2055 aView1.m_bInvalidateTiles = false;
2056 aView1.m_aInvalidations.clear();
2057 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN | KEY_MOD1);
2058 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN | KEY_MOD1);
2059 Scheduler::ProcessEventsToIdle();
2060 CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
2061 CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
2062 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(0, 13005, 26775, 267386880), aView1.m_aInvalidations[0]);
2065 // We need to ensure that views are not perterbed by rendering (!?) hmm ...
2066 void ScTiledRenderingTest::testRowColumnHeaders()
2068 ScModelObj* pModelObj = createDoc("empty.ods");
2069 ScViewData* pViewData = ScDocShell::GetViewData();
2070 CPPUNIT_ASSERT(pViewData);
2072 // view #1
2073 ViewCallback aView1;
2074 int nView1 = SfxLokHelper::getView();
2075 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
2077 // view #2
2078 SfxLokHelper::createView();
2079 int nView2 = SfxLokHelper::getView();
2080 ViewCallback aView2;
2081 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
2083 // ViewRowColumnHeaders test
2084 SfxLokHelper::setView(nView1);
2085 tools::JsonWriter aJsonWriter1;
2086 pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter1);
2087 OString aHeaders1 = aJsonWriter1.extractAsOString();
2089 SfxLokHelper::setView(nView2);
2090 // 50% zoom
2091 pModelObj->setClientVisibleArea(tools::Rectangle(0, 0, 22474, 47333));
2092 pModelObj->setClientZoom(256, 256, 6636, 6636);
2093 tools::JsonWriter aJsonWriter2;
2094 pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter2);
2095 OString aHeaders2 = aJsonWriter2.extractAsOString();
2097 // Check vs. view #1
2098 SfxLokHelper::setView(nView1);
2099 tools::JsonWriter aJsonWriter3;
2100 pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter3);
2101 OString aHeaders1_2 = aJsonWriter3.extractAsOString();
2102 CPPUNIT_ASSERT_EQUAL(aHeaders1, aHeaders1_2);
2104 // Check vs. view #2
2105 SfxLokHelper::setView(nView2);
2106 tools::JsonWriter aJsonWriter4;
2107 pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter4);
2108 OString aHeaders2_2 = aJsonWriter4.extractAsOString();
2109 CPPUNIT_ASSERT_EQUAL(aHeaders2, aHeaders2_2);
2111 SfxLokHelper::setView(nView1);
2112 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2113 SfxLokHelper::setView(nView2);
2114 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2117 // Helper structs for setup and testing of ScModelObj::getSheetGeometryData()
2118 struct SpanEntry
2120 size_t nVal;
2121 SCCOLROW nEnd;
2124 struct SheetDimData
2126 typedef std::vector<SpanEntry> SpanList;
2127 SpanList aSizes;
2128 SpanList aHidden;
2129 SpanList aFiltered;
2130 // TODO: Add group info too to test.
2132 void setDataToDoc(ScDocument* pDoc, bool bCol) const
2134 SCCOLROW nStart = 0;
2135 // sizes
2136 for (const auto& rSpan : aSizes)
2138 if (bCol)
2140 for (SCCOLROW nIdx = nStart; nIdx <= rSpan.nEnd; ++nIdx)
2141 pDoc->SetColWidthOnly(nIdx, 0, rSpan.nVal);
2143 else
2144 pDoc->SetRowHeightOnly(nStart, rSpan.nEnd, 0, rSpan.nVal);
2146 nStart = rSpan.nEnd + 1;
2149 nStart = 0;
2150 // hidden
2151 for (const auto& rSpan : aHidden)
2153 if (bCol)
2154 pDoc->SetColHidden(nStart, rSpan.nEnd, 0, !!rSpan.nVal);
2155 else
2156 pDoc->SetRowHidden(nStart, rSpan.nEnd, 0, !!rSpan.nVal);
2158 nStart = rSpan.nEnd + 1;
2161 // There is no ScDocument interface to set ScTable::mpFilteredCols
2162 // It seems ScTable::mpFilteredCols is not really used !?
2163 if (bCol)
2164 return;
2166 nStart = 0;
2167 // filtered
2168 for (const auto& rSpan : aFiltered)
2170 pDoc->SetRowFiltered(nStart, rSpan.nEnd, 0, !!rSpan.nVal);
2171 nStart = rSpan.nEnd + 1;
2175 void testPropertyTree(const boost::property_tree::ptree& rTree, bool bCol) const
2177 struct SpanListWithKey
2179 OString aKey;
2180 const SpanList& rSpanList;
2183 const SpanListWithKey aPairList[] = {
2184 { "sizes", aSizes },
2185 { "hidden", aHidden },
2186 { "filtered", aFiltered }
2189 for (const auto& rEntry : aPairList)
2191 // There is no ScDocument interface to set ScTable::mpFilteredCols
2192 // It seems ScTable::mpFilteredCols is not really used !?
2193 if (bCol && rEntry.aKey == "filtered")
2194 continue;
2196 bool bBooleanValue = rEntry.aKey != "sizes";
2197 OString aExpectedEncoding;
2198 bool bFirst = true;
2199 for (const auto& rSpan : rEntry.rSpanList)
2201 size_t nVal = rSpan.nVal;
2202 if (bBooleanValue && bFirst)
2203 nVal = static_cast<size_t>(!!nVal);
2204 if (!bBooleanValue || bFirst)
2205 aExpectedEncoding += OString::number(nVal) + ":";
2206 aExpectedEncoding += OString::number(rSpan.nEnd) + " ";
2207 bFirst = false;
2210 // Get the tree's value for the property key ("sizes"/"hidden"/"filtered").
2211 OString aTreeValue = rTree.get<std::string>(rEntry.aKey.getStr()).c_str();
2213 CPPUNIT_ASSERT_EQUAL(aExpectedEncoding, aTreeValue);
2218 class SheetGeometryData
2220 SheetDimData aCols;
2221 SheetDimData aRows;
2223 public:
2225 SheetGeometryData(const SheetDimData& rCols, const SheetDimData& rRows) :
2226 aCols(rCols), aRows(rRows)
2229 void setDataToDoc(ScDocument* pDoc) const
2231 aCols.setDataToDoc(pDoc, true);
2232 aRows.setDataToDoc(pDoc, false);
2235 void parseTest(const OString& rJSON) const
2237 // Assumes all flags passed to getSheetGeometryData() are true.
2238 boost::property_tree::ptree aTree;
2239 std::stringstream aStream(rJSON.getStr());
2240 boost::property_tree::read_json(aStream, aTree);
2242 CPPUNIT_ASSERT_EQUAL(OString(".uno:SheetGeometryData"), OString(aTree.get<std::string>("commandName").c_str()));
2244 aCols.testPropertyTree(aTree.get_child("columns"), true);
2245 aRows.testPropertyTree(aTree.get_child("rows"), false);
2249 // getSheetGeometryData() should return the exact same message
2250 // irrespective of client zoom and view-area. Switching views
2251 // should also not alter it.
2252 void ScTiledRenderingTest::testSheetGeometryDataInvariance()
2254 ScModelObj* pModelObj = createDoc("empty.ods");
2255 ScDocument* pDoc = pModelObj->GetDocument();
2256 const SheetGeometryData aSGData(
2257 // cols
2259 // width spans
2261 { STD_COL_WIDTH, 20 },
2262 { 2*STD_COL_WIDTH, 26 },
2263 { STD_COL_WIDTH, pDoc->MaxCol() }
2266 // hidden spans
2268 { 0, 5 },
2269 { 1, 12 },
2270 { 0, pDoc->MaxCol() }
2273 // filtered spans
2275 { 0, 50 },
2276 { 1, 59 },
2277 { 0, pDoc->MaxCol() }
2281 // rows
2283 // height spans
2285 { 300, 50 },
2286 { 600, 65 },
2287 { 300, pDoc->MaxRow() }
2290 // hidden spans
2292 { 1, 100 },
2293 { 0, 500 },
2294 { 1, 578 },
2295 { 0, pDoc->MaxRow() }
2298 // filtered spans
2300 { 0, 150 },
2301 { 1, 159 },
2302 { 0, pDoc->MaxRow() }
2307 ScViewData* pViewData = ScDocShell::GetViewData();
2308 CPPUNIT_ASSERT(pViewData);
2310 // view #1
2311 ViewCallback aView1;
2312 int nView1 = SfxLokHelper::getView();
2314 // view #2
2315 SfxLokHelper::createView();
2316 int nView2 = SfxLokHelper::getView();
2317 ViewCallback aView2;
2318 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
2320 // Try with the default empty document once (nIdx = 0) and then with sheet geometry settings (nIdx = 1)
2321 for (size_t nIdx = 0; nIdx < 2; ++nIdx)
2323 if (nIdx)
2324 aSGData.setDataToDoc(pDoc);
2326 SfxLokHelper::setView(nView1);
2327 OString aGeomStr1 = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2328 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2330 SfxLokHelper::setView(nView2);
2331 pModelObj->setClientVisibleArea(tools::Rectangle(0, 0, 22474, 47333));
2332 pModelObj->setClientZoom(256, 256, 6636, 6636);
2333 OString aGeomStr2 = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2334 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2336 // Check vs. view #1
2337 SfxLokHelper::setView(nView1);
2338 OString aGeomStr1_2 = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2339 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2340 CPPUNIT_ASSERT_EQUAL(aGeomStr1, aGeomStr1_2);
2342 // Check vs. view #2
2343 SfxLokHelper::setView(nView2);
2344 OString aGeomStr2_2 = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2345 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2346 CPPUNIT_ASSERT_EQUAL(aGeomStr2, aGeomStr2_2);
2349 SfxLokHelper::setView(nView1);
2350 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2351 SfxLokHelper::setView(nView2);
2352 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2355 void ScTiledRenderingTest::testSheetGeometryDataCorrectness()
2357 ScModelObj* pModelObj = createDoc("empty.ods");
2358 ScDocument* pDoc = pModelObj->GetDocument();
2359 const SheetGeometryData aDefaultSGData(
2360 // cols
2362 // width spans
2363 { { STD_COL_WIDTH, pDoc->MaxCol() } },
2364 // hidden spans
2365 { { 0, pDoc->MaxCol() } },
2366 // filtered spans
2367 { { 0, pDoc->MaxCol() } }
2369 // rows
2371 // height spans
2372 { { ScGlobal::nStdRowHeight, pDoc->MaxRow() } },
2373 // hidden spans
2374 { { 0, pDoc->MaxRow() } },
2375 // filtered spans
2376 { { 0, pDoc->MaxRow() } }
2380 const SheetGeometryData aSGData(
2381 // cols
2383 // width spans
2385 { STD_COL_WIDTH, 20 },
2386 { 2*STD_COL_WIDTH, 26 },
2387 { STD_COL_WIDTH, pDoc->MaxCol() }
2390 // hidden spans
2392 { 0, 5 },
2393 { 1, 12 },
2394 { 0, pDoc->MaxCol() }
2397 // filtered spans
2399 { 0, 50 },
2400 { 1, 59 },
2401 { 0, pDoc->MaxCol() }
2405 // rows
2407 // height spans
2409 { 300, 50 },
2410 { 600, 65 },
2411 { 300, pDoc->MaxRow() }
2414 // hidden spans
2416 { 1, 100 },
2417 { 0, 500 },
2418 { 1, 578 },
2419 { 0, pDoc->MaxRow() }
2422 // filtered spans
2424 { 0, 150 },
2425 { 1, 159 },
2426 { 0, pDoc->MaxRow() }
2431 ScViewData* pViewData = ScDocShell::GetViewData();
2432 CPPUNIT_ASSERT(pViewData);
2434 // view #1
2435 ViewCallback aView1;
2437 // with the default empty sheet and test the JSON encoding.
2438 OString aGeomDefaultStr = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2439 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2440 aDefaultSGData.parseTest(aGeomDefaultStr);
2442 // Apply geometry settings to the sheet and then test the resulting JSON encoding.
2443 aSGData.setDataToDoc(pDoc);
2444 OString aGeomStr = pModelObj->getSheetGeometryData(/*bColumns*/ true, /*bRows*/ true, /*bSizes*/ true,
2445 /*bHidden*/ true, /*bFiltered*/ true, /*bGroups*/ true);
2446 aSGData.parseTest(aGeomStr);
2448 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2451 void ScTiledRenderingTest::testDeleteCellMultilineContent()
2453 ScModelObj* pModelObj = createDoc("multiline.ods");
2454 CPPUNIT_ASSERT(pModelObj);
2455 ScViewData* pViewData = ScDocShell::GetViewData();
2456 CPPUNIT_ASSERT(pViewData);
2457 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
2458 CPPUNIT_ASSERT(pDocSh);
2460 // view #1
2461 ViewCallback aView1;
2462 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
2464 aView1.m_sInvalidateHeader = "";
2465 ScDocument& rDoc = pDocSh->GetDocument();
2466 sal_uInt16 nRow1Height = rDoc.GetRowHeight(static_cast<SCROW>(0), static_cast<SCTAB>(0), false);
2468 // delete multiline cell content in view #1
2469 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN);
2470 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN);
2471 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DELETE);
2472 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DELETE);
2473 Scheduler::ProcessEventsToIdle();
2475 // check if the row header has been invalidated and if the involved row is of the expected height
2476 CPPUNIT_ASSERT_EQUAL(OString("row"), aView1.m_sInvalidateHeader);
2477 sal_uInt16 nRow2Height = rDoc.GetRowHeight(static_cast<SCROW>(0), static_cast<SCTAB>(0), false);
2478 CPPUNIT_ASSERT_EQUAL(nRow1Height, nRow2Height);
2479 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2482 void ScTiledRenderingTest::testPasteIntoWrapTextCell()
2484 comphelper::LibreOfficeKit::setCompatFlag(
2485 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
2487 ScModelObj* pModelObj = createDoc("empty.ods");
2488 CPPUNIT_ASSERT(pModelObj);
2489 ScDocument* pDoc = pModelObj->GetDocument();
2491 // Set Wrap text in A3
2492 pDoc->ApplyAttr(0, 2, 0, ScLineBreakCell(true));
2493 const ScLineBreakCell* pItem = pDoc->GetAttr(0, 2, 0, ATTR_LINEBREAK);
2494 CPPUNIT_ASSERT(pItem->GetValue());
2496 ScViewData* pViewData = ScDocShell::GetViewData();
2497 CPPUNIT_ASSERT(pViewData);
2499 ViewCallback aView;
2500 CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData));
2502 ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2503 CPPUNIT_ASSERT(pView);
2505 // Go to A2 and paste.
2506 pView->SetCursor(0, 1);
2507 Scheduler::ProcessEventsToIdle();
2508 aView.m_sInvalidateSheetGeometry = "";
2509 pView->GetViewFrame()->GetBindings().Execute(SID_PASTE);
2510 Scheduler::ProcessEventsToIdle();
2512 // No SG invalidations
2513 CPPUNIT_ASSERT_EQUAL(OString(""), aView.m_sInvalidateSheetGeometry);
2515 // Go to A3 and paste.
2516 pView->SetCursor(0, 2);
2517 Scheduler::ProcessEventsToIdle();
2518 aView.m_sInvalidateSheetGeometry = "";
2519 pView->GetViewFrame()->GetBindings().Execute(SID_PASTE);
2520 Scheduler::ProcessEventsToIdle();
2522 // SG invalidations for all
2523 CPPUNIT_ASSERT_EQUAL(OString("all"), aView.m_sInvalidateSheetGeometry);
2525 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2528 void ScTiledRenderingTest::testSortAscendingDescending()
2530 comphelper::LibreOfficeKit::setCompatFlag(
2531 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
2532 ScModelObj* pModelObj = createDoc("sort-range.ods");
2533 ScDocument* pDoc = pModelObj->GetDocument();
2535 ViewCallback aView;
2537 // select the values in the first column
2538 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, 551, 129, 1, MOUSE_LEFT, 0);
2539 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEMOVE, 820, 1336, 1, MOUSE_LEFT, 0);
2540 pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, 820, 1359, 1, MOUSE_LEFT, 0);
2541 Scheduler::ProcessEventsToIdle();
2542 aView.m_sInvalidateSheetGeometry = "";
2544 // sort ascending
2545 uno::Sequence<beans::PropertyValue> aArgs;
2546 comphelper::dispatchCommand(".uno:SortAscending", aArgs);
2548 // check it's sorted
2549 for (SCROW r = 0; r < 6; ++r)
2551 CPPUNIT_ASSERT_EQUAL(double(r + 1), pDoc->GetValue(ScAddress(0, r, 0)));
2554 Scheduler::ProcessEventsToIdle();
2555 CPPUNIT_ASSERT_EQUAL(OString("rows"), aView.m_sInvalidateSheetGeometry);
2557 aView.m_sInvalidateSheetGeometry = "";
2558 // sort descending
2559 comphelper::dispatchCommand(".uno:SortDescending", aArgs);
2561 // check it's sorted
2562 for (SCROW r = 0; r < 6; ++r)
2564 CPPUNIT_ASSERT_EQUAL(double(6 - r), pDoc->GetValue(ScAddress(0, r, 0)));
2567 // nothing else was sorted
2568 CPPUNIT_ASSERT_EQUAL(double(1), pDoc->GetValue(ScAddress(1, 0, 0)));
2569 CPPUNIT_ASSERT_EQUAL(double(3), pDoc->GetValue(ScAddress(1, 1, 0)));
2570 CPPUNIT_ASSERT_EQUAL(double(2), pDoc->GetValue(ScAddress(1, 2, 0)));
2572 Scheduler::ProcessEventsToIdle();
2573 CPPUNIT_ASSERT_EQUAL(OString("rows"), aView.m_sInvalidateSheetGeometry);
2576 void lcl_typeCharsInCell(const std::string& aStr, SCCOL nCol, SCROW nRow, ScTabViewShell* pView,
2577 ScModelObj* pModelObj, bool bInEdit = false, bool bCommit = true)
2579 if (!bInEdit)
2580 pView->SetCursor(nCol, nRow);
2582 for (const char& cChar : aStr)
2584 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, cChar, 0);
2585 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, cChar, 0);
2586 Scheduler::ProcessEventsToIdle();
2589 if (bCommit)
2591 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
2592 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
2593 Scheduler::ProcessEventsToIdle();
2597 void ScTiledRenderingTest::testAutoInputStringBlock()
2599 ScModelObj* pModelObj = createDoc("empty.ods");
2600 CPPUNIT_ASSERT(pModelObj);
2601 ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2602 CPPUNIT_ASSERT(pView);
2603 ScDocument* pDoc = pModelObj->GetDocument();
2605 pDoc->SetString(ScAddress(0, 3, 0), "ABC"); // A4
2606 pDoc->SetString(ScAddress(0, 4, 0), "BAC"); // A5
2607 ScFieldEditEngine& rEE = pDoc->GetEditEngine();
2608 rEE.SetText("XYZ");
2609 pDoc->SetEditText(ScAddress(0, 5, 0), rEE.CreateTextObject()); // A6
2610 pDoc->SetValue(ScAddress(0, 6, 0), 123);
2611 pDoc->SetString(ScAddress(0, 7, 0), "ZZZ"); // A8
2613 ScAddress aA1(0, 0, 0);
2614 lcl_typeCharsInCell("X", aA1.Col(), aA1.Row(), pView, pModelObj); // Type 'X' in A1
2615 CPPUNIT_ASSERT_EQUAL_MESSAGE("A1 should autocomplete", OUString("XYZ"), pDoc->GetString(aA1));
2617 ScAddress aA3(0, 2, 0); // Adjacent to the string "superblock" A4:A8
2618 lcl_typeCharsInCell("X", aA3.Col(), aA3.Row(), pView, pModelObj); // Type 'X' in A3
2619 CPPUNIT_ASSERT_EQUAL_MESSAGE("A3 should autocomplete", OUString("XYZ"), pDoc->GetString(aA3));
2621 ScAddress aA9(0, 8, 0); // Adjacent to the string "superblock" A4:A8
2622 lcl_typeCharsInCell("X", aA9.Col(), aA9.Row(), pView, pModelObj); // Type 'X' in A9
2623 CPPUNIT_ASSERT_EQUAL_MESSAGE("A9 should autocomplete", OUString("XYZ"), pDoc->GetString(aA9));
2625 ScAddress aA11(0, 10, 0);
2626 lcl_typeCharsInCell("X", aA11.Col(), aA11.Row(), pView, pModelObj); // Type 'X' in A11
2627 CPPUNIT_ASSERT_EQUAL_MESSAGE("A11 should autocomplete", OUString("XYZ"), pDoc->GetString(aA11));
2630 void ScTiledRenderingTest::testAutoInputExactMatch()
2632 ScModelObj* pModelObj = createDoc("empty.ods");
2633 CPPUNIT_ASSERT(pModelObj);
2634 ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2635 CPPUNIT_ASSERT(pView);
2636 ScDocument* pDoc = pModelObj->GetDocument();
2638 pDoc->SetString(ScAddress(0, 1, 0), "Simple"); // A2
2639 pDoc->SetString(ScAddress(0, 2, 0), "Simple"); // A3
2640 pDoc->SetString(ScAddress(0, 3, 0), "Sing"); // A4
2641 ScFieldEditEngine& rEE = pDoc->GetEditEngine();
2642 rEE.SetText("Case");
2643 pDoc->SetEditText(ScAddress(0, 4, 0), rEE.CreateTextObject()); // A5
2644 pDoc->SetString(ScAddress(0, 5, 0), "Time"); // A6
2645 pDoc->SetString(ScAddress(0, 6, 0), "Castle"); // A7
2647 ScAddress aA8(0, 7, 0);
2648 lcl_typeCharsInCell("S", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "S" in A8
2649 // Should show the partial completion "i".
2650 CPPUNIT_ASSERT_EQUAL_MESSAGE("1: A8 should have partial completion Si", OUString("Si"), pDoc->GetString(aA8));
2652 lcl_typeCharsInCell("Si", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "Si" in A8
2653 // Should not show any suggestions.
2654 CPPUNIT_ASSERT_EQUAL_MESSAGE("2: A8 should not show suggestions", OUString("Si"), pDoc->GetString(aA8));
2656 lcl_typeCharsInCell("Sim", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "Sim" in A8
2657 // Should autocomplete to "Simple" which is the only match.
2658 CPPUNIT_ASSERT_EQUAL_MESSAGE("3: A8 should autocomplete", OUString("Simple"), pDoc->GetString(aA8));
2660 lcl_typeCharsInCell("Sin", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "Sin" in A8
2661 // Should autocomplete to "Sing" which is the only match.
2662 CPPUNIT_ASSERT_EQUAL_MESSAGE("4: A8 should autocomplete", OUString("Sing"), pDoc->GetString(aA8));
2664 lcl_typeCharsInCell("C", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "C" in A8
2665 // Should show the partial completion "as".
2666 CPPUNIT_ASSERT_EQUAL_MESSAGE("5: A8 should have partial completion Cas", OUString("Cas"), pDoc->GetString(aA8));
2668 lcl_typeCharsInCell("Cast", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "Cast" in A8
2669 // Should autocomplete to "Castle" which is the only match.
2670 CPPUNIT_ASSERT_EQUAL_MESSAGE("6: A8 should autocomplete", OUString("Castle"), pDoc->GetString(aA8));
2672 lcl_typeCharsInCell("T", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "T" in A8
2673 // Should autocomplete to "Time" which is the only match.
2674 CPPUNIT_ASSERT_EQUAL_MESSAGE("7: A8 should autocomplete", OUString("Time"), pDoc->GetString(aA8));
2677 void ScTiledRenderingTest::testEditCursorBounds()
2679 comphelper::LibreOfficeKit::setCompatFlag(
2680 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
2681 ScModelObj* pModelObj = createDoc("empty.ods");
2682 ScDocument* pDoc = pModelObj->GetDocument();
2684 ViewCallback aView;
2685 ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2686 CPPUNIT_ASSERT(pView);
2687 comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
2689 // ~170% zoom.
2690 pModelObj->setClientZoom(256, 256, 2222, 2222);
2691 pModelObj->setClientVisibleArea(tools::Rectangle(7725, 379832, 16240, 6449));
2692 Scheduler::ProcessEventsToIdle();
2694 constexpr SCCOL nCol = 5;
2695 constexpr SCROW nRow = 2048;
2696 pDoc->SetValue(ScAddress(nCol, nRow, 0), 123);
2698 aView.m_bOwnCursorInvalidated = false;
2699 // Obtain the cell bounds via cursor.
2700 pView->SetCursor(nCol, nRow);
2701 Scheduler::ProcessEventsToIdle();
2703 CPPUNIT_ASSERT(aView.m_bOwnCursorInvalidated);
2704 CPPUNIT_ASSERT(!aView.m_aCellCursorBounds.IsEmpty());
2705 tools::Rectangle aCellBounds(aView.m_aCellCursorBounds);
2707 aView.m_aInvalidateCursorResult.clear();
2708 // Enter edit mode in the same cell.
2709 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::F2);
2710 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::F2);
2711 Scheduler::ProcessEventsToIdle();
2713 CPPUNIT_ASSERT(!aView.m_aInvalidateCursorResult.empty());
2714 CPPUNIT_ASSERT_MESSAGE("Edit cursor must be in cell bounds!",
2715 aCellBounds.Contains(aView.m_aInvalidateCursorResult.getBounds()));
2717 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2720 void ScTiledRenderingTest::testTextSelectionBounds()
2722 comphelper::LibreOfficeKit::setCompatFlag(
2723 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
2724 ScModelObj* pModelObj = createDoc("empty.ods");
2725 ScDocument* pDoc = pModelObj->GetDocument();
2727 ViewCallback aView;
2728 ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2729 CPPUNIT_ASSERT(pView);
2730 comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
2732 // ~170% zoom.
2733 pModelObj->setClientZoom(256, 256, 2222, 2222);
2734 pModelObj->setClientVisibleArea(tools::Rectangle(7725, 379832, 16240, 6449));
2735 Scheduler::ProcessEventsToIdle();
2737 constexpr SCCOL nCol = 5;
2738 constexpr SCROW nRow = 2048;
2739 pDoc->SetValue(ScAddress(nCol, nRow, 0), 123);
2741 aView.m_bOwnCursorInvalidated = false;
2742 // Obtain the cell bounds via cursor.
2743 pView->SetCursor(nCol, nRow);
2744 Scheduler::ProcessEventsToIdle();
2746 CPPUNIT_ASSERT(aView.m_bOwnCursorInvalidated);
2747 CPPUNIT_ASSERT(!aView.m_aCellCursorBounds.IsEmpty());
2748 tools::Rectangle aCellBounds(aView.m_aCellCursorBounds);
2750 aView.m_aTextSelectionResult.clear();
2751 // Enter edit mode in the same cell and select all text.
2752 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::F2);
2753 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::F2);
2754 Scheduler::ProcessEventsToIdle();
2756 // CTRL + A
2757 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_MOD1 | awt::Key::A);
2758 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_MOD1 | awt::Key::A);
2759 Scheduler::ProcessEventsToIdle();
2761 CPPUNIT_ASSERT(!aView.m_aTextSelectionResult.empty());
2762 CPPUNIT_ASSERT_MESSAGE("Text selections must be in cell bounds!",
2763 !aCellBounds.Intersection(aView.m_aTextSelectionResult.getBounds(0)).IsEmpty());
2765 SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
2768 void ScTiledRenderingTest::testSheetViewDataCrash()
2770 ScModelObj* pModelObj = createDoc("empty.ods");
2772 // view #1
2773 int nView1 = SfxLokHelper::getView();
2774 SfxLokHelper::setView(nView1);
2776 // Imitate online while creating a new sheet on empty.ods.
2777 uno::Sequence<beans::PropertyValue> aArgs(
2778 comphelper::InitPropertySequence({
2779 { "Name", uno::Any(OUString("NewSheet")) },
2780 { "Index", uno::Any(sal_Int32(2)) }
2781 }));
2782 comphelper::dispatchCommand(".uno:Insert", aArgs);
2783 Scheduler::ProcessEventsToIdle();
2784 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN | KEY_MOD1);
2785 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN | KEY_MOD1);
2786 Scheduler::ProcessEventsToIdle();
2787 ScTabViewShell* pView1 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2788 CPPUNIT_ASSERT(pView1);
2790 // view #2
2791 SfxLokHelper::createView();
2792 ScTabViewShell* pView2 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2793 CPPUNIT_ASSERT(pView2);
2794 Scheduler::ProcessEventsToIdle();
2796 SfxLokHelper::setView(nView1);
2797 // Delete a range.
2798 pView1->SetCursor(1, 1);
2799 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN | KEY_SHIFT);
2800 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN | KEY_SHIFT);
2801 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DELETE);
2802 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DELETE);
2803 // It will crash at this point without the fix.
2804 Scheduler::ProcessEventsToIdle();
2807 void ScTiledRenderingTest::testTextBoxInsert()
2809 createDoc("empty.ods");
2810 ViewCallback aView1;
2812 // insert textbox
2813 uno::Sequence<beans::PropertyValue> aArgs(
2814 comphelper::InitPropertySequence({
2815 { "CreateDirectly", uno::Any(true) }
2816 }));
2817 comphelper::dispatchCommand(".uno:DrawText", aArgs);
2818 Scheduler::ProcessEventsToIdle();
2820 // check if we have textbox selected
2821 CPPUNIT_ASSERT(!aView1.m_ShapeSelection.isEmpty());
2822 CPPUNIT_ASSERT(aView1.m_ShapeSelection != "EMPTY");
2824 Scheduler::ProcessEventsToIdle();
2827 void ScTiledRenderingTest::testCommentCellCopyPaste()
2829 // Comments callback are emitted only if tiled annotations are off
2830 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2832 // FIXME: Hack because previous tests do not destroy ScDocument(with annotations) on exit (?).
2833 ScPostIt::mnLastPostItId = 1;
2836 ScModelObj* pModelObj = createDoc("empty.ods");
2837 ViewCallback aView;
2838 int nView = SfxLokHelper::getView();
2840 SfxLokHelper::setView(nView);
2842 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2843 CPPUNIT_ASSERT(pTabViewShell);
2845 lcl_typeCharsInCell("ABC", 0, 0, pTabViewShell, pModelObj); // Type "ABC" in A1
2847 pTabViewShell->SetCursor(1, 1);
2849 // Add a new comment
2850 uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
2852 {"Text", uno::Any(OUString("LOK Comment Cell B2"))},
2853 {"Author", uno::Any(OUString("LOK Client"))},
2854 }));
2855 comphelper::dispatchCommand(".uno:InsertAnnotation", aArgs);
2856 Scheduler::ProcessEventsToIdle();
2858 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
2859 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView.m_aCommentCallbackResult.get<std::string>("action"));
2860 CPPUNIT_ASSERT_EQUAL(std::string("1"), aView.m_aCommentCallbackResult.get<std::string>("id"));
2861 CPPUNIT_ASSERT_EQUAL(std::string("0"), aView.m_aCommentCallbackResult.get<std::string>("tab"));
2862 CPPUNIT_ASSERT_EQUAL(std::string("LOK Client"), aView.m_aCommentCallbackResult.get<std::string>("author"));
2863 CPPUNIT_ASSERT_EQUAL(std::string("LOK Comment Cell B2"), aView.m_aCommentCallbackResult.get<std::string>("text"));
2865 uno::Sequence<beans::PropertyValue> aCopyPasteArgs;
2867 // We need separate tests for single cell copy-paste and cell-range copy-paste
2868 // since they hit different code paths in ScColumn methods.
2870 // Single cell(with comment) copy paste test
2872 comphelper::dispatchCommand(".uno:Copy", aCopyPasteArgs);
2873 Scheduler::ProcessEventsToIdle();
2875 pTabViewShell->SetCursor(1, 49);
2876 Scheduler::ProcessEventsToIdle();
2877 comphelper::dispatchCommand(".uno:Paste", aCopyPasteArgs); // Paste to cell B50
2878 Scheduler::ProcessEventsToIdle();
2880 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
2881 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView.m_aCommentCallbackResult.get<std::string>("action"));
2882 // Without the fix the id will be "1".
2883 CPPUNIT_ASSERT_EQUAL(std::string("2"), aView.m_aCommentCallbackResult.get<std::string>("id"));
2884 CPPUNIT_ASSERT_EQUAL(std::string("0"), aView.m_aCommentCallbackResult.get<std::string>("tab"));
2885 CPPUNIT_ASSERT_EQUAL(std::string("LOK Client"), aView.m_aCommentCallbackResult.get<std::string>("author"));
2886 CPPUNIT_ASSERT_EQUAL(std::string("LOK Comment Cell B2"), aView.m_aCommentCallbackResult.get<std::string>("text"));
2889 // Cell range (with a comment) copy paste test
2891 // Select range A1:C3
2892 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
2893 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
2894 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_SHIFT);
2895 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_SHIFT);
2896 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_SHIFT);
2897 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_SHIFT);
2898 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT | KEY_SHIFT);
2899 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT | KEY_SHIFT);
2900 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT | KEY_SHIFT);
2901 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT | KEY_SHIFT);
2902 Scheduler::ProcessEventsToIdle();
2904 comphelper::dispatchCommand(".uno:Copy", aCopyPasteArgs);
2905 Scheduler::ProcessEventsToIdle();
2907 pTabViewShell->SetCursor(3, 49);
2908 Scheduler::ProcessEventsToIdle();
2909 comphelper::dispatchCommand(".uno:Paste", aCopyPasteArgs); // Paste to cell D50
2910 Scheduler::ProcessEventsToIdle();
2912 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
2913 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView.m_aCommentCallbackResult.get<std::string>("action"));
2914 // Without the fix the id will be "1".
2915 CPPUNIT_ASSERT_EQUAL(std::string("3"), aView.m_aCommentCallbackResult.get<std::string>("id"));
2916 CPPUNIT_ASSERT_EQUAL(std::string("0"), aView.m_aCommentCallbackResult.get<std::string>("tab"));
2917 CPPUNIT_ASSERT_EQUAL(std::string("LOK Client"), aView.m_aCommentCallbackResult.get<std::string>("author"));
2918 CPPUNIT_ASSERT_EQUAL(std::string("LOK Comment Cell B2"), aView.m_aCommentCallbackResult.get<std::string>("text"));
2921 comphelper::LibreOfficeKit::setTiledAnnotations(true);
2924 void ScTiledRenderingTest::testInvalidEntrySave()
2926 loadFromURL(u"validity.xlsx");
2928 // .uno:Save modifies the original file, make a copy first
2929 saveAndReload("Calc Office Open XML");
2930 ScModelObj* pModelObj = dynamic_cast<ScModelObj*>(mxComponent.get());
2931 CPPUNIT_ASSERT(pModelObj);
2932 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
2933 const ScDocument* pDoc = pModelObj->GetDocument();
2934 ViewCallback aView;
2935 int nView = SfxLokHelper::getView();
2937 SfxLokHelper::setView(nView);
2939 ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() );
2940 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
2941 CPPUNIT_ASSERT(pTabViewShell);
2943 // Type partial date "7/8" of "7/8/2013" that
2944 // the validation cell at A8 can accept
2945 lcl_typeCharsInCell("7/8", 0, 7, pTabViewShell, pModelObj,
2946 false /* bInEdit */, false /* bCommit */); // Type "7/8" in A8
2948 uno::Sequence<beans::PropertyValue> aArgs;
2949 comphelper::dispatchCommand(".uno:Save", aArgs);
2950 Scheduler::ProcessEventsToIdle();
2952 CPPUNIT_ASSERT_MESSAGE("Should not be marked modified after save", !pDocSh->IsModified());
2954 // Complete the date in A8 by appending "/2013" and commit.
2955 lcl_typeCharsInCell("/2013", 0, 7, pTabViewShell, pModelObj,
2956 true /* bInEdit */, true /* bCommit */);
2958 // This would hang if the date entered "7/8/2013" is not acceptable.
2959 Scheduler::ProcessEventsToIdle();
2961 // Ensure that the correct date is recorded in the document.
2962 CPPUNIT_ASSERT_EQUAL(double(41463), pDoc->GetValue(ScAddress(0, 7, 0)));
2965 void ScTiledRenderingTest::testUndoReordering()
2967 ScModelObj* pModelObj = createDoc("small.ods");
2968 CPPUNIT_ASSERT(pModelObj);
2969 ScDocument* pDoc = pModelObj->GetDocument();
2970 CPPUNIT_ASSERT(pDoc);
2971 ScUndoManager* pUndoManager = pDoc->GetUndoManager();
2972 CPPUNIT_ASSERT(pUndoManager);
2974 // view #1
2975 int nView1 = SfxLokHelper::getView();
2976 ViewCallback aView1;
2978 // view #2
2979 SfxLokHelper::createView();
2980 int nView2 = SfxLokHelper::getView();
2981 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
2982 ViewCallback aView2;
2984 // text edit a cell in view #1
2985 SfxLokHelper::setView(nView1);
2986 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
2987 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
2988 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
2989 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
2990 Scheduler::ProcessEventsToIdle();
2992 // check that undo action count is not 0
2993 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
2995 // text edit a different cell in view #2
2996 SfxLokHelper::setView(nView2);
2997 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
2998 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
2999 Scheduler::ProcessEventsToIdle();
3000 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
3001 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
3002 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
3003 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
3004 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3005 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3006 Scheduler::ProcessEventsToIdle();
3008 // check that undo action count is not 1
3009 CPPUNIT_ASSERT_EQUAL(std::size_t(2), pUndoManager->GetUndoActionCount());
3011 // try to execute undo in view #1
3012 SfxLokHelper::setView(nView1);
3013 comphelper::dispatchCommand(".uno:Undo", {});
3014 Scheduler::ProcessEventsToIdle();
3015 // check that undo has been executed on view #1
3016 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
3018 // try to execute undo in view #2
3019 SfxLokHelper::setView(nView2);
3020 comphelper::dispatchCommand(".uno:Undo", {});
3021 Scheduler::ProcessEventsToIdle();
3022 // check that undo has been executed on view #2
3023 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetUndoActionCount());
3026 void ScTiledRenderingTest::testUndoReorderingRedo()
3028 ScModelObj* pModelObj = createDoc("empty.ods");
3029 CPPUNIT_ASSERT(pModelObj);
3030 ScDocument* pDoc = pModelObj->GetDocument();
3031 CPPUNIT_ASSERT(pDoc);
3032 ScUndoManager* pUndoManager = pDoc->GetUndoManager();
3033 CPPUNIT_ASSERT(pUndoManager);
3034 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetUndoActionCount());
3036 // view #1
3037 int nView1 = SfxLokHelper::getView();
3038 SfxViewShell* pView1 = SfxViewShell::Current();
3039 ViewCallback aView1;
3041 // view #2
3042 SfxLokHelper::createView();
3043 int nView2 = SfxLokHelper::getView();
3044 SfxViewShell* pView2 = SfxViewShell::Current();
3045 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
3046 ViewCallback aView2;
3048 // text edit a cell in view #1
3049 SfxLokHelper::setView(nView1);
3050 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
3051 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
3052 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
3053 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
3054 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3055 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3056 Scheduler::ProcessEventsToIdle();
3057 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
3059 // text edit another cell in view #1
3060 SfxLokHelper::setView(nView1);
3061 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'y', 0);
3062 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'y', 0);
3063 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'y', 0);
3064 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'y', 0);
3065 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3066 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3067 Scheduler::ProcessEventsToIdle();
3068 CPPUNIT_ASSERT_EQUAL(std::size_t(2), pUndoManager->GetUndoActionCount());
3069 CPPUNIT_ASSERT_EQUAL(OUString("xx"), pDoc->GetString(ScAddress(0, 0, 0)));
3070 CPPUNIT_ASSERT_EQUAL(OUString("yy"), pDoc->GetString(ScAddress(0, 1, 0)));
3072 // text edit a different cell in view #2
3073 SfxLokHelper::setView(nView2);
3074 ScTabViewShell* pViewShell2 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
3075 pViewShell2->SetCursor(0, 2);
3076 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'C', 0);
3077 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'C', 0);
3078 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'C', 0);
3079 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'C', 0);
3080 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3081 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3082 Scheduler::ProcessEventsToIdle();
3083 CPPUNIT_ASSERT_EQUAL(std::size_t(3), pUndoManager->GetUndoActionCount());
3084 CPPUNIT_ASSERT_EQUAL(OUString("xx"), pDoc->GetString(ScAddress(0, 0, 0)));
3085 CPPUNIT_ASSERT_EQUAL(OUString("yy"), pDoc->GetString(ScAddress(0, 1, 0)));
3086 CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
3088 // View 1 presses undo, and the second cell is erased
3089 SfxLokHelper::setView(nView1);
3090 dispatchCommand(mxComponent, ".uno:Undo", {});
3091 Scheduler::ProcessEventsToIdle();
3092 CPPUNIT_ASSERT_EQUAL(std::size_t(2), pUndoManager->GetUndoActionCount());
3093 CPPUNIT_ASSERT_EQUAL(OUString("xx"), pDoc->GetString(ScAddress(0, 0, 0)));
3094 CPPUNIT_ASSERT_EQUAL(OUString(""), pDoc->GetString(ScAddress(0, 1, 0)));
3095 CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
3097 // Verify that the UNDO buttons/actions are still enabled
3099 SfxItemSet aSet1(pView1->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
3100 SfxItemSet aSet2(pView2->GetPool(), svl::Items<SID_UNDO, SID_UNDO>);
3101 pView1->GetSlotState(SID_UNDO, nullptr, &aSet1);
3102 pView2->GetSlotState(SID_UNDO, nullptr, &aSet2);
3103 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet1.GetItemState(SID_UNDO));
3104 CPPUNIT_ASSERT(dynamic_cast< const SfxStringItem* >(aSet1.GetItem(SID_UNDO)));
3105 CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aSet2.GetItemState(SID_UNDO));
3106 CPPUNIT_ASSERT(dynamic_cast< const SfxStringItem* >(aSet2.GetItem(SID_UNDO)));
3109 // View 1 presses undo again, and the first cell is erased
3110 dispatchCommand(mxComponent, ".uno:Undo", {});
3111 Scheduler::ProcessEventsToIdle();
3112 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
3113 CPPUNIT_ASSERT_EQUAL(OUString(""), pDoc->GetString(ScAddress(0, 0, 0)));
3114 CPPUNIT_ASSERT_EQUAL(OUString(""), pDoc->GetString(ScAddress(0, 1, 0)));
3115 CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
3118 void ScTiledRenderingTest::testUndoReorderingMulti()
3120 ScModelObj* pModelObj = createDoc("empty.ods");
3121 CPPUNIT_ASSERT(pModelObj);
3122 ScDocument* pDoc = pModelObj->GetDocument();
3123 CPPUNIT_ASSERT(pDoc);
3124 ScUndoManager* pUndoManager = pDoc->GetUndoManager();
3125 CPPUNIT_ASSERT(pUndoManager);
3126 CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetUndoActionCount());
3128 // view #1
3129 int nView1 = SfxLokHelper::getView();
3130 ViewCallback aView1;
3132 // view #2
3133 SfxLokHelper::createView();
3134 int nView2 = SfxLokHelper::getView();
3135 pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
3136 ViewCallback aView2;
3138 // text edit a cell in view #1
3139 SfxLokHelper::setView(nView1);
3140 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
3141 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
3142 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
3143 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
3144 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3145 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3146 Scheduler::ProcessEventsToIdle();
3147 CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
3149 // text edit a different cell in view #2
3150 SfxLokHelper::setView(nView2);
3151 ScTabViewShell* pView2 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
3152 pView2->SetCursor(0, 2);
3153 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'C', 0);
3154 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'C', 0);
3155 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'C', 0);
3156 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'C', 0);
3157 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3158 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3159 Scheduler::ProcessEventsToIdle();
3160 CPPUNIT_ASSERT_EQUAL(std::size_t(2), pUndoManager->GetUndoActionCount());
3161 CPPUNIT_ASSERT_EQUAL(OUString("xx"), pDoc->GetString(ScAddress(0, 0, 0)));
3162 CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
3164 // and another cell in view #2
3165 pView2->SetCursor(0, 3);
3166 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'D', 0);
3167 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'D', 0);
3168 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'D', 0);
3169 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'D', 0);
3170 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
3171 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
3172 Scheduler::ProcessEventsToIdle();
3173 CPPUNIT_ASSERT_EQUAL(std::size_t(3), pUndoManager->GetUndoActionCount());
3174 CPPUNIT_ASSERT_EQUAL(OUString("xx"), pDoc->GetString(ScAddress(0, 0, 0)));
3175 CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
3176 CPPUNIT_ASSERT_EQUAL(OUString("DD"), pDoc->GetString(ScAddress(0, 3, 0)));
3178 // View 1 presses undo
3179 SfxLokHelper::setView(nView1);
3180 dispatchCommand(mxComponent, ".uno:Undo", {});
3181 Scheduler::ProcessEventsToIdle();
3182 CPPUNIT_ASSERT_EQUAL(std::size_t(2), pUndoManager->GetUndoActionCount());
3183 CPPUNIT_ASSERT_EQUAL(OUString(""), pDoc->GetString(ScAddress(0, 0, 0)));
3184 CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
3185 CPPUNIT_ASSERT_EQUAL(OUString("DD"), pDoc->GetString(ScAddress(0, 3, 0)));
3190 CPPUNIT_TEST_SUITE_REGISTRATION(ScTiledRenderingTest);
3192 CPPUNIT_PLUGIN_IMPLEMENT();
3194 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */