include/LibreOfficeKit/LibreOfficeKit.hxx                           |    6 
 include/LibreOfficeKit/LibreOfficeKitEnums.h                        |    7 +
 libreofficekit/qa/gtktiledviewer/gtv-lokdocview-signal-handlers.cxx |    3 
 libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.cxx               |    4 
 libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.hxx               |    1 
 libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.cxx            |   27 ++++
 libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.hxx            |    1 
 libreofficekit/qa/gtktiledviewer/gtv.ui                             |   45 
+++++++
 sw/qa/extras/tiledrendering/tiledrendering.cxx                      |   57 
+++++++++
 sw/source/core/crsr/viscrs.cxx                                      |    5 
 sw/source/uibase/uno/unotxdoc.cxx                                   |   61 
++++++++++
 11 files changed, 217 insertions(+)

New commits:
commit abb92442d6f36bf65a36735ee33451adb1f5df2e
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Wed Jun 1 08:30:03 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Wed Jun 1 11:27:16 2022 +0200

    sw content controls, date: add LOK API
    
    - send a LOK_CALLBACK_CONTENT_CONTROL with date=true when entering a
      date content control
    
    - extend lok::Document::sendContentControlEvent() to be able to set the
      date of a date content control (after the client's date picker is
      closed)
    
    - update gtktiledviewer to work with these
    
    (cherry picked from commit 9cb1a07dc2760a30d7f321aa7fa4ce2a460dfa6c)
    
    Change-Id: I0abf21eb1d4ba233050f0aa2607b68740c048262
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135223
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx 
b/include/LibreOfficeKit/LibreOfficeKit.hxx
index 993654c88345..6dbfbf964b29 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -851,6 +851,12 @@ public:
      *     "type": "picture",
      *     "changed": "file:///path/to/test.png"
      * }
+     *
+     * To select a date of the current date content control:
+     * {
+     *     "type": "date",
+     *     "selected": "2022-05-29T00:00:00Z"
+     * }
      */
     void sendContentControlEvent(const char* pArguments)
     {
diff --git a/include/LibreOfficeKit/LibreOfficeKitEnums.h 
b/include/LibreOfficeKit/LibreOfficeKitEnums.h
index aeb36690f9d4..22577a193e52 100644
--- a/include/LibreOfficeKit/LibreOfficeKitEnums.h
+++ b/include/LibreOfficeKit/LibreOfficeKitEnums.h
@@ -822,6 +822,13 @@ typedef enum
      * {
      *     "action": "change-picture"
      * }
+     *
+     * Entered a date content control:
+     * {
+     *     "action": "show",
+     *     "rectangles": "...",
+     *     "date": "true"
+     * }
      */
     LOK_CALLBACK_CONTENT_CONTROL = 55,
 
diff --git 
a/libreofficekit/qa/gtktiledviewer/gtv-lokdocview-signal-handlers.cxx 
b/libreofficekit/qa/gtktiledviewer/gtv-lokdocview-signal-handlers.cxx
index eadcd1a01c76..6816a17fcc2f 100644
--- a/libreofficekit/qa/gtktiledviewer/gtv-lokdocview-signal-handlers.cxx
+++ b/libreofficekit/qa/gtktiledviewer/gtv-lokdocview-signal-handlers.cxx
@@ -229,6 +229,9 @@ void LOKDocViewSigHandlers::contentControl(LOKDocView* 
pDocView, gchar* pJson, g
         }
     }
 
+    boost::optional<boost::property_tree::ptree&> oDate = 
aTree.get_child_optional("date");
+    
gtk_widget_set_sensitive(GTK_WIDGET(toolbar->m_pContentControlDateSelector), 
bool(oDate));
+
     gtv_application_window_set_part_broadcast(window, true);
 }
 
