sw/Library_swqahelper.mk | 1 sw/qa/extras/tiledrendering/tiledrendering.cxx | 88 ++-- sw/qa/extras/tiledrendering/tiledrendering2.cxx | 12 sw/qa/extras/tiledrendering/tiledrenderingmodeltestbase.cxx | 221 ------------ sw/qa/inc/swtestviewcallback.hxx | 70 +++ sw/qa/unit/swtestviewcallback.cxx | 205 +++++++++++ vcl/inc/qt5/QtClipboard.hxx | 2 vcl/qt5/QtClipboard.cxx | 50 ++ 8 files changed, 369 insertions(+), 280 deletions(-)
New commits: commit 5a4ca3ce934ca3f34669bc66b8941544aef1bc89 Author: Stephan Bergmann <stephan.bergm...@allotropia.de> AuthorDate: Tue Jan 7 16:21:05 2025 +0100 Commit: Stephan Bergmann <stephan.bergm...@allotropia.de> CommitDate: Tue Jan 7 17:56:12 2025 +0100 Rudimentary Emscripten copy -> paste support ...with two hacks, so that at least copying text from LO and pasting it back into LO works (instead of the copying step either hitting the > assert(pTrans); in QtClipboard::getContents in --enable-assert-always-abort builds or doing nothing in --disable-assert-always-abort builds) Change-Id: I246986fa1beaf17a8390eccb404408605e121630 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/179894 Reviewed-by: Stephan Bergmann <stephan.bergm...@allotropia.de> Tested-by: Jenkins diff --git a/vcl/inc/qt5/QtClipboard.hxx b/vcl/inc/qt5/QtClipboard.hxx index b3360744fd6a..46bdabf925d2 100644 --- a/vcl/inc/qt5/QtClipboard.hxx +++ b/vcl/inc/qt5/QtClipboard.hxx @@ -50,7 +50,7 @@ class QtClipboard final css::uno::Reference<css::datatransfer::clipboard::XClipboardOwner> m_aOwner; std::vector<css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>> m_aListeners; - static bool isOwner(const QClipboard::Mode aMode); + bool isOwner(const QClipboard::Mode aMode); static bool isSupported(const QClipboard::Mode aMode); explicit QtClipboard(OUString aModeString, const QClipboard::Mode aMode); diff --git a/vcl/qt5/QtClipboard.cxx b/vcl/qt5/QtClipboard.cxx index a61273a734bd..a8cabc7664d8 100644 --- a/vcl/qt5/QtClipboard.cxx +++ b/vcl/qt5/QtClipboard.cxx @@ -158,14 +158,31 @@ void QtClipboard::handleChanged(QClipboard::Mode aMode) osl::ClearableMutexGuard aGuard(m_aMutex); - // QtWayland will send a second change notification (seemingly without any - // trigger). And any C'n'P operation in the Qt file picker emits a signal, - // with LO still holding the clipboard ownership, but internally having lost - // it. So ignore any signal, which still delivers the internal QtMimeData - // as the clipboard content and is no "advertised" change. - if (!m_bOwnClipboardChange && isOwner(aMode) - && qobject_cast<const QtMimeData*>(QApplication::clipboard()->mimeData(aMode))) - return; + if (!m_bOwnClipboardChange && isOwner(aMode)) + { + auto const mimeData = QApplication::clipboard()->mimeData(aMode); + + // QtWayland will send a second change notification (seemingly without any + // trigger). And any C'n'P operation in the Qt file picker emits a signal, + // with LO still holding the clipboard ownership, but internally having lost + // it. So ignore any signal, which still delivers the internal QtMimeData + // as the clipboard content and is no "advertised" change. + if (qobject_cast<const QtMimeData*>(mimeData)) + return; + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && defined EMSCRIPTEN + // At least for the Qt5 Wasm backend, copying text from LO and then pasting it back into LO + // will, for whatever reason, call here with an empty text/plain mimeData; while that + // doesn't seem to be an issue at least with the current upstream Qt6 dev branch (again, for + // whatever reason), for at least our Qt 5.15.2+wasm branch the below m_aContents.clear() + // would make us lose the text just copied from LO into the clipboard and thus make us paste + // an empty text into LO, so, as a hack, filter out this unhelpful event here: + if (mimeData != nullptr && mimeData->hasText() && mimeData->text().isEmpty()) + { + return; + } +#endif + } css::uno::Reference<css::datatransfer::clipboard::XClipboardOwner> xOldOwner(m_aOwner); css::uno::Reference<css::datatransfer::XTransferable> xOldContents(m_aContents); @@ -254,7 +271,22 @@ bool QtClipboard::isOwner(const QClipboard::Mode aMode) return pClipboard->ownsFindBuffer(); case QClipboard::Clipboard: - return pClipboard->ownsClipboard(); + if (pClipboard->ownsClipboard()) + { + return true; + } +#if defined EMSCRIPTEN + // QWasmClipboard::ownsMode (in qtbase/src/plugins/platforms/wasm/qwasmclipboard.cpp, + // which is the actual implementation of the above ownsClipboard call) unconditionally + // returns false, so as a hack use "m_aContents is a LO-internal XTransferable type" as + // a poor approximation of "LO owns the clipboard": + if (m_aContents.is() + && dynamic_cast<QtClipboardTransferable*>(m_aContents.get()) == nullptr) + { + return true; + } +#endif + return false; } return false; } commit 2af0a128b509f8c372f442636a079967ff85aec0 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Jan 7 13:55:51 2025 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Tue Jan 7 17:56:00 2025 +0100 sw: move ViewCallback to Library_swqahelper Towards not including .cxx files in Writer tests. Change-Id: I42c6dba93599819ff93c92444a91a7ddc29ece38 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/179885 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins diff --git a/sw/Library_swqahelper.mk b/sw/Library_swqahelper.mk index aa0f05575340..a2e1adbd9b6d 100644 --- a/sw/Library_swqahelper.mk +++ b/sw/Library_swqahelper.mk @@ -68,6 +68,7 @@ $(eval $(call gb_Library_use_libraries,swqahelper,\ $(eval $(call gb_Library_add_exception_objects,swqahelper,\ sw/qa/unit/swmodeltestbase \ sw/qa/unit/swtiledrenderingtest \ + sw/qa/unit/swtestviewcallback \ )) # vim: set noet sw=4 ts=4: diff --git a/sw/qa/extras/tiledrendering/tiledrendering.cxx b/sw/qa/extras/tiledrendering/tiledrendering.cxx index 0f0cd9ed3403..284e2e46cdf4 100644 --- a/sw/qa/extras/tiledrendering/tiledrendering.cxx +++ b/sw/qa/extras/tiledrendering/tiledrendering.cxx @@ -497,10 +497,10 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testMissingInvalidation) { // Create two views. SwXTextDocument* pXTextDocument = createDoc("dummy.fodt"); - ViewCallback aView1; + SwTestViewCallback aView1; int nView1 = SfxLokHelper::getView(); SfxLokHelper::createView(); - ViewCallback aView2; + SwTestViewCallback aView2; int nView2 = SfxLokHelper::getView(); // First view: put the cursor into the first word. @@ -529,9 +529,9 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testMissingInvalidation) CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewCursors) { createDoc("dummy.fodt"); - ViewCallback aView1; + SwTestViewCallback aView1; SfxLokHelper::createView(); - ViewCallback aView2; + SwTestViewCallback aView2; Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated); @@ -568,10 +568,10 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeViewCursors) { // Load a document and create a view, so we have 2 ones. SwXTextDocument* pXTextDocument = createDoc("shape.fodt"); - ViewCallback aView1; + SwTestViewCallback aView1; SfxLokHelper::createView(); pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>()); - ViewCallback aView2; + SwTestViewCallback aView2; SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell(); // Start shape text in the second view. @@ -603,10 +603,10 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewCursorVisibility) { // Load a document that has a shape and create two views. SwXTextDocument* pXTextDocument = createDoc("shape.fodt"); - ViewCallback aView1; + SwTestViewCallback aView1; SfxLokHelper::createView(); pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>()); - ViewCallback aView2; + SwTestViewCallback aView2; // This failed, initially the view cursor in the second view wasn't visible. CPPUNIT_ASSERT(aView2.m_bViewCursorVisible); @@ -627,11 +627,11 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewCursorCleanup) { // Load a document that has a shape and create two views. SwXTextDocument* pXTextDocument = createDoc("shape.fodt"); - ViewCallback aView1; + SwTestViewCallback aView1; int nView2 = SfxLokHelper::createView(); pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>()); { - ViewCallback aView2; + SwTestViewCallback aView2; // Click on the shape in the second view. SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); @@ -657,10 +657,10 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewLock) { // Load a document that has a shape and create two views. SwXTextDocument* pXTextDocument = createDoc("shape.fodt"); - ViewCallback aView1; + SwTestViewCallback aView1; SfxLokHelper::createView(); pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>()); - ViewCallback aView2; + SwTestViewCallback aView2; // Begin text edit in the second view and assert that the first gets a lock // notification. @@ -682,10 +682,10 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTextEditViewInvalidations) { // Load a document that has a shape and create two views. SwXTextDocument* pXTextDocument = createDoc("shape.fodt"); - ViewCallback aView1; + SwTestViewCallback aView1; SfxLokHelper::createView(); pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>()); - ViewCallback aView2; + SwTestViewCallback aView2; // Begin text edit in the second view. SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); @@ -708,11 +708,11 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoInvalidations) { // Load a document and create two views. SwXTextDocument* pXTextDocument = createDoc("dummy.fodt"); - ViewCallback aView1; + SwTestViewCallback aView1; int nView1 = SfxLokHelper::getView(); SfxLokHelper::createView(); pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>()); - ViewCallback aView2; + SwTestViewCallback aView2; SfxLokHelper::setView(nView1); // Insert a character the end of the document. @@ -1073,7 +1073,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeTextUndoGroupShells) { // Load a document and create a view. SwXTextDocument* pXTextDocument = createDoc("shape.fodt"); - ViewCallback aView1; + SwTestViewCallback aView1; sal_Int32 nView1 = SfxLokHelper::getView(); // Begin text edit. @@ -1112,7 +1112,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeTextUndoGroupShells) // cursor position as the old one. SfxLokHelper::createView(); pXTextDocument->initializeForTiledRendering({}); - ViewCallback aView2; + 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); @@ -1133,7 +1133,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChanges) uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY); xPropertySet->setPropertyValue(u"RecordChanges"_ustr, uno::Any(true)); SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); - ViewCallback aView(pWrtShell->GetSfxViewShell()); + SwTestViewCallback aView(pWrtShell->GetSfxViewShell()); pWrtShell->EndOfSection(); pWrtShell->Insert(u"zzz"_ustr); pWrtShell->StartOfSection(); @@ -1255,7 +1255,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetViewRenderState) addDarkLightThemes(COL_BLACK, COL_WHITE); SwXTextDocument* pXTextDocument = createDoc(); int nFirstViewId = SfxLokHelper::getView(); - ViewCallback aView1; + SwTestViewCallback aView1; { SwViewOption aViewOptions; aViewOptions.SetViewMetaChars(true); @@ -1267,7 +1267,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetViewRenderState) // Create a second view SfxLokHelper::createView(); int nSecondViewId = SfxLokHelper::getView(); - ViewCallback aView2; + SwTestViewCallback aView2; { // Give the second view different options SwViewOption aViewOptions; @@ -1330,7 +1330,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testThemeViewSeparation) addDarkLightThemes(aDarkColor, COL_WHITE); SwXTextDocument* pXTextDocument = createDoc(); int nFirstViewId = SfxLokHelper::getView(); - ViewCallback aView1; + SwTestViewCallback aView1; // Set first view to light scheme { SwView* pView = getSwDocShell()->GetView(); @@ -1347,7 +1347,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testThemeViewSeparation) // Create second view SfxLokHelper::createView(); int nSecondViewId = SfxLokHelper::getView(); - ViewCallback aView2; + SwTestViewCallback aView2; // Set second view to dark scheme { SwView* pView = getSwDocShell()->GetView(); @@ -1388,7 +1388,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testInvertBackgroundViewSeparation) addDarkLightThemes(aDarkColor, COL_WHITE); SwXTextDocument* pXTextDocument = createDoc(); int nFirstViewId = SfxLokHelper::getView(); - ViewCallback aView1; + SwTestViewCallback aView1; // Set view to dark scheme { SwView* pView = getSwDocShell()->GetView(); @@ -1405,7 +1405,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testInvertBackgroundViewSeparation) // Create second view SfxLokHelper::createView(); int nSecondViewId = SfxLokHelper::getView(); - ViewCallback aView2; + SwTestViewCallback aView2; // Set second view to dark scheme { SwView* pView = getSwDocShell()->GetView(); @@ -1482,7 +1482,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testThemeChangeBackgroundCallback) Color aDarkColor(0x1c, 0x1c, 0x1c); addDarkLightThemes(aDarkColor, COL_WHITE); createDoc(); - ViewCallback aView; + SwTestViewCallback aView; SwView* pView = getSwDocShell()->GetView(); uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface(); @@ -1513,7 +1513,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSetViewGraphicSelection) // Load a document. SwXTextDocument* pXTextDocument = createDoc("frame.odt"); int nView1 = SfxLokHelper::getView(); - ViewCallback aView1; + SwTestViewCallback aView1; // Create a second view, and switch back to the first view. SfxLokHelper::createView(); pXTextDocument->initializeForTiledRendering({}); @@ -1537,7 +1537,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCreateViewGraphicSelection) { // Load a document. createDoc("frame.odt"); - ViewCallback aView1; + SwTestViewCallback aView1; // Mark the textframe in the first view. SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); @@ -1556,8 +1556,8 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCreateViewGraphicSelection) CPPUNIT_ASSERT(aView1.m_bGraphicSelection); // Make sure that the hidden text cursor isn't visible in the second view, either. - ViewCallback aView2(SfxViewShell::Current(), - [](ViewCallback& rView) { rView.m_bViewCursorVisible = true; }); + SwTestViewCallback aView2(SfxViewShell::Current(), + [](SwTestViewCallback& rView) { rView.m_bViewCursorVisible = true; }); // This was true, the second view didn't get the visibility of the text // cursor of the first view. CPPUNIT_ASSERT(!aView2.m_bViewCursorVisible); @@ -1570,7 +1570,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCreateViewTextSelection) { // Load a document. createDoc("dummy.fodt"); - ViewCallback aView1; + SwTestViewCallback aView1; // Create a text selection: SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); @@ -1586,7 +1586,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCreateViewTextSelection) SfxLokHelper::createView(); // Make sure that the text selection is visible in the second view. - ViewCallback aView2; + SwTestViewCallback aView2; // This failed, the second view didn't get the text selection of the first view. CPPUNIT_ASSERT(!aView2.m_aViewSelection.isEmpty()); } @@ -1616,7 +1616,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCommentEndTextEdit) { // Create a document, type a character and remember the cursor position. SwXTextDocument* pXTextDocument = createDoc(); - ViewCallback aView1; + SwTestViewCallback aView1; emulateTyping(u"x"); tools::Rectangle aBodyCursor = aView1.m_aOwnCursor; @@ -1661,7 +1661,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCommentInsert) {"Text", uno::Any(u"some text"_ustr)}, {"Author", uno::Any(u"me"_ustr)}, }); - ViewCallback aView; + SwTestViewCallback aView; comphelper::dispatchCommand(u".uno:InsertAnnotation"_ustr, xFrame, aPropertyValues); Scheduler::ProcessEventsToIdle(); OString aAnchorPos(aView.m_aComment.get_child("anchorPos").get_value<std::string>()); @@ -1677,12 +1677,12 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCursorPosition) { // Load a document and register a callback, should get an own cursor. SwXTextDocument* pXTextDocument = createDoc(); - ViewCallback aView1; + SwTestViewCallback aView1; // Create a second view, so the first view gets a collaborative cursor. SfxLokHelper::createView(); pXTextDocument->initializeForTiledRendering({}); - ViewCallback aView2; + SwTestViewCallback aView2; // Make sure the two are exactly the same. // This failed, own cursor was at '1418, 1418', collaborative cursor was at @@ -1697,7 +1697,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPaintCallbacks) // Load a document and register a callback for the first view. SwXTextDocument* pXTextDocument = createDoc(); - ViewCallback aView1; + SwTestViewCallback aView1; // Create a second view and paint a tile on that second view. SfxLokHelper::createView(); @@ -1801,12 +1801,12 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDisableUndoRepair) { // Create two views. SwXTextDocument* pXTextDocument = createDoc("dummy.fodt"); - ViewCallback aView1; + SwTestViewCallback aView1; SwView* pView1 = dynamic_cast<SwView*>(SfxViewShell::Current()); CPPUNIT_ASSERT(pView1); int nView1 = SfxLokHelper::getView(); SfxLokHelper::createView(); - ViewCallback aView2; + SwTestViewCallback aView2; SwView* pView2 = dynamic_cast<SwView*>(SfxViewShell::Current()); CPPUNIT_ASSERT(pView2); int nView2 = SfxLokHelper::getView(); @@ -2428,12 +2428,12 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDeleteNodeRedlineCallback) CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testVisCursorInvalidation) { SwXTextDocument* pXTextDocument = createDoc("dummy.fodt"); - ViewCallback aView1; + SwTestViewCallback aView1; int nView1 = SfxLokHelper::getView(); SfxLokHelper::createView(); int nView2 = SfxLokHelper::getView(); - ViewCallback aView2; + SwTestViewCallback aView2; Scheduler::ProcessEventsToIdle(); // Move visible cursor in the first view @@ -3175,7 +3175,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTableCommentRemoveCallback) SwXTextDocument* pXTextDocument = createDoc("testTableCommentRemoveCallback.odt"); SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell()); - ViewCallback aView; + SwTestViewCallback aView; // delete all characters comphelper::dispatchCommand(u".uno:SelectAll"_ustr, uno::Sequence<beans::PropertyValue>()); @@ -3762,10 +3762,10 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testToggleFormattingMarks) pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>()); SfxLokHelper::setView(nView1); - ViewCallback aView1; + SwTestViewCallback aView1; SfxLokHelper::setView(nView2); - ViewCallback aView2; + SwTestViewCallback aView2; OString sOrigView2RenderState = pXTextDocument->getViewRenderState(); diff --git a/sw/qa/extras/tiledrendering/tiledrendering2.cxx b/sw/qa/extras/tiledrendering/tiledrendering2.cxx index 0f9591d5afc3..b3512b700ed3 100644 --- a/sw/qa/extras/tiledrendering/tiledrendering2.cxx +++ b/sw/qa/extras/tiledrendering/tiledrendering2.cxx @@ -44,12 +44,12 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testStatusBarPageNumber) int nView2 = SfxLokHelper::getView(); pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>()); SfxLokHelper::setView(nView1); - ViewCallback aView1; + SwTestViewCallback aView1; pWrtShell1->SttEndDoc(/*bStt=*/true); pWrtShell1->Insert(u"start"_ustr); pWrtShell1->GetView().SetVisArea(pPage1->getFrameArea().SVRect()); SfxLokHelper::setView(nView2); - ViewCallback aView2; + SwTestViewCallback aView2; SwWrtShell* pWrtShell2 = getSwDocShell()->GetWrtShell(); pWrtShell2->SttEndDoc(/*bStt=*/false); pWrtShell2->Insert(u"end"_ustr); @@ -156,7 +156,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAsyncLayout) { // Given a document with 3 pages, the first page is visible: createDoc(); - ViewCallback aView; + SwTestViewCallback aView; SwDocShell* pDocShell = getSwDocShell(); SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); pWrtShell->InsertPageBreak(); @@ -192,7 +192,7 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAnyInput) { // Given a document with 3 pages, the first page is visible: createDoc(); - ViewCallback aView; + SwTestViewCallback aView; SwDocShell* pDocShell = getSwDocShell(); SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); pWrtShell->InsertPageBreak(); @@ -284,9 +284,9 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPDFExportViewSwitch) // Given a document with 2 views: SwXTextDocument* pXTextDocument = createDoc("to-pdf.odt"); SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc(); - ViewCallback aView1; + SwTestViewCallback aView1; SfxLokHelper::createView(); - ViewCallback aView2; + SwTestViewCallback aView2; SwView* pView2 = pDoc->GetDocShell()->GetView(); uno::Reference<frame::XFrame> xFrame2 = pView2->GetViewFrame().GetFrame().GetFrameInterface(); diff --git a/sw/qa/extras/tiledrendering/tiledrenderingmodeltestbase.cxx b/sw/qa/extras/tiledrendering/tiledrenderingmodeltestbase.cxx index 3d902b99b39d..df8cd65b8e5b 100644 --- a/sw/qa/extras/tiledrendering/tiledrenderingmodeltestbase.cxx +++ b/sw/qa/extras/tiledrendering/tiledrenderingmodeltestbase.cxx @@ -8,6 +8,7 @@ */ #include <swtiledrenderingtest.hxx> +#include <swtestviewcallback.hxx> #include <com/sun/star/frame/DispatchResultState.hpp> #include <com/sun/star/frame/XDispatchResultListener.hpp> @@ -23,226 +24,6 @@ #include <unotxdoc.hxx> #include <wrtsh.hxx> -/// A view callback tracks callbacks invoked on one specific view. -class ViewCallback final -{ - SfxViewShell* mpViewShell; - int mnView; - -public: - bool m_bOwnCursorInvalidated; - int m_nOwnCursorInvalidatedBy; - bool m_bOwnCursorAtOrigin; - tools::Rectangle m_aOwnCursor; - bool m_bViewCursorInvalidated; - tools::Rectangle m_aViewCursor; - bool m_bOwnSelectionSet; - bool m_bViewSelectionSet; - OString m_aViewSelection; - OString m_aViewRenderState; - bool m_bTilesInvalidated; - bool m_bViewCursorVisible; - bool m_bGraphicViewSelection; - bool m_bGraphicSelection; - bool m_bViewLock; - OString m_aDocColor; - /// Set if any callback was invoked. - bool m_bCalled; - /// Redline table size changed payload - boost::property_tree::ptree m_aRedlineTableChanged; - /// Redline table modified payload - boost::property_tree::ptree m_aRedlineTableModified; - /// Post-it / annotation payload. - boost::property_tree::ptree m_aComment; - std::vector<OString> m_aStateChanges; - TestLokCallbackWrapper m_callbackWrapper; - OString m_aExportFile; - - ViewCallback(SfxViewShell* pViewShell = nullptr, - std::function<void(ViewCallback&)> const& rBeforeInstallFunc = {}) - : m_bOwnCursorInvalidated(false) - , m_nOwnCursorInvalidatedBy(-1) - , m_bOwnCursorAtOrigin(false) - , m_bViewCursorInvalidated(false) - , m_bOwnSelectionSet(false) - , m_bViewSelectionSet(false) - , m_bTilesInvalidated(false) - , m_bViewCursorVisible(false) - , m_bGraphicViewSelection(false) - , m_bGraphicSelection(false) - , m_bViewLock(false) - , m_bCalled(false) - , m_callbackWrapper(&callback, this) - { - // Because one call-site wants to set the bool fields up before the callback is installed - if (rBeforeInstallFunc) - rBeforeInstallFunc(*this); - - mpViewShell = pViewShell ? pViewShell : SfxViewShell::Current(); - mpViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper); - mnView = SfxLokHelper::getView(); - m_callbackWrapper.setLOKViewId(mnView); - } - - ~ViewCallback() - { - SfxLokHelper::setView(mnView); - mpViewShell->setLibreOfficeKitViewCallback(nullptr); - } - - static void callback(int nType, const char* pPayload, void* pData) - { - static_cast<ViewCallback*>(pData)->callbackImpl(nType, pPayload); - } - - void callbackImpl(int nType, const char* pPayload) - { - OString aPayload(pPayload); - m_bCalled = true; - switch (nType) - { - case LOK_CALLBACK_STATE_CHANGED: - { - m_aStateChanges.push_back(pPayload); - break; - } - case LOK_CALLBACK_INVALIDATE_TILES: - { - m_bTilesInvalidated = true; - } - break; - case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: - { - m_bOwnCursorInvalidated = true; - - OString sRect; - if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation()) - { - std::stringstream aStream(pPayload); - boost::property_tree::ptree aTree; - boost::property_tree::read_json(aStream, aTree); - sRect = OString(aTree.get_child("rectangle").get_value<std::string>()); - m_nOwnCursorInvalidatedBy = aTree.get_child("viewId").get_value<int>(); - } - else - sRect = aPayload; - uno::Sequence<OUString> aSeq - = comphelper::string::convertCommaSeparated(OUString::fromUtf8(sRect)); - if (std::string_view("EMPTY") == pPayload) - return; - CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength()); - m_aOwnCursor.SetLeft(aSeq[0].toInt32()); - m_aOwnCursor.SetTop(aSeq[1].toInt32()); - m_aOwnCursor.setWidth(aSeq[2].toInt32()); - m_aOwnCursor.setHeight(aSeq[3].toInt32()); - if (m_aOwnCursor.Left() == 0 && m_aOwnCursor.Top() == 0) - m_bOwnCursorAtOrigin = true; - } - break; - case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR: - { - m_bViewCursorInvalidated = true; - std::stringstream aStream(pPayload); - boost::property_tree::ptree aTree; - boost::property_tree::read_json(aStream, aTree); - OString aRect(aTree.get_child("rectangle").get_value<std::string>()); - - uno::Sequence<OUString> aSeq - = comphelper::string::convertCommaSeparated(OUString::fromUtf8(aRect)); - if (std::string_view("EMPTY") == pPayload) - return; - CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength()); - m_aViewCursor.SetLeft(aSeq[0].toInt32()); - m_aViewCursor.SetTop(aSeq[1].toInt32()); - m_aViewCursor.setWidth(aSeq[2].toInt32()); - m_aViewCursor.setHeight(aSeq[3].toInt32()); - } - break; - case LOK_CALLBACK_TEXT_SELECTION: - { - m_bOwnSelectionSet = true; - } - break; - case LOK_CALLBACK_TEXT_VIEW_SELECTION: - { - m_bViewSelectionSet = true; - m_aViewSelection = aPayload; - } - break; - case LOK_CALLBACK_VIEW_CURSOR_VISIBLE: - { - std::stringstream aStream(pPayload); - boost::property_tree::ptree aTree; - boost::property_tree::read_json(aStream, aTree); - m_bViewCursorVisible - = aTree.get_child("visible").get_value<std::string>() == "true"; - } - break; - case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION: - { - std::stringstream aStream(pPayload); - boost::property_tree::ptree aTree; - boost::property_tree::read_json(aStream, aTree); - m_bGraphicViewSelection - = aTree.get_child("selection").get_value<std::string>() != "EMPTY"; - } - break; - case LOK_CALLBACK_GRAPHIC_SELECTION: - { - m_bGraphicSelection = aPayload != "EMPTY"; - } - 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<std::string>() != "EMPTY"; - } - break; - case LOK_CALLBACK_VIEW_RENDER_STATE: - { - m_aViewRenderState = pPayload; - } - break; - case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED: - { - m_aRedlineTableChanged.clear(); - std::stringstream aStream(pPayload); - boost::property_tree::read_json(aStream, m_aRedlineTableChanged); - m_aRedlineTableChanged = m_aRedlineTableChanged.get_child("redline"); - } - break; - case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED: - { - m_aRedlineTableModified.clear(); - std::stringstream aStream(pPayload); - boost::property_tree::read_json(aStream, m_aRedlineTableModified); - m_aRedlineTableModified = m_aRedlineTableModified.get_child("redline"); - } - break; - case LOK_CALLBACK_COMMENT: - { - m_aComment.clear(); - std::stringstream aStream(pPayload); - boost::property_tree::read_json(aStream, m_aComment); - m_aComment = m_aComment.get_child("comment"); - } - break; - case LOK_CALLBACK_DOCUMENT_BACKGROUND_COLOR: - { - m_aDocColor = aPayload; - break; - } - case LOK_CALLBACK_EXPORT_FILE: - { - m_aExportFile = aPayload; - break; - } - } - } -}; - class TestResultListener : public cppu::WeakImplHelper<css::frame::XDispatchResultListener> { public: diff --git a/sw/qa/inc/swtestviewcallback.hxx b/sw/qa/inc/swtestviewcallback.hxx new file mode 100644 index 000000000000..a82772c07b70 --- /dev/null +++ b/sw/qa/inc/swtestviewcallback.hxx @@ -0,0 +1,70 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_SW_QA_INC_SWTESTVIEWCALLBACK_HXX +#define INCLUDED_SW_QA_INC_SWTESTVIEWCALLBACK_HXX + +#include <boost/property_tree/ptree.hpp> + +#include <rtl/string.hxx> +#include <test/lokcallback.hxx> +#include <tools/gen.hxx> + +#include "swqahelperdllapi.h" + +class SfxViewShell; + +/// A view callback tracks callbacks invoked on one specific view. +class SWQAHELPER_DLLPUBLIC SwTestViewCallback final +{ + SfxViewShell* mpViewShell; + int mnView; + +public: + bool m_bOwnCursorInvalidated; + int m_nOwnCursorInvalidatedBy; + bool m_bOwnCursorAtOrigin; + tools::Rectangle m_aOwnCursor; + bool m_bViewCursorInvalidated; + tools::Rectangle m_aViewCursor; + bool m_bOwnSelectionSet; + bool m_bViewSelectionSet; + OString m_aViewSelection; + OString m_aViewRenderState; + bool m_bTilesInvalidated; + bool m_bViewCursorVisible; + bool m_bGraphicViewSelection; + bool m_bGraphicSelection; + bool m_bViewLock; + OString m_aDocColor; + /// Set if any callback was invoked. + bool m_bCalled; + /// Redline table size changed payload + boost::property_tree::ptree m_aRedlineTableChanged; + /// Redline table modified payload + boost::property_tree::ptree m_aRedlineTableModified; + /// Post-it / annotation payload. + boost::property_tree::ptree m_aComment; + std::vector<OString> m_aStateChanges; + TestLokCallbackWrapper m_callbackWrapper; + OString m_aExportFile; + + SwTestViewCallback(SfxViewShell* pViewShell = nullptr, + std::function<void(SwTestViewCallback&)> const& rBeforeInstallFunc = {}); + + ~SwTestViewCallback(); + + static void callback(int nType, const char* pPayload, void* pData); + + void callbackImpl(int nType, const char* pPayload); +}; + +#endif // INCLUDED_SW_QA_INC_SWTESTVIEWCALLBACK_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/unit/swtestviewcallback.cxx b/sw/qa/unit/swtestviewcallback.cxx new file mode 100644 index 000000000000..b378723603a0 --- /dev/null +++ b/sw/qa/unit/swtestviewcallback.cxx @@ -0,0 +1,205 @@ +/* -*- 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 <swtestviewcallback.hxx> + +#include <boost/property_tree/json_parser.hpp> +#include <cppunit/TestAssert.h> + +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <comphelper/lok.hxx> +#include <comphelper/string.hxx> +#include <sfx2/lokhelper.hxx> + +using namespace css; + +SwTestViewCallback::SwTestViewCallback( + SfxViewShell* pViewShell, std::function<void(SwTestViewCallback&)> const& rBeforeInstallFunc) + : m_bOwnCursorInvalidated(false) + , m_nOwnCursorInvalidatedBy(-1) + , m_bOwnCursorAtOrigin(false) + , m_bViewCursorInvalidated(false) + , m_bOwnSelectionSet(false) + , m_bViewSelectionSet(false) + , m_bTilesInvalidated(false) + , m_bViewCursorVisible(false) + , m_bGraphicViewSelection(false) + , m_bGraphicSelection(false) + , m_bViewLock(false) + , m_bCalled(false) + , m_callbackWrapper(&callback, this) +{ + // Because one call-site wants to set the bool fields up before the callback is installed + if (rBeforeInstallFunc) + rBeforeInstallFunc(*this); + + mpViewShell = pViewShell ? pViewShell : SfxViewShell::Current(); + mpViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper); + mnView = SfxLokHelper::getView(); + m_callbackWrapper.setLOKViewId(mnView); +} + +SwTestViewCallback::~SwTestViewCallback() +{ + SfxLokHelper::setView(mnView); + mpViewShell->setLibreOfficeKitViewCallback(nullptr); +} + +void SwTestViewCallback::callback(int nType, const char* pPayload, void* pData) +{ + static_cast<SwTestViewCallback*>(pData)->callbackImpl(nType, pPayload); +} + +void SwTestViewCallback::callbackImpl(int nType, const char* pPayload) +{ + OString aPayload(pPayload); + m_bCalled = true; + switch (nType) + { + case LOK_CALLBACK_STATE_CHANGED: + { + m_aStateChanges.push_back(pPayload); + break; + } + case LOK_CALLBACK_INVALIDATE_TILES: + { + m_bTilesInvalidated = true; + } + break; + case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: + { + m_bOwnCursorInvalidated = true; + + OString sRect; + if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation()) + { + std::stringstream aStream(pPayload); + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + sRect = OString(aTree.get_child("rectangle").get_value<std::string>()); + m_nOwnCursorInvalidatedBy = aTree.get_child("viewId").get_value<int>(); + } + else + sRect = aPayload; + uno::Sequence<OUString> aSeq + = comphelper::string::convertCommaSeparated(OUString::fromUtf8(sRect)); + if (std::string_view("EMPTY") == pPayload) + return; + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength()); + m_aOwnCursor.SetLeft(aSeq[0].toInt32()); + m_aOwnCursor.SetTop(aSeq[1].toInt32()); + m_aOwnCursor.setWidth(aSeq[2].toInt32()); + m_aOwnCursor.setHeight(aSeq[3].toInt32()); + if (m_aOwnCursor.Left() == 0 && m_aOwnCursor.Top() == 0) + m_bOwnCursorAtOrigin = true; + } + break; + case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR: + { + m_bViewCursorInvalidated = true; + std::stringstream aStream(pPayload); + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + OString aRect(aTree.get_child("rectangle").get_value<std::string>()); + + uno::Sequence<OUString> aSeq + = comphelper::string::convertCommaSeparated(OUString::fromUtf8(aRect)); + if (std::string_view("EMPTY") == pPayload) + return; + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength()); + m_aViewCursor.SetLeft(aSeq[0].toInt32()); + m_aViewCursor.SetTop(aSeq[1].toInt32()); + m_aViewCursor.setWidth(aSeq[2].toInt32()); + m_aViewCursor.setHeight(aSeq[3].toInt32()); + } + break; + case LOK_CALLBACK_TEXT_SELECTION: + { + m_bOwnSelectionSet = true; + } + break; + case LOK_CALLBACK_TEXT_VIEW_SELECTION: + { + m_bViewSelectionSet = true; + m_aViewSelection = aPayload; + } + break; + case LOK_CALLBACK_VIEW_CURSOR_VISIBLE: + { + std::stringstream aStream(pPayload); + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + m_bViewCursorVisible = aTree.get_child("visible").get_value<std::string>() == "true"; + } + break; + case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION: + { + std::stringstream aStream(pPayload); + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + m_bGraphicViewSelection + = aTree.get_child("selection").get_value<std::string>() != "EMPTY"; + } + break; + case LOK_CALLBACK_GRAPHIC_SELECTION: + { + m_bGraphicSelection = aPayload != "EMPTY"; + } + 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<std::string>() != "EMPTY"; + } + break; + case LOK_CALLBACK_VIEW_RENDER_STATE: + { + m_aViewRenderState = pPayload; + } + break; + case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED: + { + m_aRedlineTableChanged.clear(); + std::stringstream aStream(pPayload); + boost::property_tree::read_json(aStream, m_aRedlineTableChanged); + m_aRedlineTableChanged = m_aRedlineTableChanged.get_child("redline"); + } + break; + case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED: + { + m_aRedlineTableModified.clear(); + std::stringstream aStream(pPayload); + boost::property_tree::read_json(aStream, m_aRedlineTableModified); + m_aRedlineTableModified = m_aRedlineTableModified.get_child("redline"); + } + break; + case LOK_CALLBACK_COMMENT: + { + m_aComment.clear(); + std::stringstream aStream(pPayload); + boost::property_tree::read_json(aStream, m_aComment); + m_aComment = m_aComment.get_child("comment"); + } + break; + case LOK_CALLBACK_DOCUMENT_BACKGROUND_COLOR: + { + m_aDocColor = aPayload; + break; + } + case LOK_CALLBACK_EXPORT_FILE: + { + m_aExportFile = aPayload; + break; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */