summaryrefslogtreecommitdiffstats
path: root/forms/source/component/GroupManager.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /forms/source/component/GroupManager.cxx
parentInitial commit. (diff)
downloadlibreoffice-upstream/1%7.0.4.tar.xz
libreoffice-upstream/1%7.0.4.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'forms/source/component/GroupManager.cxx')
-rw-r--r--forms/source/component/GroupManager.cxx421
1 files changed, 421 insertions, 0 deletions
diff --git a/forms/source/component/GroupManager.cxx b/forms/source/component/GroupManager.cxx
new file mode 100644
index 000000000..deec1bcd5
--- /dev/null
+++ b/forms/source/component/GroupManager.cxx
@@ -0,0 +1,421 @@
+/* -*- 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>
+
+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, 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)
+{
+}
+
+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: */