    /* -*- 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/.
     *
     * This file incorporates work covered by the following license notice:
     *
     *   Licensed to the Apache Software Foundation (ASF) under one or more
     *   contributor license agreements. See the NOTICE file distributed
     *   with this work for additional information regarding copyright
     *   ownership. The ASF licenses this file to you under the Apache
     *   License, Version 2.0 (the "License"); you may not use this file
     *   except in compliance with the License. You may obtain a copy of
     *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
     */

    #include "GroupManager.hxx"
    #include <com/sun/star/beans/XFastPropertySet.hpp>
    #include <com/sun/star/form/FormComponentType.hpp>
    #include <comphelper/property.hxx>
    #include <comphelper/types.hxx>
    #include <osl/diagnose.h>
    #include <tools/solar.h>

    #include <frm_strings.hxx>

    #include <algorithm>

    namespace frm
    {
    using namespace ::com::sun::star::uno;
    using namespace ::com::sun::star::sdbc;
    using namespace ::com::sun::star::beans;
    using namespace ::com::sun::star::container;
    using namespace ::com::sun::star::form;
    using namespace ::com::sun::star::awt;
    using namespace ::com::sun::star::lang;
    using namespace ::comphelper;

    namespace
    {
        bool isRadioButton( const Reference< XPropertySet >& _rxComponent )
        {
            bool bIs = false;
            if ( hasProperty( PROPERTY_CLASSID, _rxComponent ) )
            {
                sal_Int16 nClassId = FormComponentType::CONTROL;
                _rxComponent->getPropertyValue( PROPERTY_CLASSID ) >>= nClassId;
                if ( nClassId == FormComponentType::RADIOBUTTON )
                    bIs = true;
            }
            return bIs;
        }
    }

    OGroupCompAcc::OGroupCompAcc(const Reference<XPropertySet>& rxElement, const OGroupComp& _rGroupComp )
                   :m_xComponent( rxElement )
                   ,m_aGroupComp( _rGroupComp )
    {
    }

    bool OGroupCompAcc::operator==( const OGroupCompAcc& rCompAcc ) const
    {
        return m_xComponent == rCompAcc.m_xComponent;
    }

    class OGroupCompAccLess
    {
    public:
        bool operator() (const OGroupCompAcc& lhs, const OGroupCompAcc& rhs) const
        {
            return
                reinterpret_cast<sal_Int64>(lhs.m_xComponent.get())
            <   reinterpret_cast<sal_Int64>(rhs.m_xComponent.get());
        }
    };

    OGroupComp::OGroupComp()
        :m_nPos( -1 )
        ,m_nTabIndex( 0 )
    {
    }

    OGroupComp::OGroupComp(const Reference<XPropertySet>& rxSet, sal_Int32 nInsertPos )
        : m_xComponent( rxSet )
        , m_xControlModel(rxSet,UNO_QUERY)
        , m_nPos( nInsertPos )
        , m_nTabIndex(0)
    {
        if (m_xComponent.is())
        {
            if (hasProperty( PROPERTY_TABINDEX, m_xComponent ) )
                // Indices smaller than 0 are treated like 0
                m_nTabIndex = std::max(getINT16(m_xComponent->getPropertyValue( PROPERTY_TABINDEX )) , sal_Int16(0));
        }
    }

    bool OGroupComp::operator==( const OGroupComp& rComp ) const
    {
        return m_nTabIndex == rComp.GetTabIndex() && m_nPos == rComp.GetPos();
    }

    class OGroupCompLess
    {
    public:
        bool operator() (const OGroupComp& lhs, const OGroupComp& rhs) const
        {
            bool bResult;
            // TabIndex of 0 will be added at the end
            if (lhs.m_nTabIndex == rhs.GetTabIndex())
                bResult = lhs.m_nPos < rhs.GetPos();
            else if (lhs.m_nTabIndex && rhs.GetTabIndex())
                bResult = lhs.m_nTabIndex < rhs.GetTabIndex();
            else
                bResult = lhs.m_nTabIndex != 0;
            return bResult;
        }
    };

    OGroup::OGroup( const OUString& rGroupName )
            :m_aGroupName( rGroupName )
            ,m_nInsertPos(0)
    {
    }

    OGroup::~OGroup()
    {
    }

    void OGroup::InsertComponent( const Reference<XPropertySet>& xSet )
    {
        OGroupComp aNewGroupComp( xSet, m_nInsertPos );
        sal_Int32 nPosInserted = insert_sorted(m_aCompArray, aNewGroupComp, OGroupCompLess());

        OGroupCompAcc aNewGroupCompAcc( xSet, m_aCompArray[nPosInserted] );
        insert_sorted(m_aCompAccArray, aNewGroupCompAcc, OGroupCompAccLess());
        m_nInsertPos++;
    }

    void OGroup::RemoveComponent( const Reference<XPropertySet>& rxElement )
    {
        sal_Int32 nGroupCompAccPos;
        OGroupCompAcc aSearchCompAcc( rxElement, OGroupComp() );
        if ( seek_entry(m_aCompAccArray, aSearchCompAcc, nGroupCompAccPos, OGroupCompAccLess()) )
        {
            OGroupCompAcc& aGroupCompAcc = m_aCompAccArray[nGroupCompAccPos];
            const OGroupComp& aGroupComp = aGroupCompAcc.GetGroupComponent();

            sal_Int32 nGroupCompPos;
            if ( seek_entry(m_aCompArray, aGroupComp, nGroupCompPos, OGroupCompLess()) )
            {
                m_aCompAccArray.erase( m_aCompAccArray.begin() + nGroupCompAccPos );
                m_aCompArray.erase( m_aCompArray.begin() + nGroupCompPos );

                /*
                 * By removing the GroupComp the insertion position has become invalid.
                 * We do not to change it here, however, because its passed on continuously
                 * and ascending distinctively.
                 */
            }
            else
            {
                OSL_FAIL( "OGroup::RemoveComponent: Component not in Group" );
            }
        }
        else
        {
            OSL_FAIL( "OGroup::RemoveComponent: Component not in Group" );
        }
    }

    Sequence< Reference<XControlModel>  > OGroup::GetControlModels() const
    {
        Sequence<Reference<XControlModel> > aControlModelSeq( m_aCompArray.size() );
        Reference<XControlModel>* pModels = aControlModelSeq.getArray();

        for (auto const& rGroupComp : m_aCompArray)
        {
            *pModels++ = rGroupComp.GetControlModel();
        }
        return aControlModelSeq;
    }

    OGroupManager::OGroupManager(const Reference< XContainer >& _rxContainer)
        :m_pCompGroup( new OGroup( "AllComponentGroup" ) )
        ,m_xContainer(_rxContainer)
    {
        osl_atomic_increment(&m_refCount);
        {
            _rxContainer->addContainerListener(this);
        }
        osl_atomic_decrement(&m_refCount);
    }

    OGroupManager::~OGroupManager()
    {
    }

    // XPropertyChangeListener
    void OGroupManager::disposing(const EventObject& evt)
    {
        Reference<XContainer>  xContainer(evt.Source, UNO_QUERY);
        if (xContainer.get() == m_xContainer.get())
        {
            m_pCompGroup.reset();

            // delete group
            m_aGroupArr.clear();
            m_xContainer.clear();
        }
    }

    void OGroupManager::removeFromGroupMap(const OUString& _sGroupName,const Reference<XPropertySet>& _xSet)
    {
        // remove Component from CompGroup
        m_pCompGroup->RemoveComponent( _xSet );

        OGroupArr::iterator aFind = m_aGroupArr.find(_sGroupName);

        if ( aFind != m_aGroupArr.end() )
        {
            // group exists
            aFind->second.RemoveComponent( _xSet );

            // If the count of Group elements == 1 -> deactivate Group
            sal_Int32 nCount = aFind->second.Count();
            if ( nCount == 1 || nCount == 0 )
            {
                OActiveGroups::iterator aActiveFind = ::std::find(
                    m_aActiveGroupMap.begin(),
                    m_aActiveGroupMap.end(),
                    aFind
                );
                if ( aActiveFind != m_aActiveGroupMap.end() )
                {
                    // the group is active. Deactivate it if the remaining component
                    // is *no* radio button
                    if ( nCount == 0 || !isRadioButton( aFind->second.GetObject( 0 ) ) )
                        m_aActiveGroupMap.erase( aActiveFind );
                }
            }
        }

        // Deregister as PropertyChangeListener at Component
        _xSet->removePropertyChangeListener( PROPERTY_NAME, this );
        if (hasProperty(PROPERTY_GROUP_NAME, _xSet))
            _xSet->removePropertyChangeListener( PROPERTY_GROUP_NAME, this );
        if (hasProperty(PROPERTY_TABINDEX, _xSet))
            _xSet->removePropertyChangeListener( PROPERTY_TABINDEX, this );
    }

    void SAL_CALL OGroupManager::propertyChange(const PropertyChangeEvent& evt)
    {
        Reference<XPropertySet>  xSet(evt.Source, UNO_QUERY);

        // remove Component from group
        OUString     sGroupName;
        if (hasProperty( PROPERTY_GROUP_NAME, xSet ))
            xSet->getPropertyValue( PROPERTY_GROUP_NAME ) >>= sGroupName;
        if (evt.PropertyName == PROPERTY_NAME) {
            if (!sGroupName.isEmpty())
                return; // group hasn't changed; ignore this name change.
            // no GroupName; use Name as GroupName
            evt.OldValue >>= sGroupName;
        }
        else if (evt.PropertyName == PROPERTY_GROUP_NAME) {
            evt.OldValue >>= sGroupName;
            if (sGroupName.isEmpty()) {
                // No prior GroupName; fallback to Nme
                xSet->getPropertyValue( PROPERTY_NAME ) >>= sGroupName;
            }
        }
        else
            sGroupName = GetGroupName( xSet );

        removeFromGroupMap(sGroupName,xSet);

        // Re-insert Component
        InsertElement( xSet );
    }

    // XContainerListener
    void SAL_CALL OGroupManager::elementInserted(const ContainerEvent& Event)
    {
        Reference< XPropertySet > xProps;
        Event.Element >>= xProps;
        if ( xProps.is() )
            InsertElement( xProps );
    }

    void SAL_CALL OGroupManager::elementRemoved(const ContainerEvent& Event)
    {
        Reference<XPropertySet> xProps;
        Event.Element >>= xProps;
        if ( xProps.is() )
            RemoveElement( xProps );
    }

    void SAL_CALL OGroupManager::elementReplaced(const ContainerEvent& Event)
    {
        Reference<XPropertySet> xProps;
        Event.ReplacedElement >>= xProps;
        if ( xProps.is() )
            RemoveElement( xProps );

        xProps.clear();
        Event.Element >>= xProps;
        if ( xProps.is() )
            InsertElement( xProps );
    }

    // Other functions
    Sequence<Reference<XControlModel> > OGroupManager::getControlModels()
    {
        return m_pCompGroup->GetControlModels();
    }

    sal_Int32 OGroupManager::getGroupCount()
    {
        return m_aActiveGroupMap.size();
    }

    void OGroupManager::getGroup(sal_Int32 nGroup, Sequence< Reference<XControlModel> >& _rGroup, OUString& _rName)
    {
        OSL_ENSURE(nGroup >= 0 && static_cast<size_t>(nGroup) < m_aActiveGroupMap.size(),"OGroupManager::getGroup: Invalid group index!");
        OGroupArr::iterator aGroupPos   = m_aActiveGroupMap[nGroup];
        _rName                          = aGroupPos->second.GetGroupName();
        _rGroup                         = aGroupPos->second.GetControlModels();
    }

    void OGroupManager::getGroupByName(const OUString& _rName, Sequence< Reference<XControlModel>  >& _rGroup)
    {
        OGroupArr::iterator aFind = m_aGroupArr.find(_rName);
        if ( aFind != m_aGroupArr.end() )
            _rGroup = aFind->second.GetControlModels();
    }

    void OGroupManager::InsertElement( const Reference<XPropertySet>& xSet )
    {
        // Only ControlModels
        Reference<XControlModel>  xControl(xSet, UNO_QUERY);
        if (!xControl.is() )
            return;

        // Add Component to CompGroup
        m_pCompGroup->InsertComponent( xSet );

        // Add Component to Group
        OUString sGroupName( GetGroupName( xSet ) );

        OGroupArr::iterator aFind = m_aGroupArr.find(sGroupName);

        if ( aFind == m_aGroupArr.end() )
        {
            aFind = m_aGroupArr.emplace(sGroupName,OGroup(sGroupName)).first;
        }

        aFind->second.InsertComponent( xSet );

        // if we have at least 2 elements in the group, then this is an "active group"
        bool bActivateGroup = aFind->second.Count() == 2;

        // Additionally, if the component is a radio button, then it's group becomes active,
        // too. With this, we ensure that in a container with n radio buttons which all are
        // in different groups the selection still works reliably (means that all radios can be
        // clicked independently)
        if ( aFind->second.Count() == 1 )
        {
            if ( isRadioButton( xSet ) )
                bActivateGroup = true;
        }

        if ( bActivateGroup )
        {
            OActiveGroups::iterator aAlreadyExistent = ::std::find(
                m_aActiveGroupMap.begin(),
                m_aActiveGroupMap.end(),
                aFind
            );
            if ( aAlreadyExistent == m_aActiveGroupMap.end() )
                m_aActiveGroupMap.push_back(  aFind );
        }

        // Register as PropertyChangeListener at Component
        xSet->addPropertyChangeListener( PROPERTY_NAME, this );
        if (hasProperty(PROPERTY_GROUP_NAME, xSet))
            xSet->addPropertyChangeListener( PROPERTY_GROUP_NAME, this );

        // Not everyone needs to support Tabindex
        if (hasProperty(PROPERTY_TABINDEX, xSet))
            xSet->addPropertyChangeListener( PROPERTY_TABINDEX, this );
    }

    void OGroupManager::RemoveElement( const Reference<XPropertySet>& xSet )
    {
        // Only ControlModels
        Reference<XControlModel>  xControl(xSet, UNO_QUERY);
        if (!xControl.is() )
            return;

        // Remove Component from Group
        OUString     sGroupName( GetGroupName( xSet ) );

        removeFromGroupMap(sGroupName,xSet);
    }

    OUString OGroupManager::GetGroupName( const css::uno::Reference< css::beans::XPropertySet>& xComponent )
    {
        if (!xComponent.is())
            return OUString();
        OUString sGroupName;
        if (hasProperty( PROPERTY_GROUP_NAME, xComponent )) {
            xComponent->getPropertyValue( PROPERTY_GROUP_NAME ) >>= sGroupName;
            if (sGroupName.isEmpty())
                xComponent->getPropertyValue( PROPERTY_NAME ) >>= sGroupName;
        }
        else
            xComponent->getPropertyValue( PROPERTY_NAME ) >>= sGroupName;
        return sGroupName;
    }
    }   // namespace frm

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V547 Expression is always false.