sw/qa/uibase/docvw/docvw.cxx       |   99 +++++++++++++++++++++++++++++++++++++
 sw/source/uibase/docvw/edtwin2.cxx |   30 ++++++++++-
 vcl/source/window/window3.cxx      |   10 +++
 3 files changed, 135 insertions(+), 4 deletions(-)

New commits:
commit d046f239e363ac30901d8b9efe9dbf00eaa45af9
Author:     Miklos Vajna <[email protected]>
AuthorDate: Fri Feb 20 10:26:51 2026 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Wed Feb 25 08:44:38 2026 +0100

    cool#13988 sw redline tooltip LOK API: expose anchor range
    
    Open a document with a tracked change in a LOK client, hover your mouse
    over the tracked change, LOK_CALLBACK_TOOLTIP gets triggered, but no
    info is sent about the redline itself.
    
    Something similar already happens for content controls, there
    LOK_CALLBACK_CONTENT_CONTROL has a 'rectangles' key to describe the
    range (rectangles) of the content control.
    
    Fix the problem in a similar way: let SwEditWin::RequestHelp() check if
    the content under the cursor is a redline and if so, emit the redline
    range's rectangles as a new property of the tooltip.
    
    This time do it as a JSON array, e.g.
    { "type": "generaltooltip", "text": "Deleted: ...", "rectangle": "...", 
"anchorRectangles": [ "1418, 1966, 2390, 264", "2159, 1701, 9231, 264"]}
    to do less string parsing on the LOK client side. And finally expose the
    redline type in a similar way, so the LOK client can color the highlight
    accordingly.
    
    (cherry picked from commit 8dcecf717b5cd231adbc49e27ccf1b7aa0230a3a)
    
    Change-Id: I6b72d5ff38d2a2b4fab0bda3564dd5b2770779d6
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200207
    Reviewed-by: Miklos Vajna <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>

diff --git a/sw/qa/uibase/docvw/docvw.cxx b/sw/qa/uibase/docvw/docvw.cxx
index bc9b45d0396a..e32daae90288 100644
--- a/sw/qa/uibase/docvw/docvw.cxx
+++ b/sw/qa/uibase/docvw/docvw.cxx
@@ -9,15 +9,25 @@
 
 #include <swmodeltestbase.hxx>
 
+#include <boost/property_tree/json_parser.hpp>
+
 #include <com/sun/star/text/XTextDocument.hpp>
 
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <test/lokcallback.hxx>
 #include <vcl/event.hxx>
+#include <vcl/scheduler.hxx>
 
 #include <docsh.hxx>
 #include <edtwin.hxx>
 #include <flyfrm.hxx>
 #include <frameformats.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <unotxdoc.hxx>
 #include <view.hxx>
+#include <viscrs.hxx>
 #include <wrtsh.hxx>
 
 namespace
@@ -187,6 +197,95 @@ CPPUNIT_TEST_FIXTURE(Test, testShiftDoubleClickOnImage)
     CPPUNIT_ASSERT_GREATER(0, nGraphicDialogs);
 }
 