diff --git a/libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.cxx 
b/libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.cxx
index afe6162f76fc..84c5335b32c2 100644
--- a/libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.cxx
+++ b/libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.cxx
@@ -118,6 +118,8 @@ gtv_main_toolbar_init(GtvMainToolbar* toolbar)
     toolbar->m_pFormulabar = GTK_WIDGET(gtk_builder_get_object(builder.get(), 
"formulabar_entry"));
     toolbar->m_pContentControlSelector
         = GTK_WIDGET(gtk_builder_get_object(builder.get(), 
"combo_contentcontrolselector"));
+    toolbar->m_pContentControlDateSelector
+        = GTK_WIDGET(gtk_builder_get_object(builder.get(), 
"menu_contentcontroldateselector"));
 
     // TODO: compile with -rdynamic and get rid of it
     gtk_builder_add_callback_symbol(builder.get(), "btn_clicked", 
G_CALLBACK(btn_clicked));
@@ -132,6 +134,8 @@ gtv_main_toolbar_init(GtvMainToolbar* toolbar)
     gtk_builder_add_callback_symbol(builder.get(), "changePart", 
G_CALLBACK(changePart));
     gtk_builder_add_callback_symbol(builder.get(), "changeContentControl",
                                     G_CALLBACK(changeContentControl));
+    gtk_builder_add_callback_symbol(builder.get(), "changeDateContentControl",
+                                    G_CALLBACK(changeDateContentControl));
     gtk_builder_add_callback_symbol(builder.get(), "changeZoom", 
G_CALLBACK(changeZoom));
     gtk_builder_add_callback_symbol(builder.get(), "toggleFindbar", 
G_CALLBACK(toggleFindbar));
     gtk_builder_add_callback_symbol(builder.get(), "documentRedline", 
G_CALLBACK(documentRedline));
diff --git a/libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.hxx 
b/libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.hxx
index e385e6e855d1..91827ef92695 100644
--- a/libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.hxx
+++ b/libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.hxx
@@ -30,6 +30,7 @@ struct GtvMainToolbar
     GtkWidget* m_pAddressbar;
     GtkWidget* m_pFormulabar;
     GtkWidget* m_pContentControlSelector;
+    GtkWidget* m_pContentControlDateSelector;
 };
 
 struct GtvMainToolbarClass
diff --git a/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.cxx 
b/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.cxx
index 0afce9b671db..bb0e7edd390d 100644
--- a/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.cxx
+++ b/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.cxx
@@ -334,6 +334,33 @@ void changeContentControl(GtkWidget* pSelector, gpointer 
/*pItem*/)
     }
 }
 
+void changeDateContentControl(GtkWidget* pSelector, gpointer /*pItem*/)
+{
+    GtvApplicationWindow* window = 
GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(pSelector));
+    if (gtv_application_window_get_part_broadcast(window) && 
window->lokdocview)
+    {
+        GtkPopover* pPopover = 
GTK_POPOVER(gtk_widget_get_parent(gtk_widget_get_parent(pSelector)));
+        guint nYear, nMonth, nDay;
+        gtk_calendar_get_date(GTK_CALENDAR(pSelector), &nYear, &nMonth, &nDay);
+        gtk_popover_popdown(pPopover);
+
+        std::stringstream aDate;
+        aDate << std::setfill('0') << std::setw(4) << nYear;
+        aDate << "-";
+        aDate << std::setfill('0') << std::setw(2) << (nMonth + 1);
+        aDate << "-";
+        aDate << std::setfill('0') << std::setw(2) << nDay;
+        aDate << "T00:00:00Z";
+        boost::property_tree::ptree aValues;
+        aValues.put("type", "date");
+        aValues.put("selected", aDate.str());
+        std::stringstream aStream;
+        boost::property_tree::write_json(aStream, aValues);
+        std::string aJson = aStream.str();
+        
lok_doc_view_send_content_control_event(LOK_DOC_VIEW(window->lokdocview), 
aJson.c_str());
+    }
+}
+
 void changeZoom( GtkWidget* pButton, gpointer /* pItem */ )
 {
     static const float fZooms[] = { 0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0, 5.0 };
diff --git a/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.hxx 
b/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.hxx
index c5cf89c281fc..c06017d87414 100644
--- a/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.hxx
+++ b/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.hxx
@@ -69,6 +69,7 @@ gboolean signalFormulabar(GtkWidget* /*pWidget*/, 
GdkEventKey* /*pEvent*/, gpoin
 
 void changeContentControl(GtkWidget* pSelector, gpointer /*pItem*/);
 
+void changeDateContentControl(GtkWidget* pSelector, gpointer /*pItem*/);
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/libreofficekit/qa/gtktiledviewer/gtv.ui 
b/libreofficekit/qa/gtktiledviewer/gtv.ui
index 26e15a0a9e3d..79cb9a9ec409 100644
--- a/libreofficekit/qa/gtktiledviewer/gtv.ui
+++ b/libreofficekit/qa/gtktiledviewer/gtv.ui
@@ -445,6 +445,27 @@
         <property name="homogeneous">True</property>
       </packing>
     </child>
+    <child>
+      <object class="GtkToolItem" id="contentcontroldateselectortoolitem">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkMenuButton" id="menu_contentcontroldateselector">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="tooltip_text">Content control date</property>
+            <property name="popover">calendar</property>
+            <child>
+              <placeholder/>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="homogeneous">True</property>
+      </packing>
+    </child>
     <child>
       <object class="GtkToggleToolButton" id="btn_editmode">
         <property name="visible">True</property>
@@ -779,4 +800,28 @@
       </packing>
     </child>
   </object>
+  <object class="GtkPopover" id="calendar">
+    <property name="can-focus">False</property>
+    <property name="position">bottom</property>
+    <child>
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="can-focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkCalendar" id="date">
+            <property name="visible">True</property>
+            <property name="can-focus">True</property>
+            <signal name="day-selected-double-click" 
handler="changeDateContentControl" swapped="no"/>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
 </interface>
diff --git a/sw/qa/extras/tiledrendering/tiledrendering.cxx 
b/sw/qa/extras/tiledrendering/tiledrendering.cxx
index b852d8f91206..bdcaf03c55a7 100644
--- a/sw/qa/extras/tiledrendering/tiledrendering.cxx
+++ b/sw/qa/extras/tiledrendering/tiledrendering.cxx
@@ -172,6 +172,7 @@ public:
     void testContentControl();
     void testDropDownContentControl();
     void testPictureContentControl();
+    void testDateContentControl();
 
     CPPUNIT_TEST_SUITE(SwTiledRenderingTest);
     CPPUNIT_TEST(testRegisterCallback);
@@ -262,6 +263,7 @@ public:
     CPPUNIT_TEST(testContentControl);
     CPPUNIT_TEST(testDropDownContentControl);
     CPPUNIT_TEST(testPictureContentControl);
+    CPPUNIT_TEST(testDateContentControl);
     CPPUNIT_TEST_SUITE_END();
 
 private:
@@ -3791,6 +3793,61 @@ void SwTiledRenderingTest::testPictureContentControl()
 
 }
 
+void SwTiledRenderingTest::testDateContentControl()
+{
+    // Given a document with a date content control:
+    SwXTextDocument* pXTextDocument = createDoc();
+    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+    setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+    uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, 
uno::UNO_QUERY);
+    uno::Reference<text::XTextDocument> xTextDocument(mxComponent, 
uno::UNO_QUERY);
+    uno::Reference<text::XText> xText = xTextDocument->getText();
+    uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+    xText->insertString(xCursor, "choose a date", /*bAbsorb=*/false);
+    xCursor->gotoStart(/*bExpand=*/false);
+    xCursor->gotoEnd(/*bExpand=*/true);
+    uno::Reference<text::XTextContent> xContentControl(
+        xMSF->createInstance("com.sun.star.text.ContentControl"), 
uno::UNO_QUERY);
+    uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, 
uno::UNO_QUERY);
+    xContentControlProps->setPropertyValue("Date", uno::Any(true));
+    xContentControlProps->setPropertyValue("DateFormat", 
uno::Any(OUString("YYYY-MM-DD")));
+    xContentControlProps->setPropertyValue("DateLanguage", 
uno::Any(OUString("en-US")));
+    xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+    pWrtShell->SttEndDoc(/*bStt=*/true);
+    m_aContentControl.clear();
+
+    // When entering that content control:
+    pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, /*nCount=*/1, 
/*bBasicCall=*/false);
+
+    // Then make sure that the callback is 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());
+        boost::optional<boost::property_tree::ptree&> oDate = 
aTree.get_child_optional("date");
+        CPPUNIT_ASSERT(oDate);
+    }
+
+    // And when selecting a date:
+    std::map<OUString, OUString> aArguments;
+    aArguments.emplace("type", "date");
+    aArguments.emplace("selected", "2022-05-30T00:00:00Z");
+    pXTextDocument->executeContentControlEvent(aArguments);
+
+    // Then make sure that the document is updated accordingly:
+    SwTextNode* pTextNode = pWrtShell->GetCursor()->GetNode().GetTextNode();
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 2022-05-30
+    // - Actual  : choose a date
+    // i.e. the document text was not updated.
+    CPPUNIT_ASSERT_EQUAL(OUString("2022-05-30"), 
pTextNode->GetExpandText(pWrtShell->GetLayout()));
+}
+
 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 5925d5598d7c..79b8ab934b93 100644
