/* -*- 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/.
*/
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRegisterCallback)
{
createDoc("dummy.fodt");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell()); // Insert a character at the beginning of the document.
pWrtShell->Insert(u"x"_ustr);
Scheduler::ProcessEventsToIdle();
// Check that the top left 256x256px tile would be invalidated.
CPPUNIT_ASSERT(!m_aInvalidation.IsEmpty());
tools::Rectangle aTopLeft(0, 0, 256*15, 256*15); // 1 px = 15 twips, assuming 96 DPI.
CPPUNIT_ASSERT(m_aInvalidation.Overlaps(aTopLeft));
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPostKeyEvent)
{
createDoc("dummy.fodt");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false); // Did we manage to go after the first character?
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pShellCursor->GetPoint()->GetContentIndex());
emulateTyping(u"x"); // Did we manage to insert the character after the first one?
CPPUNIT_ASSERT_EQUAL(u"Axaa bbb."_ustr, pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPostMouseEvent)
{
SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false); // Did we manage to go after the first character?
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pShellCursor->GetPoint()->GetContentIndex());
Point aStart = pShellCursor->GetSttPos();
aStart.setX(aStart.getX() - 1000);
pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
Scheduler::ProcessEventsToIdle(); // The new cursor position must be before the first word.
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), pShellCursor->GetPoint()->GetContentIndex());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSetTextSelection)
{
SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); // Move the cursor into the second word.
pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 5, /*bBasicCall=*/false); // Create a selection on the word.
pWrtShell->SelWrd();
SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false); // Did we indeed manage to select the second word?
CPPUNIT_ASSERT_EQUAL(u"bbb"_ustr, pShellCursor->GetText());
// Now use setTextSelection() to move the start of the selection 1000 twips left.
Point aStart = pShellCursor->GetSttPos();
aStart.setX(aStart.getX() - 1000);
pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_START, aStart.getX(), aStart.getY()); // The new selection must include the first word, too -- but not the ending dot.
CPPUNIT_ASSERT_EQUAL(u"Aaa bbb"_ustr, pShellCursor->GetText());
// Next: test that LOK_SETTEXTSELECTION_RESET + LOK_SETTEXTSELECTION_END can be used to create a selection.
pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_RESET, aStart.getX(), aStart.getY());
pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_END, aStart.getX() + 1000, aStart.getY());
CPPUNIT_ASSERT_EQUAL(u"Aaa b"_ustr, pShellCursor->GetText());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetTextSelection)
{
SwXTextDocument* pXTextDocument = createDoc("shape-with-text.fodt"); // No crash, just empty output for unexpected mime type.
CPPUNIT_ASSERT_EQUAL(OString(), apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "foo/bar"_ostr));
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); // Move the cursor into the first word.
pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2, /*bBasicCall=*/false); // Create a selection by on the word.
pWrtShell->SelWrd();
// Make sure that we selected text from the body text.
CPPUNIT_ASSERT_EQUAL("Hello"_ostr, apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8"_ostr));
// Make sure we produce something for HTML.
CPPUNIT_ASSERT(!apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/html"_ostr).isEmpty());
// Now select some shape text and check again.
SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
SdrObject* pObject = pPage->GetObj(0);
SdrView* pView = pWrtShell->GetDrawView();
pView->SdrBeginTextEdit(pObject);
CPPUNIT_ASSERT(pView->GetTextEditObject());
EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
ESelection aWordSelection(0, 0, 0, 5);
rEditView.SetSelection(aWordSelection);
CPPUNIT_ASSERT_EQUAL("Shape"_ostr, apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8"_ostr));
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetTextSelectionLineLimit)
{ static OStringLiteral sOriginalText(u8"Estonian employs the Latin script as the basis for its alphabet, which adds the letters ä, ö, ü, and õ, plus the later additions š and ž. The letters c, q, w, x and y are limited to proper names of foreign origin, and f, z, š, and ž appear in loanwords and foreign names only. Ö and Ü are pronounced similarly to their equivalents in Swedish and German. Unlike in standard German but like Swedish (when followed by 'r') and Finnish, Ä is pronounced [æ], as in English mat. The vowels Ä, Ö and Ü are clearly separate phonemes and inherent in Estonian, although the letter shapes come from German. The letter õ denotes /ɤ/, unrounded /o/, or a close-mid back unrounded vowel. It is almost identical to the Bulgarian ъ /ɤ̞/ and the Vietnamese ơ, and is also used to transcribe the Russian ы."); static OStringLiteral sExpectedHtml(u8"Estonian employs the https://en.wikipedia.org/wiki/Latin_script\">Latin script</a> as the basis for <a href=\"https://en.wikipedia.org/wiki/Estonian_alphabet\">its alphabet</a>, which adds the letters <a href=\"https://en.wikipedia.org/wiki/%C3%84\"><i>ä</i></a>, <a href=\"https://en.wikipedia.org/wiki/%C3%96\"><i>ö</i></a>, <a href=\"https://en.wikipedia.org/wiki/%C3%9C\"><i>ü</i></a>, and <a href=\"https://en.wikipedia.org/wiki/%C3%95\"><i>õ</i></a>, plus the later additions <a href=\"https://en.wikipedia.org/wiki/%C5%A0\"><i>š</i></a> and <a href=\"https://en.wikipedia.org/wiki/%C5%BD\"><i>ž</i></a>. The letters <i>c</i>, <i>q</i>, <i>w</i>, <i>x</i> and <i>y</i> are limited to <a href=\"https://en.wikipedia.org/wiki/Proper_names\">proper names</a> of foreign origin, and <i>f</i>, <i>z</i>, <i>š</i>, and <i>ž</i> appear in loanwords and foreign names only. <i>Ö</i> and <i>Ü</i> are pronounced similarly to their equivalents in Swedish and German. Unlike in standard German but like Swedish (when followed by 'r') and Finnish, <i>Ä</i> is pronounced [æ], as in English <i>mat</i>. The vowels Ä, Ö and Ü are clearly separate <a href=\"https://en.wikipedia.org/wiki/Phonemes\">phonemes</a> and inherent in Estonian, although the letter shapes come from German. The letter <a href=\"https://en.wikipedia.org/wiki/%C3%95\"><i>õ</i></a> denotes /ɤ/, unrounded /o/, or a <a href=\"https://en.wikipedia.org/wiki/Close-mid_back_unrounded_vowel\">close-mid back unrounded vowel</a>. It is almost identical to the <a href=\"https://en.wikipedia.org/wiki/Bulgarian_language\">Bulgarian</a> <a href=\"https://en.wikipedia.org/wiki/%D0%AA\">ъ</a> /ɤ̞/ and the <a href=\"https://en.wikipedia.org/wiki/Vietnamese_language\">Vietnamese</a> <a href=\"https://en.wikipedia.org/wiki/%C6%A0\">ơ</a>, and is also used to transcribe the Russian <a href=\"https://en.wikipedia.org/wiki/%D0%AB\">ы</a>.");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); // Move the cursor into the first word.
pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2, /*bBasicCall=*/false); // Create a selection.
pWrtShell->SelAll();
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetTextSelectionMultiLine)
{ // Test will check if correct number of new line marks / paragraphs is generated static OStringLiteral sOriginalText(u8"Heading\n\
Let's have text; we need to be able to select the text inside the shape, but also the various individual ones too:\n\
\n\
\n\
\n\
\n\
\n\ Andthis is all for Writer shape objects\n\
Heading on second page");
static OStringLiteral sExpectedHtml(u8"Heading\n\
<p>Let's have text; we need to be able to select the text inside the shape, but also the various individual ones too:\n\
<p><br/><br/></p>\n\
<p><br/><br/></p>\n\
<p><br/><br/></p>\n\
<p><br/><br/></p>\n\
<p><br/><br/></p>\n\
<h1 class=\"western\">Andthis is all for Writer shape objects</h1>\n\
<h2 class=\"western\">Heading on second page</h2>");
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSetGraphicSelection)
{
SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
SdrObject* pObject = pPage->GetObj(0);
pWrtShell->SelectObj(Point(), 0, pObject);
SdrHdlList handleList(nullptr);
pObject->AddToHdlList(handleList); // Make sure the rectangle has 8 handles: at each corner and at the center of each edge.
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(8), handleList.GetHdlCount()); // Take the bottom center one.
SdrHdl* pHdl = handleList.GetHdl(6);
CPPUNIT_ASSERT_EQUAL(int(SdrHdlKind::Lower), static_cast<int>(pHdl->GetKind()));
tools::Rectangle aShapeBefore = pObject->GetSnapRect(); // Resize.
pXTextDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_START, pHdl->GetPos().getX(), pHdl->GetPos().getY());
pXTextDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_END, pHdl->GetPos().getX(), pHdl->GetPos().getY() + 1000);
tools::Rectangle aShapeAfter = pObject->GetSnapRect(); // Check that a resize happened, but aspect ratio is not kept.
CPPUNIT_ASSERT_EQUAL(aShapeBefore.getOpenWidth(), aShapeAfter.getOpenWidth());
CPPUNIT_ASSERT_EQUAL(aShapeBefore.getOpenHeight() + 1000, aShapeAfter.getOpenHeight());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testResetSelection)
{
SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); // Select one character.
pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 1, /*bBasicCall=*/false);
SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false); // We have a text selection.
CPPUNIT_ASSERT(pShellCursor->HasMark());
pXTextDocument->resetSelection(); // We no longer have a text selection.
CPPUNIT_ASSERT(!pShellCursor->HasMark());
SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
SdrObject* pObject = pPage->GetObj(0);
Point aPoint = pObject->GetSnapRect().Center(); // Select the shape.
pWrtShell->EnterSelFrameMode(&aPoint); // We have a graphic selection.
CPPUNIT_ASSERT(pWrtShell->IsSelFrameMode());
pXTextDocument->resetSelection(); // We no longer have a graphic selection.
CPPUNIT_ASSERT(!pWrtShell->IsSelFrameMode());
}
// check that the shape was inserted in the visible area, not outside
IDocumentDrawModelAccess &rDrawModelAccess = pWrtShell->GetDoc()->getIDocumentDrawModelAccess();
SdrPage* pPage = rDrawModelAccess.GetDrawModel()->GetPage(0);
SdrObject* pObject = pPage->GetObj(0);
// First hit, in the second paragraph, before the shape.
lcl_search(false);
CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
SwNodeOffset nActual = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
CPPUNIT_ASSERT_EQUAL(nNode + 1, nActual); /// Make sure we get search result selection for normal find as well, not only find all.
CPPUNIT_ASSERT(!m_aSearchResultSelection.empty());
// Next hit, in the shape.
lcl_search(false);
CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
// Next hit, in the shape, still.
lcl_search(false);
CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
// Last hit, in the last paragraph, after the shape.
lcl_search(false);
CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
nActual = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
CPPUNIT_ASSERT_EQUAL(nNode + 7, nActual);
// Now change direction and make sure that the first 2 hits are in the shape, but not the 3rd one.
lcl_search(true);
CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
lcl_search(true);
CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
lcl_search(true);
CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
nActual = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
CPPUNIT_ASSERT_EQUAL(nNode + 1, nActual);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchViewArea)
{
createDoc("search.odt");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); // Go to the second page, 1-based.
pWrtShell->GotoPage(2, false);
SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false); // Get the ~top left corner of the second page.
Point aPoint = pShellCursor->GetSttPos();
// Go back to the first page, search while the cursor is there, but the // visible area is the second page.
pWrtShell->GotoPage(1, false);
uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
{
{"SearchItem.SearchString", uno::Any(u"Heading"_ustr)},
{"SearchItem.Backward", uno::Any(false)},
{"SearchItem.SearchStartPointX", uno::Any(static_cast<sal_Int32>(aPoint.getX()))},
{"SearchItem.SearchStartPointY", uno::Any(static_cast<sal_Int32>(aPoint.getY()))}
}));
comphelper::dispatchCommand(u".uno:ExecuteSearch"_ustr, aPropertyValues); // This was just "Heading", i.e. SwView::SearchAndWrap() did not search from only the top of the second page.
CPPUNIT_ASSERT_EQUAL(u"Heading on second page"_ustr, pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchTextFrame)
{
createDoc("search.odt");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
{
{"SearchItem.SearchString", uno::Any(u"TextFrame"_ustr)},
{"SearchItem.Backward", uno::Any(false)},
}));
comphelper::dispatchCommand(u".uno:ExecuteSearch"_ustr, aPropertyValues); // This was empty: nothing was highlighted after searching for 'TextFrame'.
CPPUNIT_ASSERT(!m_aTextSelection.isEmpty());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchTextFrameWrapAround)
{
createDoc("search.odt");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
{
{"SearchItem.SearchString", uno::Any(u"TextFrame"_ustr)},
{"SearchItem.Backward", uno::Any(false)},
}));
comphelper::dispatchCommand(u".uno:ExecuteSearch"_ustr, aPropertyValues);
CPPUNIT_ASSERT(m_bFound);
comphelper::dispatchCommand(u".uno:ExecuteSearch"_ustr, aPropertyValues); // This failed, i.e. the second time 'not found' was reported, instead of wrapping around.
CPPUNIT_ASSERT(m_bFound);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDocumentSizeChanged)
{ // Get the current document size.
SwXTextDocument* pXTextDocument = createDoc("2-pages.odt");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
Size aSize = pXTextDocument->getDocumentSize();
// Delete the second page and see how the size changes.
pWrtShell->Down(false);
pWrtShell->DelLeft(); // Document width should not change, this was 0.
CPPUNIT_ASSERT_EQUAL(aSize.getWidth(), m_aDocumentSize.getWidth()); // Document height should be smaller now.
CPPUNIT_ASSERT(aSize.getHeight() > m_aDocumentSize.getHeight());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchAll)
{
createDoc("search.odt");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
{
{"SearchItem.SearchString", uno::Any(u"shape"_ustr)},
{"SearchItem.Backward", uno::Any(false)},
{"SearchItem.Command", uno::Any(static_cast<sal_uInt16>(SvxSearchCmd::FIND_ALL))},
}));
comphelper::dispatchCommand(u".uno:ExecuteSearch"_ustr, aPropertyValues); // This was 0; should be 2 results in the body text.
CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), m_aSearchResultSelection.size()); // Writer documents are always a single part.
CPPUNIT_ASSERT_EQUAL(0, m_aSearchResultPart[0]);
}
// This was 5, make sure that we get no notifications about selection changes during search.
CPPUNIT_ASSERT_EQUAL(0, m_nSelectionBeforeSearchResult); // But we do get the selection afterwards.
CPPUNIT_ASSERT(m_nSelectionAfterSearchResult > 0);
}
// This was 2.
CPPUNIT_ASSERT_EQUAL(0, m_nInvalidations);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPartHash)
{
SwXTextDocument* pXTextDocument = createDoc("pagedown-invalidation.odt"); int nParts = pXTextDocument->getParts(); for (int it = 0; it < nParts; it++)
{
CPPUNIT_ASSERT(!pXTextDocument->getPartHash(it).isEmpty());
}
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testMissingInvalidation)
{ // Create two views.
SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
SwTestViewCallback aView1; int nView1 = SfxLokHelper::getView();
SfxLokHelper::createView();
SwTestViewCallback aView2; int nView2 = SfxLokHelper::getView();
// First view: put the cursor into the first word.
SfxLokHelper::setView(nView1);
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
// Second view: select the first word.
SfxLokHelper::setView(nView2);
CPPUNIT_ASSERT(getSwDocShell()->GetWrtShell() != pWrtShell);
pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
pWrtShell->SelWrd();
// Now delete the selected word and make sure both views are invalidated.
Scheduler::ProcessEventsToIdle();
aView1.m_bTilesInvalidated = false;
aView2.m_bTilesInvalidated = false;
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DELETE);
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DELETE);
Scheduler::ProcessEventsToIdle();
CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
CPPUNIT_ASSERT(aView2.m_bTilesInvalidated);
}
Scheduler::ProcessEventsToIdle();
CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated); // This failed: the cursor position of view1 was only known to view2 once // it changed.
CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
// Make sure that aView1 gets a view-only selection notification, while // aView2 gets a real selection notification.
aView1.m_bOwnSelectionSet = false;
aView1.m_bViewSelectionSet = false;
aView2.m_bOwnSelectionSet = false;
aView2.m_bViewSelectionSet = false;
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); // Move the cursor into the second word.
pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 5, /*bBasicCall=*/false); // Create a selection on the word.
pWrtShell->SelWrd();
Scheduler::ProcessEventsToIdle();
SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false); // Did we indeed manage to select the second word?
CPPUNIT_ASSERT_EQUAL(u"bbb"_ustr, pShellCursor->GetText());
CPPUNIT_ASSERT(!aView1.m_bOwnSelectionSet); // This failed, aView1 did not get notification about selection changes in // aView2.
CPPUNIT_ASSERT(aView1.m_bViewSelectionSet);
CPPUNIT_ASSERT(aView2.m_bOwnSelectionSet);
CPPUNIT_ASSERT(!aView2.m_bViewSelectionSet);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeViewCursors)
{ // Load a document and create a view, so we have 2 ones.
SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
SwTestViewCallback aView1;
SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
SwTestViewCallback aView2;
SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
// Start shape text in the second view.
SdrPage* pPage = pWrtShell2->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
SdrObject* pObject = pPage->GetObj(0);
SdrView* pView = pWrtShell2->GetDrawView();
pWrtShell2->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell2->GetWin());
emulateTyping(u"x"); // Press a key in the second view, while the first one observes this.
aView1.m_bViewCursorInvalidated = false;
aView2.m_bOwnCursorInvalidated = false; const tools::Rectangle aLastOwnCursor1 = aView1.m_aOwnCursor; const tools::Rectangle aLastViewCursor1 = aView1.m_aViewCursor; const tools::Rectangle aLastOwnCursor2 = aView2.m_aOwnCursor; const tools::Rectangle aLastViewCursor2 = aView2.m_aViewCursor;
emulateTyping(u"y"); // Make sure that aView1 gets a view-only cursor notification, while // aView2 gets a real cursor notification.
CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor, aLastOwnCursor1);
CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
CPPUNIT_ASSERT(aLastViewCursor1 != aView1.m_aViewCursor);
CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
CPPUNIT_ASSERT(aLastOwnCursor2 != aView2.m_aOwnCursor);
CPPUNIT_ASSERT_EQUAL(aLastViewCursor2, aView2.m_aViewCursor);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewCursorVisibility)
{ // Load a document that has a shape and create two views.
SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
SwTestViewCallback aView1;
SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
SwTestViewCallback aView2; // This failed, initially the view cursor in the second view wasn't visible.
CPPUNIT_ASSERT(aView2.m_bViewCursorVisible);
// Click on the shape in the second view.
aView1.m_bViewCursorVisible = true;
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
SdrObject* pObject = pPage->GetObj(0);
Point aCenter = pObject->GetSnapRect().Center();
pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
Scheduler::ProcessEventsToIdle(); // Make sure the "view/text" cursor of the first view gets a notification.
CPPUNIT_ASSERT(!aView1.m_bViewCursorVisible);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewCursorCleanup)
{ // Load a document that has a shape and create two views.
SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
SwTestViewCallback aView1; int nView2 = SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
{
SwTestViewCallback aView2;
// Click on the shape in the second view.
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
SdrObject* pObject = pPage->GetObj(0);
Point aCenter = pObject->GetSnapRect().Center();
aView1.m_bGraphicViewSelection = false;
pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
Scheduler::ProcessEventsToIdle(); // Make sure there is a graphic view selection on the first view.
CPPUNIT_ASSERT(aView1.m_bGraphicViewSelection);
} // Now destroy the second view.
SfxLokHelper::destroyView(nView2);
Scheduler::ProcessEventsToIdle();
CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), SfxLokHelper::getViewsCount(0)); // Make sure that the graphic view selection on the first view is cleaned up.
CPPUNIT_ASSERT(!aView1.m_bGraphicViewSelection);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewLock)
{ // Load a document that has a shape and create two views.
SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
SwTestViewCallback aView1;
SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
SwTestViewCallback aView2;
// Begin text edit in the second view and assert that the first gets a lock // notification.
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
SdrObject* pObject = pPage->GetObj(0);
SdrView* pView = pWrtShell->GetDrawView();
aView1.m_bViewLock = false;
pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
CPPUNIT_ASSERT(aView1.m_bViewLock);
// End text edit in the second view, and assert that the lock is removed in // the first view.
pWrtShell->EndTextEdit();
CPPUNIT_ASSERT(!aView1.m_bViewLock);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTextEditViewInvalidations)
{ // Load a document that has a shape and create two views.
SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
SwTestViewCallback aView1;
SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
SwTestViewCallback aView2;
// Begin text edit in the second view.
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
SdrObject* pObject = pPage->GetObj(0);
SdrView* pView = pWrtShell->GetDrawView();
pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
emulateTyping(u"x");
// Assert that both views are invalidated when pressing a key while in text edit.
aView1.m_bTilesInvalidated = false;
emulateTyping(u"y");
CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
pWrtShell->EndTextEdit();
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoInvalidations)
{ // Load a document and create two views.
SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
SwTestViewCallback aView1; int nView1 = SfxLokHelper::getView();
SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
SwTestViewCallback aView2;
SfxLokHelper::setView(nView1);
// Insert a character the end of the document.
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->EndOfSection();
emulateTyping(u"c"); // ProcessEventsToIdle resets the view; set it again
SfxLokHelper::setView(nView1);
SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
CPPUNIT_ASSERT_EQUAL(u"Aaa bbb.c"_ustr, pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
// Undo and assert that both views are invalidated.
Scheduler::ProcessEventsToIdle();
aView1.m_bTilesInvalidated = false;
aView2.m_bTilesInvalidated = false;
comphelper::dispatchCommand(u".uno:Undo"_ustr, {});
Scheduler::ProcessEventsToIdle();
CPPUNIT_ASSERT(aView1.m_bTilesInvalidated); // Undo was dispatched on the first view, this second view was not invalidated.
CPPUNIT_ASSERT(aView2.m_bTilesInvalidated);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoLimiting)
{ // Load a document and create two views.
SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
SwWrtShell* pWrtShell1 = getSwDocShell()->GetWrtShell(); int nView1 = SfxLokHelper::getView(); int nView2 = SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
// Insert a character the end of the document in the second view.
SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
pWrtShell2->EndOfSection();
emulateTyping(u"c");
SwShellCursor* pShellCursor = pWrtShell2->getShellCursor(false);
CPPUNIT_ASSERT_EQUAL(u"Aaa bbb.c"_ustr, pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
// Assert that the first view can't undo, but the second view can.
CPPUNIT_ASSERT(!pWrtShell1->GetLastUndoInfo(nullptr, nullptr, &pWrtShell1->GetView()));
CPPUNIT_ASSERT(pWrtShell2->GetLastUndoInfo(nullptr, nullptr, &pWrtShell2->GetView()));
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReordering)
{ // Create two views and a document of 2 paragraphs.
SwXTextDocument* pXTextDocument = createDoc();
SwWrtShell* pWrtShell1 = getSwDocShell()->GetWrtShell(); int nView1 = SfxLokHelper::getView(); int nView2 = SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
pWrtShell2->SplitNode();
SfxLokHelper::setView(nView1);
pWrtShell1->SttEndDoc(/*bStt=*/true);
SwTextNode* pTextNode1 = pWrtShell1->GetCursor()->GetPointNode().GetTextNode(); // View 1 types into the first paragraph.
emulateTyping(u"a");
SfxLokHelper::setView(nView2);
pWrtShell2->SttEndDoc(/*bStt=*/false);
SwTextNode* pTextNode2 = pWrtShell2->GetCursor()->GetPointNode().GetTextNode(); // View 2 types into the second paragraph.
emulateTyping(u"z");
CPPUNIT_ASSERT_EQUAL(u"a"_ustr, pTextNode1->GetText());
CPPUNIT_ASSERT_EQUAL(u"z"_ustr, pTextNode2->GetText());
// When view 1 presses undo:
SfxLokHelper::setView(nView1);
dispatchCommand(mxComponent, u".uno:Undo"_ustr, {});
// Then make sure view 1's last undo action is invoked, out of order: // Without the accompanying fix in place, this test would have failed with: // - Expression: pTextNode1->GetText().isEmpty() // i.e. the "a" in the first paragraph was not removed.
CPPUNIT_ASSERT(pTextNode1->GetText().isEmpty()); // Last undo action is not invoked, as it belongs to view 2.
CPPUNIT_ASSERT_EQUAL(u"z"_ustr, pTextNode2->GetText());
SfxLokHelper::setView(nView1);
SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
SfxLokHelper::setView(nView2);
SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReorderingRedo)
{ // Create two views and a document of 2 paragraphs.
SwXTextDocument* pXTextDocument = createDoc();
SwWrtShell* pWrtShell1 = getSwDocShell()->GetWrtShell(); int nView1 = SfxLokHelper::getView(); int nView2 = SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
pWrtShell2->SplitNode();
SfxLokHelper::setView(nView1);
pWrtShell1->SttEndDoc(/*bStt=*/true);
SwTextNode* pTextNode1 = pWrtShell1->GetCursor()->GetPointNode().GetTextNode(); // View 1 types into the first paragraph, twice.
emulateTyping(u"f"); // Go to the start of the paragraph, to avoid grouping.
pWrtShell1->SttEndDoc(/*bStt=*/true);
emulateTyping(u"s");
SfxLokHelper::setView(nView2);
pWrtShell2->SttEndDoc(/*bStt=*/false);
SwTextNode* pTextNode2 = pWrtShell2->GetCursor()->GetPointNode().GetTextNode(); // View 2 types into the second paragraph.
emulateTyping(u"z");
CPPUNIT_ASSERT_EQUAL(u"sf"_ustr, pTextNode1->GetText());
CPPUNIT_ASSERT_EQUAL(u"z"_ustr, pTextNode2->GetText());
// When view 1 presses undo, twice:
SfxLokHelper::setView(nView1);
dispatchCommand(mxComponent, u".uno:Undo"_ustr, {}); // First just s(econd) is erased:
CPPUNIT_ASSERT_EQUAL(u"f"_ustr, pTextNode1->GetText());
dispatchCommand(mxComponent, u".uno:Undo"_ustr, {});
// Then make sure view 1's undo actions are invoked, out of order: // Without the accompanying fix in place, this test would have failed with: // - Expression: pTextNode1->GetText().isEmpty() // i.e. out of order undo was executed only once, not twice.
CPPUNIT_ASSERT(pTextNode1->GetText().isEmpty()); // The top undo action is not invoked, as it belongs to view 2.
CPPUNIT_ASSERT_EQUAL(u"z"_ustr, pTextNode2->GetText());
SfxLokHelper::setView(nView1);
SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
SfxLokHelper::setView(nView2);
SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReorderingRedo2)
{ // Create two views.
SwXTextDocument* pXTextDocument = createDoc();
SwWrtShell* pWrtShell1 = getSwDocShell()->GetWrtShell(); int nView1 = SfxLokHelper::getView(); int nView2 = SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
// Type in the first view.
SfxLokHelper::setView(nView1);
pWrtShell1->SttEndDoc(/*bStt=*/true);
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'f', 0);
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'f', 0);
Scheduler::ProcessEventsToIdle();
// Type to the same paragraph in the second view.
SfxLokHelper::setView(nView2);
pWrtShell2->SttEndDoc(/*bStt=*/true);
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 's', 0);
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 's', 0);
Scheduler::ProcessEventsToIdle();
// Delete in the first view and undo.
SfxLokHelper::setView(nView1);
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::BACKSPACE);
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::BACKSPACE);
Scheduler::ProcessEventsToIdle();
dispatchCommand(mxComponent, u".uno:Undo"_ustr, {});
Scheduler::ProcessEventsToIdle();
// Query the undo state, now that a "delete" is on the redo stack and an "insert" belongs to the // view on the undo stack, so the types are different.
SwUndoId nUndoId(SwUndoId::EMPTY); // Without the accompanying fix in place, this test would have failed with: // runtime error: downcast which does not point to an object of type 'const SwUndoInsert' // note: object is of type 'SwUndoDelete' // in an UBSan build.
pWrtShell1->GetLastUndoInfo(nullptr, &nUndoId, &pWrtShell1->GetView());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReorderingMulti)
{ // Create two views and a document of 2 paragraphs.
SwXTextDocument* pXTextDocument = createDoc();
SwWrtShell* pWrtShell1 = getSwDocShell()->GetWrtShell(); int nView1 = SfxLokHelper::getView(); int nView2 = SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
pWrtShell2->SplitNode();
SfxLokHelper::setView(nView1);
pWrtShell1->SttEndDoc(/*bStt=*/true);
SwTextNode* pTextNode1 = pWrtShell1->GetCursor()->GetPointNode().GetTextNode(); // View 1 types into the first paragraph.
emulateTyping(u"a");
SfxLokHelper::setView(nView2);
pWrtShell2->SttEndDoc(/*bStt=*/false);
SwTextNode* pTextNode2 = pWrtShell2->GetCursor()->GetPointNode().GetTextNode(); // View 2 types into the second paragraph, twice.
emulateTyping(u"x"); // Go to the start of the paragraph, to avoid grouping.
pWrtShell2->SttPara();
emulateTyping(u"y");
CPPUNIT_ASSERT_EQUAL(u"a"_ustr, pTextNode1->GetText());
CPPUNIT_ASSERT_EQUAL(u"yx"_ustr, pTextNode2->GetText());
// When view 1 presses undo:
SfxLokHelper::setView(nView1);
dispatchCommand(mxComponent, u".uno:Undo"_ustr, {});
// Then make sure view 1's undo action is invoked, out of order: // Without the accompanying fix in place, this test would have failed with: // - Expression: pTextNode1->GetText().isEmpty() // i.e. out of order undo was not executed, the first paragraph was still "a".
CPPUNIT_ASSERT(pTextNode1->GetText().isEmpty()); // The top 2 undo actions are not invoked, as they belong to view 2.
CPPUNIT_ASSERT_EQUAL(u"yx"_ustr, pTextNode2->GetText());
SfxLokHelper::setView(nView1);
SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
SfxLokHelper::setView(nView2);
SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoShapeLimiting)
{ // Load a document and create a view.
SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
SwWrtShell* pWrtShell1 = getSwDocShell()->GetWrtShell(); int nView1 = SfxLokHelper::getView(); int nView2 = SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
// Start shape text in the second view.
SdrPage* pPage = pWrtShell2->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
SdrObject* pObject = pPage->GetObj(0);
SdrView* pView = pWrtShell2->GetDrawView();
pWrtShell2->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell2->GetWin());
emulateTyping(u"x");
pWrtShell2->EndTextEdit();
// Assert that the first view can't and the second view can undo the insertion.
SwDoc* pDoc = getSwDocShell()->GetDoc();
sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
rUndoManager.SetView(&pWrtShell1->GetView()); // This was 1: first view could undo the change of the second view.
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), rUndoManager.GetUndoActionCount());
rUndoManager.SetView(&pWrtShell2->GetView());
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoDispatch)
{ // Load a document and create two views.
SwXTextDocument* pXTextDocument = createDoc("dummy.fodt"); int nView1 = SfxLokHelper::getView();
SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>()); int nView2 = SfxLokHelper::getView();
// Insert a character in the first view.
SfxLokHelper::setView(nView1);
emulateTyping(u"c");
// Click before the first word in the second view.
SfxLokHelper::setView(nView2);
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
Point aStart = pShellCursor->GetSttPos();
aStart.setX(aStart.getX() - 1000);
pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
Scheduler::ProcessEventsToIdle();
uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(comphelper::getProcessComponentContext());
uno::Reference<frame::XFrame> xFrame2 = xDesktop->getActiveFrame();
// Now switch back to the first view, and make sure that the active frame is updated.
SfxLokHelper::setView(nView1);
uno::Reference<frame::XFrame> xFrame1 = xDesktop->getActiveFrame(); // This failed: setView() did not update the active frame.
CPPUNIT_ASSERT(xFrame1 != xFrame2);
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoRepairDispatch)
{ // Load a document and create two views.
SwXTextDocument* pXTextDocument = createDoc("dummy.fodt"); int nView1 = SfxLokHelper::getView();
SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>()); int nView2 = SfxLokHelper::getView();
// Insert a character in the first view.
SfxLokHelper::setView(nView1);
emulateTyping(u"c");
// Assert that by default the second view can't undo the action.
SfxLokHelper::setView(nView2);
SwDoc* pDoc = getSwDocShell()->GetDoc();
sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
comphelper::dispatchCommand(u".uno:Undo"_ustr, {});
Scheduler::ProcessEventsToIdle();
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
// But the same is allowed in repair mode.
SfxLokHelper::setView(nView2);
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
{
{"Repair", uno::Any(true)}
}));
comphelper::dispatchCommand(u".uno:Undo"_ustr, aPropertyValues);
Scheduler::ProcessEventsToIdle(); // This was 1: repair mode couldn't undo the action, either.
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), rUndoManager.GetUndoActionCount());
// Make sure that the undo item remembers who created it.
SwDoc* pDoc = getSwDocShell()->GetDoc();
sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
CPPUNIT_ASSERT_EQUAL(size_t(1), rUndoManager.GetUndoActionCount());
CPPUNIT_ASSERT_EQUAL(u"Edit text of Shape 'Shape1'"_ustr, rUndoManager.GetUndoActionComment(0));
// This was -1: the view shell id for the undo action wasn't known.
CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), rUndoManager.GetUndoAction()->GetViewShellId());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeTextUndoGroupShells)
{ // Load a document and create a view.
SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
SwTestViewCallback aView1;
sal_Int32 nView1 = SfxLokHelper::getView();
// Make sure that the undo item remembers who created it.
SwDoc* pDoc = getSwDocShell()->GetDoc();
sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
CPPUNIT_ASSERT_EQUAL(size_t(0), rUndoManager.GetUndoActionCount());
CPPUNIT_ASSERT_EQUAL(size_t(1), rUndoManager.GetUndoActionCount());
CPPUNIT_ASSERT_EQUAL(u"Edit text of Shape 'Shape1'"_ustr, rUndoManager.GetUndoActionComment(0));
// This was -1: the view shell id for the (top) undo list action wasn't known.
CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), rUndoManager.GetUndoAction()->GetViewShellId());
// Create an editeng text selection in the first view.
EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
emulateTyping(u"x"); // 0th para, 0th char -> 0th para, 1st char.
ESelection aWordSelection(0, 0, 0, 1);
rEditView.SetSelection(aWordSelection);
// Create a second view, and make sure that the new view sees the same // cursor position as the old one.
SfxLokHelper::createView();
pXTextDocument->initializeForTiledRendering({});
SwTestViewCallback aView2; // Difference was 935 twips, the new view didn't see the editeng cursor of // the old one. The new difference should be <1px, but here we deal with twips.
CPPUNIT_ASSERT(std::abs(aView1.m_aOwnCursor.Top() - aView2.m_aViewCursor.Top()) < 10); // This was false, editeng text selection of the first view wasn't noticed // by the second view.
CPPUNIT_ASSERT(aView2.m_bViewSelectionSet); // This was false, the new view wasn't aware of the shape text lock created // by the old view.
CPPUNIT_ASSERT(aView2.m_bViewLock);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChanges)
{ // Load a document.
createDoc("dummy.fodt");
// Turn on track changes, type "zzz" at the end, and move to the start.
uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
xPropertySet->setPropertyValue(u"RecordChanges"_ustr, uno::Any(true));
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
SwTestViewCallback aView(pWrtShell->GetSfxViewShell());
pWrtShell->EndOfSection();
pWrtShell->Insert(u"zzz"_ustr);
pWrtShell->StartOfSection();
// Get the redline just created const SwRedlineTable& rTable = pWrtShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), rTable.size());
SwRangeRedline* pRedline = rTable[0];
// Reject the change by id, while the cursor does not cover the tracked change.
uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
{
{"RejectTrackedChange", uno::Any(o3tl::narrowing<sal_uInt16>(pRedline->GetId()))}
}));
comphelper::dispatchCommand(u".uno:RejectTrackedChange"_ustr, aPropertyValues);
Scheduler::ProcessEventsToIdle();
// Assert that the reject was performed.
SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false); // This was 'Aaa bbb.zzz', the change wasn't rejected.
CPPUNIT_ASSERT_EQUAL(u"Aaa bbb."_ustr, pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
}
// Turn on track changes and type "x".
uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
xPropertySet->setPropertyValue(u"RecordChanges"_ustr, uno::Any(true));
m_nRedlineTableSizeChanged = 0;
pWrtShell->Insert(u"x"_ustr);
// Assert that we get exactly one notification about the redline insert. // This was 0, as LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED wasn't sent.
CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableSizeChanged);
// Turn on track changes, type "xx" and delete the second one.
uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
xPropertySet->setPropertyValue(u"RecordChanges"_ustr, uno::Any(true));
pWrtShell->Insert(u"xx"_ustr);
m_nRedlineTableEntryModified = 0;
pWrtShell->DelLeft();
// Assert that we get exactly one notification about the redline update. // This was 0, as LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED wasn't sent.
CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableEntryModified);
// Turn off the change tracking mode, make some modification to left of the // redline so that its position changes
xPropertySet->setPropertyValue(u"RecordChanges"_ustr, uno::Any(false));
pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
pWrtShell->Insert(u"This text is left of the redline"_ustr);
// Position of the redline has changed => Modify callback
CPPUNIT_ASSERT_EQUAL(2, m_nRedlineTableEntryModified);
pWrtShell->DelLeft(); // Deletion also emits Modify callback
CPPUNIT_ASSERT_EQUAL(3, m_nRedlineTableEntryModified);
// Make changes to the right of the redline => no position change in redline
pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 100/*Go enough right */, /*bBasicCall=*/false);
pWrtShell->Insert(u"This text is right of the redline"_ustr);
// No Modify callbacks
CPPUNIT_ASSERT_EQUAL(3, m_nRedlineTableEntryModified);
}
// Create a second view
SfxLokHelper::createView(); int nSecondViewId = SfxLokHelper::getView();
SwTestViewCallback aView2;
{ // Give the second view different options
SwViewOption aViewOptions;
aViewOptions.SetViewMetaChars(false);
aViewOptions.SetOnlineSpell(true);
getSwDocShell()->GetWrtShell()->ApplyViewOptions(aViewOptions);
}
CPPUNIT_ASSERT_EQUAL("S;Default"_ostr, pXTextDocument->getViewRenderState());
// Switch back to the first view, and check that the options string is the same
SfxLokHelper::setView(nFirstViewId);
CPPUNIT_ASSERT_EQUAL("PS;Default"_ostr, pXTextDocument->getViewRenderState());
// Switch back to the second view, and change to dark mode
SfxLokHelper::setView(nSecondViewId);
{
SwView* pView = getSwDocShell()->GetView();
uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence({});
comphelper::dispatchCommand(u".uno:ChangeTheme"_ustr, xFrame, aPropertyValues);
}
CPPUNIT_ASSERT_EQUAL("S;Dark"_ostr, pXTextDocument->getViewRenderState()); // Switch back to the first view, and check that the options string is the same
SfxLokHelper::setView(nFirstViewId);
CPPUNIT_ASSERT_EQUAL("PS;Default"_ostr, pXTextDocument->getViewRenderState());
}
// Helper function to get a tile to a bitmap static Bitmap getTile(SwXTextDocument* pXTextDocument)
{
size_t nCanvasSize = 1024;
size_t nTileSize = 256;
std::vector<unsignedchar> aPixmap(nCanvasSize * nCanvasSize * 4, 0);
ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasSize, nCanvasSize),
Fraction(1.0), Point(), aPixmap.data());
pXTextDocument->paintTile(*pDevice, nCanvasSize, nCanvasSize, 0, 0, 15360, 7680);
pDevice->EnableMapMode(false); return pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
}
// Helper function to get a tile to a bitmap and check the pixel color static Color getTilePixelColor(SwXTextDocument* pXTextDocument, int nPixelX, int nPixelY)
{
Bitmap aBitmap = getTile(pXTextDocument);
BitmapScopedReadAccess pAccess(aBitmap);
Color aActualColor(pAccess->GetPixel(nPixelX, nPixelY)); return aActualColor;
}
// Test that changing the theme in one view doesn't change it in the other view
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testThemeViewSeparation)
{
Color aDarkColor(0x1c, 0x1c, 0x1c);
addDarkLightThemes(aDarkColor, COL_WHITE);
SwXTextDocument* pXTextDocument = createDoc(); int nFirstViewId = SfxLokHelper::getView();
SwTestViewCallback aView1; // Set first view to light scheme
{
SwView* pView = getSwDocShell()->GetView();
uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
{
{ "NewTheme", uno::Any(u"Light"_ustr) },
}
);
comphelper::dispatchCommand(u".uno:ChangeTheme"_ustr, xFrame, aPropertyValues);
} // First view is in light scheme
CPPUNIT_ASSERT_EQUAL(COL_WHITE, getTilePixelColor(pXTextDocument, 255, 255)); // Create second view
SfxLokHelper::createView(); int nSecondViewId = SfxLokHelper::getView();
SwTestViewCallback aView2; // Set second view to dark scheme
{
SwView* pView = getSwDocShell()->GetView();
uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
{
{ "NewTheme", uno::Any(u"Dark"_ustr) },
}
);
comphelper::dispatchCommand(u".uno:ChangeTheme"_ustr, xFrame, aPropertyValues);
}
CPPUNIT_ASSERT_EQUAL(aDarkColor, getTilePixelColor(pXTextDocument, 255, 255)); // First view still in light scheme
SfxLokHelper::setView(nFirstViewId);
CPPUNIT_ASSERT_EQUAL(COL_WHITE, getTilePixelColor(pXTextDocument, 255, 255)); // Second view still in dark scheme
SfxLokHelper::setView(nSecondViewId);
CPPUNIT_ASSERT_EQUAL(aDarkColor, getTilePixelColor(pXTextDocument, 255, 255)); // Switch second view back to light scheme
{
SwView* pView = getSwDocShell()->GetView();
uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
{
{ "NewTheme", uno::Any(u"Light"_ustr) },
}
);
comphelper::dispatchCommand(u".uno:ChangeTheme"_ustr, xFrame, aPropertyValues);
} // Now in light scheme
CPPUNIT_ASSERT_EQUAL(COL_WHITE, getTilePixelColor(pXTextDocument, 255, 255));
}
// Test that changing the theme in one view doesn't change it in the other view
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testInvertBackgroundViewSeparation)
{
Color aDarkColor(0x1c, 0x1c, 0x1c); if (ThemeColors::UseOnlyWhiteDocBackground())
aDarkColor = COL_WHITE;
addDarkLightThemes(aDarkColor, COL_WHITE);
SwXTextDocument* pXTextDocument = createDoc(); int nFirstViewId = SfxLokHelper::getView();
SwTestViewCallback aView1; // Set view to dark scheme
{
SwView* pView = getSwDocShell()->GetView();
uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.27 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.