1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "GroupManager.hxx"
21 #include <com/sun/star/form/FormComponentType.hpp>
22 #include <comphelper/property.hxx>
23 #include <comphelper/types.hxx>
24 #include <o3tl/safeint.hxx>
25 #include <osl/diagnose.h>
27 #include <frm_strings.hxx>
34 using namespace ::com::sun::star::uno
;
35 using namespace ::com::sun::star::beans
;
36 using namespace ::com::sun::star::container
;
37 using namespace ::com::sun::star::form
;
38 using namespace ::com::sun::star::awt
;
39 using namespace ::com::sun::star::lang
;
40 using namespace ::comphelper
;
44 bool isRadioButton( const Reference
< XPropertySet
>& _rxComponent
)
47 if ( hasProperty( PROPERTY_CLASSID
, _rxComponent
) )
49 sal_Int16 nClassId
= FormComponentType::CONTROL
;
50 _rxComponent
->getPropertyValue( PROPERTY_CLASSID
) >>= nClassId
;
51 if ( nClassId
== FormComponentType::RADIOBUTTON
)
58 OGroupCompAcc::OGroupCompAcc(const Reference
<XPropertySet
>& rxElement
, OGroupComp _aGroupComp
)
59 :m_xComponent( rxElement
)
60 ,m_aGroupComp(std::move( _aGroupComp
))
64 bool OGroupCompAcc::operator==( const OGroupCompAcc
& rCompAcc
) const
66 return m_xComponent
== rCompAcc
.m_xComponent
;
69 class OGroupCompAccLess
72 bool operator() (const OGroupCompAcc
& lhs
, const OGroupCompAcc
& rhs
) const
75 reinterpret_cast<sal_Int64
>(lhs
.m_xComponent
.get())
76 < reinterpret_cast<sal_Int64
>(rhs
.m_xComponent
.get());
80 OGroupComp::OGroupComp()
86 OGroupComp::OGroupComp(const Reference
<XPropertySet
>& rxSet
, sal_Int32 nInsertPos
)
87 : m_xComponent( rxSet
)
88 , m_xControlModel(rxSet
,UNO_QUERY
)
89 , m_nPos( nInsertPos
)
92 if (m_xComponent
.is())
94 if (hasProperty( PROPERTY_TABINDEX
, m_xComponent
) )
95 // Indices smaller than 0 are treated like 0
96 m_nTabIndex
= std::max(getINT16(m_xComponent
->getPropertyValue( PROPERTY_TABINDEX
)) , sal_Int16(0));
100 bool OGroupComp::operator==( const OGroupComp
& rComp
) const
102 return m_nTabIndex
== rComp
.GetTabIndex() && m_nPos
== rComp
.GetPos();
108 bool operator() (const OGroupComp
& lhs
, const OGroupComp
& rhs
) const
111 // TabIndex of 0 will be added at the end
112 if (lhs
.m_nTabIndex
== rhs
.GetTabIndex())
113 bResult
= lhs
.m_nPos
< rhs
.GetPos();
114 else if (lhs
.m_nTabIndex
&& rhs
.GetTabIndex())
115 bResult
= lhs
.m_nTabIndex
< rhs
.GetTabIndex();
117 bResult
= lhs
.m_nTabIndex
!= 0;
122 OGroup::OGroup( OUString sGroupName
)
123 :m_aGroupName(std::move( sGroupName
))
128 void OGroup::InsertComponent( const Reference
<XPropertySet
>& xSet
)
130 OGroupComp
aNewGroupComp( xSet
, m_nInsertPos
);
131 sal_Int32 nPosInserted
= insert_sorted(m_aCompArray
, aNewGroupComp
, OGroupCompLess());
133 OGroupCompAcc
aNewGroupCompAcc( xSet
, m_aCompArray
[nPosInserted
] );
134 insert_sorted(m_aCompAccArray
, aNewGroupCompAcc
, OGroupCompAccLess());
138 void OGroup::RemoveComponent( const Reference
<XPropertySet
>& rxElement
)
140 sal_Int32 nGroupCompAccPos
;
141 OGroupCompAcc
aSearchCompAcc( rxElement
, OGroupComp() );
142 if ( seek_entry(m_aCompAccArray
, aSearchCompAcc
, nGroupCompAccPos
, OGroupCompAccLess()) )
144 OGroupCompAcc
& aGroupCompAcc
= m_aCompAccArray
[nGroupCompAccPos
];
145 const OGroupComp
& aGroupComp
= aGroupCompAcc
.GetGroupComponent();
147 sal_Int32 nGroupCompPos
;
148 if ( seek_entry(m_aCompArray
, aGroupComp
, nGroupCompPos
, OGroupCompLess()) )
150 m_aCompAccArray
.erase( m_aCompAccArray
.begin() + nGroupCompAccPos
);
151 m_aCompArray
.erase( m_aCompArray
.begin() + nGroupCompPos
);
154 * By removing the GroupComp the insertion position has become invalid.
155 * We do not to change it here, however, because it's passed on continuously
156 * and ascending distinctively.
161 OSL_FAIL( "OGroup::RemoveComponent: Component not in Group" );
166 OSL_FAIL( "OGroup::RemoveComponent: Component not in Group" );
170 Sequence
< Reference
<XControlModel
> > OGroup::GetControlModels() const
172 Sequence
<Reference
<XControlModel
> > aControlModelSeq( m_aCompArray
.size() );
173 Reference
<XControlModel
>* pModels
= aControlModelSeq
.getArray();
175 for (auto const& rGroupComp
: m_aCompArray
)
177 *pModels
++ = rGroupComp
.GetControlModel();
179 return aControlModelSeq
;
182 OGroupManager::OGroupManager(const Reference
< XContainer
>& _rxContainer
)
183 :m_pCompGroup( new OGroup( u
"AllComponentGroup"_ustr
) )
184 ,m_xContainer(_rxContainer
)
186 osl_atomic_increment(&m_refCount
);
188 _rxContainer
->addContainerListener(this);
190 osl_atomic_decrement(&m_refCount
);
193 OGroupManager::~OGroupManager()
197 // XPropertyChangeListener
198 void OGroupManager::disposing(const EventObject
& evt
)
200 Reference
<XContainer
> xContainer(evt
.Source
, UNO_QUERY
);
201 if (xContainer
.get() == m_xContainer
.get())
203 m_pCompGroup
.reset();
207 m_xContainer
.clear();
211 void OGroupManager::removeFromGroupMap(const OUString
& _sGroupName
,const Reference
<XPropertySet
>& _xSet
)
213 // remove Component from CompGroup
214 m_pCompGroup
->RemoveComponent( _xSet
);
216 OGroupArr::iterator aFind
= m_aGroupArr
.find(_sGroupName
);
218 if ( aFind
!= m_aGroupArr
.end() )
221 aFind
->second
.RemoveComponent( _xSet
);
223 // If the count of Group elements == 1 -> deactivate Group
224 sal_Int32 nCount
= aFind
->second
.Count();
225 if ( nCount
== 1 || nCount
== 0 )
227 OActiveGroups::iterator aActiveFind
= ::std::find(
228 m_aActiveGroupMap
.begin(),
229 m_aActiveGroupMap
.end(),
232 if ( aActiveFind
!= m_aActiveGroupMap
.end() )
234 // the group is active. Deactivate it if the remaining component
235 // is *no* radio button
236 if ( nCount
== 0 || !isRadioButton( aFind
->second
.GetObject( 0 ) ) )
237 m_aActiveGroupMap
.erase( aActiveFind
);
243 // Deregister as PropertyChangeListener at Component
244 _xSet
->removePropertyChangeListener( PROPERTY_NAME
, this );
245 if (hasProperty(PROPERTY_GROUP_NAME
, _xSet
))
246 _xSet
->removePropertyChangeListener( PROPERTY_GROUP_NAME
, this );
247 if (hasProperty(PROPERTY_TABINDEX
, _xSet
))
248 _xSet
->removePropertyChangeListener( PROPERTY_TABINDEX
, this );
251 void SAL_CALL
OGroupManager::propertyChange(const PropertyChangeEvent
& evt
)
253 Reference
<XPropertySet
> xSet(evt
.Source
, UNO_QUERY
);
255 // remove Component from group
257 if (hasProperty( PROPERTY_GROUP_NAME
, xSet
))
258 xSet
->getPropertyValue( PROPERTY_GROUP_NAME
) >>= sGroupName
;
259 if (evt
.PropertyName
== PROPERTY_NAME
) {
260 if (!sGroupName
.isEmpty())
261 return; // group hasn't changed; ignore this name change.
262 // no GroupName; use Name as GroupName
263 evt
.OldValue
>>= sGroupName
;
265 else if (evt
.PropertyName
== PROPERTY_GROUP_NAME
) {
266 evt
.OldValue
>>= sGroupName
;
267 if (sGroupName
.isEmpty()) {
268 // No prior GroupName; fallback to Name
269 xSet
->getPropertyValue( PROPERTY_NAME
) >>= sGroupName
;
273 sGroupName
= GetGroupName( xSet
);
275 removeFromGroupMap(sGroupName
,xSet
);
277 // Re-insert Component
278 InsertElement( xSet
);
281 // XContainerListener
282 void SAL_CALL
OGroupManager::elementInserted(const ContainerEvent
& Event
)
284 Reference
< XPropertySet
> xProps
;
285 Event
.Element
>>= xProps
;
287 InsertElement( xProps
);
290 void SAL_CALL
OGroupManager::elementRemoved(const ContainerEvent
& Event
)
292 Reference
<XPropertySet
> xProps
;
293 Event
.Element
>>= xProps
;
295 RemoveElement( xProps
);
298 void SAL_CALL
OGroupManager::elementReplaced(const ContainerEvent
& Event
)
300 Reference
<XPropertySet
> xProps
;
301 Event
.ReplacedElement
>>= xProps
;
303 RemoveElement( xProps
);
306 Event
.Element
>>= xProps
;
308 InsertElement( xProps
);
312 Sequence
<Reference
<XControlModel
> > OGroupManager::getControlModels() const
314 return m_pCompGroup
->GetControlModels();
317 sal_Int32
OGroupManager::getGroupCount() const
319 return m_aActiveGroupMap
.size();
322 void OGroupManager::getGroup(sal_Int32 nGroup
, Sequence
< Reference
<XControlModel
> >& _rGroup
, OUString
& _rName
)
324 OSL_ENSURE(nGroup
>= 0 && o3tl::make_unsigned(nGroup
) < m_aActiveGroupMap
.size(),"OGroupManager::getGroup: Invalid group index!");
325 OGroupArr::iterator aGroupPos
= m_aActiveGroupMap
[nGroup
];
326 _rName
= aGroupPos
->second
.GetGroupName();
327 _rGroup
= aGroupPos
->second
.GetControlModels();
330 void OGroupManager::getGroupByName(const OUString
& _rName
, Sequence
< Reference
<XControlModel
> >& _rGroup
)
332 OGroupArr::iterator aFind
= m_aGroupArr
.find(_rName
);
333 if ( aFind
!= m_aGroupArr
.end() )
334 _rGroup
= aFind
->second
.GetControlModels();
337 void OGroupManager::InsertElement( const Reference
<XPropertySet
>& xSet
)
339 // Only ControlModels
340 Reference
<XControlModel
> xControl(xSet
, UNO_QUERY
);
344 // Add Component to CompGroup
345 m_pCompGroup
->InsertComponent( xSet
);
347 // Add Component to Group
348 OUString
sGroupName( GetGroupName( xSet
) );
350 OGroupArr::iterator aFind
= m_aGroupArr
.find(sGroupName
);
352 if ( aFind
== m_aGroupArr
.end() )
354 aFind
= m_aGroupArr
.emplace(sGroupName
,OGroup(sGroupName
)).first
;
357 aFind
->second
.InsertComponent( xSet
);
359 // if we have at least 2 elements in the group, then this is an "active group"
360 bool bActivateGroup
= aFind
->second
.Count() == 2;
362 // Additionally, if the component is a radio button, then it's group becomes active,
363 // too. With this, we ensure that in a container with n radio buttons which all are
364 // in different groups the selection still works reliably (means that all radios can be
365 // clicked independently)
366 if ( aFind
->second
.Count() == 1 )
368 if ( isRadioButton( xSet
) )
369 bActivateGroup
= true;
372 if ( bActivateGroup
)
374 OActiveGroups::iterator aAlreadyExistent
= ::std::find(
375 m_aActiveGroupMap
.begin(),
376 m_aActiveGroupMap
.end(),
379 if ( aAlreadyExistent
== m_aActiveGroupMap
.end() )
380 m_aActiveGroupMap
.push_back( aFind
);
383 // Register as PropertyChangeListener at Component
384 xSet
->addPropertyChangeListener( PROPERTY_NAME
, this );
385 if (hasProperty(PROPERTY_GROUP_NAME
, xSet
))
386 xSet
->addPropertyChangeListener( PROPERTY_GROUP_NAME
, this );
388 // Not everyone needs to support Tabindex
389 if (hasProperty(PROPERTY_TABINDEX
, xSet
))
390 xSet
->addPropertyChangeListener( PROPERTY_TABINDEX
, this );
393 void OGroupManager::RemoveElement( const Reference
<XPropertySet
>& xSet
)
395 // Only ControlModels
396 Reference
<XControlModel
> xControl(xSet
, UNO_QUERY
);
400 // Remove Component from Group
401 OUString
sGroupName( GetGroupName( xSet
) );
403 removeFromGroupMap(sGroupName
,xSet
);
406 OUString
OGroupManager::GetGroupName( const css::uno::Reference
< css::beans::XPropertySet
>& xComponent
)
408 if (!xComponent
.is())
411 if (hasProperty( PROPERTY_GROUP_NAME
, xComponent
)) {
412 xComponent
->getPropertyValue( PROPERTY_GROUP_NAME
) >>= sGroupName
;
413 if (sGroupName
.isEmpty())
414 xComponent
->getPropertyValue( PROPERTY_NAME
) >>= sGroupName
;
417 xComponent
->getPropertyValue( PROPERTY_NAME
) >>= sGroupName
;
422 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */