/* -*- 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/.
 */
 
#include "optaboutconfig.hxx"
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
 
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#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/configuration/theDefaultProvider.hpp>
#include <com/sun/star/container/XHierarchicalName.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/container/XNameReplace.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/uno/Type.hxx>
#include <com/sun/star/uno/TypeClass.hpp>
#include <com/sun/star/util/InvalidStateException.hpp>
#include <com/sun/star/util/SearchAlgorithms2.hpp>
#include <com/sun/star/util/SearchFlags.hpp>
#include <com/sun/star/util/XChangesBatch.hpp>
#include <comphelper/diagnose_ex.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/sequence.hxx>
#include <cppu/unotype.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <unotools/textsearch.hxx>
#include <utility>
#include <vcl/event.hxx>
 
#include <dialmgr.hxx>
#include <strings.hrc>
 
#include <algorithm>
#include <memory>
#include <vector>
 
using namespace ::com::sun::star;
using namespace com::sun::star::uno;
using namespace com::sun::star::container;
 
struct Prop_Impl
{
    OUString Name;
    OUString Property;
    Any Value;
 
    Prop_Impl(OUString sName, OUString sProperty, Any aValue)
        : Name(std::move(sName))
        , Property(std::move(sProperty))
        , Value(std::move(aValue))
    {
    }
};
 
struct UserData
{
    bool bIsPropertyPath;
    bool bIsReadOnly;
    bool bWasModified;
    OUString sPropertyPath;
    Any aPropertyValue;
    OUString sTooltip;
    int aLineage;
    Reference<XNameAccess> aXNameAccess;
 
    explicit UserData(OUString aPropertyPath, Any aPropValue, OUString aTooltip, bool isReadOnly,
                      bool wasModified)
        : bIsPropertyPath(true)
        , bIsReadOnly(isReadOnly)
        , bWasModified(wasModified)
        , sPropertyPath(std::move(aPropertyPath))
        , aPropertyValue(aPropValue)
        , sTooltip(std::move(aTooltip))
        , aLineage(0)
    {
    }
 
    explicit UserData(Reference<XNameAccess> const& rXNameAccess, int rIndex)
        : bIsPropertyPath(false)
        , bIsReadOnly(false)
        , bWasModified(false)
        , aLineage(rIndex)
        , aXNameAccess(rXNameAccess)
    {
    }
};
 
CuiAboutConfigTabPage::CuiAboutConfigTabPage(weld::Window* pParent)
    : GenericDialogController(pParent, u"cui/ui/aboutconfigdialog.ui"_ustr, u"AboutConfig"_ustr)
    , m_xResetBtn(m_xBuilder->weld_button(u"reset"_ustr))
    , m_xEditBtn(m_xBuilder->weld_button(u"edit"_ustr))
    , m_xSearchBtn(m_xBuilder->weld_button(u"searchButton"_ustr))
    , m_xModifiedCheckBtn(m_xBuilder->weld_check_button(u"modifiedButton"_ustr))
    , m_xSearchEdit(m_xBuilder->weld_entry(u"searchEntry"_ustr))
    , m_xPrefBox(m_xBuilder->weld_tree_view(u"preferences"_ustr))
    , m_xScratchIter(m_xPrefBox->make_iterator())
    , m_bSorted(false)
{
    m_xPrefBox->set_size_request(m_xPrefBox->get_approximate_digit_width() * 100,
                                 m_xPrefBox->get_height_rows(23));
    m_xPrefBox->connect_column_clicked(LINK(this, CuiAboutConfigTabPage, HeaderBarClick));
 
    m_xEditBtn->connect_clicked(LINK(this, CuiAboutConfigTabPage, StandardHdl_Impl));
    m_xResetBtn->connect_clicked(LINK(this, CuiAboutConfigTabPage, ResetBtnHdl_Impl));
    m_xPrefBox->connect_row_activated(LINK(this, CuiAboutConfigTabPage, DoubleClickHdl_Impl));
    m_xPrefBox->connect_expanding(LINK(this, CuiAboutConfigTabPage, ExpandingHdl_Impl));
    m_xSearchBtn->connect_clicked(LINK(this, CuiAboutConfigTabPage, SearchHdl_Impl));
    m_xModifiedCheckBtn->connect_toggled(LINK(this, CuiAboutConfigTabPage, ModifiedHdl_Impl));
 
    m_options.AlgorithmType2 = util::SearchAlgorithms2::ABSOLUTE;
    m_options.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
    m_options.searchFlag
        |= (util::SearchFlags::REG_NOT_BEGINOFLINE | util::SearchFlags::REG_NOT_ENDOFLINE);
 
    float fWidth = m_xPrefBox->get_approximate_digit_width();
    std::vector<int> aWidths{ o3tl::narrowing<int>(fWidth * 65), o3tl::narrowing<int>(fWidth * 20),
                              o3tl::narrowing<int>(fWidth * 8) };
    m_xPrefBox->set_column_fixed_widths(aWidths);
 
    m_xPrefBox->connect_query_tooltip(LINK(this, CuiAboutConfigTabPage, QueryTooltip));
}
 
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)
    {
        ret.append(CuiResId(RID_CUISTR_OPT_READONLY));
    }
    if (pUserData && !pUserData->sTooltip.isEmpty())
    {
        if (pUserData->bIsReadOnly)
            ret.append("\n\n");
        ret.append(pUserData->sTooltip);
    }
 
    return ret.makeStringAndClear();
}
 