+namespace
+{
+/// Test LOK callback, handling just LOK_CALLBACK_TOOLTIP.
+struct TooltipCallback
+{
+    std::string rect;
+    std::string anchorRectangles;
+    std::string redlineType;
+
+    static void callback(int nType, const char* pPayload, void* pData);
+    void callbackImpl(int nType, const char* pPayload);
+};
+
+void TooltipCallback::callback(int nType, const char* pPayload, void* pData)
+{
+    static_cast<TooltipCallback*>(pData)->callbackImpl(nType, pPayload);
+}
+
+void TooltipCallback::callbackImpl(int nType, const char* pPayload)
+{
+    if (nType == LOK_CALLBACK_TOOLTIP)
+    {
+        std::stringstream aStream(pPayload);
+        boost::property_tree::ptree aTree;
+        boost::property_tree::read_json(aStream, aTree);
+        rect = aTree.get_child("rectangle").get_value<std::string>();
+        auto it = aTree.find("anchorRectangles");
+        if (it != aTree.not_found())
+        {
+            std::vector<std::string> aRects;
+            for (const auto& rRect : it->second)
+                aRects.push_back(rRect.second.get_value<std::string>());
+            std::stringstream aRectStream;
+            for (size_t i = 0; i < aRects.size(); ++i)
+            {
+                if (i > 0)
+                    aRectStream << "; ";
+                aRectStream << aRects[i];
+            }
+            anchorRectangles = aRectStream.str();
+        }
+        auto itType = aTree.find("redlineType");
+        if (itType != aTree.not_found())
+            redlineType = itType->second.get_value<std::string>();
+    }
+}
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testRedlineTooltipAnchorRectangles)
+{
+    // Set up LOK:
+    comphelper::LibreOfficeKit::setActive(true);
+
+    // Given a document with a redline:
+    createSwDoc();
+    
getSwTextDoc()->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+    SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+    TooltipCallback aCallback;
+    TestLokCallbackWrapper aCallbackWrapper(&TooltipCallback::callback, 
&aCallback);
+    
pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&aCallbackWrapper);
+    
aCallbackWrapper.setLOKViewId(SfxLokHelper::getView(*pWrtShell->GetSfxViewShell()));
+    pWrtShell->SetRedlineFlagsAndCheckInsMode(RedlineFlags::On | 
RedlineFlags::ShowMask);
+    pWrtShell->Insert(u"test"_ustr);
+
+    // When moving the mouse over the redline:
+    SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+    CPPUNIT_ASSERT(pShellCursor);
+    pWrtShell->EndOfSection(/*bSelect=*/false);
+    Point aEnd = pShellCursor->GetSttPos();
+    pWrtShell->StartOfSection(/*bSelect=*/false);
+    Point aStart = pShellCursor->GetSttPos();
+    Point aMiddle((aStart.getX() + aEnd.getX()) / 2, (aStart.getY() + 
aEnd.getY()) / 2);
+    getSwTextDoc()->postMouseEvent(LOK_MOUSEEVENT_MOUSEMOVE, aMiddle.getX(), 
aMiddle.getY(), 1, 0,
+                                   0);
+    Scheduler::ProcessEventsToIdle();
+
+    // Then make sure the tooltip callback has redlineType and 
anchorRectangles:
+    // Without the accompanying fix in place, this test would have failed, no 
anchor rectangles were
+    // emitted.
+    CPPUNIT_ASSERT(!aCallback.anchorRectangles.empty());
+    CPPUNIT_ASSERT_EQUAL(std::string("Insert"), aCallback.redlineType);
+
+    // Tear down LOK:
+    pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(nullptr);
+    mxComponent->dispose();
+    mxComponent.clear();
+    comphelper::LibreOfficeKit::setActive(false);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/edtwin2.cxx 
b/sw/source/uibase/docvw/edtwin2.cxx
index 7da57f94f37c..e408a48fb54a 100644
--- a/sw/source/uibase/docvw/edtwin2.cxx
+++ b/sw/source/uibase/docvw/edtwin2.cxx
@@ -67,6 +67,7 @@
 #include <names.hxx>
 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <viscrs.hxx>
 
 namespace {
 
@@ -314,13 +315,31 @@ OUString SwEditWin::ClipLongToolTip(const OUString& rText)
     return sDisplayText;
 }
 
-static OString getTooltipPayload(const OUString& tooltip, const SwRect& rect)
+static OString getTooltipPayload(const OUString& tooltip, const SwRect& rect,
+                                 SwWrtShell& rSh,
+                                 const SwRangeRedline* pRedline = nullptr)
 {
     tools::JsonWriter writer;
     {
         writer.put("type", "generaltooltip");
         writer.put("text", tooltip);
         writer.put("rectangle", rect.SVRect().toString());
+
+        if (pRedline)
+        {
+            writer.put("redlineType",
+                       
SwRedlineTypeToOUString(pRedline->GetRedlineData().GetType()));
+
+            SwShellCursor aCursor(rSh, *pRedline->Start());
+            aCursor.SetMark();
+            *aCursor.GetMark() = *pRedline->End();
+            aCursor.FillRects();
+            auto aArray = writer.startArray("anchorRectangles");
+            for (const auto& rRect : aCursor)
+            {
+                
writer.putSimpleValue(OUString::fromUtf8(rRect.SVRect().toString()));
+            }
+        }
     }
     return writer.finishAndGetAsOString();
 }
