sw/inc/cmdid.h                         |    1 
 sw/inc/unoprnms.hxx                    |    1 
 sw/qa/core/unocore/unocore.cxx         |   35 ++++++++++++++++++++++
 sw/source/core/inc/unoport.hxx         |    9 +++++
 sw/source/core/unocore/unomap1.cxx     |    1 
 sw/source/core/unocore/unoport.cxx     |    6 +++
 sw/source/core/unocore/unoportenum.cxx |   52 ++++++++++++++++++++++++++++++++-
 7 files changed, 103 insertions(+), 2 deletions(-)

New commits:
commit cb35526e9221d9781abb3cee2ba6971736b6b333
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Mon Apr 4 08:23:06 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Apr 4 09:14:57 2022 +0200

    sw content controls: include this in the UNO API text portion enum
    
    Which is how UNO API clients (e.g. ODT export) will be able to read
    RES_TXTATR_CONTENTCONTROL.
    
    Change-Id: Idf87312b1b89a0e44e7de2578de44b263fa8689a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132491
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/inc/cmdid.h b/sw/inc/cmdid.h
index 4add360583bb..27a3763c1d64 100644
--- a/sw/inc/cmdid.h
+++ b/sw/inc/cmdid.h
@@ -642,6 +642,7 @@ class SwUINumRuleItem;
 #define FN_UNO_TRANSFORMED_GRAPHIC          (FN_EXTRA2 + 129)
 #define FN_UNO_GRAPHIC_PREVIEW              (FN_EXTRA2 + 130)
 #define FN_UNO_LINEBREAK (FN_EXTRA2 + 131)
+#define FN_UNO_CONTENT_CONTROL (FN_EXTRA2 + 132)
 
 // Area: Help
 // Region: Traveling & Selection
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index 6a04072fdae5..3e6516b3551c 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -870,6 +870,7 @@
 #define UNO_NAME_ALLOW_OVERLAP "AllowOverlap"
 #define UNO_NAME_CLEAR "Clear"
 #define UNO_NAME_LINEBREAK "LineBreak"
+#define UNO_NAME_CONTENT_CONTROL "ContentControl"
 #define UNO_NAME_SHOWING_PLACE_HOLDER "ShowingPlaceHolder"
 #endif
 
diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx
index b03c91b39425..c9bca82ae2f8 100644
--- a/sw/qa/core/unocore/unocore.cxx
+++ b/sw/qa/core/unocore/unocore.cxx
@@ -371,6 +371,41 @@ CPPUNIT_TEST_FIXTURE(SwModelTestBase, testImageTooltip)
     CPPUNIT_ASSERT_EQUAL(aExpected, getProperty<OUString>(xImageProps, 
"Tooltip"));
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlTextPortionEnum)
+{
+    // Given a document with a content control around one or more text 
portions:
+    createSwDoc();
+    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, "test", /*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);
+    xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+
+    // When enumerating the text portions of the only paragraph in the 
document:
+    uno::Reference<css::text::XTextRange> xTextPortion = 
getRun(getParagraph(1), 1);
+
+    // Then make sure that the text portion type is correct + the content can 
be read:
+    auto aPortionType = getProperty<OUString>(xTextPortion, "TextPortionType");
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: ContentControl
+    // - Actual  : Text
+    // i.e. the content control text attribute was ignored.
+    CPPUNIT_ASSERT_EQUAL(OUString("ContentControl"), aPortionType);
+    xContentControl
+        = getProperty<uno::Reference<text::XTextContent>>(xTextPortion, 
"ContentControl");
+    uno::Reference<text::XTextRange> xContentControlRange(xContentControl, 
uno::UNO_QUERY);
+    xText = xContentControlRange->getText();
+    uno::Reference<container::XEnumerationAccess> xContentEnumAccess(xText, 
uno::UNO_QUERY);
+    uno::Reference<container::XEnumeration> xContentEnum = 
xContentEnumAccess->createEnumeration();
+    uno::Reference<text::XTextRange> xContent(xContentEnum->nextElement(), 
uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(OUString("test"), xContent->getString());
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unoport.hxx b/sw/source/core/inc/unoport.hxx
index 76cf877db70b..936bc01e0521 100644
--- a/sw/source/core/inc/unoport.hxx
+++ b/sw/source/core/inc/unoport.hxx
@@ -75,7 +75,8 @@ enum SwTextPortionType
     PORTION_FIELD_START_END,
     PORTION_ANNOTATION,
     PORTION_ANNOTATION_END,
-    PORTION_LINEBREAK
+    PORTION_LINEBREAK,
+    PORTION_CONTENT_CONTROL
 };
 
 class SwXTextPortion : public cppu::WeakImplHelper
@@ -109,6 +110,7 @@ private:
     css::uno::Reference< css::text::XTextContent >
         m_xMeta;
     css::uno::Reference<css::text::XTextContent> m_xLineBreak;
+    css::uno::Reference<css::text::XTextContent> m_xContentControl;
     std::unique_ptr< css::uno::Any > m_pRubyText;
     std::unique_ptr< css::uno::Any > m_pRubyStyle;
     std::unique_ptr< css::uno::Any > m_pRubyAdjust;
@@ -229,6 +231,11 @@ public:
         m_xLineBreak = xLineBreak;
     }
 
+    void SetContentControl(const css::uno::Reference<css::text::XTextContent>& 
xContentControl)
+    {
+        m_xContentControl = xContentControl;
+    }
+
     void SetCollapsed(bool bSet)        { m_bIsCollapsed = bSet;}
 
     SwTextPortionType GetTextPortionType() const { return m_ePortionType; }
diff --git a/sw/source/core/unocore/unomap1.cxx 
b/sw/source/core/unocore/unomap1.cxx
index 0b138f1d772c..acf803d34253 100644
--- a/sw/source/core/unocore/unomap1.cxx
+++ b/sw/source/core/unocore/unomap1.cxx
@@ -986,6 +986,7 @@ const SfxItemPropertyMapEntry*  
SwUnoPropertyMapProvider::GetTextPortionExtensio
         {u"" UNO_NAME_TEXT_PORTION_TYPE, FN_UNO_TEXT_PORTION_TYPE, 
cppu::UnoType<OUString>::get(),                        
PropertyAttribute::READONLY, 0},
         {u"" UNO_NAME_META, FN_UNO_META, 
cppu::UnoType<css::text::XTextContent>::get(), 
PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 },
         { u"" UNO_NAME_LINEBREAK, FN_UNO_LINEBREAK, 
cppu::UnoType<css::text::XTextContent>::get(),  
PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },
+        { u"" UNO_NAME_CONTENT_CONTROL, FN_UNO_CONTENT_CONTROL, 
cppu::UnoType<css::text::XTextContent>::get(), 
PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 },
         { u"", 0, css::uno::Type(), 0, 0 }
     };
 
diff --git a/sw/source/core/unocore/unoport.cxx 
b/sw/source/core/unocore/unoport.cxx
index fdc90023e41d..dd44538edbd1 100644
--- a/sw/source/core/unocore/unoport.cxx
+++ b/sw/source/core/unocore/unoport.cxx
@@ -253,6 +253,9 @@ void SwXTextPortion::GetPropertyValue(
             case PORTION_LINEBREAK:
                 pRet = "LineBreak";
                 break;
+            case PORTION_CONTENT_CONTROL:
+                pRet = UNO_NAME_CONTENT_CONTROL;
+                break;
             default:
                 pRet = nullptr;
             }
@@ -286,6 +289,9 @@ void SwXTextPortion::GetPropertyValue(
         case FN_UNO_LINEBREAK:
             rVal <<= m_xLineBreak;
             break;
+        case FN_UNO_CONTENT_CONTROL:
+            rVal <<= m_xContentControl;
+            break;
         case FN_UNO_IS_COLLAPSED:
         {
             switch (m_ePortionType)
diff --git a/sw/source/core/unocore/unoportenum.cxx 
b/sw/source/core/unocore/unoportenum.cxx
index 2dd92b0c71f0..30a24721b031 100644
--- a/sw/source/core/unocore/unoportenum.cxx
+++ b/sw/source/core/unocore/unoportenum.cxx
@@ -43,6 +43,7 @@
 #include <unofield.hxx>
 #include <unometa.hxx>
 #include <unolinebreak.hxx>
+#include <unocontentcontrol.hxx>
 #include <fmtfld.hxx>
 #include <fldbas.hxx>
 #include <fmtmeta.hxx>
@@ -548,6 +549,21 @@ lcl_CreateMetaPortion(
     return pPortion;
 }
 
+/// Creates a text portion that has a non-empty ContentControl property.
+static uno::Reference<text::XTextRange>
+lcl_CreateContentControlPortion(const uno::Reference<text::XText>& xParent,
+                                const SwUnoCursor* pUnoCursor, SwTextAttr& 
rAttr,
+                                std::unique_ptr<const TextRangeList_t>&& 
pPortions)
+{
+    uno::Reference<text::XTextContent> xContentControl = 
SwXContentControl::CreateXContentControl(
+        
*static_cast<SwFormatContentControl&>(rAttr.GetAttr()).GetContentControl(), 
xParent,
+        std::move(pPortions));
+    rtl::Reference<SwXTextPortion> pPortion;
+    pPortion = new SwXTextPortion(pUnoCursor, xParent, 
PORTION_CONTENT_CONTROL);
+    pPortion->SetContentControl(xContentControl);
+    return pPortion;
+}
+
 /**
  * Exports all bookmarks from rBkmArr into rPortions that have the same start
  * or end position as nIndex.
@@ -704,7 +720,7 @@ lcl_ExportHints(
     bool & o_rbCursorMoved,
     sal_Int32 & o_rNextAttrPosition)
 {
-    // if the attribute has a dummy character, then xRef is set (except META)
+    // if the attribute has a dummy character, then xRef is set (except META 
and CONTENT_CONTROL)
     // otherwise, the portion for the attribute is inserted into rPortions!
     Reference<XTextRange> xRef;
     SwDoc& rDoc = pUnoCursor->GetDoc();
@@ -784,6 +800,39 @@ lcl_ExportHints(
                         }
                     }
                     break;
+                    case RES_TXTATR_CONTENTCONTROL:
+                    {
+                        if (pAttr->GetStart() == *pAttr->GetEnd())
+                        {
+                            SAL_WARN("sw.core", "lcl_ExportHints: empty 
content control");
+                        }
+                        if ((i_nStartPos > 0) && (pAttr->GetStart() < 
i_nStartPos))
+                        {
+                            // If the start pos is the start of the content of 
the content control,
+                            // skip it: it'll be handled in 
SwXContentControl::createEnumeration().
+                            if (pAttr->GetStart() + 1 == i_nStartPos)
+                            {
+                                nEndIndex = pHints->Count() - 1;
+                            }
+                            break;
+                        }
+                        PortionList_t Top = rPortionStack.top();
+                        if (Top.second != pAttr)
+                        {
+                            SAL_WARN("sw.core", "lcl_ExportHints: content 
control is not at the "
+                                                "top of the portion stack");
+                        }
+                        else
+                        {
+                            std::unique_ptr<const TextRangeList_t> 
pCurrentPortions(Top.first);
+                            rPortionStack.pop();
+                            uno::Reference<text::XTextRange> xPortion(
+                                lcl_CreateContentControlPortion(xParent, 
pUnoCursor, *pAttr,
+                                                                
std::move(pCurrentPortions)));
+                            rPortionStack.top().first->push_back(xPortion);
+                        }
+                    }
+                    break;
                 }
             }
         }
@@ -937,6 +986,7 @@ lcl_ExportHints(
                 break;
                 case RES_TXTATR_META:
                 case RES_TXTATR_METAFIELD:
+                case RES_TXTATR_CONTENTCONTROL:
                     if (pAttr->GetStart() != *pAttr->GetEnd())
                     {
                         if (!bRightMoveForbidden)

Reply via email to