IMPL_LINK(CuiAboutConfigTabPage, HeaderBarClick, int, nColumn, void)
{
    if (!m_bSorted)
    {
        m_xPrefBox->make_sorted();
        m_bSorted = true;
    }
 
    bool bSortAtoZ = m_xPrefBox->get_sort_order();
 
    //set new arrow positions in headerbar
    if (nColumn == m_xPrefBox->get_sort_column())
    {
        bSortAtoZ = !bSortAtoZ;
        m_xPrefBox->set_sort_order(bSortAtoZ);
    }
    else
    {
        int nOldSortColumn = m_xPrefBox->get_sort_column();
        if (nOldSortColumn != -1)
            m_xPrefBox->set_sort_indicator(TRISTATE_INDET, nOldSortColumn);
        m_xPrefBox->set_sort_column(nColumn);
    }
 
    if (nColumn != -1)
    {
        //sort lists
        m_xPrefBox->set_sort_indicator(bSortAtoZ ? TRISTATE_TRUE : TRISTATE_FALSE, nColumn);
    }
}
 
IMPL_STATIC_LINK_NOARG(CuiAboutConfigTabPage, ValidNameHdl, SvxNameDialog&, bool)
{
    // Allow empty value
    return true;
}
 
CuiAboutConfigTabPage::~CuiAboutConfigTabPage() {}
 
void CuiAboutConfigTabPage::InsertEntry(const OUString& rPropertyPath, Any aPropertyValue,
                                        const OUString& rProp, const OUString& rStatus,
                                        const OUString& rType, const OUString& rValue,
                                        const OUString& rTooltip,
                                        const weld::TreeIter* pParentEntry, bool bInsertToPrefBox,
                                        bool bIsReadOnly, bool bWasModified)
{
    bool bOnlyModified = m_xModifiedCheckBtn->get_active();
    if (bOnlyModified && !bWasModified)
        return;
 
    m_vectorUserData.push_back(std::make_unique<UserData>(rPropertyPath, aPropertyValue, rTooltip,
                                                          bIsReadOnly, bWasModified));
    if (bInsertToPrefBox)
    {
        OUString sId(weld::toId(m_vectorUserData.back().get()));
        m_xPrefBox->insert(pParentEntry, -1, &rProp, &sId, nullptr, nullptr, false,
                           m_xScratchIter.get());
        m_xPrefBox->set_text(*m_xScratchIter, rStatus, 1);
        m_xPrefBox->set_text(*m_xScratchIter, rType, 2);
        m_xPrefBox->set_text(*m_xScratchIter, rValue, 3);
        m_xPrefBox->set_text_emphasis(*m_xScratchIter, bWasModified, -1);
        m_xPrefBox->set_sensitive(*m_xScratchIter, !bIsReadOnly, -1);
    }
    else
    {
        m_prefBoxEntries.push_back(
            { rProp, rStatus, rType, rValue, m_vectorUserData.back().get() });
    }
}
 
void CuiAboutConfigTabPage::InputChanged()
{
    weld::WaitObject aWait(m_xDialog.get());
 
    m_xPrefBox->hide();
    m_xPrefBox->clear();
    m_xPrefBox->freeze();
 
    if (m_bSorted)
        m_xPrefBox->make_unsorted();
 
    if (m_xSearchEdit->get_text().isEmpty())
    {
        m_xPrefBox->clear();
        Reference<XNameAccess> xConfigAccess = getConfigAccess(u"/"_ustr, false);
        FillItems(xConfigAccess);
    }
    else
    {
        m_options.searchString = m_xSearchEdit->get_text();
        utl::TextSearch textSearch(m_options);
        for (auto const& it : m_prefBoxEntries)
        {
            sal_Int32 endPos, startPos = 0;
 
            for (size_t i = 0; i < 5; ++i)
            {
                OUString scrTxt;
 
                if (i == 0)
                    scrTxt = it.pUserData->sPropertyPath;
                else if (i == 1)
                    scrTxt = it.sProp;
                else if (i == 2)
                    scrTxt = it.sStatus;
                else if (i == 3)
                    scrTxt = it.sType;
                else if (i == 4)
                    scrTxt = it.sValue;
 
                endPos = scrTxt.getLength();
                if (textSearch.SearchForward(scrTxt, &startPos, &endPos))
                {
                    InsertEntry(it);
                    break;
                }
            }
        }
    }
 
    m_xPrefBox->thaw();
    if (m_bSorted)
        m_xPrefBox->make_sorted();
 
    m_xPrefBox->all_foreach([this](weld::TreeIter& rEntry) {
        m_xPrefBox->expand_row(rEntry);
        return false;
    });
    m_xPrefBox->show();
}
 
void CuiAboutConfigTabPage::Reset()
{
    weld::WaitObject aWait(m_xDialog.get());
 
    m_xPrefBox->clear();
    m_vectorOfModified.clear();
    if (m_bSorted)
    {
        m_xPrefBox->set_sort_indicator(TRISTATE_INDET, m_xPrefBox->get_sort_column());
        m_xPrefBox->make_unsorted();
        m_bSorted = false;
    }
    m_prefBoxEntries.clear();
    m_modifiedPrefBoxEntries.clear();
 
    m_xPrefBox->freeze();
    Reference<XNameAccess> xConfigAccess = getConfigAccess(u"/"_ustr, false);
    //Load all XNameAccess to m_prefBoxEntries
    FillItems(xConfigAccess, nullptr, 0, true);
    //Load xConfigAccess' children to m_prefBox
    FillItems(xConfigAccess);
    m_xPrefBox->thaw();
}
 
void CuiAboutConfigTabPage::FillItemSet()
{
    std::vector<std::shared_ptr<Prop_Impl>>::iterator pIter;
    for (pIter = m_vectorOfModified.begin(); pIter != m_vectorOfModified.end(); ++pIter)
    {
        Reference<XNameAccess> xUpdateAccess = getConfigAccess((*pIter)->Name, true);
        Reference<XNameReplace> xNameReplace(xUpdateAccess, UNO_QUERY_THROW);
 
        xNameReplace->replaceByName((*pIter)->Property, (*pIter)->Value);
 
        Reference<util::XChangesBatch> xChangesBatch(xUpdateAccess, UNO_QUERY_THROW);
        xChangesBatch->commitChanges();
    }
}
 
namespace
{
OUString lcl_StringListToString(const uno::Sequence<OUString>& seq)
{
    OUStringBuffer sBuffer;
    for (sal_Int32 i = 0; i != seq.getLength(); ++i)
    {
        if (i != 0)
            sBuffer.append(",");
        sBuffer.append(seq[i]);
    }
    return sBuffer.makeStringAndClear();
}
 
OUString lcl_IntListToString(const uno::Sequence<sal_Int16>& seq)
{
    OUStringBuffer sBuffer;
    for (sal_Int32 i = 0; i != seq.getLength(); ++i)
    {
        if (i != 0)
            sBuffer.append(",");
        sBuffer.append(OUString::number(seq[i]));
    }
    return sBuffer.makeStringAndClear();
}
 
OUString lcl_IntListToString(const uno::Sequence<sal_Int32>& seq)
{
    OUStringBuffer sBuffer;
    for (sal_Int32 i = 0; i != seq.getLength(); ++i)
    {
        if (i != 0)
            sBuffer.append(",");
        sBuffer.append(OUString::number(seq[i]));
    }
    return sBuffer.makeStringAndClear();
}
 
OUString lcl_IntListToString(const uno::Sequence<sal_Int64>& seq)
{
    OUStringBuffer sBuffer;
    for (sal_Int32 i = 0; i != seq.getLength(); ++i)
    {
        if (i != 0)
            sBuffer.append(",");
        sBuffer.append(OUString::number(seq[i]));
    }
    return sBuffer.makeStringAndClear();
}
 
OUString lcl_DoubleListToString(const uno::Sequence<double>& seq)
{
    OUStringBuffer sBuffer;
    for (sal_Int32 i = 0; i != seq.getLength(); ++i)
    {
        if (i != 0)
            sBuffer.append(",");
        sBuffer.append(OUString::number(seq[i]));
    }
    return sBuffer.makeStringAndClear();
}
}
 
void CuiAboutConfigTabPage::FillItems(const Reference<XNameAccess>& xNameAccess,
                                      const weld::TreeIter* pParentEntry, int lineage,
                                      bool bLoadAll)
{
    OUString sPath
        = Reference<XHierarchicalName>(xNameAccess, uno::UNO_QUERY_THROW)->getHierarchicalName();
    const uno::Sequence<OUString> seqItems = xNameAccess->getElementNames();
    for (const OUString& item : seqItems)
    {
        Any aNode = xNameAccess->getByName(item);
 
        bool bNotLeaf = false;
 
        Reference<XNameAccess> xNextNameAccess;
        try
        {
            xNextNameAccess.set(aNode, uno::UNO_QUERY);
            bNotLeaf = xNextNameAccess.is();
        }
        catch (const RuntimeException&)
        {
            TOOLS_WARN_EXCEPTION("cui.options", "CuiAboutConfigTabPage");
        }
 
        if (bNotLeaf)
        {
            if (bLoadAll)
                FillItems(xNextNameAccess, nullptr, lineage + 1, true);
            else
            {
                // not leaf node
                m_vectorUserData.push_back(
                    std::make_unique<UserData>(xNextNameAccess, lineage + 1));
                OUString sId(weld::toId(m_vectorUserData.back().get()));
 
                m_xPrefBox->insert(pParentEntry, -1, &item, &sId, nullptr, nullptr, true,
                                   m_xScratchIter.get());
                // Necessary, without this the selection line will be truncated.
                m_xPrefBox->set_text(*m_xScratchIter, u""_ustr, 1);
                m_xPrefBox->set_text(*m_xScratchIter, u""_ustr, 2);
                m_xPrefBox->set_text(*m_xScratchIter, u""_ustr, 3);
                m_xPrefBox->set_text_emphasis(*m_xScratchIter, false, -1);
                m_xPrefBox->set_sensitive(*m_xScratchIter, true);
            }
        }
        else
        {
            // leaf node
            OUString sPropertyName = item;
            auto it = std::find_if(m_modifiedPrefBoxEntries.begin(), m_modifiedPrefBoxEntries.end(),
                                   [&sPath, &sPropertyName](const prefBoxEntry& rEntry) -> bool {
                                       return rEntry.pUserData->sPropertyPath == sPath
                                              && rEntry.sStatus == sPropertyName;
                                   });
 
            css::uno::Reference<css::configuration::XReadWriteAccess> m_xReadWriteAccess;
            m_xReadWriteAccess = css::configuration::ReadWriteAccess::create(
                ::comphelper::getProcessComponentContext(), u"*"_ustr);
            beans::Property aProperty;
            bool bReadOnly = false;
            OUString sFullPath(sPath + "/" + sPropertyName);
            try
            {
                aProperty = m_xReadWriteAccess->getPropertyByHierarchicalName(sFullPath);
                bReadOnly = (aProperty.Attributes & beans::PropertyAttribute::READONLY) != 0;
            }
            catch (css::beans::UnknownPropertyException)
            {
                SAL_WARN("cui.options", "unknown property: " << sFullPath);
            }
 
            OUString sTooltip;
            OUString sType;
            bool bWasModified = false;
            css::uno::Type aType = cppu::UnoType<void>::get();
            OUString sDynamicType = aNode.getValueTypeName();
            try
            {
                Reference<configuration::XDocumentation> xDocumentation(xNameAccess,
                                                                        UNO_QUERY_THROW);
                sTooltip
                    = xDocumentation->getDescriptionByHierarchicalName(sPath + "/" + sPropertyName);
                aType = xDocumentation->getTypeByHierarchicalName(sFullPath);
                bWasModified = xDocumentation->getModifiedByHierarchicalName(sFullPath);
            }
            catch (css::container::NoSuchElementException)
            {
            }
            catch (css::util::InvalidStateException)
            {
            }
 
            OUStringBuffer sValue;
 
            // Fall back to dynamic type when this is empty
            if (aType == cppu::UnoType<void>::get() && sDynamicType != "void")
            {
                if (sDynamicType == "boolean")
                    aType = cppu::UnoType<sal_Bool>::get();
                else if (sDynamicType == "short")
                    aType = cppu::UnoType<sal_Int16>::get();
                else if (sDynamicType == "long")
                    aType = cppu::UnoType<sal_Int32>::get();
                else if (sDynamicType == "hyper")
                    aType = cppu::UnoType<sal_Int64>::get();
                else if (sDynamicType == "double")
                    aType = cppu::UnoType<double>::get();
                else if (sDynamicType == "string")
                    aType = cppu::UnoType<OUString>::get();
                else if (sDynamicType == "[]byte")
                    aType = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
                else if (sDynamicType == "[]boolean")
                    aType = cppu::UnoType<css::uno::Sequence<sal_Bool>>::get();
                else if (sDynamicType == "[]short")
                    aType = cppu::UnoType<css::uno::Sequence<sal_Int16>>::get();
                else if (sDynamicType == "[]long")
                    aType = cppu::UnoType<css::uno::Sequence<sal_Int32>>::get();
                else if (sDynamicType == "[]hyper")
                    aType = cppu::UnoType<css::uno::Sequence<sal_Int64>>::get();
                else if (sDynamicType == "[]double")
                    aType = cppu::UnoType<css::uno::Sequence<double>>::get();
                else if (sDynamicType == "[]string")
                    aType = cppu::UnoType<css::uno::Sequence<OUString>>::get();
                else if (sDynamicType == "[][]byte")
                    aType = cppu::UnoType<css::uno::Sequence<css::uno::Sequence<sal_Int8>>>::get();
            }
 
            if (it != m_modifiedPrefBoxEntries.end())
                sValue = it->sValue;
            else
            {
                bool bHasValue = sDynamicType != "void";
                if (aType == cppu::UnoType<sal_Bool>::get())
                {
                    if (bHasValue)
                        sValue = OUString::boolean(aNode.get<bool>());
                    sType = "boolean";
                }
                else if (aType == cppu::UnoType<sal_Int16>::get())
                {
                    if (bHasValue)
                        sValue = OUString::number(aNode.get<sal_Int16>());
                    sType = "short";
                }
                else if (aType == cppu::UnoType<sal_Int32>::get())
                {
                    if (bHasValue)
                        sValue = OUString::number(aNode.get<sal_Int32>());
                    sType = "int";
                }
                else if (aType == cppu::UnoType<sal_Int64>::get())
                {
                    if (bHasValue)
                        sValue = OUString::number(aNode.get<sal_Int64>());
                    sType = "long";
                }
                else if (aType == cppu::UnoType<double>::get())
                {
                    if (bHasValue)
                        sValue = OUString::number(aNode.get<double>());
                    sType = "double";
                }
                else if (aType == cppu::UnoType<OUString>::get())
                {
                    if (bHasValue)
                        sValue = aNode.get<OUString>();
                    sType = "string";
                }
                else if (aType == cppu::UnoType<css::uno::Sequence<sal_Int8>>::get())
                {
                    if (bHasValue)
                    {
                        const uno::Sequence<sal_Int8> seq = aNode.get<uno::Sequence<sal_Int8>>();
                        for (sal_Int8 j : seq)
                        {
                            OUString s = OUString::number(static_cast<sal_uInt8>(j), 16);
                            if (s.getLength() == 1)
                            {
                                sValue.append("0");
                            }
                            sValue.append(s.toAsciiUpperCase());
                        }
                    }
                    sType = "hexBinary";
                }
                else if (aType == cppu::UnoType<css::uno::Sequence<sal_Bool>>::get())
                {
                    if (bHasValue)
                    {
                        uno::Sequence<sal_Bool> seq = aNode.get<uno::Sequence<sal_Bool>>();
                        for (sal_Int32 j = 0; j != seq.getLength(); ++j)
                        {
                            if (j != 0)
                            {
                                sValue.append(",");
                            }
                            sValue.append(OUString::boolean(seq[j]));
                        }
                    }
                    sType = "boolean-list";
                }
                else if (aType == cppu::UnoType<css::uno::Sequence<sal_Int16>>::get())
                {
                    if (bHasValue)
                    {
                        uno::Sequence<sal_Int16> seq = aNode.get<uno::Sequence<sal_Int16>>();
                        for (sal_Int32 j = 0; j != seq.getLength(); ++j)
                        {
                            if (j != 0)
                            {
                                sValue.append(",");
                            }
                            sValue.append(static_cast<sal_Int32>(seq[j]));
                        }
                    }
                    sType = "short-list";
                }
                else if (aType == cppu::UnoType<css::uno::Sequence<sal_Int32>>::get())
                {
                    if (bHasValue)
                    {
                        uno::Sequence<sal_Int32> seq = aNode.get<uno::Sequence<sal_Int32>>();
                        for (sal_Int32 j = 0; j != seq.getLength(); ++j)
                        {
                            if (j != 0)
                            {
                                sValue.append(",");
                            }
                            sValue.append(seq[j]);
                        }
                    }
                    sType = "int-list";
                }
                else if (aType == cppu::UnoType<css::uno::Sequence<sal_Int64>>::get())
                {
                    if (bHasValue)
                    {
                        uno::Sequence<sal_Int64> seq = aNode.get<uno::Sequence<sal_Int64>>();
                        for (sal_Int32 j = 0; j != seq.getLength(); ++j)
                        {
                            if (j != 0)
                            {
                                sValue.append(",");
                            }
                            sValue.append(seq[j]);
                        }
                    }
                    sType = "long-list";
                }
                else if (aType == cppu::UnoType<css::uno::Sequence<double>>::get())
                {
                    if (bHasValue)
                    {
                        uno::Sequence<double> seq = aNode.get<uno::Sequence<double>>();
                        for (sal_Int32 j = 0; j != seq.getLength(); ++j)
                        {
                            if (j != 0)
                            {
                                sValue.append(",");
                            }
                            sValue.append(seq[j]);
                        }
                    }
                    sType = "double-list";
                }
                else if (aType == cppu::UnoType<css::uno::Sequence<OUString>>::get())
                {
                    if (bHasValue)
                        sValue = lcl_StringListToString(aNode.get<uno::Sequence<OUString>>());
                    sType = "string-list";
                }
                else if (aType
                         == cppu::UnoType<css::uno::Sequence<css::uno::Sequence<sal_Int8>>>::get())
                {
                    if (bHasValue)
                    {
                        const uno::Sequence<uno::Sequence<sal_Int8>> seq
                            = aNode.get<uno::Sequence<uno::Sequence<sal_Int8>>>();
                        for (sal_Int32 j = 0; j != seq.getLength(); ++j)
                        {
                            if (j != 0)
                            {
                                sValue.append(",");
                            }
                            for (sal_Int8 k : seq[j])
                            {
                                OUString s = OUString::number(static_cast<sal_uInt8>(k), 16);
                                if (s.getLength() == 1)
                                {
                                    sValue.append("0");
                                }
                                sValue.append(s.toAsciiUpperCase());
                            }
                        }
                    }
                    sType = "hexBinary-list";
                }
                else
                {
                    SAL_INFO("cui.options", "path \"" << sPath << "\" member " << item
                                                      << " of unsupported type " << sType);
                    continue;
                }
            }
 
            //Short name
            int index = 0;
            for (int j = 1; j < lineage; ++j)
                index = sPath.indexOf("/", index + 1);
 
            InsertEntry(sPath, aNode, sPath.copy(index + 1), item, sType,
                        sValue.makeStringAndClear(), sTooltip, pParentEntry, !bLoadAll, bReadOnly,
                        bWasModified);
        }
    }
}
 
