sw/qa/core/text/itrpaint.cxx        |   21 ++++++++++++++++-----
 sw/source/core/inc/drawfont.hxx     |    6 ++++++
 sw/source/core/text/inftxt.cxx      |    8 ++++++++
 sw/source/core/text/inftxt.hxx      |    4 ++++
 sw/source/core/text/itrpaint.cxx    |   32 ++++++++++++++++++++++++++++++++
 sw/source/core/txtnode/fntcache.cxx |   25 +++++++++++++++++++++----
 sw/source/ui/dialog/uiregionsw.cxx  |    4 ++--
 7 files changed, 89 insertions(+), 11 deletions(-)

New commits:
commit ee4857814178d028b69352d5126be46485fc9b45
Author:     Caolán McNamara <[email protected]>
AuthorDate: Mon Jan 19 11:24:51 2026 +0000
Commit:     Caolán McNamara <[email protected]>
CommitDate: Tue Jan 20 15:20:35 2026 +0100

    make this infobox async
    
    Change-Id: I0b4781cbbd97586be6e17c82a132b98ee058761f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197577
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/sw/source/ui/dialog/uiregionsw.cxx 
b/sw/source/ui/dialog/uiregionsw.cxx
index 896144f953b1..b78cfd7b6bb4 100644
--- a/sw/source/ui/dialog/uiregionsw.cxx
+++ b/sw/source/ui/dialog/uiregionsw.cxx
@@ -1668,10 +1668,10 @@ void SwInsertSectionTabPage::ChangePasswd(bool bChange)
                 }
                 else
                 {
-                    std::unique_ptr<weld::MessageDialog> 
xInfoBox(Application::CreateMessageDialog(GetFrameWeld(),
+                    std::shared_ptr<weld::MessageDialog> 
xInfoBox(Application::CreateMessageDialog(GetFrameWeld(),
                                                                   
VclMessageType::Info, VclButtonsType::Ok,
                                                                   
SwResId(STR_WRONG_PASSWD_REPEAT)));
-                    xInfoBox->run();
+                    xInfoBox->runAsync(xInfoBox, [](sal_uInt32) {});
                 }
             }
             else if(!bChange)
commit 9bc163b5637572684ac6cc5985d276c4bc01679f
Author:     Miklos Vajna <[email protected]>
AuthorDate: Tue Jan 20 09:50:21 2026 +0100
Commit:     Caolán McNamara <[email protected]>
CommitDate: Tue Jan 20 15:20:24 2026 +0100

    cool#13574 sw redline render mode: somewhat color ins/del as green/red
    
    So far the non-standard redline render mode focused on hiding inserts
    and deletes: typically hiding inserts on the left hand side, and hiding
    deletes on the right hand side, a bit similar to the side-by-side source
    code diff. What's missing is to do a color transform for the opposite
    cases, i.e. deletes on the left and inserts on the right.
    
    The intent is to color deletes with a red-like color on the left and
    inserts with a green-like color on the right.
    
    We still need to do this in SwFntObj::DrawText(), because automatic
    colors are not expanded before that point. To keep this readable in both
    light and dark mode, do the color transform in HSL space and set the
    "hue" of the color to red/green, and constrain saturation/lightness a
    bit to ensure readability.
    
    The images are not yet touched for this, they are still simply
    grayscale when hiding and unchanged otherwise.
    
    Change-Id: I21e0721d66a5b0d4e85476a0631f360610c92d8a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197635
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Caolán McNamara <[email protected]>

diff --git a/sw/qa/core/text/itrpaint.cxx b/sw/qa/core/text/itrpaint.cxx
index 9ecd2d4a5466..6b395efcef24 100644
--- a/sw/qa/core/text/itrpaint.cxx
+++ b/sw/qa/core/text/itrpaint.cxx
@@ -47,13 +47,24 @@ sal_Int16 GetColorLightness(std::u16string_view rRGB)
     return nBrightness;
 }
 