--- a/sw/source/core/crsr/viscrs.cxx
+++ b/sw/source/core/crsr/viscrs.cxx
@@ -707,6 +707,11 @@ void SwSelPaintRects::HighlightContentControl()
                 }
             }
 
+            if (pContentControl && pContentControl->GetDate())
+            {
+                aJson.put("date", "true");
+            }
+
             std::unique_ptr<char, o3tl::free_delete> 
pJson(aJson.extractData());
             
GetShell()->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CONTENT_CONTROL,
 pJson.get());
         }
diff --git a/sw/source/uibase/uno/unotxdoc.cxx 
b/sw/source/uibase/uno/unotxdoc.cxx
index d2622e08b564..1d68e037d949 100644
--- a/sw/source/uibase/uno/unotxdoc.cxx
+++ b/sw/source/uibase/uno/unotxdoc.cxx
@@ -3364,6 +3364,11 @@ SwXTextDocument::getSearchResultRectangles(const char* 
pPayload)
     return std::vector<basegfx::B2DRange>();
 }
 
+namespace
+{
+inline constexpr OUStringLiteral SELECTED_DATE_FORMAT = u"YYYY-MM-DD";
+}
+
 void SwXTextDocument::executeContentControlEvent(const StringMap& rArguments)
 {
     auto it = rArguments.find("type");
@@ -3426,6 +3431,62 @@ void SwXTextDocument::executeContentControlEvent(const 
StringMap& rArguments)
         pView->GetViewFrame()->GetDispatcher()->ExecuteList(SID_INSERT_GRAPHIC,
                                                             
SfxCallMode::SYNCHRON, { &aItem });
     }
+    else if (it->second == "date")
+    {
+        SwWrtShell* pWrtShell = m_pDocShell->GetWrtShell();
+        const SwPosition* pStart = pWrtShell->GetCursor()->Start();
+        SwTextNode* pTextNode = pStart->nNode.GetNode().GetTextNode();
+        if (!pTextNode)
+        {
+            return;
+        }
+
+        SwTextAttr* pAttr = 
pTextNode->GetTextAttrAt(pStart->nContent.GetIndex(),
+                                                     
RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT);
+        if (!pAttr)
+        {
+            return;
+        }
+
+        auto pTextContentControl = 
static_txtattr_cast<SwTextContentControl*>(pAttr);
+        const SwFormatContentControl& rFormatContentControl
+            = pTextContentControl->GetContentControl();
+        std::shared_ptr<SwContentControl> pContentControl
+            = rFormatContentControl.GetContentControl();
+        if (!pContentControl->GetDate())
+        {
+            return;
+        }
+
+        it = rArguments.find("selected");
+        if (it == rArguments.end())
+        {
+            return;
+        }
+
+        OUString aSelectedDate = it->second.replaceAll("T00:00:00Z", "");
+        SwDoc& rDoc = pTextNode->GetDoc();
+        SvNumberFormatter* pNumberFormatter = rDoc.GetNumberFormatter();
+        sal_uInt32 nFormat
+            = pNumberFormatter->GetEntryKey(SELECTED_DATE_FORMAT, 
LANGUAGE_ENGLISH_US);
+        if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+        {
+            sal_Int32 nCheckPos = 0;
+            SvNumFormatType nType;
+            OUString sFormat = SELECTED_DATE_FORMAT;
+            pNumberFormatter->PutEntry(sFormat, nCheckPos, nType, nFormat, 
LANGUAGE_ENGLISH_US);
+        }
+
+        if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+        {
+            return;
+        }
+
+        double dCurrentDate = 0;
+        pNumberFormatter->IsNumberFormat(aSelectedDate, nFormat, dCurrentDate);
+        pContentControl->SetSelectedDate(dCurrentDate);
+        pWrtShell->GotoContentControl(rFormatContentControl);
+    }
 }
 
 int SwXTextDocument::getPart()

Reply via email to