diff options
-rw-r--r-- | include/LibreOfficeKit/LibreOfficeKitEnums.h | 18 | ||||
-rw-r--r-- | libreofficekit/source/gtk/lokdocview.cxx | 55 | ||||
-rw-r--r-- | sw/qa/extras/tiledrendering/data/content-control.odt | bin | 0 -> 9683 bytes | |||
-rw-r--r-- | sw/qa/extras/tiledrendering/tiledrendering.cxx | 44 | ||||
-rw-r--r-- | sw/source/core/crsr/viscrs.cxx | 22 |
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 Binary files differnew file mode 100644 index 000000000000..624063fbd606 --- /dev/null +++ b/sw/qa/extras/tiledrendering/data/content-control.odt 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(); } } |