+int GetColorHue(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 nHue;
+}
+
 CPPUNIT_TEST_FIXTURE(Test, testRedlineRenderModeOmitInsertDelete)
 {
     // Reset redline author IDs to a predictable default.
     SwGlobals::ensure();
     SwModule::get()->ClearRedlineAuthors();
 
-    // Default rendering: default, normal lightness, normal lightness.
+    // Default rendering: default, delete, insert.
     createSwDoc("redline.docx");
 
     SwDocShell* pDocShell = getSwDocShell();
@@ -84,7 +95,7 @@ CPPUNIT_TEST_FIXTURE(Test, 
testRedlineRenderModeOmitInsertDelete)
         = getXPath(pXmlDoc, 
"(//textarray)[3]/preceding-sibling::textcolor[1]", "color");
     CPPUNIT_ASSERT_EQUAL(aRedlineColorString, aColor3);
 
-    // Omit inserts: default, normal lightness, increased lightness.
+    // Omit inserts, color deletes: default, red-like, increased lightness.
     SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
     SwViewOption aOpt(*pWrtShell->GetViewOptions());
     aOpt.SetRedlineRenderMode(SwRedlineRenderMode::OmitInserts);
@@ -103,7 +114,7 @@ CPPUNIT_TEST_FIXTURE(Test, 
testRedlineRenderModeOmitInsertDelete)
     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_EQUAL(u"#000000"_ustr, aColor2);
+    CPPUNIT_ASSERT_EQUAL(0, GetColorHue(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));
@@ -114,7 +125,7 @@ CPPUNIT_TEST_FIXTURE(Test, 
testRedlineRenderModeOmitInsertDelete)
     // i.e. the 3rd text portion had no increased lightness from black.
     CPPUNIT_ASSERT_GREATEREQUAL(static_cast<sal_Int16>(49), 
GetColorLightness(aColor3));
 
-    // Omit deletes: default, de-saturated, normal saturation.
+    // Omit deletes, color inserts: default, increased lightness, green-like.
     aOpt.SetRedlineRenderMode(SwRedlineRenderMode::OmitDeletes);
     pWrtShell->ApplyViewOptions(aOpt);
 
@@ -136,7 +147,7 @@ CPPUNIT_TEST_FIXTURE(Test, 
testRedlineRenderModeOmitInsertDelete)
     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_EQUAL(u"#000000"_ustr, aColor3);
+    CPPUNIT_ASSERT_EQUAL(120, GetColorHue(aColor3));
 }
 
 bool IsGrayScale(const BitmapEx& rBitmap)
diff --git a/sw/source/core/inc/drawfont.hxx b/sw/source/core/inc/drawfont.hxx
index 369edaa7f342..8b2e8f3ca017 100644
--- a/sw/source/core/inc/drawfont.hxx
+++ b/sw/source/core/inc/drawfont.hxx
@@ -90,6 +90,8 @@ class SW_DLLPUBLIC SwDrawTextInfo
     // inside second half of bound rect, used for Accessibility
     bool m_bPosMatchesBounds : 1 = false;
     bool m_bOmitPaint = false;
+    bool m_bInsertColorPaint = false;
+    bool m_bDeleteColorPaint = false;
 
 #ifdef DBG_UTIL
     // These flags should control that the appropriate Set-function has been
@@ -640,6 +642,10 @@ public:
 
     void SetOmitPaint(bool bOmitPaint) { m_bOmitPaint = bOmitPaint; }
     bool GetOmitPaint() const { return m_bOmitPaint; }
+    void SetInsertColorPaint(bool bInsertColorPaint) { m_bInsertColorPaint = 
bInsertColorPaint; }
+    bool GetInsertColorPaint() const { return m_bInsertColorPaint; }
+    void SetDeleteColorPaint(bool bDeleteColorPaint) { m_bDeleteColorPaint = 
bDeleteColorPaint; }
+    bool GetDeleteColorPaint() const { return m_bDeleteColorPaint; }
 };
 
 #endif
diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
index 01bb0b52ce42..e701e2746a77 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -713,6 +713,14 @@ void SwTextPaintInfo::DrawText_( const OUString &rText, 
const SwLinePortion &rPo
     {
         aDrawInf.SetOmitPaint(m_bOmitPaint);
     }
+    else if (m_bInsertColorPaint)
+    {
+        aDrawInf.SetInsertColorPaint(m_bInsertColorPaint);
+    }
+    else if (m_bDeleteColorPaint)
+    {
+        aDrawInf.SetDeleteColorPaint(m_bDeleteColorPaint);
+    }
 
     // Draw text next to the left border
     Point aFontPos(m_aPos);
diff --git a/sw/source/core/text/inftxt.hxx b/sw/source/core/text/inftxt.hxx
index 1cf03282eeb5..70e17059adda 100644
--- a/sw/source/core/text/inftxt.hxx
+++ b/sw/source/core/text/inftxt.hxx
@@ -367,6 +367,8 @@ class SwTextPaintInfo : public SwTextSizeInfo
 
     sal_uInt16 m_nSpaceIdx;
     bool m_bOmitPaint = false;
+    bool m_bInsertColorPaint = false;
+    bool m_bDeleteColorPaint = false;
     void DrawText_(const OUString &rText, const SwLinePortion &rPor,
                    const TextFrameIndex nIdx, const TextFrameIndex nLen,
                    const bool bKern, const bool bWrong = false,
@@ -484,6 +486,8 @@ public:
     void SetSmartTags(sw::WrongListIterator *const pNew) { m_pSmartTags = 
pNew; }
     sw::WrongListIterator* GetSmartTags() const { return m_pSmartTags; }
     void SetOmitPaint(bool bOmitPaint) { m_bOmitPaint = bOmitPaint; }
+    void SetInsertColorPaint(bool bInsertColorPaint) { m_bInsertColorPaint = 
bInsertColorPaint; }
+    void SetDeleteColorPaint(bool bDeleteColorPaint) { m_bDeleteColorPaint = 
bDeleteColorPaint; }
 };
 
 class SwTextFormatInfo : public SwTextPaintInfo
diff --git a/sw/source/core/text/itrpaint.cxx b/sw/source/core/text/itrpaint.cxx
index c97828886f21..711605d8cb60 100644
--- a/sw/source/core/text/itrpaint.cxx
+++ b/sw/source/core/text/itrpaint.cxx
@@ -128,6 +128,8 @@ class SwTextPaintOmitter
 {
     SwTextPainter& m_rPainter;
     bool m_bOmitPaint;
+    bool m_bInsertColorPaint;
+    bool m_bDeleteColorPaint;
 
 public:
     SwTextPaintOmitter(SwTextPainter& rPainter, const SwRedlineTable& 
rRedlineTable);
@@ -137,6 +139,8 @@ public:
 SwTextPaintOmitter::SwTextPaintOmitter(SwTextPainter& rPainter, const 
SwRedlineTable& rRedlineTable)
     : m_rPainter(rPainter)
     , m_bOmitPaint(false)
+    , m_bInsertColorPaint(false)
+    , m_bDeleteColorPaint(false)
 {
     if (!rPainter.GetRedln() || !rPainter.GetRedln()->IsOn())
     {
@@ -152,19 +156,39 @@ SwTextPaintOmitter::SwTextPaintOmitter(SwTextPainter& 
rPainter, const SwRedlineT
     SwRedlineRenderMode eRedlineRenderMode = 
rPainter.GetInfo().GetOpt().GetRedlineRenderMode();
     const SwRangeRedline* pRedline = rRedlineTable[nRedline];
     RedlineType eType = pRedline->GetType();
+    // We have a matrix of redline render mode and redline types. The intent 
is to show the "omit
+    // inserts" mode on the left (inserts are semi-hidden, deletes are 
colored), and to show the
+    // "omit deletes" mode on the right (deletes are semi-hidden, inserts are 
colored). And do none
+    // of this in the standard (default) case.
     if (eRedlineRenderMode == SwRedlineRenderMode::OmitInserts && eType == 
RedlineType::Insert)
     {
         m_bOmitPaint = true;
     }
+    else if (eRedlineRenderMode == SwRedlineRenderMode::OmitInserts && eType 
== RedlineType::Delete)
+    {
+        m_bDeleteColorPaint = true;
+    }
     else if (eRedlineRenderMode == SwRedlineRenderMode::OmitDeletes && eType 
== RedlineType::Delete)
     {
         m_bOmitPaint = true;
     }
+    else if (eRedlineRenderMode == SwRedlineRenderMode::OmitDeletes && eType 
== RedlineType::Insert)
+    {
+        m_bInsertColorPaint = true;
+    }
 
     if (m_bOmitPaint)
     {
         rPainter.GetInfo().SetOmitPaint(true);
     }
+    else if (m_bInsertColorPaint)
+    {
+        rPainter.GetInfo().SetInsertColorPaint(true);
+    }
+    else if (m_bDeleteColorPaint)
+    {
+        rPainter.GetInfo().SetDeleteColorPaint(true);
+    }
 }
 
 SwTextPaintOmitter::~SwTextPaintOmitter()
@@ -173,6 +197,14 @@ SwTextPaintOmitter::~SwTextPaintOmitter()
     {
         m_rPainter.GetInfo().SetOmitPaint(false);
     }
+    else if (m_bInsertColorPaint)
+    {
+        m_rPainter.GetInfo().SetInsertColorPaint(false);
+    }
+    else if (m_bDeleteColorPaint)
+    {
+        m_rPainter.GetInfo().SetDeleteColorPaint(false);
+    }
 }
 }
 
