summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/LibreOfficeKit/LibreOfficeKitEnums.h18
-rw-r--r--libreofficekit/source/gtk/lokdocview.cxx55
-rw-r--r--sw/qa/extras/tiledrendering/data/content-control.odtbin0 -> 9683 bytes
-rw-r--r--sw/qa/extras/tiledrendering/tiledrendering.cxx44
-rw-r--r--sw/source/core/crsr/viscrs.cxx22
5 files changed, 139 insertions, 0 deletions
diff --git a/include/LibreOfficeKit/LibreOfficeKitEnums.h b/include/LibreOfficeKit/LibreOfficeKitEnums.h
index d15e0e5a70ba..0fda66777a09 100644
--- a/include/LibreOfficeKit/LibreOfficeKitEnums.h
+++ b/include/LibreOfficeKit/LibreOfficeKitEnums.h
@@ -795,6 +795,22 @@ typedef enum
* Rectangle format is the same as LOK_CALLBACK_INVALIDATE_TILES.
*/
LOK_CALLBACK_SC_FOLLOW_JUMP = 54,
+
+ /**
+ * Sends all information for displaying metadata for a text based content control.
+ *
+ * The payload example:
+ * {
+ * "action": "show",
+ * "rectangles": "1418, 1694, 720, 551; 10291, 1418, 1099, 275"
+ * }
+ *
+ * or
+ * {
+ * "action": "hide"
+ * }
+ */
+ LOK_CALLBACK_CONTENT_CONTROL = 55,
}
LibreOfficeKitCallbackType;
@@ -933,6 +949,8 @@ static inline const char* lokCallbackTypeToString(int nType)
return "LOK_COMMAND_BLOCKED";
case LOK_CALLBACK_SC_FOLLOW_JUMP:
return "LOK_CALLBACK_SC_FOLLOW_JUMP";
+ case LOK_CALLBACK_CONTENT_CONTROL:
+ return "LOK_CALLBACK_CONTENT_CONTROL";
}
assert(!"Unknown LibreOfficeKitCallbackType type.");
diff --git a/libreofficekit/source/gtk/lokdocview.cxx b/libreofficekit/source/gtk/lokdocview.cxx
index 45c9cacedff6..1bf7e7301d7c 100644
--- a/libreofficekit/source/gtk/lokdocview.cxx
+++ b/libreofficekit/source/gtk/lokdocview.cxx
@@ -124,6 +124,8 @@ struct LOKDocViewPrivateImpl
guint32 m_nKeyModifier;
/// Rectangles of the current text selection.
std::vector<GdkRectangle> m_aTextSelectionRectangles;
+ /// Rectangles of the current content control.
+ std::vector<GdkRectangle> m_aContentControlRectangles;
/// Rectangles of view selections. The current view can only see
/// them, can't modify them. Key is the view id.
std::map<int, ViewRectangles> m_aTextViewSelectionRectangles;
@@ -281,6 +283,7 @@ enum
ADDRESS_CHANGED,
FORMULA_CHANGED,
TEXT_SELECTION,
+ CONTENT_CONTROL,
PASSWORD_REQUIRED,
COMMENT,
RULER,
@@ -1392,6 +1395,27 @@ callback (gpointer pData)
break;
}
+ case LOK_CALLBACK_CONTENT_CONTROL:
+ {
+ std::stringstream aStream(pCallback->m_aPayload);
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ auto aAction = aTree.get<std::string>("action");
+ if (aAction == "show")
+ {
+ auto aRectangles = aTree.get<std::string>("rectangles");
+ priv->m_aContentControlRectangles = payloadToRectangles(pDocView, aRectangles.c_str());
+ }
+ else if (aAction == "hide")
+ {
+ priv->m_aContentControlRectangles.clear();
+ }
+ bool bIsTextSelected = !priv->m_aContentControlRectangles.empty();
+ g_signal_emit(pDocView, doc_view_signals[CONTENT_CONTROL], 0, bIsTextSelected);
+ gtk_widget_queue_draw(GTK_WIDGET(pDocView));
+ }
+ break;
+
case LOK_CALLBACK_STATUS_INDICATOR_START:
case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
case LOK_CALLBACK_STATUS_INDICATOR_FINISH:
@@ -1809,6 +1833,21 @@ renderOverlay(LOKDocView* pDocView, cairo_t* pCairo)
}
}
+ if (!priv->m_aContentControlRectangles.empty())
+ {
+ for (const GdkRectangle& rRectangle : priv->m_aContentControlRectangles)
+ {
+ // Black with 75% transparency.
+ cairo_set_source_rgba(pCairo, (double(0x7f))/255, (double(0x7f))/255, (double(0x7f))/255, 0.25);
+ cairo_rectangle(pCairo,
+ twipToPixel(rRectangle.x, priv->m_fZoom),
+ twipToPixel(rRectangle.y, priv->m_fZoom),
+ twipToPixel(rRectangle.width, priv->m_fZoom),
+ twipToPixel(rRectangle.height, priv->m_fZoom));
+ cairo_fill(pCairo);
+ }
+ }
+
// Selections of other views.
for (const auto& rPair : priv->m_aTextViewSelectionRectangles)
{
@@ -3284,6 +3323,21 @@ static void lok_doc_view_class_init (LOKDocViewClass* pClass)
G_TYPE_BOOLEAN);
/**
+ * LOKDocView::content-control:
+ * @pDocView: the #LOKDocView on which the signal is emitted
+ * @bIsTextSelected: whether current content control is non-null
+ */
+ doc_view_signals[CONTENT_CONTROL] =
+ g_signal_new("content-control",
+ G_TYPE_FROM_CLASS(pGObjectClass),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ nullptr, nullptr,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1,
+ G_TYPE_BOOLEAN);
+
+ /**
* LOKDocView::password-required:
* @pDocView: the #LOKDocView on which the signal is emitted
* @pUrl: URL of the document for which password is required
@@ -3727,6 +3781,7 @@ lok_doc_view_reset_view(LOKDocView* pDocView)
priv->m_nLastButtonPressTime = 0;
priv->m_nLastButtonReleaseTime = 0;
priv->m_aTextSelectionRectangles.clear();
+ priv->m_aContentControlRectangles.clear();
memset(&priv->m_aTextSelectionStart, 0, sizeof(priv->m_aTextSelectionStart));
memset(&priv->m_aTextSelectionEnd, 0, sizeof(priv->m_aTextSelectionEnd));
diff --git a/sw/qa/extras/tiledrendering/data/content-control.odt b/sw/qa/extras/tiledrendering/data/content-control.odt
new file mode 100644
index 000000000000..624063fbd606
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/content-control.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/tiledrendering.cxx b/sw/qa/extras/tiledrendering/tiledrendering.cxx
index 826f7fbfa5c6..270dee370c66 100644
--- a/sw/qa/extras/tiledrendering/tiledrendering.cxx
+++ b/sw/qa/extras/tiledrendering/tiledrendering.cxx
@@ -167,6 +167,7 @@ public:
void testCondCollCopy();
void testMoveShapeHandle();
void testRedlinePortions();
+ void testContentControl();
CPPUNIT_TEST_SUITE(SwTiledRenderingTest);
CPPUNIT_TEST(testRegisterCallback);
@@ -254,6 +255,7 @@ public:
CPPUNIT_TEST(testCondCollCopy);
CPPUNIT_TEST(testMoveShapeHandle);
CPPUNIT_TEST(testRedlinePortions);
+ CPPUNIT_TEST(testContentControl);
CPPUNIT_TEST_SUITE_END();
private:
@@ -279,6 +281,7 @@ private:
OString m_sHyperlinkText;
OString m_sHyperlinkLink;
OString m_aFormFieldButton;
+ OString m_aContentControl;
OString m_ShapeSelection;
TestLokCallbackWrapper m_callbackWrapper;
};
@@ -448,6 +451,11 @@ void SwTiledRenderingTest::callbackImpl(int nType, const char* pPayload)
m_aFormFieldButton = OString(pPayload);
}
break;
+ case LOK_CALLBACK_CONTENT_CONTROL:
+ {
+ m_aContentControl = OString(pPayload);
+ }
+ break;
case LOK_CALLBACK_GRAPHIC_SELECTION:
{
m_ShapeSelection = OString(pPayload);
@@ -3593,6 +3601,42 @@ void SwTiledRenderingTest::testRedlinePortions()
assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[4]", "portion", " after");
}
+void SwTiledRenderingTest::testContentControl()
+{
+ // Given a document with a content control:
+ SwXTextDocument* pXTextDocument = createDoc("content-control.odt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ pWrtShell->SttEndDoc(/*bStt=*/true);
+ m_aContentControl.clear();
+
+ // When entering that content control (chars 2-7 are the content control):
+ pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, /*nCount=*/5, /*bBasicCall=*/false);
+
+ // Then make sure that the callback is emitted:
+ // Without the accompanying fix in place, this test would have failed, no callback was emitted.
+ CPPUNIT_ASSERT(!m_aContentControl.isEmpty());
+ {
+ std::stringstream aStream(m_aContentControl.getStr());
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ OString sAction = aTree.get_child("action").get_value<std::string>().c_str();
+ CPPUNIT_ASSERT_EQUAL(OString("show"), sAction);
+ OString sRectangles = aTree.get_child("rectangles").get_value<std::string>().c_str();
+ CPPUNIT_ASSERT(!sRectangles.isEmpty());
+ }
+
+ // And when leaving that content control:
+ pWrtShell->SttEndDoc(/*bStt=*/true);
+
+ // Then make sure that the callback is emitted again:
+ std::stringstream aStream(m_aContentControl.getStr());
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ OString sAction = aTree.get_child("action").get_value<std::string>().c_str();
+ CPPUNIT_ASSERT_EQUAL(OString("hide"), sAction);
+}
+
CPPUNIT_TEST_SUITE_REGISTRATION(SwTiledRenderingTest);
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx
index 90f20d09207e..b73b157d1b36 100644
--- a/sw/source/core/crsr/viscrs.cxx
+++ b/sw/source/core/crsr/viscrs.cxx
@@ -58,6 +58,7 @@
#include <docsh.hxx>
#include <svtools/optionsdrawinglayer.hxx>
#include <o3tl/string_view.hxx>
+#include <tools/json_writer.hxx>
#include <cellfrm.hxx>
#include <wrtsh.hxx>
#include <textcontentcontrol.hxx>
@@ -635,6 +636,7 @@ void SwSelPaintRects::HighlightInputField()
void SwSelPaintRects::HighlightContentControl()
{
std::vector<basegfx::B2DRange> aContentControlRanges;
+ std::vector<OString> aLOKRectangles;
if (m_bShowContentControlOverlay)
{
@@ -669,12 +671,25 @@ void SwSelPaintRects::HighlightContentControl()
aContentControlRanges.emplace_back(aRect.Left(), aRect.Top(), aRect.Right() + 1,
aRect.Bottom() + 1);
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ aLOKRectangles.push_back(aRect.toString());
+ }
}
}
}
if (!aContentControlRanges.empty())
{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OString aPayload = comphelper::string::join("; ", aLOKRectangles);
+ tools::JsonWriter aJson;
+ aJson.put("action", "show");
+ aJson.put("rectangles", aPayload);
+ std::unique_ptr<char, o3tl::free_delete> pJson(aJson.extractData());
+ GetShell()->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CONTENT_CONTROL, pJson.get());
+ }
if (m_pContentControlOverlay)
{
m_pContentControlOverlay->setRanges(std::move(aContentControlRanges));
@@ -700,6 +715,13 @@ void SwSelPaintRects::HighlightContentControl()
}
else
{
+ if (comphelper::LibreOfficeKit::isActive() && m_pContentControlOverlay)
+ {
+ tools::JsonWriter aJson;
+ aJson.put("action", "hide");
+ std::unique_ptr<char, o3tl::free_delete> pJson(aJson.extractData());
+ GetShell()->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CONTENT_CONTROL, pJson.get());
+ }
m_pContentControlOverlay.reset();
}
}