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/+/199849
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins
This commit is contained in:
Miklos Vajna
2026-02-20 17:02:32 +01:00
parent c5ed66adbe
commit 8dcecf717b
2 changed files with 126 additions and 3 deletions
+99
View File
@@ -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: */
+27 -3
View File
@@ -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
{