sw/inc/swmodule.hxx | 5 +-- sw/qa/core/text/itrpaint.cxx | 61 +++++++++++++++++++++++++++++++------- sw/source/core/text/itrpaint.cxx | 31 ------------------- sw/source/core/text/redlnitr.cxx | 16 ++++++++- sw/source/core/text/txtfld.cxx | 4 +- sw/source/core/text/txtftn.cxx | 5 +-- sw/source/uibase/app/swmodul1.cxx | 33 +++++++++++++++++--- 7 files changed, 101 insertions(+), 54 deletions(-)
New commits: commit b3656a81b40bd5012d6e9a52527e241cd6586571 Author: Miklos Vajna <[email protected]> AuthorDate: Thu Nov 27 08:38:13 2025 +0100 Commit: Miklos Vajna <[email protected]> CommitDate: Sat Nov 29 08:21:02 2025 +0100 cool#13574 sw lok: improve non-standard redline render mode colors Instead of completely omitting insertions and deletions when requested, just de-saturate the original colors when the content is to be semi-hidden. Do this by undoing the changes to SwTextPainter::DrawTextLine() and instead let SwModule::GetInsertAuthorAttr() / SwModule::GetDeletedAuthorAttr() decide if the standard rendering for that redline is OK or we want a custom color. If we want something custom, then halve the saturation in lcl_FillAuthorAttr() to paint it semi-hidden. Also adapt the testcase, to check the saturation instead of the number of painted strings. Change-Id: I62362463bd1b6d6968a503c802e17a56a1aea1b1 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194774 Tested-by: Jenkins Reviewed-by: Miklos Vajna <[email protected]> diff --git a/sw/inc/swmodule.hxx b/sw/inc/swmodule.hxx index d860b75f62af..d3a42be17ba5 100644 --- a/sw/inc/swmodule.hxx +++ b/sw/inc/swmodule.hxx @@ -70,6 +70,7 @@ namespace com::sun::star::linguistic2 { class XLanguageGuessing; } namespace ooo::vba { class XSinkCaller; } class SwLinguServiceEventListener; class SwTableAutoFormatTable; +enum class SwRedlineRenderMode; class SAL_DLLPUBLIC_RTTI SwModule final : public SfxModule, public SfxListener, public utl::ConfigurationListener { @@ -207,8 +208,8 @@ public: std::size_t InsertRedlineAuthor(const OUString& rAuthor); SW_DLLPUBLIC void SetRedlineAuthor(const OUString& rAuthor); // for unit tests - void GetInsertAuthorAttr(std::size_t nAuthor, SfxItemSet &rSet); - void GetDeletedAuthorAttr(std::size_t nAuthor, SfxItemSet &rSet); + void GetInsertAuthorAttr(std::size_t nAuthor, SfxItemSet &rSet, SwRedlineRenderMode eMode); + void GetDeletedAuthorAttr(std::size_t nAuthor, SfxItemSet &rSet, SwRedlineRenderMode eMode); void GetFormatAuthorAttr(std::size_t nAuthor, SfxItemSet &rSet); sal_uInt16 GetRedlineMarkPos() const; diff --git a/sw/qa/core/text/itrpaint.cxx b/sw/qa/core/text/itrpaint.cxx index 6f08dfc82a89..7eaed14187c8 100644 --- a/sw/qa/core/text/itrpaint.cxx +++ b/sw/qa/core/text/itrpaint.cxx @@ -11,6 +11,8 @@ #include <memory> +#include <o3tl/string_view.hxx> + #include <docsh.hxx> #include <wrtsh.hxx> #include <ndtxt.hxx> @@ -27,9 +29,21 @@ public: } }; +/// #RRGGBB -> HSL saturation. +sal_Int16 GetColorSaturation(std::u16string_view rRGB) +{ + Color aColor(o3tl::toInt32(rRGB.substr(1, 2), 16), o3tl::toInt32(rRGB.substr(3, 2), 16), + o3tl::toInt32(rRGB.substr(5, 2), 16)); + sal_uInt16 nHue; + sal_uInt16 nSaturation; + sal_uInt16 nBrightness; + aColor.RGBtoHSB(nHue, nSaturation, nBrightness); + return nSaturation; +} + CPPUNIT_TEST_FIXTURE(Test, testRedlineRenderModeOmitInsertDelete) { - // Default rendering: + // Default rendering: default, normal saturation, normal saturation. createSwDoc("redline.docx"); SwDocShell* pDocShell = getSwDocShell(); @@ -42,14 +56,23 @@ CPPUNIT_TEST_FIXTURE(Test, testRedlineRenderModeOmitInsertDelete) sal_Int32 nIndex1 = getXPath(pXmlDoc, "(//textarray)[1]", "index").toInt32(); sal_Int32 nLength1 = getXPath(pXmlDoc, "(//textarray)[1]", "length").toInt32(); CPPUNIT_ASSERT_EQUAL(u"baseline "_ustr, aContent.copy(nIndex1, nLength1)); + OUString aColor1 + = getXPath(pXmlDoc, "(//textarray)[1]/preceding-sibling::textcolor[1]", "color"); + CPPUNIT_ASSERT_EQUAL(u"#000000"_ustr, aColor1); sal_Int32 nIndex2 = getXPath(pXmlDoc, "(//textarray)[2]", "index").toInt32(); sal_Int32 nLength2 = getXPath(pXmlDoc, "(//textarray)[2]", "length").toInt32(); CPPUNIT_ASSERT_EQUAL(u"oldcontent"_ustr, aContent.copy(nIndex2, nLength2)); + OUString aColor2 + = getXPath(pXmlDoc, "(//textarray)[2]/preceding-sibling::textcolor[1]", "color"); + CPPUNIT_ASSERT_GREATER(static_cast<sal_Int16>(50), GetColorSaturation(aColor2)); sal_Int32 nIndex3 = getXPath(pXmlDoc, "(//textarray)[3]", "index").toInt32(); sal_Int32 nLength3 = getXPath(pXmlDoc, "(//textarray)[3]", "length").toInt32(); CPPUNIT_ASSERT_EQUAL(u"newcontent"_ustr, aContent.copy(nIndex3, nLength3)); + OUString aColor3 + = getXPath(pXmlDoc, "(//textarray)[3]/preceding-sibling::textcolor[1]", "color"); + CPPUNIT_ASSERT_GREATER(static_cast<sal_Int16>(50), GetColorSaturation(aColor3)); - // Omit inserts: + // Omit inserts: default, normal saturation, de-saturated. SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); SwViewOption aOpt(*pWrtShell->GetViewOptions()); aOpt.SetRedlineRenderMode(SwRedlineRenderMode::OmitInserts); @@ -58,32 +81,50 @@ CPPUNIT_TEST_FIXTURE(Test, testRedlineRenderModeOmitInsertDelete) xMetaFile = pDocShell->GetPreviewMetaFile(); pXmlDoc = dumpAndParse(dumper, *xMetaFile); - // Without the accompanying fix in place, this test would have failed with: - // - Expected: 2 - // - Actual : 3 - // i.e. the inserts were not omitted. - assertXPath(pXmlDoc, "//textarray", 2); + assertXPath(pXmlDoc, "//textarray", 3); nIndex1 = getXPath(pXmlDoc, "(//textarray)[1]", "index").toInt32(); nLength1 = getXPath(pXmlDoc, "(//textarray)[1]", "length").toInt32(); CPPUNIT_ASSERT_EQUAL(u"baseline "_ustr, aContent.copy(nIndex1, nLength1)); + aColor1 = getXPath(pXmlDoc, "(//textarray)[1]/preceding-sibling::textcolor[1]", "color"); + CPPUNIT_ASSERT_EQUAL(u"#000000"_ustr, aColor1); nIndex2 = getXPath(pXmlDoc, "(//textarray)[2]", "index").toInt32(); nLength2 = getXPath(pXmlDoc, "(//textarray)[2]", "length").toInt32(); CPPUNIT_ASSERT_EQUAL(u"oldcontent"_ustr, aContent.copy(nIndex2, nLength2)); + aColor2 = getXPath(pXmlDoc, "(//textarray)[2]/preceding-sibling::textcolor[1]", "color"); + CPPUNIT_ASSERT_GREATER(static_cast<sal_Int16>(50), GetColorSaturation(aColor2)); + nIndex3 = getXPath(pXmlDoc, "(//textarray)[3]", "index").toInt32(); + nLength3 = getXPath(pXmlDoc, "(//textarray)[3]", "length").toInt32(); + CPPUNIT_ASSERT_EQUAL(u"newcontent"_ustr, aContent.copy(nIndex3, nLength3)); + aColor3 = getXPath(pXmlDoc, "(//textarray)[3]/preceding-sibling::textcolor[1]", "color"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected less or equal than: 50 + // - Actual : 100 + // i.e. the 3rd text portion was not de-saturated. + CPPUNIT_ASSERT_LESSEQUAL(static_cast<sal_Int16>(50), GetColorSaturation(aColor3)); - // Omit deletes: + // Omit deletes: default, de-saturated, normal saturation. aOpt.SetRedlineRenderMode(SwRedlineRenderMode::OmitDeletes); pWrtShell->ApplyViewOptions(aOpt); xMetaFile = pDocShell->GetPreviewMetaFile(); pXmlDoc = dumpAndParse(dumper, *xMetaFile); - assertXPath(pXmlDoc, "//textarray", 2); + assertXPath(pXmlDoc, "//textarray", 3); nIndex1 = getXPath(pXmlDoc, "(//textarray)[1]", "index").toInt32(); nLength1 = getXPath(pXmlDoc, "(//textarray)[1]", "length").toInt32(); CPPUNIT_ASSERT_EQUAL(u"baseline "_ustr, aContent.copy(nIndex1, nLength1)); + aColor1 = getXPath(pXmlDoc, "(//textarray)[1]/preceding-sibling::textcolor[1]", "color"); + CPPUNIT_ASSERT_EQUAL(u"#000000"_ustr, aColor1); nIndex2 = getXPath(pXmlDoc, "(//textarray)[2]", "index").toInt32(); nLength2 = getXPath(pXmlDoc, "(//textarray)[2]", "length").toInt32(); - CPPUNIT_ASSERT_EQUAL(u"newcontent"_ustr, aContent.copy(nIndex2, nLength2)); + CPPUNIT_ASSERT_EQUAL(u"oldcontent"_ustr, aContent.copy(nIndex2, nLength2)); + aColor2 = getXPath(pXmlDoc, "(//textarray)[2]/preceding-sibling::textcolor[1]", "color"); + CPPUNIT_ASSERT_LESSEQUAL(static_cast<sal_Int16>(50), GetColorSaturation(aColor2)); + nIndex3 = getXPath(pXmlDoc, "(//textarray)[3]", "index").toInt32(); + nLength3 = getXPath(pXmlDoc, "(//textarray)[3]", "length").toInt32(); + CPPUNIT_ASSERT_EQUAL(u"newcontent"_ustr, aContent.copy(nIndex3, nLength3)); + aColor3 = getXPath(pXmlDoc, "(//textarray)[3]/preceding-sibling::textcolor[1]", "color"); + CPPUNIT_ASSERT_GREATER(static_cast<sal_Int16>(50), GetColorSaturation(aColor3)); } } diff --git a/sw/source/core/text/itrpaint.cxx b/sw/source/core/text/itrpaint.cxx index 3a030dd8d232..14693858cda8 100644 --- a/sw/source/core/text/itrpaint.cxx +++ b/sw/source/core/text/itrpaint.cxx @@ -42,8 +42,6 @@ #include "pormulti.hxx" #include <doc.hxx> #include <fmturl.hxx> -#include <IDocumentRedlineAccess.hxx> -#include <redline.hxx> // Returns, if we have an underline breaking situation // Adding some more conditions here means you also have to change them @@ -307,9 +305,6 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip, // Reference portion for the paragraph end portion SwLinePortion* pEndTempl = m_pCurr->GetFirstPortion(); - const SwDoc& rDoc = GetInfo().GetTextFrame()->GetDoc(); - const IDocumentRedlineAccess& rIDRA = rDoc.getIDocumentRedlineAccess(); - const SwRedlineTable& rRedlineTable = rIDRA.GetRedlineTable(); while( pPor ) { bool bSeeked = true; @@ -427,32 +422,6 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip, roTaggedLabel.emplace(nullptr, nullptr, &aPorInfo, *pOut); } - // See if the redline render mode requires to omit the paint of the text portion. - SwRedlineTable::size_type nRedline = SwRedlineTable::npos; - SwRedlineRenderMode eRedlineRenderMode = SwRedlineRenderMode::Standard; - if (GetRedln() && GetRedln()->IsOn()) - { - nRedline = GetRedln()->GetAct(); - eRedlineRenderMode = GetInfo().GetOpt().GetRedlineRenderMode(); - } - bool bOmitPaint = false; - if (nRedline != SwRedlineTable::npos) - { - const SwRangeRedline* pRedline = rRedlineTable[nRedline]; - RedlineType eType = pRedline->GetType(); - if (eRedlineRenderMode == SwRedlineRenderMode::OmitInserts - && eType == RedlineType::Insert) - { - bOmitPaint = true; - } - else if (eRedlineRenderMode == SwRedlineRenderMode::OmitDeletes - && eType == RedlineType::Delete) - { - bOmitPaint = true; - } - } - - if (!bOmitPaint) { // #i16816# tagged pdf support Por_Info aPorInfo(*pPor, *this, 0); diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx index d9ec1283e159..ce7988b64d84 100644 --- a/sw/source/core/text/redlnitr.cxx +++ b/sw/source/core/text/redlnitr.cxx @@ -55,6 +55,9 @@ #include <editeng/formatbreakitem.hxx> #include <editeng/udlnitem.hxx> #include <officecfg/Office/Writer.hxx> +#include <viewopt.hxx> +#include <docsh.hxx> +#include <wrtsh.hxx> using namespace ::com::sun::star; @@ -1009,13 +1012,22 @@ short SwRedlineItr::Seek(SwFont& rFnt, void SwRedlineItr::FillHints( std::size_t nAuthor, RedlineType eType ) { + const SwDocShell* pDocShell = m_rDoc.GetDocShell(); + const SwWrtShell* pWrtShell = pDocShell ? pDocShell->GetWrtShell() : nullptr; + SwRedlineRenderMode eRenderMode = SwRedlineRenderMode::Standard; + if (pWrtShell) + { + const SwViewOption* pOptions = pWrtShell->GetViewOptions(); + eRenderMode = pOptions->GetRedlineRenderMode(); + } + switch ( eType ) { case RedlineType::Insert: - SwModule::get()->GetInsertAuthorAttr(nAuthor, *m_pSet); + SwModule::get()->GetInsertAuthorAttr(nAuthor, *m_pSet, eRenderMode); break; case RedlineType::Delete: - SwModule::get()->GetDeletedAuthorAttr(nAuthor, *m_pSet); + SwModule::get()->GetDeletedAuthorAttr(nAuthor, *m_pSet, eRenderMode); break; case RedlineType::Format: case RedlineType::FmtColl: diff --git a/sw/source/core/text/txtfld.cxx b/sw/source/core/text/txtfld.cxx index 13100a23ed77..dbb178c66d10 100644 --- a/sw/source/core/text/txtfld.cxx +++ b/sw/source/core/text/txtfld.cxx @@ -491,9 +491,9 @@ static bool lcl_setRedlineAttr( SwTextFormatInfo &rInf, const SwTextNode& rTextN : pRedlineNum->GetAuthor(); if ( RedlineType::Delete == pRedlineNum->GetType() ) - SwModule::get()->GetDeletedAuthorAttr(aAuthor, aSet); + SwModule::get()->GetDeletedAuthorAttr(aAuthor, aSet, SwRedlineRenderMode::Standard); else - SwModule::get()->GetInsertAuthorAttr(aAuthor, aSet); + SwModule::get()->GetInsertAuthorAttr(aAuthor, aSet, SwRedlineRenderMode::Standard); if (const SvxColorItem* pItem = aSet.GetItemIfSet(RES_CHRATR_COLOR)) pNumFnt->SetColor(pItem->GetValue()); diff --git a/sw/source/core/text/txtftn.cxx b/sw/source/core/text/txtftn.cxx index 8a6c1613a774..b1dbc84860fc 100644 --- a/sw/source/core/text/txtftn.cxx +++ b/sw/source/core/text/txtftn.cxx @@ -66,6 +66,7 @@ #include <com/sun/star/beans/XPropertySet.hpp> #include <com/sun/star/awt/CharSet.hpp> #include <com/sun/star/text/XTextRange.hpp> +#include <viewopt.hxx> using namespace ::com::sun::star; @@ -1016,9 +1017,9 @@ SwNumberPortion *SwTextFormatter::NewFootnoteNumPortion( SwTextFormatInfo const : pRedline->GetAuthor(); if ( RedlineType::Delete == pRedline->GetType() ) - SwModule::get()->GetDeletedAuthorAttr(aAuthor, aSet); + SwModule::get()->GetDeletedAuthorAttr(aAuthor, aSet, SwRedlineRenderMode::Standard); else - SwModule::get()->GetInsertAuthorAttr(aAuthor, aSet); + SwModule::get()->GetInsertAuthorAttr(aAuthor, aSet, SwRedlineRenderMode::Standard); if (const SvxColorItem* pItem = aSet.GetItemIfSet(RES_CHRATR_COLOR)) pNumFnt->SetColor(pItem->GetValue()); diff --git a/sw/source/uibase/app/swmodul1.cxx b/sw/source/uibase/app/swmodul1.cxx index 35b53b37faf0..e361ed9a23ec 100644 --- a/sw/source/uibase/app/swmodul1.cxx +++ b/sw/source/uibase/app/swmodul1.cxx @@ -482,13 +482,26 @@ std::size_t SwModule::InsertRedlineAuthor(const OUString& rAuthor) } static void lcl_FillAuthorAttr( std::size_t nAuthor, SfxItemSet &rSet, - const AuthorCharAttr &rAttr ) + const AuthorCharAttr &rAttr, SwRedlineRenderMode eRenderMode = SwRedlineRenderMode::Standard ) { Color aCol( rAttr.m_nColor ); if( rAttr.m_nColor == COL_TRANSPARENT ) + { aCol = lcl_GetAuthorColor(nAuthor); + // See if the redline render mode requires to de-saturize the color of the text portion. + if (eRenderMode != SwRedlineRenderMode::Standard) + { + sal_uInt16 nHue; + sal_uInt16 nSaturation; + sal_uInt16 nBrightness; + aCol.RGBtoHSB(nHue, nSaturation, nBrightness); + nSaturation = nSaturation / 2; + aCol = Color::HSBtoRGB(nHue, nSaturation, nBrightness); + } + } + bool bBackGr = rAttr.m_nColor == COL_NONE_COLOR; switch (rAttr.m_nItemId) @@ -540,14 +553,24 @@ static void lcl_FillAuthorAttr( std::size_t nAuthor, SfxItemSet &rSet, rSet.Put( SvxColorItem( aCol, RES_CHRATR_COLOR ) ); } -void SwModule::GetInsertAuthorAttr(std::size_t nAuthor, SfxItemSet &rSet) +void SwModule::GetInsertAuthorAttr(std::size_t nAuthor, SfxItemSet &rSet, SwRedlineRenderMode eRenderMode) { - lcl_FillAuthorAttr(nAuthor, rSet, m_pModuleConfig->GetInsertAuthorAttr()); + SwRedlineRenderMode eMode = SwRedlineRenderMode::Standard; + if (eRenderMode == SwRedlineRenderMode::OmitInserts) + { + eMode = eRenderMode; + } + lcl_FillAuthorAttr(nAuthor, rSet, m_pModuleConfig->GetInsertAuthorAttr(), eMode); } -void SwModule::GetDeletedAuthorAttr(std::size_t nAuthor, SfxItemSet &rSet) +void SwModule::GetDeletedAuthorAttr(std::size_t nAuthor, SfxItemSet &rSet, SwRedlineRenderMode eRenderMode) { - lcl_FillAuthorAttr(nAuthor, rSet, m_pModuleConfig->GetDeletedAuthorAttr()); + SwRedlineRenderMode eMode = SwRedlineRenderMode::Standard; + if (eRenderMode == SwRedlineRenderMode::OmitDeletes) + { + eMode = eRenderMode; + } + lcl_FillAuthorAttr(nAuthor, rSet, m_pModuleConfig->GetDeletedAuthorAttr(), eMode); } // For future extension:
