/* -*- 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/.
*/
namespace
{
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testStatusBarPageNumber)
{ // Given a document with 2 pages, first view on page 1, second view on page 2:
SwXTextDocument* pXTextDocument = createDoc(); int nView1 = SfxLokHelper::getView();
SwWrtShell* pWrtShell1 = getSwDocShell()->GetWrtShell();
pWrtShell1->InsertPageBreak();
SwRootFrame* pLayout = pWrtShell1->getIDocumentLayoutAccess().GetCurrentLayout();
SwFrame* pPage1 = pLayout->GetLower();
CPPUNIT_ASSERT(pPage1);
SwFrame* pPage2 = pPage1->GetNext();
CPPUNIT_ASSERT(pPage2);
SfxLokHelper::createView(); int nView2 = SfxLokHelper::getView();
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
SfxLokHelper::setView(nView1);
SwTestViewCallback aView1;
pWrtShell1->SttEndDoc(/*bStt=*/true);
pWrtShell1->Insert(u"start"_ustr);
pWrtShell1->GetView().SetVisArea(pPage1->getFrameArea().SVRect());
SfxLokHelper::setView(nView2);
SwTestViewCallback aView2;
SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell();
pWrtShell2->SttEndDoc(/*bStt=*/false);
pWrtShell2->Insert(u"end"_ustr);
pWrtShell2->GetView().SetVisArea(pPage2->getFrameArea().SVRect());
{ // Listen to StatePageNumber changes in view 2:
SfxViewFrame& rFrame = pWrtShell2->GetView().GetViewFrame();
SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(&rFrame);
uno::Reference<util::XURLTransformer> xParser(util::URLTransformer::create(m_xContext));
util::URL aCommandURL;
aCommandURL.Complete = ".uno:StatePageNumber";
xParser->parseStrict(aCommandURL); const SfxSlot* pSlot = rSlotPool.GetUnoSlot(aCommandURL.Path);
rFrame.GetBindings().GetDispatch(pSlot, aCommandURL, false);
}
aView2.m_aStateChanges.clear();
// When deleting a character in view 2 and processing jobs with view 1 set to active:
pWrtShell2->DelLeft();
SfxLokHelper::setView(nView1);
pWrtShell2->GetView().GetViewFrame().GetBindings().GetTimer().Invoke(); // Once more to hit the pImpl->bMsgDirty = false case in SfxBindings::NextJob_Impl().
pWrtShell2->GetView().GetViewFrame().GetBindings().GetTimer().Invoke();
// Then make sure the page number in view 2 is correct: // FIXME this should not happen, but it does from time to time. if (aView2.m_aStateChanges.empty())
{ return;
}
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aView2.m_aStateChanges.size()); // Without the accompanying fix in place, this test would have failed with: // - Expected: .uno:StatePageNumber=Page 2 of 2 // - Actual : .uno:StatePageNumber=Page 1 of 2 // i.e. view 2 got the page number of view 1.
CPPUNIT_ASSERT_EQUAL(".uno:StatePageNumber=Page 2 of 2"_ostr, aView2.m_aStateChanges[0]);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPasteInvalidateNumRules)
{ // Given a document with 3 pages: first page is ~empty, then page break, then pages 2 & 3 have // bullets:
createDoc("numrules.odt");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
pWrtShell->SttEndDoc(/*bStt=*/true);
pWrtShell->Down(/*bSelect=*/false);
pWrtShell->Insert(u"test"_ustr);
pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 4, /*bBasicCall=*/false);
dispatchCommand(mxComponent, u".uno:Cut"_ustr, {});
m_aInvalidations = tools::Rectangle();
m_bFullInvalidateSeen = false;
// When pasting at the end of page 1:
dispatchCommand(mxComponent, u".uno:PasteUnformatted"_ustr, {});
// Then make sure we only invalidate page 1, not page 2 or page 3:
CPPUNIT_ASSERT(!m_bFullInvalidateSeen);
SwRootFrame* pLayout = pWrtShell->GetLayout();
SwFrame* pPage1 = pLayout->GetLower();
CPPUNIT_ASSERT(m_aInvalidations.Overlaps(pPage1->getFrameArea().SVRect()));
SwFrame* pPage2 = pPage1->GetNext(); // Without the accompanying fix in place, this test would have failed, we invalidated page 2 and // page 3 as well.
CPPUNIT_ASSERT(!m_aInvalidations.Overlaps(pPage2->getFrameArea().SVRect()));
SwFrame* pPage3 = pPage2->GetNext();
CPPUNIT_ASSERT(!m_aInvalidations.Overlaps(pPage3->getFrameArea().SVRect()));
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPasteInvalidateNumRulesBullet)
{ // Given a document with 3 pages: first page is ~empty, then page break, then pages 2 & 3 have // bullets:
createDoc("numrules.odt");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
pWrtShell->SttEndDoc(/*bStt=*/true);
pWrtShell->Down(/*bSelect=*/false);
pWrtShell->Insert(u"test"_ustr);
pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 4, /*bBasicCall=*/false);
dispatchCommand(mxComponent, u".uno:Cut"_ustr, {});
dispatchCommand(mxComponent, u".uno:DefaultBullet"_ustr, {});
m_aInvalidations = tools::Rectangle();
m_bFullInvalidateSeen = false;
// When pasting at the end of page 1, in a paragraph that is a bullet (a list, but not a // numbering):
dispatchCommand(mxComponent, u".uno:PasteUnformatted"_ustr, {});
// Then make sure we only invalidate page 1, not page 2 or page 3:
CPPUNIT_ASSERT(!m_bFullInvalidateSeen);
SwRootFrame* pLayout = pWrtShell->GetLayout();
SwFrame* pPage1 = pLayout->GetLower();
CPPUNIT_ASSERT(m_aInvalidations.Overlaps(pPage1->getFrameArea().SVRect()));
SwFrame* pPage2 = pPage1->GetNext(); // Without the accompanying fix in place, this test would have failed, we invalidated page 2 and // page 3 as well.
CPPUNIT_ASSERT(!m_aInvalidations.Overlaps(pPage2->getFrameArea().SVRect()));
SwFrame* pPage3 = pPage2->GetNext();
CPPUNIT_ASSERT(!m_aInvalidations.Overlaps(pPage3->getFrameArea().SVRect()));
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAsyncLayout)
{ // Given a document with 3 pages, the first page is visible:
createDoc();
SwTestViewCallback aView;
SwDocShell* pDocShell = getSwDocShell();
SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
pWrtShell->InsertPageBreak();
pWrtShell->InsertPageBreak();
SwRootFrame* pLayout = pWrtShell->GetLayout();
SwPageFrame* pPage1 = pLayout->GetLower()->DynCastPageFrame();
pWrtShell->setLOKVisibleArea(pPage1->getFrameArea().SVRect());
// When all pages get invalidated:
pWrtShell->StartAllAction();
pPage1->InvalidateContent();
SwPageFrame* pPage2 = pPage1->GetNext()->DynCastPageFrame();
pPage2->InvalidateContent();
SwPageFrame* pPage3 = pPage2->GetNext()->DynCastPageFrame();
pPage3->InvalidateContent();
pWrtShell->EndAllAction();
// Then make sure only the first page gets a synchronous layout:
CPPUNIT_ASSERT(!pPage1->IsInvalidContent());
CPPUNIT_ASSERT(pPage2->IsInvalidContent());
CPPUNIT_ASSERT(pPage3->IsInvalidContent());
// And then processing all idle events:
Scheduler::ProcessEventsToIdle();
// Then make sure all pages get an async layout:
CPPUNIT_ASSERT(!pPage1->IsInvalidContent());
CPPUNIT_ASSERT(!pPage2->IsInvalidContent());
CPPUNIT_ASSERT(!pPage3->IsInvalidContent());
}
/// Test callback that works with comphelper::LibreOfficeKit::setAnyInputCallback(). class AnyInputCallback final
{ public: staticbool callback(void* /*pData*/, int /*nPriority*/) { return true; }
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAnyInput)
{ // Given a document with 3 pages, the first page is visible:
createDoc();
SwTestViewCallback aView;
SwDocShell* pDocShell = getSwDocShell();
SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
pWrtShell->InsertPageBreak();
pWrtShell->InsertPageBreak();
SwRootFrame* pLayout = pWrtShell->GetLayout();
SwPageFrame* pPage1 = pLayout->GetLower()->DynCastPageFrame();
pWrtShell->setLOKVisibleArea(pPage1->getFrameArea().SVRect());
// When all pages get invalidated:
pWrtShell->StartAllAction();
pPage1->InvalidateContent();
SwPageFrame* pPage2 = pPage1->GetNext()->DynCastPageFrame();
pPage2->InvalidateContent();
SwPageFrame* pPage3 = pPage2->GetNext()->DynCastPageFrame();
pPage3->InvalidateContent();
pWrtShell->EndAllAction();
// Then make sure sync layout calculates page 1:
CPPUNIT_ASSERT(!pPage1->IsInvalidContent());
CPPUNIT_ASSERT(pPage2->IsInvalidContent());
CPPUNIT_ASSERT(pPage3->IsInvalidContent());
// And when doing one idle layout:
AnyInputCallback aAnyInput;
pWrtShell->LayoutIdle();
// Then make sure async layout calculates page 2:
CPPUNIT_ASSERT(!pPage1->IsInvalidContent());
CPPUNIT_ASSERT(!pPage2->IsInvalidContent()); // Without the fix in place, async layout calculated all pages, even if there were pending input // events.
CPPUNIT_ASSERT(pPage3->IsInvalidContent());
Scheduler::ProcessEventsToIdle();
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSignatureState)
{ // Given a document with a signature where the digest matches:
SwXTextDocument* pXTextDocument = createDoc("signed-doc.odt");
// When initializing tiled rendering with an author name:
uno::Sequence<beans::PropertyValue> aPropertyValues
= { comphelper::makePropertyValue(".uno:Author", uno::Any(u"A"_ustr)) };
pXTextDocument->initializeForTiledRendering(aPropertyValues);
SignatureState eState = getSwDocShell()->GetDocumentSignatureState();
// Then make sure the signature state is unchanged: // Without the accompanying fix in place, this test would have failed with: // - Expected: 4 (NOTVALIDATED) // - Actual : 3 (INVALID) // i.e. the doc was modified by the time the signature state was calculated.
CPPUNIT_ASSERT_EQUAL(SignatureState::NOTVALIDATED, eState);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testFormatInsertStartList)
{ // Given a document containing a list where the text has a changed font
SwXTextDocument* pXTextDocument = createDoc("format-insert-list.docx");
VclPtr<vcl::Window> pDocWindow = pXTextDocument->getDocWindow();
SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
assert(pView);
// Insert a string at the beginning of a list item
pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, u"a"_ustr);
pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, u""_ustr);
Scheduler::ProcessEventsToIdle();
// The inserted text should have the same font as the rest
std::unique_ptr<SvxFontItem> pFontItem;
pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_FONT, pFontItem);
CPPUNIT_ASSERT(pFontItem);
CPPUNIT_ASSERT_EQUAL(u"Calibri"_ustr, pFontItem->GetFamilyName()); // Without the accompanying fix in place, this test fails with: // - Expected: Calibri // - Actual : MS Sans Serif
}
/// Job on the main loop that switches to the first view. class ViewSwitcher
{ public:
DECL_STATIC_LINK(ViewSwitcher, SwitchView, void*, void);
};
// When exporting to PDF on the second view and a job on the main loop that switches to the // first view:
uno::Sequence<beans::PropertyValue> aPropertyValues = {
comphelper::makePropertyValue("SynchronMode", false),
comphelper::makePropertyValue("URL", maTempFile.GetURL()),
};
comphelper::dispatchCommand(".uno:ExportDirectToPDF", xFrame2, aPropertyValues);
Application::PostUserEvent(LINK(nullptr, ViewSwitcher, SwitchView), nullptr);
Scheduler::ProcessEventsToIdle();
// Then make sure the callback is invoked exactly on the second view: // Without the accompanying fix in place, this test failed, as the callback was invoked on the // first view.
CPPUNIT_ASSERT(aView1.m_aExportFile.isEmpty());
CPPUNIT_ASSERT(!aView2.m_aExportFile.isEmpty());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testLoadVisibleArea)
{ // Given a document with 3 pages, the LOK visible area at load time is set to the first page:
awt::Rectangle aVisibleArea{ 0, 0, 12240, 15840 };
comphelper::LibreOfficeKit::setInitialClientVisibleArea(aVisibleArea);
comphelper::ScopeGuard g([] { comphelper::LibreOfficeKit::setInitialClientVisibleArea({}); });
// When loading that document:
OUString aURL = createFileURL(u"3pages.odt");
UnoApiXmlTest::loadFromURL(aURL);
// Then make sure only the first page is laid out:
SwDocShell* pDocShell = getSwDocShell();
SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
SwRootFrame* pLayout = pWrtShell->GetLayout();
SwPageFrame* pPage1 = pLayout->GetLower()->DynCastPageFrame();
CPPUNIT_ASSERT(!pPage1->IsInvalidContent());
SwPageFrame* pPage2 = pPage1->GetNext()->DynCastPageFrame(); // Without the accompanying fix in place, this test failed, as the entire document was laid out // before the loading finished.
CPPUNIT_ASSERT(pPage2->IsInvalidContent());
SwPageFrame* pPage3 = pPage2->GetNext()->DynCastPageFrame();
CPPUNIT_ASSERT(pPage3->IsInvalidContent());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChangesPerViewEnableOne)
{ // Given a document with two views:
SwXTextDocument* pXTextDocument = createDoc();
CPPUNIT_ASSERT(pXTextDocument);
SwTestViewCallback aView1; int nView1 = SfxLokHelper::getView();
SfxLokHelper::createView();
SwTestViewCallback aView2; int nView2 = SfxLokHelper::getView();
// When recording changes in view1:
SfxLokHelper::setView(nView1);
aView1.m_aStateChanges.clear();
aView2.m_aStateChanges.clear();
dispatchCommand(mxComponent, ".uno:TrackChangesInThisView", {});
// Then make sure view1 gets a state track changes state change, but not view2: // Filter out .uno:ModifiedStatus=true, which is not interesting here.
std::vector<OString> aRecord1 = FilterStateChanges(aView1.m_aStateChanges, ".uno:TrackChanges");
CPPUNIT_ASSERT(!aRecord1.empty());
std::vector<OString> aRecord2 = FilterStateChanges(aView2.m_aStateChanges, ".uno:TrackChanges");
CPPUNIT_ASSERT(aRecord2.empty());
// And given a reset state (both view1 and view2 recording is disabled):
uno::Sequence<beans::PropertyValue> aArgs = {
comphelper::makePropertyValue("TrackChanges", false),
};
dispatchCommand(mxComponent, ".uno:TrackChanges", aArgs);
// When recording changes in view2:
SfxLokHelper::setView(nView2);
aView1.m_aStateChanges.clear();
aView2.m_aStateChanges.clear();
dispatchCommand(mxComponent, ".uno:TrackChangesInThisView", {});
// Then make sure view2 gets a 'track changes is now on' state change, but not view1:
aRecord1 = FilterStateChanges(aView1.m_aStateChanges, ".uno:TrackChanges=true");
CPPUNIT_ASSERT(aRecord1.empty());
aRecord2 = FilterStateChanges(aView2.m_aStateChanges, ".uno:TrackChanges=true");
CPPUNIT_ASSERT(!aRecord2.empty());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChangesPerViewEnableBoth)
{ // Given a document with 2 views, view1 record changes:
SwXTextDocument* pXTextDocument = createDoc();
CPPUNIT_ASSERT(pXTextDocument);
SwTestViewCallback aView1; int nView1 = SfxLokHelper::getView();
SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
SfxLokHelper::createView();
SwTestViewCallback aView2; int nView2 = SfxLokHelper::getView();
SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
SfxLokHelper::setView(nView1);
comphelper::dispatchCommand(".uno:TrackChangesInThisView", {});
SfxLokHelper::setView(nView2);
CPPUNIT_ASSERT(pWrtShell1->GetViewOptions()->IsRedlineRecordingOn());
CPPUNIT_ASSERT(!pWrtShell2->GetViewOptions()->IsRedlineRecordingOn());
// When turning on track changes for view2:
comphelper::dispatchCommand(".uno:TrackChangesInThisView", {});
// Then make sure both views have track changes turned on:
CPPUNIT_ASSERT(pWrtShell1->GetViewOptions()->IsRedlineRecordingOn()); // Without the accompanying fix in place, this test would have failed, .uno:TrackChanges in // view2 was ignored when view1 already tracked changes.
CPPUNIT_ASSERT(pWrtShell2->GetViewOptions()->IsRedlineRecordingOn());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChangesPerViewAllToOneTransition)
{ // Given 2 views, recording is on in all views:
SwXTextDocument* pXTextDocument = createDoc();
CPPUNIT_ASSERT(pXTextDocument);
SwTestViewCallback aView1; int nView1 = SfxLokHelper::getView();
SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
SfxLokHelper::createView();
SwTestViewCallback aView2;
SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
SfxLokHelper::setView(nView1);
comphelper::dispatchCommand(".uno:TrackChangesInAllViews", {});
CPPUNIT_ASSERT(pWrtShell1->GetViewOptions()->IsRedlineRecordingOn());
CPPUNIT_ASSERT(pWrtShell2->GetViewOptions()->IsRedlineRecordingOn());
// When limiting recording to just view 1:
comphelper::dispatchCommand(".uno:TrackChangesInThisView", {});
// Then make sure view 2 has recording off:
CPPUNIT_ASSERT(pWrtShell1->GetViewOptions()->IsRedlineRecordingOn()); // Without the accompanying fix in place, this test would have failed, // .uno:TrackChangesInThisView didn't turn off recording for view 2.
CPPUNIT_ASSERT(!pWrtShell2->GetViewOptions()->IsRedlineRecordingOn());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChangesPerViewInsert)
{ // Given 2 views, view 1 records changes, view does not record changes:
SwXTextDocument* pXTextDocument = createDoc();
CPPUNIT_ASSERT(pXTextDocument);
SwTestViewCallback aView1; int nView1 = SfxLokHelper::getView();
SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
pWrtShell1->Insert(u"X"_ustr);
SfxLokHelper::createView();
SwTestViewCallback aView2; int nView2 = SfxLokHelper::getView();
SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
SfxLokHelper::setView(nView1);
comphelper::dispatchCommand(".uno:TrackChangesInThisView", {});
// When view 1 types:
pWrtShell1->SttEndDoc(/*bStt=*/true);
pWrtShell1->Insert(u"A"_ustr); // Then make sure a redline is created:
CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), pWrtShell1->GetRedlineCount());
// When view 2 types:
SfxLokHelper::setView(nView2);
pWrtShell2->SttEndDoc(/*bStt=*/false);
pWrtShell2->Insert(u"Z"_ustr); // Then make sure no redline is created: // Without the accompanying fix in place, this test would have failed with: // - Expected: 1 // - Actual : 2 // i.e. the insertion in view 2 was recorded.
CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), pWrtShell2->GetRedlineCount());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChangesPerViewDelete)
{ // Given 2 views, view 1 records changes, view does not record changes:
SwXTextDocument* pXTextDocument = createDoc();
CPPUNIT_ASSERT(pXTextDocument);
SwTestViewCallback aView1; int nView1 = SfxLokHelper::getView();
SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
pWrtShell1->Insert(u"test"_ustr);
SfxLokHelper::createView();
SwTestViewCallback aView2; int nView2 = SfxLokHelper::getView();
SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
SfxLokHelper::setView(nView1);
comphelper::dispatchCommand(".uno:TrackChangesInThisView", {});
// When view 1 deletes:
pWrtShell1->SttEndDoc(/*bStt=*/true);
pWrtShell1->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 1, /*bBasicCall=*/false);
pWrtShell1->DelRight(); // Then make sure a redline is created:
CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), pWrtShell1->GetRedlineCount());
// When view 2 deletes:
SfxLokHelper::setView(nView2);
pWrtShell2->SttEndDoc(/*bStt=*/false);
pWrtShell2->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 1, /*bBasicCall=*/false);
pWrtShell2->DelLeft(); // Then make sure no redline is created: // Without the accompanying fix in place, this test would have failed with: // - Expected: 1 // - Actual : 2 // i.e. the deletion in view 2 was recorded.
CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), pWrtShell2->GetRedlineCount());
}
// When view 1 types:
pWrtShell1->SttEndDoc(/*bStt=*/true);
pWrtShell1->Insert(u"A"_ustr); // Then make sure a redline is created:
CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), pWrtShell1->GetRedlineCount());
// When view 2 types:
SfxLokHelper::setView(nView2);
pWrtShell2->SttEndDoc(/*bStt=*/false);
pWrtShell2->Insert(u"Z"_ustr); // Without the accompanying fix in place, this test would have failed with: // - Expected: 2 // - Actual : 1 // i.e. track changes recording was unconditionally per-view.
CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(2), pWrtShell2->GetRedlineCount());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChangesStates)
{ // Given a document with 2 views:
SwXTextDocument* pXTextDocument = createDoc();
CPPUNIT_ASSERT(pXTextDocument);
SwTestViewCallback aView1; int nView1 = SfxLokHelper::getView();
SwView* pView1 = pXTextDocument->GetDocShell()->GetView();
SfxLokHelper::createView();
SwTestViewCallback aView2;
SwView* pView2 = pXTextDocument->GetDocShell()->GetView();
SfxLokHelper::setView(nView1);
// When enabling recording in view1 only:
pView1->GetViewFrame().GetDispatcher()->Execute(FN_TRACK_CHANGES_IN_THIS_VIEW,
SfxCallMode::SYNCHRON);
// Then make sure the "is record", "is record for this view on" and "is record for all views on" // statuses are correct:
std::unique_ptr<SfxPoolItem> pItem;
pView1->GetViewFrame().GetBindings().QueryState(FN_REDLINE_ON, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_REDLINE_ON), pItem->Which()); // Without the accompanying fix in place, this test would have failed, enabling recording for // this view didn't enable the toolbar button in the same view, which looked confusing.
CPPUNIT_ASSERT(dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView1->GetViewFrame().GetBindings().QueryState(FN_TRACK_CHANGES_IN_THIS_VIEW, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_TRACK_CHANGES_IN_THIS_VIEW), pItem->Which());
CPPUNIT_ASSERT(dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView1->GetViewFrame().GetBindings().QueryState(FN_TRACK_CHANGES_IN_ALL_VIEWS, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_TRACK_CHANGES_IN_ALL_VIEWS), pItem->Which());
CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView2->GetViewFrame().GetBindings().QueryState(FN_REDLINE_ON, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_REDLINE_ON), pItem->Which());
CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView2->GetViewFrame().GetBindings().QueryState(FN_TRACK_CHANGES_IN_THIS_VIEW, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_TRACK_CHANGES_IN_THIS_VIEW), pItem->Which());
CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView2->GetViewFrame().GetBindings().QueryState(FN_TRACK_CHANGES_IN_ALL_VIEWS, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_TRACK_CHANGES_IN_ALL_VIEWS), pItem->Which());
CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
// Then make sure the "is record", "is record for this view on" and "is record for all views on" // statuses are correct:
pView1->GetViewFrame().GetBindings().QueryState(FN_REDLINE_ON, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_REDLINE_ON), pItem->Which());
CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView1->GetViewFrame().GetBindings().QueryState(FN_TRACK_CHANGES_IN_THIS_VIEW, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_TRACK_CHANGES_IN_THIS_VIEW), pItem->Which());
CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView1->GetViewFrame().GetBindings().QueryState(FN_TRACK_CHANGES_IN_ALL_VIEWS, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_TRACK_CHANGES_IN_ALL_VIEWS), pItem->Which());
CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView2->GetViewFrame().GetBindings().QueryState(FN_REDLINE_ON, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_REDLINE_ON), pItem->Which());
CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView2->GetViewFrame().GetBindings().QueryState(FN_TRACK_CHANGES_IN_THIS_VIEW, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_TRACK_CHANGES_IN_THIS_VIEW), pItem->Which());
CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView2->GetViewFrame().GetBindings().QueryState(FN_TRACK_CHANGES_IN_ALL_VIEWS, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_TRACK_CHANGES_IN_ALL_VIEWS), pItem->Which());
CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
// When enabling recording in all views:
pView1->GetViewFrame().GetDispatcher()->Execute(FN_TRACK_CHANGES_IN_ALL_VIEWS,
SfxCallMode::SYNCHRON);
// Then make sure the "is record", "is record for this view on" and "is record for all views on" // statuses are correct:
pView1->GetViewFrame().GetBindings().QueryState(FN_REDLINE_ON, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_REDLINE_ON), pItem->Which());
CPPUNIT_ASSERT(dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView1->GetViewFrame().GetBindings().QueryState(FN_TRACK_CHANGES_IN_THIS_VIEW, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_TRACK_CHANGES_IN_THIS_VIEW), pItem->Which());
CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView1->GetViewFrame().GetBindings().QueryState(FN_TRACK_CHANGES_IN_ALL_VIEWS, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_TRACK_CHANGES_IN_ALL_VIEWS), pItem->Which());
CPPUNIT_ASSERT(dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView2->GetViewFrame().GetBindings().QueryState(FN_REDLINE_ON, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_REDLINE_ON), pItem->Which());
CPPUNIT_ASSERT(dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView2->GetViewFrame().GetBindings().QueryState(FN_TRACK_CHANGES_IN_THIS_VIEW, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_TRACK_CHANGES_IN_THIS_VIEW), pItem->Which());
CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
pView2->GetViewFrame().GetBindings().QueryState(FN_TRACK_CHANGES_IN_ALL_VIEWS, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_TRACK_CHANGES_IN_ALL_VIEWS), pItem->Which());
CPPUNIT_ASSERT(dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testControlCodesCursor)
{ // Given a document with hidden formatting marks:
SwXTextDocument* pXTextDocument = createDoc();
CPPUNIT_ASSERT(pXTextDocument);
SwTestViewCallback aView1;
aView1.m_bCursorVisible = false;
// When showing formatting marks:
dispatchCommand(mxComponent, ".uno:ControlCodes", {});
// Then make sure this doesn't result in a LOK_CALLBACK_CURSOR_VISIBLE callback: // Without the accompanying fix in place, this test would have failed, the view jumped to the // cursor when showing formatting marks.
CPPUNIT_ASSERT(!aView1.m_bCursorVisible);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChangesInsertUndo)
{ // Given a document with 2 views, first view enables redline record for that view only:
SwXTextDocument* pXTextDocument = createDoc();
CPPUNIT_ASSERT(pXTextDocument);
SwTestViewCallback aView1; int nView1 = SfxLokHelper::getView();
SwView* pView1 = pXTextDocument->GetDocShell()->GetView();
SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
SfxLokHelper::createView();
SwTestViewCallback aView2;
SfxLokHelper::setView(nView1);
comphelper::dispatchCommand(".uno:TrackChangesInThisView", {});
// When typing and undoing:
pWrtShell1->Insert(u"A"_ustr);
pWrtShell1->Undo();
// Then make sure the record mode doesn't change:
std::unique_ptr<SfxPoolItem> pItem;
pView1->GetViewFrame().GetBindings().QueryState(FN_TRACK_CHANGES_IN_THIS_VIEW, pItem);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_TRACK_CHANGES_IN_THIS_VIEW), pItem->Which()); // Without the accompanying fix in place, this test would have failed, undo changed "this view" // record mode to "all views", which is unexpected.
CPPUNIT_ASSERT(dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCommentsOnLoad)
{ // Given a document of 3 pages, with a small enough visible area that document load doesn't lay // out the entire document:
awt::Rectangle aVisibleArea{ 0, 0, 12240, 15840 };
comphelper::LibreOfficeKit::setInitialClientVisibleArea(aVisibleArea);
comphelper::ScopeGuard g([] { comphelper::LibreOfficeKit::setInitialClientVisibleArea({}); });
OUString aURL = createFileURL(u"comments-on-load.docx");
loadFromURL(aURL);
SwXTextDocument* pXTextDocument = getSwTextDoc();
SwTestViewCallback aView;
tools::JsonWriter aWriter;
// When getting the list of comments from the document + listening for notifications from idle // layout:
pXTextDocument->getPostIts(aWriter);
Scheduler::ProcessEventsToIdle();
// Then make sure that: // 1) We test the interesting scenario, so getPostIts() reports 0 comments and // 2) A callback is emitted to notify about the comment on the last page once user events are // processed.
OString aPostIts = aWriter.finishAndGetAsOString();
std::stringstream aStream(aPostIts.getStr());
boost::property_tree::ptree aTree;
boost::property_tree::read_json(aStream, aTree);
size_t nCommentCount = aTree.get_child("comments").size();
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), nCommentCount);
CPPUNIT_ASSERT_EQUAL(1, aView.m_nCommentCallbackCount); // Without the accompanying fix in place, this test would have failed with: // uncaught exception of type std::exception (or derived). // - No such node (action) // i.e. there was no notification about the comment that was positioned by the user event, // seemingly the comment was load on load (it was there, but not visible). auto aAction = aView.m_aComment.get_child("action").get_value<std::string>();
CPPUNIT_ASSERT_EQUAL(std::string("Add"), aAction);
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSpellcheckVisibleArea)
{ // Given a document with 3 pages, the first page is visible:
OUString aURL = createFileURL(u"3pages.odt");
UnoApiXmlTest::loadFromURL(aURL);
SwDocShell* pDocShell = getSwDocShell();
SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
SwRootFrame* pLayout = pWrtShell->GetLayout();
SwPageFrame* pPage1 = pLayout->GetLower()->DynCastPageFrame();
pWrtShell->setLOKVisibleArea(pPage1->getFrameArea().SVRect());
pPage1->InvalidateAll();
SwPageFrame* pPage2 = pPage1->GetNext()->DynCastPageFrame();
pPage2->InvalidateAll();
SwPageFrame* pPage3 = pPage2->GetNext()->DynCastPageFrame();
pPage3->InvalidateAll();
// When doing idle layout and it's interrupted:
AnyInputCallback aAnyInput;
pWrtShell->LayoutIdle();
// Then make sure the visible area is spellchecked, but not the rest:
CPPUNIT_ASSERT(!pPage1->IsInvalidSpelling());
CPPUNIT_ASSERT(pPage2->IsInvalidSpelling());
CPPUNIT_ASSERT(pPage3->IsInvalidSpelling());
}
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testIdleLayoutShape)
{ // Given a loaded document with a defined viewport:
awt::Rectangle aVisibleArea{ 0, 0, 12240, 15840 };
comphelper::LibreOfficeKit::setInitialClientVisibleArea(aVisibleArea);
comphelper::ScopeGuard g([] { comphelper::LibreOfficeKit::setInitialClientVisibleArea({}); });
OUString aURL = createFileURL(u"3pages-shape.odt");
UnoApiXmlTest::loadFromURL(aURL);
// Then make sure that the draw task started during idle layout has no high priority, either:
SdrPaintView* pDrawView = pWrtShell->Imp()->GetDrawView();
CPPUNIT_ASSERT(pDrawView); const Idle& rDrawIdle = pDrawView->GetComeBackIdle();
CPPUNIT_ASSERT(rDrawIdle.IsActive()); // Without the accompanying fix in place, this test would have failed with: // - Expected greater than: 4 // - Actual : 4 // i.e. the priority was TaskPriority::REPAINT instead of TaskPriority::DEFAULT_IDLE.
CPPUNIT_ASSERT_GREATER(TaskPriority::REPAINT, rDrawIdle.GetPriority());
}
}
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.