@@ -699,8 +718,13 @@ void SwEditWin::RequestHelp(const HelpEvent &rEvt)
             {
                 if (comphelper::LibreOfficeKit::isActive())
                 {
-                    m_rView.libreOfficeKitViewCallback(
-                        LOK_CALLBACK_TOOLTIP, getTooltipPayload(sText, 
aFieldRect));
+                    const SwRangeRedline* pRedline = nullptr;
+                    if (aContentAtPos.eContentAtPos == IsAttrAtPos::Redline)
+                    {
+                        pRedline = aContentAtPos.aFnd.pRedl;
+                    }
+                    OString aPayload = getTooltipPayload(sText, aFieldRect, 
rSh, pRedline);
+                    m_rView.libreOfficeKitViewCallback(LOK_CALLBACK_TOOLTIP, 
aPayload);
                 }
                 else
                 {
commit 6dba177a0fa39258f03ff3c9704feade01ed59cf
Author:     Miklos Vajna <[email protected]>
AuthorDate: Tue Feb 17 16:56:34 2026 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Wed Feb 25 08:44:23 2026 +0100

    vcl: fix crash in Window::GetOutputSizePixel()
    
    gdb on the crashreport core dump:
    
            #0  OutputDevice::GetOutputSizePixel (this=0x0) at 
include/vcl/outdev.hxx:324
            #1  vcl::Window::GetOutputSizePixel (this=this@entry=0x5479f1e0) at 
vcl/source/window/window3.cxx:90
            #2  0x0000733bcba58cca in vcl::RoadmapWizard::ImplPosCtrls 
(this=0x5479f1e0)
                at vcl/source/control/wizardmachine.cxx:135
            #3  0x0000733bcba5c25d in 
vcl::RoadmapWizard::ImplHandleWizardLayoutTimerHdl (this=0x5479f1e0)
                at vcl/source/control/wizardmachine.cxx:129
            #4  vcl::RoadmapWizard::LinkStubImplHandleWizardLayoutTimerHdl 
(instance=0x5479f1e0, data=<optimized out>)
                at vcl/source/control/wizardmachine.cxx:127
            #5  0x0000733bc8df817d in Scheduler::CallbackTaskScheduling () at 
vcl/source/app/scheduler.cxx:585
    
    and
    
            (gdb) up
            #1  vcl::Window::GetOutputSizePixel (this=this@entry=0x5479f1e0) at 
vcl/source/window/window3.cxx:90
            90      Size Window::GetOutputSizePixel() const { return 
GetOutDev()->GetOutputSizePixel(); }
            (gdb) print mpWindowImpl
            $2 = std::unique_ptr<WindowImpl> = {get() = 0x0}
    
    I.e. this is similar to commit e66599cc396a79e4fc0f59ee256148bc00ca6e15
    (vcl: fix crash in Window::ImplInvalidate(), 2025-11-18), you can't
    assume mpWindowImpl is a non-nullptr here.
    
    (cherry picked from commit 8dd7dede5a54e36edb26c28391a1bd6f8265179f)
    
    Change-Id: I09c93f568c96cf52369f4eef1798c84f230b880e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200206
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/vcl/source/window/window3.cxx b/vcl/source/window/window3.cxx
index ce1f3ff9030c..06dacd172062 100644
--- a/vcl/source/window/window3.cxx
+++ b/vcl/source/window/window3.cxx
@@ -87,7 +87,15 @@ bool Window::GetNativeControlRegion(ControlType nType, 
ControlPart nPart,
                                                rNativeBoundingRegion, 
rNativeContentRegion);
 }
 
-Size Window::GetOutputSizePixel() const { return 
GetOutDev()->GetOutputSizePixel(); }
+Size Window::GetOutputSizePixel() const
+{
+    if (!mpWindowImpl)
+    {
+        return Size();
+    }
+
+    return GetOutDev()->GetOutputSizePixel();
+}
 
 tools::Rectangle Window::GetOutputRectPixel() const { return 
GetOutDev()->GetOutputRectPixel(); }
 

Reply via email to