/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace css; namespace { const char* DATA_DIRECTORY = "/sc/qa/unit/tiledrendering/data/"; class ScTiledRenderingTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools { public: ScTiledRenderingTest(); virtual void setUp() SAL_OVERRIDE; virtual void tearDown() SAL_OVERRIDE; void testRowColumnSelections(); void testSortAscendingDescending(); void testPartHash(); void testDocumentSize(); void testEmptyColumnSelection(); void testViewCursors(); void testTextViewSelection(); void testDocumentSizeChanged(); void testViewLock(); void testColRowResize(); void testUndoShells(); void testCreateViewGraphicSelection(); void testTextEditViews(); void testTextEditViewInvalidations(); void testGraphicInvalidate(); void testAutoSum(); void testHideColRow(); void testInvalidateOnCopyPasteCells(); void testInvalidateOnInserRowCol(); void testCommentCallback(); void testUndoLimiting(); void testUndoRepairDispatch(); CPPUNIT_TEST_SUITE(ScTiledRenderingTest); CPPUNIT_TEST(testRowColumnSelections); CPPUNIT_TEST(testSortAscendingDescending); CPPUNIT_TEST(testPartHash); CPPUNIT_TEST(testDocumentSize); CPPUNIT_TEST(testEmptyColumnSelection); CPPUNIT_TEST(testViewCursors); CPPUNIT_TEST(testTextViewSelection); CPPUNIT_TEST(testDocumentSizeChanged); CPPUNIT_TEST(testViewLock); CPPUNIT_TEST(testColRowResize); CPPUNIT_TEST(testUndoShells); CPPUNIT_TEST(testCreateViewGraphicSelection); CPPUNIT_TEST(testTextEditViews); CPPUNIT_TEST(testTextEditViewInvalidations); CPPUNIT_TEST(testGraphicInvalidate); CPPUNIT_TEST(testAutoSum); CPPUNIT_TEST(testHideColRow); CPPUNIT_TEST(testInvalidateOnCopyPasteCells); CPPUNIT_TEST(testInvalidateOnInserRowCol); CPPUNIT_TEST(testCommentCallback); CPPUNIT_TEST(testUndoLimiting); CPPUNIT_TEST(testUndoRepairDispatch); CPPUNIT_TEST_SUITE_END(); private: ScModelObj* createDoc(const char* pName); static void callback(int nType, const char* pPayload, void* pData); void callbackImpl(int nType, const char* pPayload); /// document size changed callback. osl::Condition m_aDocSizeCondition; Size m_aDocumentSize; uno::Reference mxComponent; }; ScTiledRenderingTest::ScTiledRenderingTest() { } void ScTiledRenderingTest::setUp() { test::BootstrapFixture::setUp(); mxDesktop.set(css::frame::Desktop::create(comphelper::getComponentContext(getMultiServiceFactory()))); } void ScTiledRenderingTest::tearDown() { if (mxComponent.is()) mxComponent->dispose(); test::BootstrapFixture::tearDown(); } ScModelObj* ScTiledRenderingTest::createDoc(const char* pName) { if (mxComponent.is()) mxComponent->dispose(); mxComponent = loadFromDesktop(m_directories.getURLFromSrc(DATA_DIRECTORY) + OUString::createFromAscii(pName), "com.sun.star.sheet.SpreadsheetDocument"); ScModelObj* pModelObj = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pModelObj); pModelObj->initializeForTiledRendering(uno::Sequence()); return pModelObj; } void ScTiledRenderingTest::callback(int nType, const char* pPayload, void* pData) { static_cast(pData)->callbackImpl(nType, pPayload); } /* TODO when needed... static std::vector lcl_convertSeparated(const OUString& rString, sal_Unicode nSeparator) { std::vector aRet; sal_Int32 nIndex = 0; do { OUString aToken = rString.getToken(0, nSeparator, nIndex); aToken = aToken.trim(); if (!aToken.isEmpty()) aRet.push_back(aToken); } while (nIndex >= 0); return aRet; } static void lcl_convertRectangle(const OUString& rString, Rectangle& rRectangle) { uno::Sequence aSeq = comphelper::string::convertCommaSeparated(rString); CPPUNIT_ASSERT_EQUAL(static_cast(4), aSeq.getLength()); rRectangle.setX(aSeq[0].toInt32()); rRectangle.setY(aSeq[1].toInt32()); rRectangle.setWidth(aSeq[2].toInt32()); rRectangle.setHeight(aSeq[3].toInt32()); } */ void ScTiledRenderingTest::callbackImpl(int nType, const char* pPayload) { switch (nType) { case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED: { OString aPayload(pPayload); sal_Int32 nIndex = 0; OString aToken = aPayload.getToken(0, ',', nIndex); m_aDocumentSize.setWidth(aToken.toInt32()); aToken = aPayload.getToken(0, ',', nIndex); m_aDocumentSize.setHeight(aToken.toInt32()); m_aDocSizeCondition.set(); } break; } } void ScTiledRenderingTest::testRowColumnSelections() { comphelper::LibreOfficeKit::setActive(); ScModelObj* pModelObj = createDoc("select-row-cols.ods"); uno::Sequence aArgs(2); // Select the 5th row with no modifier aArgs[0].Name = OUString::fromUtf8("Row"); aArgs[0].Value <<= static_cast(5 - 1); aArgs[1].Name = OUString::fromUtf8("Modifier"); aArgs[1].Value <<= static_cast(0); comphelper::dispatchCommand(".uno:SelectRow", aArgs); // Check if it is selected OString aUsedMimeType; OString aResult = pModelObj->getTextSelection("text/plain;charset=utf-8", aUsedMimeType); OString aExpected("1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\n"); CPPUNIT_ASSERT_EQUAL(aExpected, aResult); // Select the 10th row with shift modifier aArgs[0].Name = OUString::fromUtf8("Row"); aArgs[0].Value <<= static_cast(10 - 1); aArgs[1].Name = OUString::fromUtf8("Modifier"); aArgs[1].Value <<= static_cast(KEY_SHIFT); comphelper::dispatchCommand(".uno:SelectRow", aArgs); // Check if all the rows from 5th to 10th get selected aResult = pModelObj->getTextSelection("text/plain;charset=utf-8", aUsedMimeType); 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"; CPPUNIT_ASSERT_EQUAL(aExpected, aResult); // Select the 10th row with ctrl modifier aArgs[0].Name = OUString::fromUtf8("Row"); aArgs[0].Value <<= static_cast(13 - 1); aArgs[1].Name = OUString::fromUtf8("Modifier"); aArgs[1].Value <<= static_cast(KEY_MOD1); comphelper::dispatchCommand(".uno:SelectRow", aArgs); // When we copy this, we don't get anything useful, but we must not crash // (used to happen) aResult = pModelObj->getTextSelection("text/plain;charset=utf-8", aUsedMimeType); CPPUNIT_ASSERT_EQUAL(OString(), aResult); // TODO check that we really selected what we wanted here // Select Column 5 with ctrl modifier aArgs[0].Name = OUString::fromUtf8("Col"); aArgs[0].Value <<= static_cast(5 - 1); aArgs[1].Name = OUString::fromUtf8("Modifier"); aArgs[1].Value <<= static_cast(KEY_MOD1); comphelper::dispatchCommand(".uno:SelectColumn", aArgs); // When we copy this, we don't get anything useful, but we must not crash // (used to happen) aResult = pModelObj->getTextSelection("text/plain;charset=utf-8", aUsedMimeType); CPPUNIT_ASSERT_EQUAL(OString(), aResult); // TODO check that we really selected what we wanted here // Test for deselection of already selected rows // First Deselect Row 13 because copy doesn't work for multiple selections aArgs[0].Name = OUString::fromUtf8("Row"); aArgs[0].Value <<= static_cast(13 - 1); aArgs[1].Name = OUString::fromUtf8("Modifier"); aArgs[1].Value <<= static_cast(KEY_MOD1); comphelper::dispatchCommand(".uno:SelectRow", aArgs); // Deselect row 10 aArgs[0].Name = OUString::fromUtf8("Row"); aArgs[0].Value <<= static_cast(10 - 1); aArgs[1].Name = OUString::fromUtf8("Modifier"); aArgs[1].Value <<= static_cast(KEY_MOD1); comphelper::dispatchCommand(".uno:SelectRow", aArgs); // Click at row 6 holding shift aArgs[0].Name = OUString::fromUtf8("Row"); aArgs[0].Value <<= static_cast(6 - 1); aArgs[1].Name = OUString::fromUtf8("Modifier"); aArgs[1].Value <<= static_cast(KEY_SHIFT); comphelper::dispatchCommand(".uno:SelectRow", aArgs); // only row 5 should remain selected aResult = pModelObj->getTextSelection("text/plain;charset=utf-8", aUsedMimeType); aExpected = "1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\n"; CPPUNIT_ASSERT_EQUAL(aExpected, aResult); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testSortAscendingDescending() { comphelper::LibreOfficeKit::setActive(); ScModelObj* pModelObj = createDoc("sort-range.ods"); ScDocument* pDoc = pModelObj->GetDocument(); // select the values in the first column pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, 551, 129, 1, MOUSE_LEFT, 0); pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEMOVE, 820, 1336, 1, MOUSE_LEFT, 0); pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, 820, 1359, 1, MOUSE_LEFT, 0); // sort ascending uno::Sequence aArgs; comphelper::dispatchCommand(".uno:SortAscending", aArgs); // check it's sorted for (SCROW r = 0; r < 6; ++r) { CPPUNIT_ASSERT_EQUAL(double(r + 1), pDoc->GetValue(ScAddress(0, r, 0))); } // sort descending comphelper::dispatchCommand(".uno:SortDescending", aArgs); // check it's sorted for (SCROW r = 0; r < 6; ++r) { CPPUNIT_ASSERT_EQUAL(double(6 - r), pDoc->GetValue(ScAddress(0, r, 0))); } // nothing else was sorted CPPUNIT_ASSERT_EQUAL(double(1), pDoc->GetValue(ScAddress(1, 0, 0))); CPPUNIT_ASSERT_EQUAL(double(3), pDoc->GetValue(ScAddress(1, 1, 0))); CPPUNIT_ASSERT_EQUAL(double(2), pDoc->GetValue(ScAddress(1, 2, 0))); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testPartHash() { comphelper::LibreOfficeKit::setActive(); ScModelObj* pModelObj = createDoc("sort-range.ods"); int nParts = pModelObj->getParts(); for (int it = 0; it < nParts; it++) { CPPUNIT_ASSERT(!pModelObj->getPartHash(it).isEmpty()); } // check part that it does not exists CPPUNIT_ASSERT(pModelObj->getPartHash(100).isEmpty()); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testDocumentSize() { comphelper::LibreOfficeKit::setActive(); ScModelObj* pModelObj = createDoc("sort-range.ods"); ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() ); CPPUNIT_ASSERT(pDocSh); ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false); CPPUNIT_ASSERT(pViewShell); pViewShell->registerLibreOfficeKitViewCallback(&ScTiledRenderingTest::callback, this); // check initial document size Size aDocSize = pModelObj->getDocumentSize(); CPPUNIT_ASSERT(aDocSize.Width() > 0); CPPUNIT_ASSERT(aDocSize.Height() > 0); // Set cursor column pViewShell->SetCursor(100, 0); // 2 seconds osl::Condition::Result aResult = m_aDocSizeCondition.wait(std::chrono::seconds(2)); CPPUNIT_ASSERT_EQUAL(aResult, osl::Condition::result_ok); // Set cursor row pViewShell->SetCursor(0, 100); // 2 seconds aResult = m_aDocSizeCondition.wait(std::chrono::seconds(2)); CPPUNIT_ASSERT_EQUAL(aResult, osl::Condition::result_ok); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testEmptyColumnSelection() { comphelper::LibreOfficeKit::setActive(); ScModelObj* pModelObj = createDoc("select-row-cols.ods"); uno::Sequence aArgs(2); // Select empty column, 1000 aArgs[0].Name = OUString::fromUtf8("Col"); aArgs[0].Value <<= static_cast(1000 - 1); aArgs[1].Name = OUString::fromUtf8("Modifier"); aArgs[1].Value <<= static_cast(0); comphelper::dispatchCommand(".uno:SelectColumn", aArgs); // Get plain selection OString aUsedMimeType; OString aResult = pModelObj->getTextSelection("text/plain;charset=utf-8", aUsedMimeType); // should be an empty string CPPUNIT_ASSERT_EQUAL(OString(), aResult); comphelper::LibreOfficeKit::setActive(false); } /// A view callback tracks callbacks invoked on one specific view. class ViewCallback { public: bool m_bOwnCursorInvalidated; bool m_bViewCursorInvalidated; bool m_bTextViewSelectionInvalidated; bool m_bGraphicSelection; bool m_bGraphicViewSelection; bool m_bFullInvalidateTiles; bool m_bInvalidateTiles; bool m_bViewLock; OString m_sCellFormula; boost::property_tree::ptree m_aCommentCallbackResult; ViewCallback() : m_bOwnCursorInvalidated(false), m_bViewCursorInvalidated(false), m_bTextViewSelectionInvalidated(false), m_bGraphicSelection(false), m_bGraphicViewSelection(false), m_bFullInvalidateTiles(false), m_bInvalidateTiles(false), m_bViewLock(false) { } static void callback(int nType, const char* pPayload, void* pData) { static_cast(pData)->callbackImpl(nType, pPayload); } void callbackImpl(int nType, const char* pPayload) { switch (nType) { case LOK_CALLBACK_CELL_CURSOR: { m_bOwnCursorInvalidated = true; } break; case LOK_CALLBACK_CELL_VIEW_CURSOR: { m_bViewCursorInvalidated = true; } break; case LOK_CALLBACK_TEXT_VIEW_SELECTION: { m_bTextViewSelectionInvalidated = true; } break; case LOK_CALLBACK_VIEW_LOCK: { std::stringstream aStream(pPayload); boost::property_tree::ptree aTree; boost::property_tree::read_json(aStream, aTree); m_bViewLock = aTree.get_child("rectangle").get_value() != "EMPTY"; } break; case LOK_CALLBACK_GRAPHIC_SELECTION: { m_bGraphicSelection = true; } break; case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION: { m_bGraphicViewSelection = true; } break; case LOK_CALLBACK_INVALIDATE_TILES: { OString text(pPayload); if (text.startsWith("EMPTY")) { m_bFullInvalidateTiles = true; } else { m_bInvalidateTiles = true; } } break; case LOK_CALLBACK_CELL_FORMULA: { m_sCellFormula = pPayload; } break; case LOK_CALLBACK_COMMENT: { m_aCommentCallbackResult.clear(); std::stringstream aStream(pPayload); boost::property_tree::read_json(aStream, m_aCommentCallbackResult); m_aCommentCallbackResult = m_aCommentCallbackResult.get_child("comment"); } break; } } }; void ScTiledRenderingTest::testViewCursors() { comphelper::LibreOfficeKit::setActive(); ScModelObj* pModelObj = createDoc("select-row-cols.ods"); ViewCallback aView1; SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1); SfxLokHelper::createView(); pModelObj->initializeForTiledRendering(uno::Sequence()); ViewCallback aView2; aView2.m_bViewCursorInvalidated = false; aView2.m_bOwnCursorInvalidated = false; SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2); // This was false, the new view did not get the view (cell) cursor of the old view. CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated); CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN); Scheduler::ProcessEventsToIdle(); SfxLokHelper::destroyView(SfxLokHelper::getView()); CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } void lcl_dispatchCommand(const uno::Reference& xComponent, const OUString& rCommand, const uno::Sequence& rArguments) { uno::Reference xController = uno::Reference(xComponent, uno::UNO_QUERY)->getCurrentController(); CPPUNIT_ASSERT(xController.is()); uno::Reference xFrame(xController->getFrame(), uno::UNO_QUERY); CPPUNIT_ASSERT(xFrame.is()); uno::Reference xContext = ::comphelper::getProcessComponentContext(); uno::Reference xDispatchHelper(frame::DispatchHelper::create(xContext)); CPPUNIT_ASSERT(xDispatchHelper.is()); xDispatchHelper->executeDispatch(xFrame, rCommand, OUString(), 0, rArguments); } void ScTiledRenderingTest::testTextViewSelection() { comphelper::LibreOfficeKit::setActive(); // Create two views, and leave the second one current. ScModelObj* pModelObj = createDoc("select-row-cols.ods"); ViewCallback aView1; SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1); SfxLokHelper::createView(); ViewCallback aView2; pModelObj->initializeForTiledRendering(uno::Sequence()); SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2); // Create a selection on two cells in the second view, that's a text selection in LOK terms. aView1.m_bTextViewSelectionInvalidated = false; lcl_dispatchCommand(mxComponent, ".uno:GoRightSel", {}); Scheduler::ProcessEventsToIdle(); // Make sure the first view got its notification. CPPUNIT_ASSERT(aView1.m_bTextViewSelectionInvalidated); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testDocumentSizeChanged() { comphelper::LibreOfficeKit::setActive(); // Load a document that doesn't have much content. createDoc("small.ods"); SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ScTiledRenderingTest::callback, this); // Go to the A30 cell -- that will extend the document size. uno::Sequence aPropertyValues = { comphelper::makePropertyValue("ToPoint", OUString("$A$30")), }; lcl_dispatchCommand(mxComponent, ".uno:GoToCell", aPropertyValues); Scheduler::ProcessEventsToIdle(); // Assert that the size in the payload is not 0. CPPUNIT_ASSERT(m_aDocumentSize.getWidth() > 0); CPPUNIT_ASSERT(m_aDocumentSize.getHeight() > 0); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testViewLock() { comphelper::LibreOfficeKit::setActive(); // Load a document that has a shape and create two views. ScModelObj* pModelObj = createDoc("shape.ods"); ViewCallback aView1; SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1); SfxLokHelper::createView(); ViewCallback aView2; pModelObj->initializeForTiledRendering(uno::Sequence()); SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2); // Begin text edit in the second view and assert that the first gets a lock // notification. const ScViewData* pViewData = ScDocShell::GetViewData(); ScTabViewShell* pViewShell = pViewData->GetViewShell(); CPPUNIT_ASSERT(pViewShell); SdrModel* pDrawModel = pViewData->GetDocument()->GetDrawLayer(); SdrPage* pDrawPage = pDrawModel->GetPage(0); SdrObject* pObject = pDrawPage->GetObj(0); SdrView* pView = pViewShell->GetSdrView(); aView1.m_bViewLock = false; pView->SdrBeginTextEdit(pObject); CPPUNIT_ASSERT(aView1.m_bViewLock); // End text edit in the second view, and assert that the lock is removed in // the first view. pView->SdrEndTextEdit(); CPPUNIT_ASSERT(!aView1.m_bViewLock); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testColRowResize() { comphelper::LibreOfficeKit::setActive(); ScModelObj* pModelObj = createDoc("sort-range.ods"); ScDocShell* pDocSh = dynamic_cast< ScDocShell* >( pModelObj->GetEmbeddedObject() ); CPPUNIT_ASSERT(pDocSh); ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false); CPPUNIT_ASSERT(pViewShell); pViewShell->registerLibreOfficeKitViewCallback(&ScTiledRenderingTest::callback, this); uno::Sequence aArgs(2); ScDocument& rDoc = pDocSh->GetDocument(); // Col 3, Tab 0 int nOldWidth = rDoc.GetColWidth(static_cast(2), static_cast(0), false); aArgs[0].Name = OUString::fromUtf8("Column"); aArgs[0].Value <<= static_cast(3); aArgs[1].Name = OUString::fromUtf8("Width"); aArgs[1].Value <<= static_cast(nOldWidth + 100); comphelper::dispatchCommand(".uno:ColumnWidth", aArgs); int nNewWidth = rDoc.GetColWidth(static_cast(2), static_cast(0), false); CPPUNIT_ASSERT(nNewWidth > nOldWidth); // Row 5, Tab 0 int nOldHeight = rDoc.GetRowHeight(static_cast(4), static_cast(0), false); aArgs[0].Name = OUString::fromUtf8("Row"); aArgs[0].Value <<= static_cast(5); aArgs[1].Name = OUString::fromUtf8("Height"); aArgs[1].Value <<= static_cast(nOldHeight + 100); comphelper::dispatchCommand(".uno:RowHeight", aArgs); int nNewHeight = rDoc.GetRowHeight(static_cast(4), static_cast(0), false); CPPUNIT_ASSERT(nNewHeight > nOldHeight); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testUndoShells() { comphelper::LibreOfficeKit::setActive(); ScModelObj* pModelObj = createDoc("small.ods"); // Clear the currently selected cell. comphelper::dispatchCommand(".uno:ClearContents", {}); auto pDocShell = dynamic_cast(pModelObj->GetEmbeddedObject()); CPPUNIT_ASSERT(pDocShell); ScDocument& rDoc = pDocShell->GetDocument(); SfxUndoManager* pUndoManager = rDoc.GetUndoManager(); CPPUNIT_ASSERT(pUndoManager); CPPUNIT_ASSERT_EQUAL(static_cast(1), pUndoManager->GetUndoActionCount()); sal_Int32 nView1 = SfxLokHelper::getView(); // This was -1: ScSimpleUndo did not remember what view shell created it. CPPUNIT_ASSERT_EQUAL(nView1, pUndoManager->GetUndoAction()->GetViewShellId()); comphelper::LibreOfficeKit::setActive(false); } bool lcl_hasEditView(ScViewData& rViewData) { bool bResult = false; for (unsigned int i=0; i<4; i++) { bResult = rViewData.HasEditView( (ScSplitPos) i ); if (bResult) break; } return bResult; } void ScTiledRenderingTest::testTextEditViews() { comphelper::LibreOfficeKit::setActive(); ScModelObj* pModelObj = createDoc("small.ods"); CPPUNIT_ASSERT(pModelObj); ScViewData* pViewData = ScDocShell::GetViewData(); CPPUNIT_ASSERT(pViewData); // view #1 ViewCallback aView1; SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1); CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData)); // text edit a cell in view #1 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0); Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT(lcl_hasEditView(*pViewData)); // view #2 SfxLokHelper::createView(); ViewCallback aView2; pModelObj->initializeForTiledRendering(uno::Sequence()); SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2); // move cell cursor i view #2 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN); Scheduler::ProcessEventsToIdle(); // check that text edit view in view #1 has not be killed CPPUNIT_ASSERT(lcl_hasEditView(*pViewData)); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testTextEditViewInvalidations() { comphelper::LibreOfficeKit::setActive(); ScModelObj* pModelObj = createDoc("small.ods"); CPPUNIT_ASSERT(pModelObj); ScViewData* pViewData = ScDocShell::GetViewData(); CPPUNIT_ASSERT(pViewData); // view #1 ViewCallback aView1; int nView1 = SfxLokHelper::getView(); SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1); CPPUNIT_ASSERT(!lcl_hasEditView(*pViewData)); // view #2 SfxLokHelper::createView(); ViewCallback aView2; pModelObj->initializeForTiledRendering(uno::Sequence()); SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2); // text edit a cell in view #1 SfxLokHelper::setView(nView1); aView2.m_bInvalidateTiles = false; pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0); Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT(lcl_hasEditView(*pViewData)); CPPUNIT_ASSERT(aView2.m_bInvalidateTiles); // text edit a cell in view #1 until // we can be sure we are out of the initial tile for (int i = 0; i < 40; ++i) { pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0); } Scheduler::ProcessEventsToIdle(); // text edit a cell in view #1 inside the new tile and // check that view #2 receive a tile invalidate message aView2.m_bInvalidateTiles = false; pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0); Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT(aView2.m_bInvalidateTiles); // view #3 SfxLokHelper::createView(); ViewCallback aView3; pModelObj->initializeForTiledRendering(uno::Sequence()); SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView3); // text edit a cell in view #1 SfxLokHelper::setView(nView1); aView3.m_bInvalidateTiles = false; pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'y', 0); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'y', 0); Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT(aView3.m_bInvalidateTiles); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testCreateViewGraphicSelection() { // Load a document comphelper::LibreOfficeKit::setActive(); // Load a document that has a shape and create two views. ScModelObj* pModelObj = createDoc("shape.ods"); ViewCallback aView1; SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1); // Mark the graphic in the first view. const ScViewData* pViewData = ScDocShell::GetViewData(); ScTabViewShell* pViewShell = pViewData->GetViewShell(); CPPUNIT_ASSERT(pViewShell); SdrModel* pDrawModel = pViewData->GetDocument()->GetDrawLayer(); SdrPage* pDrawPage = pDrawModel->GetPage(0); SdrObject* pObject = pDrawPage->GetObj(0); SdrView* pView = pViewShell->GetSdrView(); aView1.m_bGraphicSelection = false; aView1.m_bGraphicViewSelection = false; pView->MarkObj(pObject, pView->GetSdrPageView()); CPPUNIT_ASSERT(aView1.m_bGraphicSelection); // Create a second view. SfxLokHelper::createView(); ViewCallback aView2; aView2.m_bGraphicViewSelection = false; pModelObj->initializeForTiledRendering(uno::Sequence()); SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2); CPPUNIT_ASSERT(aView2.m_bGraphicViewSelection); CPPUNIT_ASSERT(aView1.m_bGraphicViewSelection); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testGraphicInvalidate() { // Load a document comphelper::LibreOfficeKit::setActive(); // Load a document that has a shape and create two views. ScModelObj* pModelObj = createDoc("shape.ods"); ViewCallback aView; SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView); // Click to select graphic aView.m_bGraphicSelection = false; pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, /*x=*/ 1,/*y=*/ 1,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0); pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, /*x=*/ 1, /*y=*/ 1, /*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0); CPPUNIT_ASSERT(aView.m_bGraphicSelection); // Drag Drop graphic aView.m_bGraphicSelection = false; pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, /*x=*/ 1,/*y=*/ 1,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0); pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEMOVE, /*x=*/ 1,/*y=*/ 10,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0); pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, /*x=*/ 1, /*y=*/ 10, /*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0); Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT(!aView.m_bFullInvalidateTiles); // Check again Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT(!aView.m_bFullInvalidateTiles); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testAutoSum() { // Load a document comphelper::LibreOfficeKit::setActive(); createDoc("small.ods"); ViewCallback aView; SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView); uno::Sequence aArgs; comphelper::dispatchCommand(".uno:AutoSum", aArgs); Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT(aView.m_sCellFormula.startsWith("=SUM(")); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testHideColRow() { // Load a document comphelper::LibreOfficeKit::setActive(); createDoc("small.ods"); { uno::Sequence aArgs(2); aArgs[0].Name = OUString::fromUtf8("Col"); aArgs[0].Value <<= static_cast(2 - 1); aArgs[1].Name = OUString::fromUtf8("Modifier"); aArgs[1].Value <<= static_cast(KEY_SHIFT); comphelper::dispatchCommand(".uno:SelectColumn", aArgs); aArgs[0].Name = OUString::fromUtf8("Col"); aArgs[0].Value <<= static_cast(3 - 1); aArgs[1].Name = OUString::fromUtf8("Modifier"); aArgs[1].Value <<= static_cast(0); comphelper::dispatchCommand(".uno:SelectColumn", aArgs); Scheduler::ProcessEventsToIdle(); } SCCOL nOldCurX = ScDocShell::GetViewData()->GetCurX(); SCROW nOldCurY = ScDocShell::GetViewData()->GetCurY(); { uno::Sequence aArgs; comphelper::dispatchCommand(".uno:HideColumn", aArgs); Scheduler::ProcessEventsToIdle(); } SCCOL nNewCurX = ScDocShell::GetViewData()->GetCurX(); SCROW nNewCurY = ScDocShell::GetViewData()->GetCurY(); CPPUNIT_ASSERT(nNewCurX > nOldCurX); CPPUNIT_ASSERT_EQUAL(nOldCurY, nNewCurY); { uno::Sequence aArgs(2); aArgs[0].Name = OUString::fromUtf8("Row"); aArgs[0].Value <<= static_cast(6 - 1); aArgs[1].Name = OUString::fromUtf8("Modifier"); aArgs[1].Value <<= static_cast(KEY_SHIFT); comphelper::dispatchCommand(".uno:SelectRow", aArgs); aArgs[0].Name = OUString::fromUtf8("Row"); aArgs[0].Value <<= static_cast(7 - 1); aArgs[1].Name = OUString::fromUtf8("Modifier"); aArgs[1].Value <<= static_cast(0); comphelper::dispatchCommand(".uno:SelectRow", aArgs); Scheduler::ProcessEventsToIdle(); } nOldCurX = ScDocShell::GetViewData()->GetCurX(); nOldCurY = ScDocShell::GetViewData()->GetCurY(); { uno::Sequence aArgs; comphelper::dispatchCommand(".uno:HideRow", aArgs); Scheduler::ProcessEventsToIdle(); } nNewCurX = ScDocShell::GetViewData()->GetCurX(); nNewCurY = ScDocShell::GetViewData()->GetCurY(); CPPUNIT_ASSERT(nNewCurY > nOldCurY); CPPUNIT_ASSERT_EQUAL(nOldCurX, nNewCurX); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testInvalidateOnCopyPasteCells() { // Load a document comphelper::LibreOfficeKit::setActive(); ScModelObj* pModelObj = createDoc("small.ods"); CPPUNIT_ASSERT(pModelObj); // view ViewCallback aView; SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView); uno::Sequence aArgs; // select and copy cells pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_SHIFT); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_SHIFT); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT | KEY_SHIFT); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT | KEY_SHIFT); Scheduler::ProcessEventsToIdle(); comphelper::dispatchCommand(".uno:Copy", aArgs); // move to destination cell pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_MOD1); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_MOD1); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_UP); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_UP); Scheduler::ProcessEventsToIdle(); // paste cells aView.m_bInvalidateTiles = false; comphelper::dispatchCommand(".uno:Paste", aArgs); Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT(aView.m_bInvalidateTiles); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testInvalidateOnInserRowCol() { // Load a document comphelper::LibreOfficeKit::setActive(); ScModelObj* pModelObj = createDoc("small.ods"); CPPUNIT_ASSERT(pModelObj); // view ViewCallback aView; SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView); uno::Sequence aArgs; // move downward for (int i = 0; i < 200; ++i) { pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN); } Scheduler::ProcessEventsToIdle(); // insert row aView.m_bInvalidateTiles = false; comphelper::dispatchCommand(".uno:InsertRows", aArgs); Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT(aView.m_bInvalidateTiles); // move on the right for (int i = 0; i < 200; ++i) { pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT); } Scheduler::ProcessEventsToIdle(); // insert column aView.m_bInvalidateTiles = false; comphelper::dispatchCommand(".uno:InsertColumns", aArgs); Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT(aView.m_bInvalidateTiles); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testCommentCallback() { // Load a document comphelper::LibreOfficeKit::setActive(); // Comments callback are emitted only if tiled annotations are off comphelper::LibreOfficeKit::setTiledAnnotations(false); ScModelObj* pModelObj = createDoc("small.ods"); ViewCallback aView1; SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1); int nView1 = SfxLokHelper::getView(); // Create a 2nd view SfxLokHelper::createView(); pModelObj->initializeForTiledRendering({}); ViewCallback aView2; SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2); SfxLokHelper::setView(nView1); // Add a new comment uno::Sequence aArgs(comphelper::InitPropertySequence( { {"Text", uno::makeAny(OUString("Comment"))}, {"Author", uno::makeAny(OUString("LOK User1"))}, })); comphelper::dispatchCommand(".uno:InsertAnnotation", aArgs); Scheduler::ProcessEventsToIdle(); // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1.m_aCommentCallbackResult.get("action")); CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2.m_aCommentCallbackResult.get("action")); CPPUNIT_ASSERT_EQUAL(std::string("Sheet1.A2"), aView1.m_aCommentCallbackResult.get("id")); CPPUNIT_ASSERT_EQUAL(std::string("Sheet1.A2"), aView2.m_aCommentCallbackResult.get("id")); CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), aView1.m_aCommentCallbackResult.get("author")); CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), aView2.m_aCommentCallbackResult.get("author")); CPPUNIT_ASSERT_EQUAL(std::string("Comment"), aView1.m_aCommentCallbackResult.get("text")); CPPUNIT_ASSERT_EQUAL(std::string("Comment"), aView2.m_aCommentCallbackResult.get("text")); CPPUNIT_ASSERT_EQUAL(std::string("0, 255, 1274, 254"), aView1.m_aCommentCallbackResult.get("cellPos")); CPPUNIT_ASSERT_EQUAL(std::string("0, 255, 1274, 254"), aView2.m_aCommentCallbackResult.get("cellPos")); // Edit a comment // Select some random cell, we should be able to edit the cell note without // selecting the cell ScTabViewShell* pTabViewShell = dynamic_cast(SfxViewShell::Current()); if (pTabViewShell) pTabViewShell->SetCursor(3, 100); aArgs = comphelper::InitPropertySequence( { {"Id", uno::makeAny(OUString("Sheet1.A2"))}, {"Text", uno::makeAny(OUString("Edited comment"))}, {"Author", uno::makeAny(OUString("LOK User2"))}, }); comphelper::dispatchCommand(".uno:EditAnnotation", aArgs); Scheduler::ProcessEventsToIdle(); // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1.m_aCommentCallbackResult.get("action")); CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView2.m_aCommentCallbackResult.get("action")); CPPUNIT_ASSERT_EQUAL(std::string("Sheet1.A2"), aView1.m_aCommentCallbackResult.get("id")); CPPUNIT_ASSERT_EQUAL(std::string("Sheet1.A2"), aView2.m_aCommentCallbackResult.get("id")); CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), aView1.m_aCommentCallbackResult.get("author")); CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), aView2.m_aCommentCallbackResult.get("author")); CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView1.m_aCommentCallbackResult.get("text")); CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView2.m_aCommentCallbackResult.get("text")); CPPUNIT_ASSERT_EQUAL(std::string("0, 255, 1274, 254"), aView1.m_aCommentCallbackResult.get("cellPos")); CPPUNIT_ASSERT_EQUAL(std::string("0, 255, 1274, 254"), aView2.m_aCommentCallbackResult.get("cellPos")); // Delete the comment if (pTabViewShell) pTabViewShell->SetCursor(4, 43); aArgs = comphelper::InitPropertySequence( { {"Id", uno::makeAny(OUString("Sheet1.A2"))} }); comphelper::dispatchCommand(".uno:DeleteNote", aArgs); Scheduler::ProcessEventsToIdle(); // We received a LOK_CALLBACK_COMMENT callback with comment 'Remove' action CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView1.m_aCommentCallbackResult.get("action")); CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView2.m_aCommentCallbackResult.get("action")); CPPUNIT_ASSERT_EQUAL(std::string("Sheet1.A2"), aView1.m_aCommentCallbackResult.get("id")); CPPUNIT_ASSERT_EQUAL(std::string("Sheet1.A2"), aView2.m_aCommentCallbackResult.get("id")); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setTiledAnnotations(true); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testUndoLimiting() { comphelper::LibreOfficeKit::setActive(); ScModelObj* pModelObj = createDoc("small.ods"); CPPUNIT_ASSERT(pModelObj); ScDocument* pDoc = pModelObj->GetDocument(); CPPUNIT_ASSERT(pDoc); SfxUndoManager* pUndoManager = pDoc->GetUndoManager(); CPPUNIT_ASSERT(pUndoManager); // view #1 ViewCallback aView1; int nView1 = SfxLokHelper::getView(); SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1); // view #2 SfxLokHelper::createView(); ViewCallback aView2; int nView2 = SfxLokHelper::getView(); pModelObj->initializeForTiledRendering(uno::Sequence()); SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2); // text edit a cell in view #1 SfxLokHelper::setView(nView1); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN); Scheduler::ProcessEventsToIdle(); // check that undo action count in not 0 CPPUNIT_ASSERT(pUndoManager->GetUndoActionCount() == 1); // try to execute undo in view #2 SfxLokHelper::setView(nView2); comphelper::dispatchCommand(".uno:Undo", {}); Scheduler::ProcessEventsToIdle(); // check that undo has not been executed on view #2 CPPUNIT_ASSERT(pUndoManager->GetUndoActionCount() == 1); // try to execute undo in view #1 SfxLokHelper::setView(nView1); comphelper::dispatchCommand(".uno:Undo", {}); Scheduler::ProcessEventsToIdle(); // check that undo has been executed on view #1 CPPUNIT_ASSERT(pUndoManager->GetUndoActionCount() == 0); // check that redo action count in not 0 CPPUNIT_ASSERT(pUndoManager->GetRedoActionCount() == 1); // try to execute redo in view #2 SfxLokHelper::setView(nView2); comphelper::dispatchCommand(".uno:Redo", {}); Scheduler::ProcessEventsToIdle(); // check that redo has not been executed on view #2 CPPUNIT_ASSERT(pUndoManager->GetRedoActionCount() == 1); // try to execute redo in view #1 SfxLokHelper::setView(nView1); comphelper::dispatchCommand(".uno:Redo", {}); Scheduler::ProcessEventsToIdle(); // check that redo has been executed on view #1 CPPUNIT_ASSERT(pUndoManager->GetRedoActionCount() == 0); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } void ScTiledRenderingTest::testUndoRepairDispatch() { comphelper::LibreOfficeKit::setActive(); ScModelObj* pModelObj = createDoc("small.ods"); CPPUNIT_ASSERT(pModelObj); ScDocument* pDoc = pModelObj->GetDocument(); CPPUNIT_ASSERT(pDoc); SfxUndoManager* pUndoManager = pDoc->GetUndoManager(); CPPUNIT_ASSERT(pUndoManager); // view #1 ViewCallback aView1; int nView1 = SfxLokHelper::getView(); SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1); // view #2 SfxLokHelper::createView(); ViewCallback aView2; int nView2 = SfxLokHelper::getView(); pModelObj->initializeForTiledRendering(uno::Sequence()); SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2); // text edit a cell in view #1 SfxLokHelper::setView(nView1); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN); Scheduler::ProcessEventsToIdle(); // check that undo action count in not 0 CPPUNIT_ASSERT(pUndoManager->GetUndoActionCount() == 1); // try to execute undo in view #2 SfxLokHelper::setView(nView2); comphelper::dispatchCommand(".uno:Undo", {}); Scheduler::ProcessEventsToIdle(); // check that undo has not been executed on view #2 CPPUNIT_ASSERT(pUndoManager->GetUndoActionCount() == 1); // try to execute undo in view #2 in repair mode SfxLokHelper::setView(nView2); uno::Sequence aPropertyValues(comphelper::InitPropertySequence( { {"Repair", uno::makeAny(true)} })); comphelper::dispatchCommand(".uno:Undo", aPropertyValues); Scheduler::ProcessEventsToIdle(); // check that undo has been executed on view #2 in repair mode CPPUNIT_ASSERT(pUndoManager->GetUndoActionCount() == 0); mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } } CPPUNIT_TEST_SUITE_REGISTRATION(ScTiledRenderingTest); CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */