diff options
Diffstat (limited to 'forms/source/component/GroupManager.cxx')
-rw-r--r-- | forms/source/component/GroupManager.cxx | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/forms/source/component/GroupManager.cxx b/forms/source/component/GroupManager.cxx new file mode 100644 index 0000000000..edd296d6c7 --- /dev/null +++ b/forms/source/component/GroupManager.cxx @@ -0,0 +1,422 @@ +/* -*- 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/form/FormComponentType.hpp> +#include <comphelper/property.hxx> +#include <comphelper/types.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> + +#include <frm_strings.hxx> + +#include <algorithm> +#include <utility> + +namespace frm +{ +using namespace ::com::sun::star::uno; +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, OGroupComp _aGroupComp ) + :m_xComponent( rxElement ) + ,m_aGroupComp(std::move( _aGroupComp )) +{ +} + +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( OUString sGroupName ) + :m_aGroupName(std::move( sGroupName )) + ,m_nInsertPos(0) +{ +} + +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 it's 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 Name + 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() const +{ + return m_pCompGroup->GetControlModels(); +} + +sal_Int32 OGroupManager::getGroupCount() const +{ + return m_aActiveGroupMap.size(); +} + +void OGroupManager::getGroup(sal_Int32 nGroup, Sequence< Reference<XControlModel> >& _rGroup, OUString& _rName) +{ + OSL_ENSURE(nGroup >= 0 && o3tl::make_unsigned(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: */ |