Files
libreoffice/forms/source/component/GroupManager.cxx

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

423 lines
13 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
re-base on ALv2 code. Includes: Patch contributed by Christian Lippka impress212: #i113063# patch: dubios self assign in svx/source/dialog/framelink.cxx http://svn.apache.org/viewvc?view=revision&revision=1167619 Patches contributed by Mathias Bauer gnumake4 work variously http://svn.apache.org/viewvc?view=revision&revision=1394707 http://svn.apache.org/viewvc?view=revision&revision=1394326 cws mba34issues01: #i117712#: fix several resource errors introduced by IAccessible2 implementation http://svn.apache.org/viewvc?view=revision&revision=1172343 cws mba34issues01: #i117719#: use correct resource ID http://svn.apache.org/viewvc?view=revision&revision=1172351 Patch contributed by Andre Fischer Do not add targets for junit tests when junit is disabled. http://svn.apache.org/viewvc?view=revision&revision=1241508 Patches contributed by Armin Le-Grand #118804# corrected GraphicExporter behaviour on shortcut when pixel graphic is requested http://svn.apache.org/viewvc?view=revision&revision=1240195 fix for #118525#: Using primitives for chart sub-geometry visualisation http://svn.apache.org/viewvc?view=revision&revision=1226879 #118485# - Styles for OLEs are not saved. http://svn.apache.org/viewvc?view=revision&revision=1182166 #118524: apply patch, followup fixes to 118485 http://svn.apache.org/viewvc?view=revision&revision=1186077 13f79535-47bb-0310-9956-ffa450edef68 Patch contributed by Regina Henschel linecap: Reintegrating finished LineCap feature http://svn.apache.org/viewvc?view=revision&revision=1232507 Patch contributed by Wang Lei (leiw) #i118760# split the first table cell vertically, then undo&redo, the Presentation app will crash http://svn.apache.org/viewvc?view=revision&revision=1301361 cleanup globlmn hacks, undo dependent fixmes.
2012-11-21 22:06:52 +00:00
/*
* 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 .
*/
2000-09-18 15:33:13 +00:00
#include "GroupManager.hxx"
#include <com/sun/star/form/FormComponentType.hpp>
2000-10-19 10:52:20 +00:00
#include <comphelper/property.hxx>
#include <comphelper/types.hxx>
New loplugin:unsignedcompare "Find explicit casts from signed to unsigned integer in comparison against unsigned integer, where the cast is presumably used to avoid warnings about signed vs. unsigned comparisons, and could thus be replaced with o3tl::make_unsigned for clairty." (compilerplugins/clang/unsignedcompare.cxx) o3tl::make_unsigned requires its argument to be non-negative, and there is a chance that some original code like static_cast<sal_uInt32>(n) >= c used the explicit cast to actually force a (potentially negative) value of sal_Int32 to be interpreted as an unsigned sal_uInt32, rather than using the cast to avoid a false "signed vs. unsigned comparison" warning in a case where n is known to be non-negative. It appears that restricting this plugin to non- equality comparisons (<, >, <=, >=) and excluding equality comparisons (==, !=) is a useful heuristic to avoid such false positives. The only remainging false positive I found was 0288c8ffecff4956a52b9147d441979941e8b87f "Rephrase cast from sal_Int32 to sal_uInt32". But which of course does not mean that there were no further false positivies that I missed. So this commit may accidentally introduce some false hits of the assert in o3tl::make_unsigned. At least, it passed a full (Linux ASan+UBSan --enable-dbgutil) `make check && make screenshot`. It is by design that o3tl::make_unsigned only accepts signed integer parameter types (and is not defined as a nop for unsigned ones), to avoid unnecessary uses which would in general be suspicious. But the STATIC_ARRAY_SELECT macro in include/oox/helper/helper.hxx is used with both signed and unsigned types, so needs a little oox::detail::make_unsigned helper function for now. (The ultimate fix being to get rid of the macro in the first place.) Change-Id: Ia4adc9f44c70ad1dfd608784cac39ee922c32175 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/87556 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-01-27 09:30:39 +01:00
#include <o3tl/safeint.hxx>
#include <osl/diagnose.h>
2000-09-18 15:33:13 +00:00
#include <frm_strings.hxx>
2000-09-18 15:33:13 +00:00
2001-10-25 16:55:52 +00:00
#include <algorithm>
#include <utility>
2001-10-25 16:55:52 +00:00
2000-09-18 15:33:13 +00:00
namespace frm
{
2000-11-23 08:05:32 +00:00
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;
}
}
2000-11-23 08:05:32 +00:00
OGroupCompAcc::OGroupCompAcc(const Reference<XPropertySet>& rxElement, OGroupComp _aGroupComp )
2000-09-18 15:33:13 +00:00
:m_xComponent( rxElement )
,m_aGroupComp(std::move( _aGroupComp ))
2000-09-18 15:33:13 +00:00
{
}
bool OGroupCompAcc::operator==( const OGroupCompAcc& rCompAcc ) const
2000-09-18 15:33:13 +00:00
{
return m_xComponent == rCompAcc.m_xComponent;
2000-09-18 15:33:13 +00:00
}
class OGroupCompAccLess
2000-09-18 15:33:13 +00:00
{
public:
bool operator() (const OGroupCompAcc& lhs, const OGroupCompAcc& rhs) const
2000-09-18 15:33:13 +00:00
{
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 )
2000-09-18 15:33:13 +00:00
{
}
2000-11-23 08:05:32 +00:00
OGroupComp::OGroupComp(const Reference<XPropertySet>& rxSet, sal_Int32 nInsertPos )
: m_xComponent( rxSet )
2010-11-12 11:33:12 +00:00
, m_xControlModel(rxSet,UNO_QUERY)
, m_nPos( nInsertPos )
, m_nTabIndex(0)
2000-09-18 15:33:13 +00:00
{
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));
2000-09-18 15:33:13 +00:00
}
}
bool OGroupComp::operator==( const OGroupComp& rComp ) const
2000-09-18 15:33:13 +00:00
{
return m_nTabIndex == rComp.GetTabIndex() && m_nPos == rComp.GetPos();
}
class OGroupCompLess
2000-09-18 15:33:13 +00:00
{
public:
bool operator() (const OGroupComp& lhs, const OGroupComp& rhs) const
2000-09-18 15:33:13 +00:00
{
bool bResult;
// TabIndex of 0 will be added at the end
2000-09-18 15:33:13 +00:00
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 ))
2000-09-18 15:33:13 +00:00
,m_nInsertPos(0)
{
}
2000-11-23 08:05:32 +00:00
void OGroup::InsertComponent( const Reference<XPropertySet>& xSet )
2000-09-18 15:33:13 +00:00
{
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++;
}
2000-11-23 08:05:32 +00:00
void OGroup::RemoveComponent( const Reference<XPropertySet>& rxElement )
2000-09-18 15:33:13 +00:00
{
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.
*/
2000-09-18 15:33:13 +00:00
}
else
{
OSL_FAIL( "OGroup::RemoveComponent: Component not in Group" );
2000-09-18 15:33:13 +00:00
}
}
else
{
OSL_FAIL( "OGroup::RemoveComponent: Component not in Group" );
2000-09-18 15:33:13 +00:00
}
}
2000-11-23 08:05:32 +00:00
Sequence< Reference<XControlModel> > OGroup::GetControlModels() const
2000-09-18 15:33:13 +00:00
{
Sequence<Reference<XControlModel> > aControlModelSeq( m_aCompArray.size() );
2000-11-23 08:05:32 +00:00
Reference<XControlModel>* pModels = aControlModelSeq.getArray();
2000-09-18 15:33:13 +00:00
for (auto const& rGroupComp : m_aCompArray)
2000-09-18 15:33:13 +00:00
{
*pModels++ = rGroupComp.GetControlModel();
2000-09-18 15:33:13 +00:00
}
return aControlModelSeq;
}
OGroupManager::OGroupManager(const Reference< XContainer >& _rxContainer)
:m_pCompGroup( new OGroup( "AllComponentGroup" ) )
,m_xContainer(_rxContainer)
2000-09-18 15:33:13 +00:00
{
osl_atomic_increment(&m_refCount);
{
_rxContainer->addContainerListener(this);
}
osl_atomic_decrement(&m_refCount);
2000-09-18 15:33:13 +00:00
}
OGroupManager::~OGroupManager()
{
}
2000-11-23 08:05:32 +00:00
// XPropertyChangeListener
void OGroupManager::disposing(const EventObject& evt)
2000-09-18 15:33:13 +00:00
{
2000-11-23 08:05:32 +00:00
Reference<XContainer> xContainer(evt.Source, UNO_QUERY);
if (xContainer.get() == m_xContainer.get())
2000-09-18 15:33:13 +00:00
{
m_pCompGroup.reset();
2000-09-18 15:33:13 +00:00
// delete group
m_aGroupArr.clear();
m_xContainer.clear();
2000-09-18 15:33:13 +00:00
}
}
void OGroupManager::removeFromGroupMap(const OUString& _sGroupName,const Reference<XPropertySet>& _xSet)
2000-09-18 15:33:13 +00:00
{
// remove Component from CompGroup
m_pCompGroup->RemoveComponent( _xSet );
2000-09-18 15:33:13 +00:00
OGroupArr::iterator aFind = m_aGroupArr.find(_sGroupName);
2000-09-18 15:33:13 +00:00
if ( aFind != m_aGroupArr.end() )
2000-09-18 15:33:13 +00:00
{
// group exists
aFind->second.RemoveComponent( _xSet );
2000-09-18 15:33:13 +00:00
// If the count of Group elements == 1 -> deactivate Group
sal_Int32 nCount = aFind->second.Count();
if ( nCount == 1 || nCount == 0 )
2000-09-18 15:33:13 +00:00
{
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 );
}
2000-09-18 15:33:13 +00:00
}
}
// 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);
2000-09-18 15:33:13 +00:00
// Re-insert Component
2000-09-18 15:33:13 +00:00
InsertElement( xSet );
}
2000-11-23 08:05:32 +00:00
// XContainerListener
void SAL_CALL OGroupManager::elementInserted(const ContainerEvent& Event)
2000-09-18 15:33:13 +00:00
{
Reference< XPropertySet > xProps;
Event.Element >>= xProps;
if ( xProps.is() )
InsertElement( xProps );
2000-09-18 15:33:13 +00:00
}
void SAL_CALL OGroupManager::elementRemoved(const ContainerEvent& Event)
2000-09-18 15:33:13 +00:00
{
Reference<XPropertySet> xProps;
Event.Element >>= xProps;
if ( xProps.is() )
RemoveElement( xProps );
2000-09-18 15:33:13 +00:00
}
void SAL_CALL OGroupManager::elementReplaced(const ContainerEvent& Event)
2000-09-18 15:33:13 +00:00
{
Reference<XPropertySet> xProps;
Event.ReplacedElement >>= xProps;
if ( xProps.is() )
RemoveElement( xProps );
xProps.clear();
Event.Element >>= xProps;
if ( xProps.is() )
InsertElement( xProps );
2000-09-18 15:33:13 +00:00
}
// Other functions
Sequence<Reference<XControlModel> > OGroupManager::getControlModels() const
2000-09-18 15:33:13 +00:00
{
return m_pCompGroup->GetControlModels();
}
sal_Int32 OGroupManager::getGroupCount() const
2000-09-18 15:33:13 +00:00
{
return m_aActiveGroupMap.size();
}
void OGroupManager::getGroup(sal_Int32 nGroup, Sequence< Reference<XControlModel> >& _rGroup, OUString& _rName)
2000-09-18 15:33:13 +00:00
{
New loplugin:unsignedcompare "Find explicit casts from signed to unsigned integer in comparison against unsigned integer, where the cast is presumably used to avoid warnings about signed vs. unsigned comparisons, and could thus be replaced with o3tl::make_unsigned for clairty." (compilerplugins/clang/unsignedcompare.cxx) o3tl::make_unsigned requires its argument to be non-negative, and there is a chance that some original code like static_cast<sal_uInt32>(n) >= c used the explicit cast to actually force a (potentially negative) value of sal_Int32 to be interpreted as an unsigned sal_uInt32, rather than using the cast to avoid a false "signed vs. unsigned comparison" warning in a case where n is known to be non-negative. It appears that restricting this plugin to non- equality comparisons (<, >, <=, >=) and excluding equality comparisons (==, !=) is a useful heuristic to avoid such false positives. The only remainging false positive I found was 0288c8ffecff4956a52b9147d441979941e8b87f "Rephrase cast from sal_Int32 to sal_uInt32". But which of course does not mean that there were no further false positivies that I missed. So this commit may accidentally introduce some false hits of the assert in o3tl::make_unsigned. At least, it passed a full (Linux ASan+UBSan --enable-dbgutil) `make check && make screenshot`. It is by design that o3tl::make_unsigned only accepts signed integer parameter types (and is not defined as a nop for unsigned ones), to avoid unnecessary uses which would in general be suspicious. But the STATIC_ARRAY_SELECT macro in include/oox/helper/helper.hxx is used with both signed and unsigned types, so needs a little oox::detail::make_unsigned helper function for now. (The ultimate fix being to get rid of the macro in the first place.) Change-Id: Ia4adc9f44c70ad1dfd608784cac39ee922c32175 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/87556 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-01-27 09:30:39 +01:00
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();
2000-09-18 15:33:13 +00:00
}
void OGroupManager::getGroupByName(const OUString& _rName, Sequence< Reference<XControlModel> >& _rGroup)
2000-09-18 15:33:13 +00:00
{
OGroupArr::iterator aFind = m_aGroupArr.find(_rName);
if ( aFind != m_aGroupArr.end() )
_rGroup = aFind->second.GetControlModels();
2000-09-18 15:33:13 +00:00
}
2000-11-23 08:05:32 +00:00
void OGroupManager::InsertElement( const Reference<XPropertySet>& xSet )
2000-09-18 15:33:13 +00:00
{
// Only ControlModels
2000-11-23 08:05:32 +00:00
Reference<XControlModel> xControl(xSet, UNO_QUERY);
2000-09-18 15:33:13 +00:00
if (!xControl.is() )
return;
// Add Component to CompGroup
2000-09-18 15:33:13 +00:00
m_pCompGroup->InsertComponent( xSet );
// Add Component to Group
OUString sGroupName( GetGroupName( xSet ) );
2000-09-18 15:33:13 +00:00
OGroupArr::iterator aFind = m_aGroupArr.find(sGroupName);
2000-09-18 15:33:13 +00:00
if ( aFind == m_aGroupArr.end() )
{
aFind = m_aGroupArr.emplace(sGroupName,OGroup(sGroupName)).first;
2000-09-18 15:33:13 +00:00
}
aFind->second.InsertComponent( xSet );
2000-09-18 15:33:13 +00:00
// 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;
}
2000-09-18 15:33:13 +00:00
if ( bActivateGroup )
2000-09-18 15:33:13 +00:00
{
OActiveGroups::iterator aAlreadyExistent = ::std::find(
m_aActiveGroupMap.begin(),
m_aActiveGroupMap.end(),
aFind
);
if ( aAlreadyExistent == m_aActiveGroupMap.end() )
m_aActiveGroupMap.push_back( aFind );
2000-09-18 15:33:13 +00:00
}
// Register as PropertyChangeListener at Component
2000-09-18 15:33:13 +00:00
xSet->addPropertyChangeListener( PROPERTY_NAME, this );
if (hasProperty(PROPERTY_GROUP_NAME, xSet))
xSet->addPropertyChangeListener( PROPERTY_GROUP_NAME, this );
2000-09-18 15:33:13 +00:00
// Not everyone needs to support Tabindex
2000-09-18 15:33:13 +00:00
if (hasProperty(PROPERTY_TABINDEX, xSet))
xSet->addPropertyChangeListener( PROPERTY_TABINDEX, this );
}
2000-11-23 08:05:32 +00:00
void OGroupManager::RemoveElement( const Reference<XPropertySet>& xSet )
2000-09-18 15:33:13 +00:00
{
// Only ControlModels
2000-11-23 08:05:32 +00:00
Reference<XControlModel> xControl(xSet, UNO_QUERY);
2000-09-18 15:33:13 +00:00
if (!xControl.is() )
return;
// Remove Component from Group
OUString sGroupName( GetGroupName( xSet ) );
2000-09-18 15:33:13 +00:00
removeFromGroupMap(sGroupName,xSet);
2000-09-18 15:33:13 +00:00
}
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;
}
2000-09-18 15:33:13 +00:00
} // namespace frm
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */