sw/qa/uibase/docvw/docvw.cxx | 99 +++++++++++++++++++++++++++++++++++++ sw/source/uibase/docvw/edtwin2.cxx | 30 ++++++++++- 2 files changed, 126 insertions(+), 3 deletions(-)
New commits: commit ad895723d7d326dad709410618a1a5af9716c95e Author: Miklos Vajna <[email protected]> AuthorDate: Fri Feb 20 10:26:51 2026 +0100 Commit: Caolán McNamara <[email protected]> CommitDate: Fri Feb 20 12:14:14 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. Change-Id: I6b72d5ff38d2a2b4fab0bda3564dd5b2770779d6 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199801 Tested-by: Jenkins CollaboraOffice <[email protected]> Tested-by: Caolán McNamara <[email protected]> Reviewed-by: Caolán McNamara <[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 f5ca79d55df6..703e4acd495c 100644 --- a/sw/source/uibase/docvw/edtwin2.cxx +++ b/sw/source/uibase/docvw/edtwin2.cxx @@ -65,6 +65,7 @@ #include <unomap.hxx> #include <com/sun/star/style/XStyleFamiliesSupplier.hpp> #include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <viscrs.hxx> namespace { @@ -312,13 +313,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(); } @@ -659,8 +678,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 {
