configmgr/source/access.cxx                          |   16 +++
 configmgr/source/access.hxx                          |    6 +
 configmgr/source/node.cxx                            |    1 
 configmgr/source/node.hxx                            |    6 +
 configmgr/source/xcsparser.cxx                       |   90 +++++++++++++++----
 configmgr/source/xcsparser.hxx                       |    2 
 cui/source/options/optaboutconfig.cxx                |   33 +++++-
 cui/source/options/optaboutconfig.hxx                |    3 
 offapi/UnoApi_offapi.mk                              |    1 
 offapi/com/sun/star/configuration/XDocumentation.idl |   34 +++++++
 officecfg/util/schema_trim.xsl                       |    2 
 solenv/bin/packregistry.xslt                         |    4 
 12 files changed, 171 insertions(+), 27 deletions(-)

New commits:
commit db3078bd8c8e3ce3a99fc3987bb6e93b609990c1
Author:     Samuel Mehrbrodt <samuel.mehrbr...@allotropia.de>
AuthorDate: Thu Sep 21 16:00:40 2023 +0200
Commit:     Samuel Mehrbrodt <samuel.mehrbr...@allotropia.de>
CommitDate: Thu Nov 9 08:14:38 2023 +0100

    tdf#157431 Show description for expert config items
    
    Change-Id: I7d0257c2e06ed384f90ca3b51a6d2549044f2cf3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/157148
    Tested-by: Jenkins
    Reviewed-by: Samuel Mehrbrodt <samuel.mehrbr...@allotropia.de>

diff --git a/configmgr/source/access.cxx b/configmgr/source/access.cxx
index 668192fee63c..f2b0931b0beb 100644
--- a/configmgr/source/access.cxx
+++ b/configmgr/source/access.cxx
@@ -216,6 +216,8 @@ css::uno::Sequence< css::uno::Type > Access::getTypes()
     } else {
         types.push_back(
             cppu::UnoType< css::container::XHierarchicalNameAccess >::get());
+        types.push_back(
+            cppu::UnoType< css::configuration::XDocumentation >::get());
     }
     addTypes(&types);
     return comphelper::containerToSequence(types);
@@ -440,6 +442,19 @@ css::uno::Any Access::getByHierarchicalName(OUString const 
& aName)
     return child->asValue();
 }
 
+OUString Access::getDescriptionByHierarchicalName(OUString const & aName)
+{
+    assert(thisIs(IS_ANY));
+    osl::MutexGuard g(*lock_);
+    checkLocalizedPropertyAccess();
+    rtl::Reference< ChildAccess > child(getSubChild(aName));
+    if (!child.is()) {
+        throw css::container::NoSuchElementException(
+            aName, getXWeak());
+    }
+    return child->getNode()->getDescription();
+}
+
 sal_Bool Access::hasByHierarchicalName(OUString const & aName)
 {
     assert(thisIs(IS_ANY));
@@ -1300,6 +1315,7 @@ css::uno::Any Access::queryInterface(css::uno::Type const 
& aType)
         static_cast< css::lang::XServiceInfo * >(this),
         static_cast< css::lang::XComponent * >(this),
         static_cast< css::container::XHierarchicalNameAccess * >(this),
+        static_cast< css::configuration::XDocumentation * >(this),
         static_cast< css::container::XContainer * >(this),
         static_cast< css::beans::XExactName * >(this),
         static_cast< css::container::XHierarchicalName * >(this),
diff --git a/configmgr/source/access.hxx b/configmgr/source/access.hxx
index daa5f1d1f6be..4efa910b68c5 100644
--- a/configmgr/source/access.hxx
+++ b/configmgr/source/access.hxx
@@ -35,6 +35,8 @@
 #include <com/sun/star/beans/XPropertySetInfo.hpp>
 #include <com/sun/star/container/XContainer.hpp>
 #include <com/sun/star/container/XHierarchicalName.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/configuration/XDocumentation.hpp>
 #include <com/sun/star/container/XHierarchicalNameReplace.hpp>
 #include <com/sun/star/container/XNameContainer.hpp>
 #include <com/sun/star/container/XNamed.hpp>
@@ -82,6 +84,7 @@ class Access:
     public cppu::OWeakObject, public css::lang::XTypeProvider,
     public css::lang::XServiceInfo,
     public css::lang::XComponent,
+    public css::configuration::XDocumentation,
     public css::container::XHierarchicalNameReplace,
     public css::container::XContainer,
     public css::beans::XExactName,
@@ -159,6 +162,9 @@ public:
     virtual css::uno::Any SAL_CALL getByHierarchicalName(
         OUString const & aName) override;
 
+    virtual OUString SAL_CALL getDescriptionByHierarchicalName(
+        OUString const & aName) override;
+
     virtual sal_Bool SAL_CALL hasByHierarchicalName(OUString const & aName) 
override;
 
     virtual void SAL_CALL replaceByHierarchicalName(
diff --git a/configmgr/source/node.cxx b/configmgr/source/node.cxx
index 8f00d3887da3..2c8c697b5338 100644
--- a/configmgr/source/node.cxx
+++ b/configmgr/source/node.cxx
@@ -59,7 +59,6 @@ void Node::setFinalized(int layer) {
     finalized_ = layer;
 }
 
-
 rtl::Reference< Node > Node::getMember(OUString const & name) {
     NodeMap const & members = getMembers();
     NodeMap::const_iterator i(members.find(name));
diff --git a/configmgr/source/node.hxx b/configmgr/source/node.hxx
index b858c9e42e95..cce8e3d4abb3 100644
--- a/configmgr/source/node.hxx
+++ b/configmgr/source/node.hxx
@@ -23,7 +23,9 @@
 
 #include <rtl/ref.hxx>
 #include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
 #include <salhelper/simplereferenceobject.hxx>
+#include <xmlreader/span.hxx>
 
 namespace configmgr {
 
@@ -51,6 +53,9 @@ public:
     void setFinalized(int layer);
     int getFinalized() const { return finalized_;}
 
+    void setDescription(OUString const& description) { description_ = 
description; };
+    OUString getDescription() { return description_; }
+
     rtl::Reference< Node > getMember(OUString const & name);
 
 protected:
@@ -61,6 +66,7 @@ protected:
 private:
     int layer_;
     int finalized_;
+    OUString description_;
 };
 
 }
diff --git a/configmgr/source/xcsparser.cxx b/configmgr/source/xcsparser.cxx
index 947792c0a62e..4f9cf2ee2ddb 100644
--- a/configmgr/source/xcsparser.cxx
+++ b/configmgr/source/xcsparser.cxx
@@ -109,12 +109,14 @@ void merge(
 }
 
 XcsParser::XcsParser(int layer, Data & data):
-    valueParser_(layer), data_(data), state_(STATE_START), ignoring_()
+    valueParser_(layer), data_(data), state_(STATE_START), ignoring_(), 
bIsParsingInfo_(false)
 {}
 
 XcsParser::~XcsParser() {}
 
 xmlreader::XmlReader::Text XcsParser::getTextMode() {
+    if (bIsParsingInfo_)
+        return xmlreader::XmlReader::Text::Raw;
     return valueParser_.getTextMode();
 }
 
@@ -122,6 +124,20 @@ bool XcsParser::startElement(
     xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
     std::set< OUString > const * /*existingDependencies*/)
 {
+    //TODO: ignoring component-schema import, component-schema uses, and
+    // prop constraints; accepting all four at illegal places (and with
+    // illegal content):
+    if (ignoring_ > 0
+        || (nsId == xmlreader::XmlReader::NAMESPACE_NONE
+            && (name == "import" || name == "uses" || name == "constraints" || 
name == "desc")))
+    {
+        assert(ignoring_ < LONG_MAX);
+        ++ignoring_;
+        return true;
+    }
+
+    if (bIsParsingInfo_)
+        return true;
     if (valueParser_.startElement(reader, nsId, name)) {
         return true;
     }
@@ -135,18 +151,6 @@ bool XcsParser::startElement(
             return true;
         }
     } else {
-        //TODO: ignoring component-schema import, component-schema uses, and
-        // prop constraints; accepting all four at illegal places (and with
-        // illegal content):
-        if (ignoring_ > 0 ||
-            (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
-             (name == "info" || name == "import" ||
-              name == "uses" || name == "constraints")))
-        {
-            assert(ignoring_ < LONG_MAX);
-            ++ignoring_;
-            return true;
-        }
         switch (state_) {
         case STATE_COMPONENT_SCHEMA:
             if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
@@ -155,6 +159,12 @@ bool XcsParser::startElement(
                 state_ = STATE_TEMPLATES;
                 return true;
             }
+            if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+                name == "info")
+            {
+                bIsParsingInfo_ = true;
+                return true;
+            }
             [[fallthrough]];
         case STATE_TEMPLATES_DONE:
             if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
@@ -170,6 +180,12 @@ bool XcsParser::startElement(
             }
             break;
         case STATE_TEMPLATES:
+            if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+                    name == "info")
+            {
+                bIsParsingInfo_ = true;
+                return true;
+            }
             if (elements_.empty()) {
                 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
                     name == "group")
@@ -183,6 +199,12 @@ bool XcsParser::startElement(
                     handleSet(reader, true);
                     return true;
                 }
+                if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+                    name == "info")
+                {
+                    bIsParsingInfo_ = true;
+                    return true;
+                }
                 break;
             }
             [[fallthrough]];
@@ -197,6 +219,12 @@ bool XcsParser::startElement(
                     handlePropValue(reader, elements_.top().node);
                     return true;
                 }
+                if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+                    name == "info")
+                {
+                    bIsParsingInfo_ = true;
+                    return true;
+                }
                 break;
             case Node::KIND_GROUP:
                 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
@@ -223,6 +251,12 @@ bool XcsParser::startElement(
                     handleSet(reader, false);
                     return true;
                 }
+                if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+                    name == "info")
+                {
+                    bIsParsingInfo_ = true;
+                    return true;
+                }
                 break;
             case Node::KIND_SET:
                 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
@@ -233,6 +267,12 @@ bool XcsParser::startElement(
                         static_cast< SetNode * >(elements_.top().node.get()));
                     return true;
                 }
+                if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+                    name == "info")
+                {
+                    bIsParsingInfo_ = true;
+                    return true;
+                }
                 break;
             default: // Node::KIND_LOCALIZED_VALUE
                 assert(false); // this cannot happen
@@ -251,15 +291,28 @@ bool XcsParser::startElement(
 }
 
 void XcsParser::endElement(xmlreader::XmlReader const & reader) {
+    if (ignoring_ > 0) {
+        --ignoring_;
+        return;
+    }
+    if (bIsParsingInfo_)
+    {
+        bIsParsingInfo_ = false;
+        return;
+    }
     if (valueParser_.endElement()) {
         return;
     }
-    if (ignoring_ > 0) {
-        --ignoring_;
-    } else if (!elements_.empty()) {
+    if (!elements_.empty()) {
         Element top(std::move(elements_.top()));
         elements_.pop();
         if (top.node.is()) {
+            // Remove whitespace from description_ resulting from line 
breaks/indentation in xml files
+            OUString desc(description_.makeStringAndClear());
+            desc = desc.trim();
+            while (desc.indexOf("  ") != -1)
+                desc = desc.replaceAll("  ", " ");
+            top.node->setDescription(desc);
             if (elements_.empty()) {
                 switch (state_) {
                 case STATE_TEMPLATES:
@@ -316,6 +369,11 @@ void XcsParser::endElement(xmlreader::XmlReader const & 
reader) {
 }
 
 void XcsParser::characters(xmlreader::Span const & text) {
+    if (bIsParsingInfo_)
+    {
+        description_.append(text.convertFromUtf8());
+        return;
+    }
     valueParser_.characters(text);
 }
 
diff --git a/configmgr/source/xcsparser.hxx b/configmgr/source/xcsparser.hxx
index f2c5c77429d6..aedcccde1147 100644
--- a/configmgr/source/xcsparser.hxx
+++ b/configmgr/source/xcsparser.hxx
@@ -94,6 +94,8 @@ private:
     State state_;
     long ignoring_;
     ElementStack elements_;
+    bool bIsParsingInfo_;
+    OUStringBuffer description_;
 };
 
 }
diff --git a/cui/source/options/optaboutconfig.cxx 
b/cui/source/options/optaboutconfig.cxx
index 4578bc027581..74bf672a3cf1 100644
--- a/cui/source/options/optaboutconfig.cxx
+++ b/cui/source/options/optaboutconfig.cxx
@@ -20,6 +20,7 @@
 #include <com/sun/star/beans/UnknownPropertyException.hpp>
 #include <com/sun/star/beans/XPropertySetInfo.hpp>
 #include <com/sun/star/configuration/ReadWriteAccess.hpp>
+#include <com/sun/star/configuration/XDocumentation.hpp>
 #include <com/sun/star/container/XNameAccess.hpp>
 #include <com/sun/star/container/XNameReplace.hpp>
 #include <com/sun/star/container/XHierarchicalName.hpp>
@@ -70,13 +71,15 @@ struct UserData
     bool bIsPropertyPath;
     bool bIsReadOnly;
     OUString sPropertyPath;
+    OUString sTooltip;
     int aLineage;
     Reference<XNameAccess> aXNameAccess;
 
-    explicit UserData( OUString aPropertyPath, bool isReadOnly )
+    explicit UserData( OUString aPropertyPath, OUString aTooltip, bool 
isReadOnly )
         : bIsPropertyPath( true )
         , bIsReadOnly( isReadOnly )
         , sPropertyPath(std::move(aPropertyPath))
+        , sTooltip(std::move(aTooltip))
         , aLineage(0)
     {}
 
@@ -186,12 +189,19 @@ 
CuiAboutConfigTabPage::CuiAboutConfigTabPage(weld::Window* pParent)
 IMPL_LINK(CuiAboutConfigTabPage, QueryTooltip, const weld::TreeIter&, rIter, 
OUString)
 {
     UserData *pUserData = weld::fromId<UserData*>(m_xPrefBox->get_id(rIter));
+    OUStringBuffer ret;
     if (pUserData && pUserData->bIsReadOnly)
     {
-        return CuiResId(RID_CUISTR_OPT_READONLY);
+        ret.append(CuiResId(RID_CUISTR_OPT_READONLY));
+    }
+    if (pUserData && !pUserData->sTooltip.isEmpty())
+    {
+        if (pUserData->bIsReadOnly)
+            ret.append("\n\n");
+        ret.append(pUserData->sTooltip);
     }
 
-    return OUString();
+    return ret.makeStringAndClear();
 }
 
 IMPL_LINK(CuiAboutConfigTabPage, HeaderBarClick, int, nColumn, void)
@@ -230,10 +240,11 @@ CuiAboutConfigTabPage::~CuiAboutConfigTabPage()
 }
 
 void CuiAboutConfigTabPage::InsertEntry(const OUString& rPropertyPath, const 
OUString& rProp, const OUString& rStatus,
-                                        const OUString& rType, const OUString& 
rValue, const weld::TreeIter* pParentEntry,
+                                        const OUString& rType, const OUString& 
rValue, const OUString& rTooltip,
+                                        const weld::TreeIter* pParentEntry,
                                         bool bInsertToPrefBox, bool 
bIsReadOnly)
 {
-    m_vectorUserData.push_back(std::make_unique<UserData>(rPropertyPath, 
bIsReadOnly));
+    m_vectorUserData.push_back(std::make_unique<UserData>(rPropertyPath, 
rTooltip, bIsReadOnly));
     if (bInsertToPrefBox)
     {
         OUString sId(weld::toId(m_vectorUserData.back().get()));
@@ -357,6 +368,16 @@ void CuiAboutConfigTabPage::FillItems(const Reference< 
XNameAccess >& xNameAcces
                 SAL_WARN("cui.options", "unknown property: " << sPath + "/" + 
sPropertyName);
             }
 
+            OUString sTooltip;
+            try
+            {
+                Reference<configuration::XDocumentation> xObjProp(xNameAccess, 
UNO_QUERY_THROW);
+                sTooltip = xObjProp->getDescriptionByHierarchicalName(sPath + 
"/" + sPropertyName);
+            }
+            catch (css::container::NoSuchElementException)
+            {
+            }
+
             OUString sType = aNode.getValueTypeName();
             OUStringBuffer sValue;
 
@@ -518,7 +539,7 @@ void CuiAboutConfigTabPage::FillItems(const Reference< 
XNameAccess >& xNameAcces
             for(int j = 1; j < lineage; ++j)
                 index = sPath.indexOf("/", index + 1);
 
-            InsertEntry(sPath, sPath.copy(index + 1), item, sType, 
sValue.makeStringAndClear(),
+            InsertEntry(sPath, sPath.copy(index + 1), item, sType, 
sValue.makeStringAndClear(), sTooltip,
                         pParentEntry, !bLoadAll, bReadOnly);
         }
     }
diff --git a/cui/source/options/optaboutconfig.hxx 
b/cui/source/options/optaboutconfig.hxx
index 6cbb76e4cbde..c7278be7b19b 100644
--- a/cui/source/options/optaboutconfig.hxx
+++ b/cui/source/options/optaboutconfig.hxx
@@ -66,7 +66,8 @@ private:
 public:
    explicit CuiAboutConfigTabPage(weld::Window* pParent);
    virtual ~CuiAboutConfigTabPage() override;
-   void     InsertEntry(const OUString &rPropertyPath, const OUString& rProp, 
const OUString& rStatus, const OUString& rType, const OUString& rValue,
+   void     InsertEntry(const OUString &rPropertyPath, const OUString& rProp, 
const OUString& rStatus,
+                        const OUString& rType, const OUString& rValue, const 
OUString& rTooltip,
                         const weld::TreeIter* pParentEntry, bool 
bInsertToPrefBox, bool bIsReadOnly);
    void     Reset();
    void     FillItems(const css::uno::Reference<css::container::XNameAccess>& 
xNameAccess,
diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk
index eba803973aa5..2e2ee40aa8b4 100644
--- a/offapi/UnoApi_offapi.mk
+++ b/offapi/UnoApi_offapi.mk
@@ -2083,6 +2083,7 @@ $(eval $(call 
gb_UnoApi_add_idlfiles,offapi,com/sun/star/configuration,\
        InstallationIncompleteException \
        InvalidBootstrapFileException \
        MissingBootstrapFileException \
+    XDocumentation \
     XReadWriteAccess \
        XTemplateContainer \
        XTemplateInstance \
diff --git a/offapi/com/sun/star/configuration/XDocumentation.idl 
b/offapi/com/sun/star/configuration/XDocumentation.idl
new file mode 100644
index 000000000000..3bf3c638b141
--- /dev/null
+++ b/offapi/com/sun/star/configuration/XDocumentation.idl
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+module com { module sun { module star { module configuration {
+
+/* Is used to deliver documentation for the configuration.
+
+   <p>This interface is still unpublished and unstable.</p>
+
+   @since LibreOffice 24.2
+*/
+interface XDocumentation {
+    /** @returns
+            the description for the requested object
+
+        @param aName
+            the hierarchical name of the object.
+
+        @throws NoSuchElementException
+            if an element under aName does not exist.
+     */
+    string getDescriptionByHierarchicalName( [in] string aName )
+            raises( com::sun::star::container::NoSuchElementException );
+};
+
+}; }; }; };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/officecfg/util/schema_trim.xsl b/officecfg/util/schema_trim.xsl
index 41c25990719d..0e0f06fba75d 100644
--- a/officecfg/util/schema_trim.xsl
+++ b/officecfg/util/schema_trim.xsl
@@ -54,7 +54,7 @@
   <xsl:template match = "label[../deprecated]"/>
 
   <!-- copy all other documentation with content -->
-  <xsl:template match="desc|label">
+  <xsl:template match="info|desc|label">
     <xsl:copy>
       <xsl:apply-templates select="@*"/>
       <xsl:value-of select="."/>
diff --git a/solenv/bin/packregistry.xslt b/solenv/bin/packregistry.xslt
index 9079bbc6b43d..cf89c1a7dc30 100644
--- a/solenv/bin/packregistry.xslt
+++ b/solenv/bin/packregistry.xslt
@@ -56,7 +56,7 @@
   </xsl:template>
   <xsl:template
      match="oor:component-schema|oor:component-data|templates|component|group|
-            set|node-ref|prop|item|value|it|unicode|node">
+            set|node-ref|prop|item|value|it|unicode|node|info">
     <xsl:copy copy-namespaces="no">
       <!-- prune oor:component-data xmlns:install="..." namespaces (would only
            work in XSLT 2.0, however) -->
@@ -71,7 +71,7 @@
         <!-- ignore text elements (which must be whitespace only) -->
     </xsl:copy>
   </xsl:template>
-  <xsl:template match="info|import|uses|constraints"/>
+  <xsl:template match="import|uses|constraints"/>
     <!-- TODO: no longer strip elements when they are eventually read by
          configmgr implementation -->
   <xsl:template match="@*">

Reply via email to