diff --git a/sw/source/core/txtnode/fntcache.cxx 
b/sw/source/core/txtnode/fntcache.cxx
index 117f2ad9ffb9..7ba1ba147719 100644
--- a/sw/source/core/txtnode/fntcache.cxx
+++ b/sw/source/core/txtnode/fntcache.cxx
@@ -1008,16 +1008,33 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
     Color aOldColor( pTmpFont->GetColor() );
     bool bChgColor = rInf.ApplyAutoColor( pTmpFont );
 
-    if (rInf.GetOmitPaint())
+    if (rInf.GetOmitPaint() || rInf.GetInsertColorPaint() || 
rInf.GetDeleteColorPaint())
     {
         Color aColor = pTmpFont->GetColor();
         sal_uInt16 nHue;
         sal_uInt16 nSaturation;
         sal_uInt16 nBrightness;
         aColor.RGBtoHSB(nHue, nSaturation, nBrightness);
-        // 50% lightness: balance between completely omitting the paint and 
hard-to-notice small
-        // difference.
-        nBrightness = 50;
+        if (rInf.GetOmitPaint())
+        {
+            // 50% lightness: balance between completely omitting the paint 
and hard-to-notice small
+            // difference.
+            nBrightness = 50;
+        }
+        else if (rInf.GetInsertColorPaint())
+        {
+            // Insert: color transform to produce a green-like color.
+            nHue = 120;
+            nSaturation = std::max<sal_uInt16>(nSaturation, 75);
+            nBrightness = std::clamp<sal_uInt16>(nBrightness, 40, 60);
+        }
+        else if (rInf.GetDeleteColorPaint())
+        {
+            // Delete: color transform to produce a red-like color.
+            nHue = 0;
+            nSaturation = std::max<sal_uInt16>(nSaturation, 75);
+            nBrightness = std::clamp<sal_uInt16>(nBrightness, 40, 60);
+        }
         aColor = Color::HSBtoRGB(nHue, nSaturation, nBrightness);
         if (aColor != pTmpFont->GetColor())
         {

Reply via email to