Reference<XNameAccess> CuiAboutConfigTabPage::getConfigAccess(const OUString& sNodePath,
                                                              bool bUpdate)
{
    const uno::Reference<uno::XComponentContext>& xContext(
        ::comphelper::getProcessComponentContext());
 
    uno::Reference<lang::XMultiServiceFactory> xConfigProvider(
        css::configuration::theDefaultProvider::get(xContext));
 
    beans::NamedValue aProperty;
    aProperty.Name = "nodepath";
    aProperty.Value <<= sNodePath;
 
    uno::Sequence<uno::Any> aArgumentList{ uno::Any(aProperty) };
 
    OUString sAccessString;
 
    if (bUpdate)
        sAccessString = "com.sun.star.configuration.ConfigurationUpdateAccess";
    else
        sAccessString = "com.sun.star.configuration.ConfigurationAccess";
 
    uno::Reference<container::XNameAccess> xNameAccess(
        xConfigProvider->createInstanceWithArguments(sAccessString, aArgumentList),
        uno::UNO_QUERY_THROW);
 
    return xNameAccess;
}
 
void CuiAboutConfigTabPage::AddToModifiedVector(const std::shared_ptr<Prop_Impl>& rProp)
{
    bool isModifiedBefore = false;
    //Check if value modified before
    for (std::shared_ptr<Prop_Impl>& nInd : m_vectorOfModified)
    {
        if (rProp->Name == nInd->Name && rProp->Property == nInd->Property)
        {
            //property modified before. Assign reference to the modified value
            //do your changes on this object. They will be saved later.
            nInd = rProp;
            isModifiedBefore = true;
            break;
        }
    }
 
    if (!isModifiedBefore)
        m_vectorOfModified.push_back(rProp);
    //property is not modified before
}
 
std::vector<OUString>
CuiAboutConfigTabPage::commaStringToSequence(std::u16string_view rCommaSepString)
{
    std::vector<OUString> tempVector;
 
    sal_Int32 index = 0;
    do
    {
        OUString word(o3tl::getToken(rCommaSepString, 0, u',', index));
        word = word.trim();
        if (!word.isEmpty())
            tempVector.push_back(word);
    } while (index >= 0);
    return tempVector;
}
 
IMPL_LINK_NOARG(CuiAboutConfigTabPage, ResetBtnHdl_Impl, weld::Button&, void) { Reset(); }
 
IMPL_LINK_NOARG(CuiAboutConfigTabPage, DoubleClickHdl_Impl, weld::TreeView&, bool)
{
    StandardHdl_Impl(*m_xEditBtn);
    return true;
}
 
IMPL_LINK_NOARG(CuiAboutConfigTabPage, StandardHdl_Impl, weld::Button&, void)
{
    if (!m_xPrefBox->get_selected(m_xScratchIter.get()))
        return;
 
    UserData* pUserData = weld::fromId<UserData*>(m_xPrefBox->get_id(*m_xScratchIter));
    if (!pUserData || !pUserData->bIsPropertyPath || pUserData->bIsReadOnly)
        return;
 
    //if selection is a node
    OUString sPropertyName = m_xPrefBox->get_text(*m_xScratchIter, 1);
    OUString sPropertyType = m_xPrefBox->get_text(*m_xScratchIter, 2);
    OUString sPropertyValue = m_xPrefBox->get_text(*m_xScratchIter, 3);
 
    auto pProperty
        = std::make_shared<Prop_Impl>(pUserData->sPropertyPath, sPropertyName, Any(sPropertyValue));
    bool bSaveChanges = false;
 
    bool bOpenDialog = true;
    OUString sDialogValue;
 
    if (sPropertyType == "boolean")
    {
        bool bValue;
        if (sPropertyValue == "true")
        {
            sDialogValue = "false";
            bValue = false;
        }
        else
        {
            sDialogValue = "true";
            bValue = true;
        }
 
        pProperty->Value <<= bValue;
        bOpenDialog = false;
        bSaveChanges = true;
    }
    else
    {
        sDialogValue = sPropertyValue;
        bOpenDialog = true;
    }
 
    try
    {
        if (bOpenDialog)
        {
            if (sPropertyType == "short" || sPropertyType == "int" || sPropertyType == "long")
            {
                sal_Int64 nMin = sPropertyType == "short"
                                     ? SAL_MIN_INT16
                                     : sPropertyType == "int" ? SAL_MIN_INT32 : SAL_MIN_INT64;
                sal_Int64 nMax = sPropertyType == "short"
                                     ? SAL_MAX_INT16
                                     : sPropertyType == "int" ? SAL_MAX_INT32 : SAL_MAX_INT64;
                SvxNumberDialog aNumberDialog(m_xDialog.get(), sPropertyName,
                                              sDialogValue.toInt64(), nMin, nMax);
                if (aNumberDialog.run() == RET_OK)
                {
                    sal_Int64 nNewValue = aNumberDialog.GetNumber();
                    if (sPropertyType == "short")
                    {
                        pProperty->Value <<= static_cast<sal_Int16>(nNewValue);
                    }
                    else if (sPropertyType == "int")
                    {
                        pProperty->Value <<= static_cast<sal_Int32>(nNewValue);
                    }
                    else if (sPropertyType == "long")
                    {
                        pProperty->Value <<= nNewValue;
                    }
                    bSaveChanges = true;
                    sDialogValue = OUString::number(nNewValue);
                }
            }
            else if (sPropertyType == "double")
            {
                SvxDecimalNumberDialog aNumberDialog(m_xDialog.get(), sPropertyName,
                                                     sDialogValue.toDouble());
                if (aNumberDialog.run() == RET_OK)
                {
                    double fNewValue = aNumberDialog.GetNumber();
                    pProperty->Value <<= fNewValue;
                    bSaveChanges = true;
                    sDialogValue = OUString::number(fNewValue);
                }
            }
            else if (sPropertyType == "string")
            {
                SvxNameDialog aNameDialog(m_xDialog.get(), sDialogValue, sPropertyName);
                aNameDialog.SetCheckNameHdl(LINK(this, CuiAboutConfigTabPage, ValidNameHdl));
                if (aNameDialog.run() == RET_OK)
                {
                    sDialogValue = aNameDialog.GetName();
                    pProperty->Value <<= sDialogValue;
                    bSaveChanges = true;
                }
            }
            else if (sPropertyType == "short-list")
            {
                SvxListDialog aListDialog(m_xDialog.get());
                aListDialog.SetEntries(commaStringToSequence(sDialogValue));
                aListDialog.SetMode(ListMode::Int16);
                if (aListDialog.run() == RET_OK)
                {
                    std::vector<OUString> seqStr = aListDialog.GetEntries();
                    uno::Sequence<sal_Int16> seqShort(seqStr.size());
                    std::transform(
                        seqStr.begin(), seqStr.end(), seqShort.getArray(),
                        [](const auto& str) { return static_cast<sal_Int16>(str.toInt32()); });
                    pProperty->Value <<= seqShort;
                    sDialogValue = lcl_IntListToString(seqShort);
                    bSaveChanges = true;
                }
            }
            else if (sPropertyType == "int-list")
            {
                SvxListDialog aListDialog(m_xDialog.get());
                aListDialog.SetEntries(commaStringToSequence(sDialogValue));
                aListDialog.SetMode(ListMode::Int32);
                if (aListDialog.run() == RET_OK)
                {
                    std::vector<OUString> seqStr = aListDialog.GetEntries();
                    uno::Sequence<sal_Int32> seq(seqStr.size());
                    std::transform(
                        seqStr.begin(), seqStr.end(), seq.getArray(),
                        [](const auto& str) { return static_cast<sal_Int32>(str.toInt32()); });
                    pProperty->Value <<= seq;
                    sDialogValue = lcl_IntListToString(seq);
                    bSaveChanges = true;
                }
            }
            else if (sPropertyType == "long-list")
            {
                SvxListDialog aListDialog(m_xDialog.get());
                aListDialog.SetEntries(commaStringToSequence(sDialogValue));
                aListDialog.SetMode(ListMode::Int64);
                if (aListDialog.run() == RET_OK)
                {
                    std::vector<OUString> seqStr = aListDialog.GetEntries();
                    uno::Sequence<sal_Int64> seq(seqStr.size());
                    std::transform(
                        seqStr.begin(), seqStr.end(), seq.getArray(),
                        [](const auto& str) { return static_cast<sal_Int64>(str.toInt32()); });
                    pProperty->Value <<= seq;
                    sDialogValue = lcl_IntListToString(seq);
                    bSaveChanges = true;
                }
            }
            else if (sPropertyType == "double-list")
            {
                SvxListDialog aListDialog(m_xDialog.get());
                aListDialog.SetEntries(commaStringToSequence(sDialogValue));
                aListDialog.SetMode(ListMode::Double);
                if (aListDialog.run() == RET_OK)
                {
                    std::vector<OUString> seqStr = aListDialog.GetEntries();
                    uno::Sequence<double> seq(seqStr.size());
                    std::transform(
                        seqStr.begin(), seqStr.end(), seq.getArray(),
                        [](const auto& str) { return static_cast<double>(str.toDouble()); });
                    pProperty->Value <<= seq;
                    sDialogValue = lcl_DoubleListToString(seq);
                    bSaveChanges = true;
                }
            }
            else if (sPropertyType == "string-list")
            {
                SvxListDialog aListDialog(m_xDialog.get());
                uno::Sequence<OUString> aList
                    = pUserData->aPropertyValue.get<uno::Sequence<OUString>>();
                aListDialog.SetEntries(
                    comphelper::sequenceToContainer<std::vector<OUString>>(aList));
                aListDialog.SetMode(ListMode::String);
                if (aListDialog.run() == RET_OK)
                {
                    auto seq = comphelper::containerToSequence(aListDialog.GetEntries());
                    sDialogValue = lcl_StringListToString(seq);
                    pProperty->Value <<= seq;
                    bSaveChanges = true;
                }
            }
            else //unknown
                throw uno::Exception("unknown property type " + sPropertyType, nullptr);
        }
 
        if (bSaveChanges)
        {
            AddToModifiedVector(pProperty);
            pUserData->aPropertyValue = pProperty->Value;
 
            //update listbox value.
            m_xPrefBox->set_text(*m_xScratchIter, sPropertyType, 2);
            m_xPrefBox->set_text(*m_xScratchIter, sDialogValue, 3);
            m_xPrefBox->set_text_emphasis(*m_xScratchIter, true, -1);
            //update m_prefBoxEntries
            auto it = std::find_if(
                m_prefBoxEntries.begin(), m_prefBoxEntries.end(),
                [&pUserData, &sPropertyName](const prefBoxEntry& rEntry) -> bool {
                    return rEntry.pUserData->sPropertyPath == pUserData->sPropertyPath
                           && rEntry.sStatus == sPropertyName;
                });
            if (it != m_prefBoxEntries.end())
            {
                it->sValue = sDialogValue;
                it->pUserData->bWasModified = true;
 
                auto modifiedIt = std::find_if(
                    m_modifiedPrefBoxEntries.begin(), m_modifiedPrefBoxEntries.end(),
                    [&pUserData, &sPropertyName](const prefBoxEntry& rEntry) -> bool {
                        return rEntry.pUserData->sPropertyPath == pUserData->sPropertyPath
                               && rEntry.sStatus == sPropertyName;
                    });
 
                if (modifiedIt != m_modifiedPrefBoxEntries.end())
                {
                    modifiedIt->sValue = sDialogValue;
                    modifiedIt->pUserData->bWasModified = true;
                }
                else
                {
                    m_modifiedPrefBoxEntries.push_back(*it);
                }
            }
        }
    }
    catch (uno::Exception&)
    {
    }
}
 
