sw/inc/PostItMgr.hxx | 16 sw/qa/extras/uiwriter/data/tdf108791_comments_with_tracked_changes.docx |binary sw/qa/extras/uiwriter/data/tdf108791_comments_with_tracked_changes.fodt | 40 + sw/qa/extras/uiwriter/uiwriter11.cxx | 290 ++++++++++ sw/source/uibase/docvw/AnnotationWin.cxx | 1 sw/source/uibase/docvw/PostItMgr.cxx | 44 + 6 files changed, 390 insertions(+), 1 deletion(-)
New commits: commit 3bf75e92e9ea089b707cb4e163def156ad28f7e0 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Fri Aug 29 19:11:39 2025 +0500 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Tue Sep 2 19:53:45 2025 +0200 tdf#108791: Ignore deleted comments in redlines of non-native documents ODF (and its native applications) can properly handle redline data of deleted comments. Other formats (or rather, respective applications like MS Word) don't understand the "comment anchor deletion" markup, and show the deleted comment as normal. Commit 419b70b5d4db227509614bdea5b4b89bcf7a6032 (tdf#105485 DOCX: import deleted comments as deleted, 2019-08-26) had implemented correct round- trip of the redline through DOCX. However, since Word doesn't support that, it created a situation when users can't correctly collaborate on documents. This change introduces a special handling of all non-native file formats in Writer, where deletion of a comment is handled just as in Word: it is deleted immediately, instead of marking as deleted. This doesn't affect deleting ranges of text, which contain comments: they are handled by both Writer and Word correctly. The solution works without any compatibility flags. This allows to have consistent behavior (same types of files will work the same way on all systems), zero configuration, and maximum functionality using native formats, unaffected by any settings. Change-Id: I2f8d3caaecd78af38e11a1ab6900d0afd73ed614 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190376 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/sw/inc/PostItMgr.hxx b/sw/inc/PostItMgr.hxx index 02acfcdd7b99..9ea58c7fa1d5 100644 --- a/sw/inc/PostItMgr.hxx +++ b/sw/inc/PostItMgr.hxx @@ -206,7 +206,7 @@ class SAL_DLLPUBLIC_RTTI SwPostItMgr final : public SfxListener, tools::Long GetNextBorder(); sw::annotation::SwAnnotationWin* GetActiveSidebarWin() { return mpActivePostIt; } - void SetActiveSidebarWin( sw::annotation::SwAnnotationWin* p); + SW_DLLPUBLIC void SetActiveSidebarWin( sw::annotation::SwAnnotationWin* p); SW_DLLPUBLIC bool HasActiveSidebarWin() const; bool HasActiveAnnotationWin() const; void GrabFocusOnActiveSidebarWin(); @@ -253,6 +253,20 @@ class SAL_DLLPUBLIC_RTTI SwPostItMgr final : public SfxListener, void PaintTile(OutputDevice& rRenderContext); sw::sidebarwindows::SidebarPosition GetSidebarPos(const Point& rPointLogic); + + // The commands that directly delete comments (as opposed to deletion of the text that the + // comments are anchored to) behave differently, depending on the document type, when the + // change tracking is on. For ODF, we allow comments to be marked as deleted; for external + // document types, we delete them immediately, as if change tracking os off. + class CommentDeleteFlagsRestore + { + public: + virtual ~CommentDeleteFlagsRestore() = default; + }; + // Checks if the respective configuration must be changed for 'delete' operation, makes the + // change if needed, and returns a RAII object that will restore the original configuration + // in its dtor. + [[nodiscard]] std::unique_ptr<CommentDeleteFlagsRestore> ConfigureForCommentDelete(); }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/extras/uiwriter/data/tdf108791_comments_with_tracked_changes.docx b/sw/qa/extras/uiwriter/data/tdf108791_comments_with_tracked_changes.docx new file mode 100644 index 000000000000..9eefbdd403b0 Binary files /dev/null and b/sw/qa/extras/uiwriter/data/tdf108791_comments_with_tracked_changes.docx differ diff --git a/sw/qa/extras/uiwriter/data/tdf108791_comments_with_tracked_changes.fodt b/sw/qa/extras/uiwriter/data/tdf108791_comments_with_tracked_changes.fodt new file mode 100644 index 000000000000..ed727ab0fd69 --- /dev/null +++ b/sw/qa/extras/uiwriter/data/tdf108791_comments_with_tracked_changes.fodt @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:ooow="http://openoffice.org/200 4/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns :css3t="http://www.w3.org/TR/css3-text/" xmlns:officeooo="http://openoffice.org/2009/office" office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:settings> + <config:config-item-set config:name="ooo:view-settings"> + <config:config-item config:name="ShowRedlineChanges" config:type="boolean">true</config:config-item> + </config:config-item-set> + </office:settings> + <office:body> + <office:text> + <text:tracked-changes text:track-changes="true"/> + <text:p>First paragraph <office:annotation office:name="__Annotation__1"> + <dc:creator>Author1</dc:creator> + <dc:date>2025-09-01T10:00:00</dc:date> + <text:p>Comment thread 1 head</text:p> + </office:annotation><office:annotation loext:parent-name="__Annotation__1"> + <dc:creator>Author2</dc:creator> + <dc:date>2025-09-01T10:02:00</dc:date> + <text:p>Comment thread 1 reply 1</text:p> + </office:annotation><office:annotation loext:parent-name="__Annotation__1"> + <dc:creator>Author3</dc:creator> + <dc:date>2025-09-01T10:04:00</dc:date> + <text:p>Comment thread 1 reply 2</text:p> + </office:annotation>text.</text:p> + <text:p>Second paragraph <office:annotation office:name="__Annotation__2"> + <dc:creator>Author1</dc:creator> + <dc:date>2025-09-01T10:01:00</dc:date> + <text:p>Comment thread 2 head</text:p> + </office:annotation><office:annotation loext:parent-name="__Annotation__2"> + <dc:creator>Author2</dc:creator> + <dc:date>2025-09-01T10:03:00</dc:date> + <text:p>Comment thread 2 reply 1</text:p> + </office:annotation><office:annotation loext:parent-name="__Annotation__2"> + <dc:creator>Author3</dc:creator> + <dc:date>2025-09-01T10:05:00</dc:date> + <text:p>Comment thread 2 reply 2</text:p> + </office:annotation>text.</text:p> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/extras/uiwriter/uiwriter11.cxx b/sw/qa/extras/uiwriter/uiwriter11.cxx index 4fa9f14f4238..13aab01fd1c8 100644 --- a/sw/qa/extras/uiwriter/uiwriter11.cxx +++ b/sw/qa/extras/uiwriter/uiwriter11.cxx @@ -15,7 +15,11 @@ #include <comphelper/scopeguard.hxx> #include <comphelper/configuration.hxx> +#include <AnnotationWin.hxx> +#include <cmdid.h> +#include <docufld.hxx> #include <edtwin.hxx> +#include <PostItMgr.hxx> #include <view.hxx> #include <wrtsh.hxx> #include <unotxdoc.hxx> @@ -66,6 +70,292 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest11, testTdf167760_numberedPara) CPPUNIT_ASSERT_EQUAL(OUString(), getProperty<OUString>(getParagraph(1), "ListLabelString")); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest11, testTdf108791) +{ + auto getPostItMgr = [](SwDocShell* pDocShell) { + CPPUNIT_ASSERT(pDocShell); + SwView* pView = pDocShell->GetView(); + CPPUNIT_ASSERT(pView); + SwPostItMgr* pPostItMgr = pView->GetPostItMgr(); + CPPUNIT_ASSERT(pPostItMgr); + return pPostItMgr; + }; + + // Given a document with tracked changes enabled, having some threads of comments: + + createSwDoc("tdf108791_comments_with_tracked_changes.fodt"); + + { + // Test "Delete Comment": the selected comment must be marked as deleted + + SwPostItMgr* pPostItMgr = getPostItMgr(getSwDocShell()); + auto& aPostItFields = pPostItMgr->GetPostItFields(); + + CPPUNIT_ASSERT_EQUAL(size_t(6), aPostItFields.size()); + for (size_t i = 0; i < aPostItFields.size(); ++i) + { + CPPUNIT_ASSERT(aPostItFields[i]); + CPPUNIT_ASSERT_EQUAL(SwPostItHelper::SwLayoutStatus::VISIBLE, + aPostItFields[i]->mLayoutStatus); + } + + // Select "Comment thread 1 reply 2" + CPPUNIT_ASSERT(aPostItFields[2]->mpPostIt); + const SwPostItField* pPostItField = aPostItFields[2]->mpPostIt->GetPostItField(); + CPPUNIT_ASSERT(pPostItField); + CPPUNIT_ASSERT_EQUAL(u"Comment thread 1 reply 2"_ustr, pPostItField->GetText()); + + pPostItMgr->SetActiveSidebarWin(aPostItFields[2]->mpPostIt); + dispatchCommand(mxComponent, u".uno:DeleteComment"_ustr, {}); + + CPPUNIT_ASSERT_EQUAL(size_t(6), aPostItFields.size()); + for (size_t i = 0; i < aPostItFields.size(); ++i) + { + CPPUNIT_ASSERT(aPostItFields[i]); + CPPUNIT_ASSERT_EQUAL(i == 2 ? SwPostItHelper::SwLayoutStatus::DELETED + : SwPostItHelper::SwLayoutStatus::VISIBLE, + aPostItFields[i]->mLayoutStatus); + } + } + + // load it anew + createSwDoc("tdf108791_comments_with_tracked_changes.fodt"); + + { + // Test "Delete Comment Tread": the comment, and all its thread, must be marked as deleted + + SwPostItMgr* pPostItMgr = getPostItMgr(getSwDocShell()); + auto& aPostItFields = pPostItMgr->GetPostItFields(); + + CPPUNIT_ASSERT_EQUAL(size_t(6), aPostItFields.size()); + for (size_t i = 0; i < aPostItFields.size(); ++i) + { + CPPUNIT_ASSERT(aPostItFields[i]); + CPPUNIT_ASSERT_EQUAL(SwPostItHelper::SwLayoutStatus::VISIBLE, + aPostItFields[i]->mLayoutStatus); + } + + // Select "Comment thread 1 reply 2" + CPPUNIT_ASSERT(aPostItFields[2]->mpPostIt); + const SwPostItField* pPostItField = aPostItFields[2]->mpPostIt->GetPostItField(); + CPPUNIT_ASSERT(pPostItField); + CPPUNIT_ASSERT_EQUAL(u"Comment thread 1 reply 2"_ustr, pPostItField->GetText()); + + pPostItMgr->SetActiveSidebarWin(aPostItFields[2]->mpPostIt); + dispatchCommand(mxComponent, u".uno:DeleteCommentThread"_ustr, {}); + + CPPUNIT_ASSERT_EQUAL(size_t(6), aPostItFields.size()); + for (size_t i = 0; i < aPostItFields.size(); ++i) + { + CPPUNIT_ASSERT(aPostItFields[i]); + CPPUNIT_ASSERT_EQUAL(i <= 2 ? SwPostItHelper::SwLayoutStatus::DELETED + : SwPostItHelper::SwLayoutStatus::VISIBLE, + aPostItFields[i]->mLayoutStatus); + } + } + + // load it anew + createSwDoc("tdf108791_comments_with_tracked_changes.fodt"); + + { + // Test "Delete Comments by Author3": the author's comments must be marked as deleted + + SwPostItMgr* pPostItMgr = getPostItMgr(getSwDocShell()); + auto& aPostItFields = pPostItMgr->GetPostItFields(); + + CPPUNIT_ASSERT_EQUAL(size_t(6), aPostItFields.size()); + for (size_t i = 0; i < aPostItFields.size(); ++i) + { + CPPUNIT_ASSERT(aPostItFields[i]); + CPPUNIT_ASSERT_EQUAL(SwPostItHelper::SwLayoutStatus::VISIBLE, + aPostItFields[i]->mLayoutStatus); + } + + // Select "Comment thread 1 reply 2" + CPPUNIT_ASSERT(aPostItFields[2]->mpPostIt); + const SwPostItField* pPostItField = aPostItFields[2]->mpPostIt->GetPostItField(); + CPPUNIT_ASSERT(pPostItField); + CPPUNIT_ASSERT_EQUAL(u"Author3"_ustr, pPostItField->GetPar1()); + + pPostItMgr->SetActiveSidebarWin(aPostItFields[2]->mpPostIt); + dispatchCommand(mxComponent, u".uno:DeleteAuthor"_ustr, {}); + + CPPUNIT_ASSERT_EQUAL(size_t(6), aPostItFields.size()); + for (size_t i = 0; i < aPostItFields.size(); ++i) + { + CPPUNIT_ASSERT(aPostItFields[i]); + CPPUNIT_ASSERT(aPostItFields[i]->mpPostIt); + pPostItField = aPostItFields[i]->mpPostIt->GetPostItField(); + CPPUNIT_ASSERT(pPostItField); + CPPUNIT_ASSERT_EQUAL(pPostItField->GetPar1() == "Author3" + ? SwPostItHelper::SwLayoutStatus::DELETED + : SwPostItHelper::SwLayoutStatus::VISIBLE, + aPostItFields[i]->mLayoutStatus); + } + } + + // load it anew + createSwDoc("tdf108791_comments_with_tracked_changes.fodt"); + + { + // Test "Delete All Comments": all comments must be marked as deleted + + SwPostItMgr* pPostItMgr = getPostItMgr(getSwDocShell()); + auto& aPostItFields = pPostItMgr->GetPostItFields(); + + CPPUNIT_ASSERT_EQUAL(size_t(6), aPostItFields.size()); + for (size_t i = 0; i < aPostItFields.size(); ++i) + { + CPPUNIT_ASSERT(aPostItFields[i]); + CPPUNIT_ASSERT_EQUAL(SwPostItHelper::SwLayoutStatus::VISIBLE, + aPostItFields[i]->mLayoutStatus); + } + + // Select any comment + CPPUNIT_ASSERT(aPostItFields[2]->mpPostIt); + + pPostItMgr->SetActiveSidebarWin(aPostItFields[2]->mpPostIt); + dispatchCommand(mxComponent, u".uno:DeleteAllNotes"_ustr, {}); + + CPPUNIT_ASSERT_EQUAL(size_t(6), aPostItFields.size()); + for (size_t i = 0; i < aPostItFields.size(); ++i) + { + CPPUNIT_ASSERT(aPostItFields[i]); + CPPUNIT_ASSERT_EQUAL(SwPostItHelper::SwLayoutStatus::DELETED, + aPostItFields[i]->mLayoutStatus); + } + } + + // Now test the same with an equivalent DOCX: comment deletion must delete immediately + + createSwDoc("tdf108791_comments_with_tracked_changes.docx"); + + { + // Test "Delete Comment": the selected comment must be deleted + + SwPostItMgr* pPostItMgr = getPostItMgr(getSwDocShell()); + auto& aPostItFields = pPostItMgr->GetPostItFields(); + + CPPUNIT_ASSERT_EQUAL(size_t(6), aPostItFields.size()); + for (size_t i = 0; i < aPostItFields.size(); ++i) + { + CPPUNIT_ASSERT(aPostItFields[i]); + CPPUNIT_ASSERT_EQUAL(SwPostItHelper::SwLayoutStatus::VISIBLE, + aPostItFields[i]->mLayoutStatus); + } + + // Select "Comment thread 1 reply 2" + CPPUNIT_ASSERT(aPostItFields[2]->mpPostIt); + const SwPostItField* pPostItField = aPostItFields[2]->mpPostIt->GetPostItField(); + CPPUNIT_ASSERT(pPostItField); + CPPUNIT_ASSERT_EQUAL(u"Comment thread 1 reply 2"_ustr, pPostItField->GetText()); + + pPostItMgr->SetActiveSidebarWin(aPostItFields[2]->mpPostIt); + dispatchCommand(mxComponent, u".uno:DeleteComment"_ustr, {}); + + CPPUNIT_ASSERT_EQUAL(size_t(5), aPostItFields.size()); + for (size_t i = 0; i < aPostItFields.size(); ++i) + { + CPPUNIT_ASSERT(aPostItFields[i]); + CPPUNIT_ASSERT(aPostItFields[i]->mpPostIt); + CPPUNIT_ASSERT(aPostItFields[i]->mpPostIt->GetPostItField()); + CPPUNIT_ASSERT(aPostItFields[i]->mpPostIt->GetPostItField()->GetText() + != "Comment thread 1 reply 2"); + } + } + + // load it anew + createSwDoc("tdf108791_comments_with_tracked_changes.docx"); + + { + // Test "Delete Comment Tread": the comment, and all its thread, must be deleted + + SwPostItMgr* pPostItMgr = getPostItMgr(getSwDocShell()); + auto& aPostItFields = pPostItMgr->GetPostItFields(); + + CPPUNIT_ASSERT_EQUAL(size_t(6), aPostItFields.size()); + for (size_t i = 0; i < aPostItFields.size(); ++i) + { + CPPUNIT_ASSERT(aPostItFields[i]); + CPPUNIT_ASSERT_EQUAL(SwPostItHelper::SwLayoutStatus::VISIBLE, + aPostItFields[i]->mLayoutStatus); + } + + // Select "Comment thread 1 reply 2" + CPPUNIT_ASSERT(aPostItFields[2]->mpPostIt); + const SwPostItField* pPostItField = aPostItFields[2]->mpPostIt->GetPostItField(); + CPPUNIT_ASSERT(pPostItField); + CPPUNIT_ASSERT_EQUAL(u"Comment thread 1 reply 2"_ustr, pPostItField->GetText()); + + pPostItMgr->SetActiveSidebarWin(aPostItFields[2]->mpPostIt); + dispatchCommand(mxComponent, u".uno:DeleteCommentThread"_ustr, {}); + + CPPUNIT_ASSERT_EQUAL(size_t(3), aPostItFields.size()); + } + + // load it anew + createSwDoc("tdf108791_comments_with_tracked_changes.docx"); + + { + // Test "Delete Comments by Author3": the author's comments must be deleted + + SwPostItMgr* pPostItMgr = getPostItMgr(getSwDocShell()); + auto& aPostItFields = pPostItMgr->GetPostItFields(); + + CPPUNIT_ASSERT_EQUAL(size_t(6), aPostItFields.size()); + for (size_t i = 0; i < aPostItFields.size(); ++i) + { + CPPUNIT_ASSERT(aPostItFields[i]); + CPPUNIT_ASSERT_EQUAL(SwPostItHelper::SwLayoutStatus::VISIBLE, + aPostItFields[i]->mLayoutStatus); + } + + // Select "Comment thread 1 reply 2" + CPPUNIT_ASSERT(aPostItFields[2]->mpPostIt); + const SwPostItField* pPostItField = aPostItFields[2]->mpPostIt->GetPostItField(); + CPPUNIT_ASSERT(pPostItField); + CPPUNIT_ASSERT_EQUAL(u"Author3"_ustr, pPostItField->GetPar1()); + + pPostItMgr->SetActiveSidebarWin(aPostItFields[2]->mpPostIt); + dispatchCommand(mxComponent, u".uno:DeleteAuthor"_ustr, {}); + + CPPUNIT_ASSERT_EQUAL(size_t(4), aPostItFields.size()); + for (size_t i = 0; i < aPostItFields.size(); ++i) + { + CPPUNIT_ASSERT(aPostItFields[i]); + CPPUNIT_ASSERT(aPostItFields[i]->mpPostIt); + CPPUNIT_ASSERT(aPostItFields[i]->mpPostIt->GetPostItField()); + CPPUNIT_ASSERT(aPostItFields[i]->mpPostIt->GetPostItField()->GetPar1() != "Author3"); + } + } + + // load it anew + createSwDoc("tdf108791_comments_with_tracked_changes.docx"); + + { + // Test "Delete All Comments": all comments must be deleted + + SwPostItMgr* pPostItMgr = getPostItMgr(getSwDocShell()); + auto& aPostItFields = pPostItMgr->GetPostItFields(); + + CPPUNIT_ASSERT_EQUAL(size_t(6), aPostItFields.size()); + for (size_t i = 0; i < aPostItFields.size(); ++i) + { + CPPUNIT_ASSERT(aPostItFields[i]); + CPPUNIT_ASSERT_EQUAL(SwPostItHelper::SwLayoutStatus::VISIBLE, + aPostItFields[i]->mLayoutStatus); + } + + // Select any comment + CPPUNIT_ASSERT(aPostItFields[2]->mpPostIt); + + pPostItMgr->SetActiveSidebarWin(aPostItFields[2]->mpPostIt); + dispatchCommand(mxComponent, u".uno:DeleteAllNotes"_ustr, {}); + + CPPUNIT_ASSERT(aPostItFields.empty()); + } +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/uibase/docvw/AnnotationWin.cxx b/sw/source/uibase/docvw/AnnotationWin.cxx index 21a79b078755..63c80ddcc3fc 100644 --- a/sw/source/uibase/docvw/AnnotationWin.cxx +++ b/sw/source/uibase/docvw/AnnotationWin.cxx @@ -417,6 +417,7 @@ void SwAnnotationWin::Delete() // we delete the field directly, the Mgr cleans up the PostIt by listening GrabFocusToDocument(); pWrtShell->ClearMark(); + auto restoreGuard = mrMgr.ConfigureForCommentDelete(); pWrtShell->DelRight(); } diff --git a/sw/source/uibase/docvw/PostItMgr.cxx b/sw/source/uibase/docvw/PostItMgr.cxx index 237c6bc0fb46..4fb7a9f33406 100644 --- a/sw/source/uibase/docvw/PostItMgr.cxx +++ b/sw/source/uibase/docvw/PostItMgr.cxx @@ -45,6 +45,7 @@ #include <doc.hxx> #include <IDocumentSettingAccess.hxx> #include <IDocumentFieldsAccess.hxx> +#include <IDocumentRedlineAccess.hxx> #if ENABLE_YRS #include <IDocumentState.hxx> #endif @@ -64,6 +65,8 @@ #include <strings.hrc> #include <cmdid.h> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> #include <sfx2/request.hxx> #include <sfx2/event.hxx> #include <svl/srchitem.hxx> @@ -372,6 +375,29 @@ namespace { } }; +// a RAII object that sets Ignore redline flag, and restores previous redline flags in dtor +class CommentDeleteFlagsRestoreImpl : public SwPostItMgr::CommentDeleteFlagsRestore +{ +public: + CommentDeleteFlagsRestoreImpl(SwWrtShell* shell) + : m_pWrtShell(shell) + , m_eRestreFlags(m_pWrtShell->GetRedlineFlags()) + { + m_pWrtShell->SetRedlineFlags(m_eRestreFlags | RedlineFlags::Ignore); + } + ~CommentDeleteFlagsRestoreImpl() { m_pWrtShell->SetRedlineFlags(m_eRestreFlags); } + +private: + SwWrtShell* m_pWrtShell; + RedlineFlags m_eRestreFlags; +}; + +bool isOwnFileFormat(SfxMedium* pMedium) +{ + // Assume that unsaved documents are own format + return !pMedium || !pMedium->GetFilter() || pMedium->GetFilter()->IsOwnFormat(); +} + } // anonymous namespace SwPostItMgr::SwPostItMgr(SwView* pView) @@ -1640,6 +1666,17 @@ static bool ConfirmDeleteAll(const SwView& pView, const OUString& sText) return bConfirm; } +std::unique_ptr<SwPostItMgr::CommentDeleteFlagsRestore> SwPostItMgr::ConfigureForCommentDelete() +{ + if (!mpWrtShell->IsRedlineOn()) + return {}; // No track changes - no need to disable it + if (isOwnFileFormat(mpView->GetDocShell()->GetMedium())) + return {}; // Format is smart enough to handle deleted comments in redlines + + return std::unique_ptr<CommentDeleteFlagsRestore>( + new CommentDeleteFlagsRestoreImpl(mpWrtShell)); +} + // copy to new vector, otherwise RemoveItem would operate and delete stuff on mvPostItFields as well // RemoveItem will clean up the core field and visible postit if necessary // we cannot just delete everything as before, as postits could move into change tracking @@ -1667,11 +1704,13 @@ void SwPostItMgr::Delete(const OUString& rAuthor) IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess()); IsFieldNotDeleted aFilter2(rIDRA, aFilter); FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter2); + auto restoreGuard = ConfigureForCommentDelete(); while (const SwFormatField* pField = aStack.pop()) { if (mpWrtShell->GotoField(*pField)) mpWrtShell->DelRight(); } + restoreGuard.reset(); mpWrtShell->EndUndo(); PrepareView(); mpWrtShell->EndAllAction(); @@ -1702,7 +1741,10 @@ void SwPostItMgr::Delete(sal_uInt32 nPostItId) FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter2); const SwFormatField* pField = aStack.pop(); if (pField && mpWrtShell->GotoField(*pField)) + { + auto restoreGuard = ConfigureForCommentDelete(); mpWrtShell->DelRight(); + } mpWrtShell->EndUndo(); PrepareView(); mpWrtShell->EndAllAction(); @@ -1802,11 +1844,13 @@ void SwPostItMgr::Delete() IsFieldNotDeleted aFilter2(rIDRA, aFilter); FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter2); + auto restoreGuard = ConfigureForCommentDelete(); while (const SwFormatField* pField = aStack.pop()) { if (mpWrtShell->GotoField(*pField)) mpWrtShell->DelRight(); } + restoreGuard.reset(); mpWrtShell->EndUndo(); PrepareView();