desktop/qa/desktop_lib/test_desktop_lib.cxx | 22 --- include/sfx2/objsh.hxx | 2 sc/source/ui/docshell/docsh.cxx | 2 sc/source/ui/inc/docsh.hxx | 2 sfx2/source/doc/objxtor.cxx | 2 sw/CppunitTest_sw_uibase_shells.mk | 1 sw/inc/IDocumentRedlineAccess.hxx | 3 sw/inc/docsh.hxx | 2 sw/qa/extras/tiledrendering/tiledrendering.cxx | 152 +++++++++++++++++++++++++ sw/qa/uibase/shells/basesh.cxx | 114 ++++++++++++++++++ sw/source/core/doc/DocumentRedlineManager.cxx | 81 ++++++++----- sw/source/core/edit/edredln.cxx | 2 sw/source/core/inc/DocumentRedlineManager.hxx | 4 sw/source/uibase/app/docsh.cxx | 17 ++ sw/source/uibase/shells/basesh.cxx | 30 ++++ sw/source/uibase/uiview/viewstat.cxx | 4 16 files changed, 374 insertions(+), 66 deletions(-)
New commits: commit 5cdf101b11b9e81ddb97cf7ca369d7ac1b064ee0 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Mon Mar 3 08:31:55 2025 +0100 Commit: Caolán McNamara <caolan.mcnam...@collabora.com> CommitDate: Mon Mar 3 09:27:36 2025 +0100 Related: cool#11226 sw per-view redline on: fix ratio buttons of is-show Redline recording is now per-view, but it would be even better to have UI where the user can decide how to record (don't record, record per-view, record in all views). While looking for an example to follow, I noticed that the redline show UI is similar, but the radio buttons always have a false state. Turns out simily the command state wasn't implemented in commit 6aeeef8807fef36295b65d0a300a21466bfbeda0 (tdf#116757 sw ChangesInMargin: add "Show Insertions in Margin", 2021-01-20), add this. Change-Id: Ie50c13c45c4e53fbfbb6befa36b6a29be084f2d8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182418 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com> diff --git a/sw/CppunitTest_sw_uibase_shells.mk b/sw/CppunitTest_sw_uibase_shells.mk index fdd8a8d7ce7b..1730efe9b238 100644 --- a/sw/CppunitTest_sw_uibase_shells.mk +++ b/sw/CppunitTest_sw_uibase_shells.mk @@ -14,6 +14,7 @@ $(eval $(call gb_CppunitTest_CppunitTest,sw_uibase_shells)) $(eval $(call gb_CppunitTest_use_common_precompiled_header,sw_uibase_shells)) $(eval $(call gb_CppunitTest_add_exception_objects,sw_uibase_shells, \ + sw/qa/uibase/shells/basesh \ sw/qa/uibase/shells/textfld \ sw/qa/uibase/shells/textsh \ sw/qa/uibase/shells/textsh1 \ diff --git a/sw/qa/uibase/shells/basesh.cxx b/sw/qa/uibase/shells/basesh.cxx new file mode 100644 index 000000000000..40f10f76787b --- /dev/null +++ b/sw/qa/uibase/shells/basesh.cxx @@ -0,0 +1,114 @@ +/* -*- 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 <swmodeltestbase.hxx> + +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> + +#include <cmdid.h> +#include <view.hxx> +#include <docsh.hxx> + +namespace +{ +/// Covers sw/source/uibase/shells/basesh.hxx fixes. +class Test : public SwModelTestBase +{ +public: + Test() + : SwModelTestBase("/sw/qa/uibase/shells/data/") + { + } +}; +} + +CPPUNIT_TEST_FIXTURE(Test, testShowChangesStatus) +{ + // Given an empty document: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + SwView* pView = pDoc->GetDocShell()->GetView(); + + // When showing changes inline: + pView->GetViewFrame().GetDispatcher()->Execute(FN_SET_TRACKED_CHANGES_IN_TEXT, + SfxCallMode::SYNCHRON); + + // Then make sure the state of the 3 show modes are correct: + std::unique_ptr<SfxPoolItem> pItem; + pView->GetViewFrame().GetBindings().QueryState(FN_SET_TRACKED_CHANGES_IN_TEXT, pItem); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 20267 (FN_SET_TRACKED_CHANGES_IN_TEXT) + // - Actual : 0 + // i.e. the status of these uno commands were not implemented. + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_SET_TRACKED_CHANGES_IN_TEXT), pItem->Which()); + CPPUNIT_ASSERT(dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue()); + pView->GetViewFrame().GetBindings().QueryState(FN_SET_TRACKED_DELETIONS_IN_MARGIN, pItem); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_SET_TRACKED_DELETIONS_IN_MARGIN), + pItem->Which()); + CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue()); + pView->GetViewFrame().GetBindings().QueryState(FN_SET_TRACKED_INSERTIONS_IN_MARGIN, pItem); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_SET_TRACKED_INSERTIONS_IN_MARGIN), + pItem->Which()); + CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue()); + + // When showing deletions on the margin: + pView->GetViewFrame().GetDispatcher()->Execute(FN_SET_TRACKED_DELETIONS_IN_MARGIN, + SfxCallMode::SYNCHRON); + + // Then make sure the state of the 3 show modes are correct: + pView->GetViewFrame().GetBindings().QueryState(FN_SET_TRACKED_CHANGES_IN_TEXT, pItem); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_SET_TRACKED_CHANGES_IN_TEXT), pItem->Which()); + CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue()); + pView->GetViewFrame().GetBindings().QueryState(FN_SET_TRACKED_DELETIONS_IN_MARGIN, pItem); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_SET_TRACKED_DELETIONS_IN_MARGIN), + pItem->Which()); + CPPUNIT_ASSERT(dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue()); + pView->GetViewFrame().GetBindings().QueryState(FN_SET_TRACKED_INSERTIONS_IN_MARGIN, pItem); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_SET_TRACKED_INSERTIONS_IN_MARGIN), + pItem->Which()); + CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue()); + + // When showing insertions on the margin: + pView->GetViewFrame().GetDispatcher()->Execute(FN_SET_TRACKED_INSERTIONS_IN_MARGIN, + SfxCallMode::SYNCHRON); + + // Then make sure the state of the 3 show modes are correct: + pView->GetViewFrame().GetBindings().QueryState(FN_SET_TRACKED_CHANGES_IN_TEXT, pItem); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_SET_TRACKED_CHANGES_IN_TEXT), pItem->Which()); + CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue()); + pView->GetViewFrame().GetBindings().QueryState(FN_SET_TRACKED_DELETIONS_IN_MARGIN, pItem); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_SET_TRACKED_DELETIONS_IN_MARGIN), + pItem->Which()); + CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue()); + pView->GetViewFrame().GetBindings().QueryState(FN_SET_TRACKED_INSERTIONS_IN_MARGIN, pItem); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_SET_TRACKED_INSERTIONS_IN_MARGIN), + pItem->Which()); + CPPUNIT_ASSERT(dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue()); + + // Finally, when not showing changes: + SfxBoolItem aShow(FN_REDLINE_SHOW, false); + pView->GetViewFrame().GetDispatcher()->ExecuteList(FN_REDLINE_SHOW, SfxCallMode::SYNCHRON, + { &aShow }); + + // Then make sure the state of the 3 show modes are correct: + pView->GetViewFrame().GetBindings().QueryState(FN_SET_TRACKED_CHANGES_IN_TEXT, pItem); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_SET_TRACKED_CHANGES_IN_TEXT), pItem->Which()); + CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue()); + pView->GetViewFrame().GetBindings().QueryState(FN_SET_TRACKED_DELETIONS_IN_MARGIN, pItem); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_SET_TRACKED_DELETIONS_IN_MARGIN), + pItem->Which()); + CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue()); + pView->GetViewFrame().GetBindings().QueryState(FN_SET_TRACKED_INSERTIONS_IN_MARGIN, pItem); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(FN_SET_TRACKED_INSERTIONS_IN_MARGIN), + pItem->Which()); + CPPUNIT_ASSERT(!dynamic_cast<SfxBoolItem*>(pItem.get())->GetValue()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/basesh.cxx b/sw/source/uibase/shells/basesh.cxx index 52e24b0b6077..06c753de2366 100644 --- a/sw/source/uibase/shells/basesh.cxx +++ b/sw/source/uibase/shells/basesh.cxx @@ -115,6 +115,7 @@ #include <fmtrfmrk.hxx> #include <txtrfmrk.hxx> #include <translatehelper.hxx> +#include <rootfrm.hxx> FlyMode SwBaseShell::s_eFrameMode = FLY_DRAG_END; @@ -2148,6 +2149,35 @@ void SwBaseShell::GetState( SfxItemSet &rSet ) rSet.DisableItem(nWhich); } break; + case FN_SET_TRACKED_CHANGES_IN_TEXT: + { + bool bShowRedlines = !rSh.GetLayout()->IsHideRedlines(); + const SwViewOption* pOptions = rSh.GetViewOptions(); + // First setting is false: inline. + bool bAllInText = bShowRedlines && !pOptions->IsShowChangesInMargin(); + rSet.Put(SfxBoolItem(nWhich, bAllInText)); + } + break; + case FN_SET_TRACKED_DELETIONS_IN_MARGIN: + { + bool bShowRedlines = !rSh.GetLayout()->IsHideRedlines(); + const SwViewOption* pOptions = rSh.GetViewOptions(); + // Second setting is false: show deletions in margin. + bool bDelInMargin = bShowRedlines && pOptions->IsShowChangesInMargin() + && !pOptions->IsShowChangesInMargin2(); + rSet.Put(SfxBoolItem(nWhich, bDelInMargin)); + } + break; + case FN_SET_TRACKED_INSERTIONS_IN_MARGIN: + { + bool bShowRedlines = !rSh.GetLayout()->IsHideRedlines(); + const SwViewOption* pOptions = rSh.GetViewOptions(); + // Second setting is true: show insertions in margin. + bool bInsInMargin = bShowRedlines && pOptions->IsShowChangesInMargin() + && pOptions->IsShowChangesInMargin2(); + rSet.Put(SfxBoolItem(nWhich, bInsInMargin)); + } + break; } nWhich = aIter.NextWhich(); } commit 79ce382f8d6bd5e1a7b0d1f32ed0b4f73a9b51ae Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Fri Feb 28 09:00:22 2025 +0100 Commit: Caolán McNamara <caolan.mcnam...@collabora.com> CommitDate: Mon Mar 3 09:27:29 2025 +0100 cool#11226 sw per-view redline on: add view-aware getter Have 2 views, view 1 enables tracked changes recording by dispatching .uno:TrackChanges, view 2 does not. The result is that this gets enabled in both views, including the toolbar button state, insertion recording, deletion recording. The toolbar button state behavior was introduced in commit d890ec2f130188af9d998abf5968f06e7218b7a4 (tdf#101592 sw: track changes state is doc-specific, not view-specific, 2016-08-19), but there the motivation was that *if* the setting is per-doc, then all views should show the same toolbar button state. Fix the problem by: - improving SwDocShell::IsChangeRecording(), so it takes a hitn on what is the relevant view; the relevant view is the view where the state is updated, it may not be the current view - using sw::DocumentRedlineManager::GetRedlineFlags() everywhere, so we'll have a central points for reading these flags - extending sw::DocumentRedlineManager::GetRedlineFlags() to try to take the "is recording" setting from the current view (if there is such a view) - improving SwEditShell::GetRedlineFlags() to pass itself to sw::DocumentRedlineManager::GetRedlineFlags(), so the entire chain of calls for the toolbar button work with the correct view Also add tests to make sure all of uno command state, insert & delete works. Drop the desktop/ test that is now redundant -- and testing per-view callbacks there is harder than in sw/. Change-Id: I6dbd826ea27facb9f8d06029efe805c637b7e080 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182345 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com> diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx index f16073deb9de..f4766980b026 100644 --- a/desktop/qa/desktop_lib/test_desktop_lib.cxx +++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx @@ -186,7 +186,6 @@ public: void testBinaryCallback(); void testInput(); void testRedlineWriter(); - void testTrackChanges(); void testRedlineCalc(); void testPaintPartTile(); void testPaintPartTileDifferentSchemes(); @@ -259,7 +258,6 @@ public: CPPUNIT_TEST(testBinaryCallback); CPPUNIT_TEST(testInput); CPPUNIT_TEST(testRedlineWriter); - CPPUNIT_TEST(testTrackChanges); CPPUNIT_TEST(testRedlineCalc); CPPUNIT_TEST(testPaintPartTile); CPPUNIT_TEST(testPaintPartTileDifferentSchemes); @@ -1045,26 +1043,6 @@ void DesktopLOKTest::testWriterComments() CPPUNIT_ASSERT_EQUAL(OUString("test"), xTextField->getPropertyValue("Content").get<OUString>()); } -void DesktopLOKTest::testTrackChanges() -{ - // Load a document and create two views. - LibLibreOffice_Impl aOffice; - LibLODocument_Impl* pDocument = loadDoc("blank_text.odt"); - pDocument->pClass->initializeForRendering(pDocument, nullptr); - pDocument->pClass->registerCallback(pDocument, &DesktopLOKTest::callback, this); - pDocument->pClass->createView(pDocument); - pDocument->pClass->initializeForRendering(pDocument, nullptr); - pDocument->pClass->registerCallback(pDocument, &DesktopLOKTest::callback, this); - Scheduler::ProcessEventsToIdle(); - - // Enable track changes and assert that both views get notified. - m_nTrackChanges = 0; - pDocument->pClass->postUnoCommand(pDocument, ".uno:TrackChanges", nullptr, false); - Scheduler::ProcessEventsToIdle(); - // This was 1, only the active view was notified. - CPPUNIT_ASSERT_EQUAL(2, m_nTrackChanges); -} - void DesktopLOKTest::testSheetOperations() { LibLODocument_Impl* pDocument = loadDoc("sheets.ods"); diff --git a/include/sfx2/objsh.hxx b/include/sfx2/objsh.hxx index 0fbe13c0ff2f..3922a820a7dd 100644 --- a/include/sfx2/objsh.hxx +++ b/include/sfx2/objsh.hxx @@ -688,7 +688,7 @@ public: // change recording and respective passwword protection for Writer and Calc // slots available for Writer: FN_REDLINE_ON, FN_REDLINE_ON // slots used for Calc: FID_CHG_RECORD, SID_CHG_PROTECT - virtual bool IsChangeRecording() const; + virtual bool IsChangeRecording(SfxViewShell* pViewShell = nullptr) const; virtual bool HasChangeRecordProtection() const; virtual void SetChangeRecording( bool bActivate, bool bLockAllViews = false ); virtual void SetProtectionPassword( const OUString &rPassword ); diff --git a/sc/source/ui/docshell/docsh.cxx b/sc/source/ui/docshell/docsh.cxx index 2c11b26d22b2..71d46c36cb27 100644 --- a/sc/source/ui/docshell/docsh.cxx +++ b/sc/source/ui/docshell/docsh.cxx @@ -3368,7 +3368,7 @@ void ScDocShellModificator::SetDocumentModified() } } -bool ScDocShell::IsChangeRecording() const +bool ScDocShell::IsChangeRecording(SfxViewShell* /*pViewShell*/) const { ScChangeTrack* pChangeTrack = m_pDocument->GetChangeTrack(); return pChangeTrack != nullptr; diff --git a/sc/source/ui/inc/docsh.hxx b/sc/source/ui/inc/docsh.hxx index d42ffc9ef8e8..b443d8db5624 100644 --- a/sc/source/ui/inc/docsh.hxx +++ b/sc/source/ui/inc/docsh.hxx @@ -425,7 +425,7 @@ public: // password protection for Calc (derived from SfxObjectShell) // see also: FID_CHG_RECORD, SID_CHG_PROTECT - virtual bool IsChangeRecording() const override; + virtual bool IsChangeRecording(SfxViewShell* pViewShell = nullptr) const override; virtual bool HasChangeRecordProtection() const override; virtual void SetChangeRecording( bool bActivate, bool bLockAllViews = false ) override; virtual void SetProtectionPassword( const OUString &rPassword ) override; diff --git a/sfx2/source/doc/objxtor.cxx b/sfx2/source/doc/objxtor.cxx index 9bd6adef3734..486a9bae26cb 100644 --- a/sfx2/source/doc/objxtor.cxx +++ b/sfx2/source/doc/objxtor.cxx @@ -1092,7 +1092,7 @@ void SfxObjectShell::SetInitialized_Impl( const bool i_fromInitNew ) } -bool SfxObjectShell::IsChangeRecording() const +bool SfxObjectShell::IsChangeRecording(SfxViewShell* /*pViewShell*/) const { // currently this function needs to be overwritten by Writer and Calc only SAL_WARN( "sfx.doc", "function not implemented" ); diff --git a/sw/inc/IDocumentRedlineAccess.hxx b/sw/inc/IDocumentRedlineAccess.hxx index eeb793530337..ede30c26d39e 100644 --- a/sw/inc/IDocumentRedlineAccess.hxx +++ b/sw/inc/IDocumentRedlineAccess.hxx @@ -38,6 +38,7 @@ class SwPaM; struct SwPosition; class SwStartNode; class SwNode; +class SwViewShell; enum class RedlineFlags { @@ -99,7 +100,7 @@ public: @returns the currently set redline mode */ - virtual RedlineFlags GetRedlineFlags() const = 0; + virtual RedlineFlags GetRedlineFlags(const SwViewShell* pViewShell = nullptr) const = 0; /** Set a new redline mode. diff --git a/sw/inc/docsh.hxx b/sw/inc/docsh.hxx index 66bc38a56372..17b1b26f6e99 100644 --- a/sw/inc/docsh.hxx +++ b/sw/inc/docsh.hxx @@ -321,7 +321,7 @@ public: /** passwword protection for Writer (derived from SfxObjectShell) see also: FN_REDLINE_ON, FN_REDLINE_ON */ - virtual bool IsChangeRecording() const override; + virtual bool IsChangeRecording(SfxViewShell* pViewShell = nullptr) const override; virtual bool HasChangeRecordProtection() const override; virtual void SetChangeRecording( bool bActivate, bool bLockAllViews = false ) override; virtual void SetProtectionPassword( const OUString &rPassword ) override; diff --git a/sw/qa/extras/tiledrendering/tiledrendering.cxx b/sw/qa/extras/tiledrendering/tiledrendering.cxx index d90b2b15efe8..5cfdbec5d6e4 100644 --- a/sw/qa/extras/tiledrendering/tiledrendering.cxx +++ b/sw/qa/extras/tiledrendering/tiledrendering.cxx @@ -4819,6 +4819,158 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testFindAndReplaceInComments) CPPUNIT_ASSERT_GREATER(static_cast<tools::Long>(2000), m_aCursorRectangle.getY()); } +namespace +{ +std::vector<OString> FilterStateChanges(const std::vector<OString>& rChanges, std::string_view rPrefix) +{ + std::vector<OString> aRet; + for (const auto& rChange : rChanges) + { + if (rChange.startsWith(rPrefix)) + { + aRet.push_back(rChange); + } + } + return aRet; +} +} + +CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChangesPerViewEnableOne) +{ + // Given a document with two views: + SwXTextDocument* pXTextDocument = createDoc(); + CPPUNIT_ASSERT(pXTextDocument); + ViewCallback aView1; + int nView1 = SfxLokHelper::getView(); + SfxLokHelper::createView(); + ViewCallback aView2; + int nView2 = SfxLokHelper::getView(); + + // When recording changes in view1: + SfxLokHelper::setView(nView1); + aView1.m_aStateChanges.clear(); + aView2.m_aStateChanges.clear(); + comphelper::dispatchCommand(".uno:TrackChanges", {}); + + // 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): + comphelper::dispatchCommand(".uno:TrackChanges", {}); + + // When recording changes in view2: + SfxLokHelper::setView(nView2); + aView1.m_aStateChanges.clear(); + aView2.m_aStateChanges.clear(); + comphelper::dispatchCommand(".uno:TrackChanges", {}); + + // Then make sure view2 gets a state track changes state change, but not view1: + CPPUNIT_ASSERT(aView1.m_aStateChanges.empty()); + CPPUNIT_ASSERT(!aView2.m_aStateChanges.empty()); +} + +CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChangesPerViewEnableBoth) +{ + // Given a document with 2 views, view1 record changes: + SwXTextDocument* pXTextDocument = createDoc(); + CPPUNIT_ASSERT(pXTextDocument); + ViewCallback aView1; + int nView1 = SfxLokHelper::getView(); + SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell(); + SfxLokHelper::createView(); + ViewCallback aView2; + int nView2 = SfxLokHelper::getView(); + SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell(); + SfxLokHelper::setView(nView1); + comphelper::dispatchCommand(".uno:TrackChanges", {}); + SfxLokHelper::setView(nView2); + CPPUNIT_ASSERT(pWrtShell1->GetViewOptions()->IsRedlineRecordingOn()); + CPPUNIT_ASSERT(!pWrtShell2->GetViewOptions()->IsRedlineRecordingOn()); + + // When turning on track changes for view2: + comphelper::dispatchCommand(".uno:TrackChanges", {}); + + // 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, testTrackChangesPerViewInsert) +{ + // Given 2 views, view 1 records changes, view does not record changes: + SwXTextDocument* pXTextDocument = createDoc(); + CPPUNIT_ASSERT(pXTextDocument); + ViewCallback aView1; + int nView1 = SfxLokHelper::getView(); + SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell(); + pWrtShell1->Insert(u"X"_ustr); + SfxLokHelper::createView(); + ViewCallback aView2; + int nView2 = SfxLokHelper::getView(); + SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell(); + SfxLokHelper::setView(nView1); + comphelper::dispatchCommand(".uno:TrackChanges", {}); + + // 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); + ViewCallback aView1; + int nView1 = SfxLokHelper::getView(); + SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell(); + pWrtShell1->Insert(u"test"_ustr); + SfxLokHelper::createView(); + ViewCallback aView2; + int nView2 = SfxLokHelper::getView(); + SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell(); + SfxLokHelper::setView(nView1); + comphelper::dispatchCommand(".uno:TrackChanges", {}); + + // 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()); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx b/sw/source/core/doc/DocumentRedlineManager.cxx index f6479c7709a8..f5c32a701981 100644 --- a/sw/source/core/doc/DocumentRedlineManager.cxx +++ b/sw/source/core/doc/DocumentRedlineManager.cxx @@ -1148,17 +1148,38 @@ DocumentRedlineManager::DocumentRedlineManager(SwDoc& i_rSwdoc) { } -RedlineFlags DocumentRedlineManager::GetRedlineFlags() const +RedlineFlags DocumentRedlineManager::GetRedlineFlags(const SwViewShell* pViewShell) const { - return meRedlineFlags; + if (!pViewShell) + { + SwDocShell* pDocShell = m_rDoc.GetDocShell(); + if (pDocShell) + { + pViewShell = pDocShell->GetWrtShell(); + } + } + + RedlineFlags eRedlineFlags = meRedlineFlags; + + if (pViewShell) + { + // Recording can be per-view, the rest is per-document. + eRedlineFlags = eRedlineFlags & ~RedlineFlags::On; + if (pViewShell->GetViewOptions()->IsRedlineRecordingOn()) + { + eRedlineFlags |= RedlineFlags::On; + } + } + + return eRedlineFlags; } void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode ) { - if( meRedlineFlags == eMode ) + if( GetRedlineFlags() == eMode ) return; - if( (RedlineFlags::ShowMask & meRedlineFlags) != (RedlineFlags::ShowMask & eMode) + if( (RedlineFlags::ShowMask & GetRedlineFlags()) != (RedlineFlags::ShowMask & eMode) || !(RedlineFlags::ShowMask & eMode) ) { bool bSaveInXMLImportFlag = m_rDoc.IsInXMLImport(); @@ -1236,27 +1257,27 @@ void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode ) bool DocumentRedlineManager::IsRedlineOn() const { - return IDocumentRedlineAccess::IsRedlineOn(meRedlineFlags); + return IDocumentRedlineAccess::IsRedlineOn(GetRedlineFlags()); } bool DocumentRedlineManager::IsIgnoreRedline() const { - return bool(RedlineFlags::Ignore & meRedlineFlags); + return bool(RedlineFlags::Ignore & GetRedlineFlags()); } void DocumentRedlineManager::SetRedlineFlags_intern(RedlineFlags eMode) { SwDocShell* pDocShell = m_rDoc.GetDocShell(); - SwWrtShell* pWrtShell = pDocShell ? pDocShell->GetWrtShell() : nullptr; - if (pWrtShell) + SwViewShell* pViewShell = pDocShell ? pDocShell->GetWrtShell() : nullptr; + if (pViewShell) { // Recording can be per-view, the rest is per-document. auto bRedlineRecordingOn = bool(eMode & RedlineFlags::On); - SwViewOption aOpt(*pWrtShell->GetViewOptions()); + SwViewOption aOpt(*pViewShell->GetViewOptions()); if (aOpt.IsRedlineRecordingOn() != bRedlineRecordingOn) { aOpt.SetRedlineRecordingOn(bRedlineRecordingOn); - pWrtShell->ApplyViewOptions(aOpt); + pViewShell->ApplyViewOptions(aOpt); } } @@ -1336,11 +1357,11 @@ DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCall { CHECK_REDLINE( *this ) - if (!IsRedlineOn() || IsShowOriginal(meRedlineFlags)) + if (!IsRedlineOn() || IsShowOriginal(GetRedlineFlags())) { if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() ) { - RedlineFlags eOld = meRedlineFlags; + RedlineFlags eOld = GetRedlineFlags(); // Set to NONE, so that the Delete::Redo merges the Redline data correctly! // The ShowMode needs to be retained! SetRedlineFlags_intern(eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore)); @@ -1777,7 +1798,7 @@ DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCall if( pRedl->IsOwnRedline( *pNewRedl ) && pRedl->CanCombine( *pNewRedl ) ) { - if( IsHideChanges( meRedlineFlags )) + if( IsHideChanges( GetRedlineFlags() )) { // Before we can merge, we make it visible! // We insert temporarily so that pNew is @@ -1826,7 +1847,7 @@ DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCall { // b62341295: Do not throw away redlines // even if they are not allowed to be combined - RedlineFlags eOld = meRedlineFlags; + RedlineFlags eOld = GetRedlineFlags(); if( !( eOld & RedlineFlags::DontCombineRedlines ) && pRedl->IsOwnRedline( *pNewRedl ) && // tdf#116084 tdf#121176 don't combine anonymized deletion @@ -1962,7 +1983,7 @@ DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCall pRedl->PushData( *pNewRedl ); delete pNewRedl; pNewRedl = nullptr; - if( IsHideChanges( meRedlineFlags )) + if( IsHideChanges( GetRedlineFlags() )) { pRedl->Hide(0, maRedlineTable.GetPos(pRedl)); } @@ -2038,7 +2059,7 @@ DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCall { pRedl->PushData( *pNewRedl ); pNewRedl->SetEnd( *pRStt, pEnd ); - if( IsHideChanges( meRedlineFlags )) + if( IsHideChanges( GetRedlineFlags() )) { maRedlineTable.Insert(pNewRedl); pRedl->Hide(0, maRedlineTable.GetPos(pRedl)); @@ -2066,7 +2087,7 @@ DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCall { pRedl->PushData( *pNewRedl ); pNewRedl->SetStart( *pREnd, pStt ); - if( IsHideChanges( meRedlineFlags )) + if( IsHideChanges( GetRedlineFlags() )) { maRedlineTable.Insert( pNewRedl ); pRedl->Hide(0, maRedlineTable.GetPos(pRedl)); @@ -2491,7 +2512,7 @@ bool DocumentRedlineManager::AppendTableRowRedline( SwTableRowRedline* pNewRedl CHECK_REDLINE( this ) */ - if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags)) + if (IsRedlineOn() && !IsShowOriginal(GetRedlineFlags())) { // #TODO - equivalent for 'SwTableRowRedline' /* @@ -2508,7 +2529,7 @@ bool DocumentRedlineManager::AppendTableRowRedline( SwTableRowRedline* pNewRedl /* if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() ) { - RedlineFlags eOld = meRedlineFlags; + RedlineFlags eOld = GetRedlineFlags(); // Set to NONE, so that the Delete::Redo merges the Redline data correctly! // The ShowMode needs to be retained! SetRedlineFlags_intern(eOld & ~(RedlineFlags::On | RedlineFlags::Ignore)); @@ -2533,7 +2554,7 @@ bool DocumentRedlineManager::AppendTableCellRedline( SwTableCellRedline* pNewRed CHECK_REDLINE( this ) */ - if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags)) + if (IsRedlineOn() && !IsShowOriginal(GetRedlineFlags())) { // #TODO - equivalent for 'SwTableCellRedline' /* @@ -2550,7 +2571,7 @@ bool DocumentRedlineManager::AppendTableCellRedline( SwTableCellRedline* pNewRed /* if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() ) { - RedlineFlags eOld = meRedlineFlags; + RedlineFlags eOld = GetRedlineFlags(); // Set to NONE, so that the Delete::Redo merges the Redline data correctly! // The ShowMode needs to be retained! SetRedlineFlags_intern(eOld & ~(RedlineFlags::On | RedlineFlags::Ignore)); @@ -2573,7 +2594,7 @@ void DocumentRedlineManager::CompressRedlines(size_t nStartIndex) CHECK_REDLINE( *this ) void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool) = nullptr; - RedlineFlags eShow = RedlineFlags::ShowMask & meRedlineFlags; + RedlineFlags eShow = RedlineFlags::ShowMask & GetRedlineFlags(); if( eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete)) pFnc = &SwRangeRedline::Show; else if (eShow == RedlineFlags::ShowInsert) @@ -3136,8 +3157,8 @@ bool DocumentRedlineManager::AcceptRedline(SwRedlineTable::size_type nPos, bool // Switch to visible in any case if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) != - (RedlineFlags::ShowMask & meRedlineFlags) ) - SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags ); + (RedlineFlags::ShowMask & GetRedlineFlags()) ) + SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | GetRedlineFlags() ); SwRangeRedline* pTmp = maRedlineTable[ nPos ]; bool bAnonym = pTmp->GetRedlineData(0).IsAnonymized(); @@ -3230,8 +3251,8 @@ bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete, { // Switch to visible in any case if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) != - (RedlineFlags::ShowMask & meRedlineFlags) ) - SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags ); + (RedlineFlags::ShowMask & GetRedlineFlags()) ) + SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | GetRedlineFlags() ); // The Selection is only in the ContentSection. If there are Redlines // to Non-ContentNodes before or after that, then the Selections @@ -3426,8 +3447,8 @@ bool DocumentRedlineManager::RejectRedline(SwRedlineTable::size_type nPos, // Switch to visible in any case if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) != - (RedlineFlags::ShowMask & meRedlineFlags) ) - SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags ); + (RedlineFlags::ShowMask & GetRedlineFlags()) ) + SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | GetRedlineFlags() ); SwRangeRedline* pTmp = maRedlineTable[ nPos ]; bool bAnonym = pTmp->GetRedlineData(0).IsAnonymized(); @@ -3520,8 +3541,8 @@ bool DocumentRedlineManager::RejectRedline( const SwPaM& rPam, bool bCallDelete, { // Switch to visible in any case if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) != - (RedlineFlags::ShowMask & meRedlineFlags) ) - SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags ); + (RedlineFlags::ShowMask & GetRedlineFlags()) ) + SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | GetRedlineFlags() ); // The Selection is only in the ContentSection. If there are Redlines // to Non-ContentNodes before or after that, then the Selections diff --git a/sw/source/core/edit/edredln.cxx b/sw/source/core/edit/edredln.cxx index 1778745f57e4..76f529dff7b0 100644 --- a/sw/source/core/edit/edredln.cxx +++ b/sw/source/core/edit/edredln.cxx @@ -26,7 +26,7 @@ RedlineFlags SwEditShell::GetRedlineFlags() const { - return GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags(); + return GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags(this); } void SwEditShell::SetRedlineFlags( RedlineFlags eMode ) diff --git a/sw/source/core/inc/DocumentRedlineManager.hxx b/sw/source/core/inc/DocumentRedlineManager.hxx index 48bc454f2fd9..5476c34e5407 100644 --- a/sw/source/core/inc/DocumentRedlineManager.hxx +++ b/sw/source/core/inc/DocumentRedlineManager.hxx @@ -34,9 +34,9 @@ public: /** * Replaced by SwRootFrame::IsHideRedlines() (this is model-level redline - * hiding). + * hiding) for hide/show. */ - virtual RedlineFlags GetRedlineFlags() const override; + virtual RedlineFlags GetRedlineFlags(const SwViewShell* pViewShell = nullptr) const override; virtual void SetRedlineFlags_intern(/*[in]*/RedlineFlags eMode) override; diff --git a/sw/source/uibase/app/docsh.cxx b/sw/source/uibase/app/docsh.cxx index e74ecfa34974..6c64c98d0e74 100644 --- a/sw/source/uibase/app/docsh.cxx +++ b/sw/source/uibase/app/docsh.cxx @@ -1346,11 +1346,22 @@ const ::sfx2::IXmlIdRegistry* SwDocShell::GetXmlIdRegistry() const return m_xDoc ? &m_xDoc->GetXmlIdRegistry() : nullptr; } -bool SwDocShell::IsChangeRecording() const +bool SwDocShell::IsChangeRecording(SfxViewShell* pViewShell) const { - if (!m_pWrtShell) + SwWrtShell* pWrtShell = nullptr; + auto pView = dynamic_cast<SwView*>(pViewShell); + if (pView) + { + pWrtShell = pView->GetWrtShellPtr(); + } + if (!pWrtShell) + { + pWrtShell = m_pWrtShell; + } + + if (!pWrtShell) return false; - return bool(m_pWrtShell->GetRedlineFlags() & RedlineFlags::On); + return bool(pWrtShell->GetRedlineFlags() & RedlineFlags::On); } bool SwDocShell::HasChangeRecordProtection() const diff --git a/sw/source/uibase/uiview/viewstat.cxx b/sw/source/uibase/uiview/viewstat.cxx index 3c624274f4c5..e7e8957846b1 100644 --- a/sw/source/uibase/uiview/viewstat.cxx +++ b/sw/source/uibase/uiview/viewstat.cxx @@ -325,7 +325,7 @@ void SwView::GetState(SfxItemSet &rSet) } break; case FN_REDLINE_ON: - rSet.Put( SfxBoolItem( nWhich, GetDocShell()->IsChangeRecording() ) ); + rSet.Put( SfxBoolItem( nWhich, GetDocShell()->IsChangeRecording(this) ) ); // When the view is new (e.g. on load), show the Hidden Track Changes infobar // if Show Changes is disabled, but recording of changes is enabled // or hidden tracked changes are there already in the document. @@ -333,7 +333,7 @@ void SwView::GetState(SfxItemSet &rSet) // enabled, see in sfx2. if ( m_bForceChangesToolbar && m_pWrtShell->GetLayout()->IsHideRedlines() ) { - bool isRecording = GetDocShell()->IsChangeRecording(); + bool isRecording = GetDocShell()->IsChangeRecording(this); bool hasRecorded = m_pWrtShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().size(); if ( isRecording || hasRecorded )