IMPL_LINK_NOARG(CuiAboutConfigTabPage, SearchHdl_Impl, weld::Button&, void) { InputChanged(); }
 
IMPL_LINK_NOARG(CuiAboutConfigTabPage, ModifiedHdl_Impl, weld::Toggleable&, void)
{
    InputChanged();
}
 
void CuiAboutConfigTabPage::InsertEntry(const prefBoxEntry& rEntry)
{
    bool bOnlyModified = m_xModifiedCheckBtn->get_active();
    if (bOnlyModified && !rEntry.pUserData->bWasModified)
        return;
 
    OUString sPathWithProperty = rEntry.pUserData->sPropertyPath;
    sal_Int32 index = sPathWithProperty.lastIndexOf(rEntry.sProp);
    OUString sPath = sPathWithProperty.copy(0, index);
    index = 0;
    std::unique_ptr<weld::TreeIter> xParentEntry(m_xPrefBox->make_iterator());
    std::unique_ptr<weld::TreeIter> xGrandParentEntry;
 
    do
    {
        int prevIndex = index;
        index = sPath.indexOf("/", index + 1);
        // deal with no parent case (tdf#107811)
        if (index < 0)
        {
            OUString sId(weld::toId(rEntry.pUserData));
            m_xPrefBox->insert(nullptr, -1, &rEntry.sProp, &sId, nullptr, nullptr, false,
                               m_xScratchIter.get());
            m_xPrefBox->set_text(*m_xScratchIter, rEntry.sStatus, 1);
            m_xPrefBox->set_text(*m_xScratchIter, rEntry.sType, 2);
            m_xPrefBox->set_text(*m_xScratchIter, rEntry.sValue, 3);
            m_xPrefBox->set_text_emphasis(*m_xScratchIter, rEntry.pUserData->bWasModified, -1);
            m_xPrefBox->set_sensitive(*m_xScratchIter, !rEntry.pUserData->bIsReadOnly);
            return;
        }
        OUString sParentName = sPath.copy(prevIndex + 1, index - prevIndex - 1);
 
        bool hasEntry = false;
        bool bStartOk;
 
        if (!xGrandParentEntry)
            bStartOk = m_xPrefBox->get_iter_first(*xParentEntry);
        else
        {
            m_xPrefBox->copy_iterator(*xGrandParentEntry, *xParentEntry);
            bStartOk = m_xPrefBox->iter_children(*xParentEntry);
        }
 
        if (bStartOk)
        {
            do
            {
                if (m_xPrefBox->get_text(*xParentEntry, 0) == sParentName)
                {
                    hasEntry = true;
                    break;
                }
            } while (m_xPrefBox->iter_next_sibling(*xParentEntry));
        }
 
        if (!hasEntry)
        {
            m_xPrefBox->insert(xGrandParentEntry.get(), -1, &sParentName, nullptr, nullptr, nullptr,
                               false, xParentEntry.get());
            //It is needed, without this the selection line will be truncated.
            m_xPrefBox->set_text(*xParentEntry, u""_ustr, 1);
            m_xPrefBox->set_text(*xParentEntry, u""_ustr, 2);
            m_xPrefBox->set_text(*xParentEntry, u""_ustr, 3);
            m_xPrefBox->set_text_emphasis(*xParentEntry, false, -1);
            m_xPrefBox->set_sensitive(*xParentEntry, true);
        }
 
        xGrandParentEntry = m_xPrefBox->make_iterator(xParentEntry.get());
    } while (index < sPath.getLength() - 1);
 
    OUString sId(weld::toId(rEntry.pUserData));
    m_xPrefBox->insert(xParentEntry.get(), -1, &rEntry.sProp, &sId, nullptr, nullptr, false,
                       m_xScratchIter.get());
    m_xPrefBox->set_text(*m_xScratchIter, rEntry.sStatus, 1);
    m_xPrefBox->set_text(*m_xScratchIter, rEntry.sType, 2);
    m_xPrefBox->set_text(*m_xScratchIter, rEntry.sValue, 3);
    m_xPrefBox->set_text_emphasis(*m_xScratchIter, rEntry.pUserData->bWasModified, -1);
    m_xPrefBox->set_sensitive(*m_xScratchIter, !rEntry.pUserData->bIsReadOnly);
}
 
IMPL_LINK(CuiAboutConfigTabPage, ExpandingHdl_Impl, const weld::TreeIter&, rEntry, bool)
{
    if (m_xPrefBox->iter_has_child(rEntry))
        return true;
    UserData* pUserData = weld::fromId<UserData*>(m_xPrefBox->get_id(rEntry));
    if (pUserData && !pUserData->bIsPropertyPath)
    {
        assert(pUserData->aXNameAccess.is());
        FillItems(pUserData->aXNameAccess, &rEntry, pUserData->aLineage);
    }
    return true;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V1048 The 'bOpenDialog' variable was assigned the same value.