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>
33 using namespace ::com::sun::star::uno
;
34 using namespace ::com::sun::star::beans
;
35 using namespace ::com::sun::star::container
;
36 using namespace ::com::sun::star::form
;
37 using namespace ::com::sun::star::awt
;
38 using namespace ::com::sun::star::lang
;
39 using namespace ::comphelper
;
43 bool isRadioButton( const Reference
< XPropertySet
>& _rxComponent
)
46 if ( hasProperty( PROPERTY_CLASSID
, _rxComponent
) )
48 sal_Int16 nClassId
= FormComponentType::CONTROL
;
49 _rxComponent
->getPropertyValue( PROPERTY_CLASSID
) >>= nClassId
;
50 if ( nClassId
== FormComponentType::RADIOBUTTON
)
57 OGroupCompAcc::OGroupCompAcc(const Reference
<XPropertySet
>& rxElement
, const OGroupComp
& _rGroupComp
)
58 :m_xComponent( rxElement
)
59 ,m_aGroupComp( _rGroupComp
)
63 bool OGroupCompAcc::operator==( const OGroupCompAcc
& rCompAcc
) const
65 return m_xComponent
== rCompAcc
.m_xComponent
;
68 class OGroupCompAccLess
71 bool operator() (const OGroupCompAcc
& lhs
, const OGroupCompAcc
& rhs
) const
74 reinterpret_cast<sal_Int64
>(lhs
.m_xComponent
.get())
75 < reinterpret_cast<sal_Int64
>(rhs
.m_xComponent
.get());
79 OGroupComp::OGroupComp()
85 OGroupComp::OGroupComp(const Reference
<XPropertySet
>& rxSet
, sal_Int32 nInsertPos
)
86 : m_xComponent( rxSet
)
87 , m_xControlModel(rxSet
,UNO_QUERY
)
88 , m_nPos( nInsertPos
)
91 if (m_xComponent
.is())
93 if (hasProperty( PROPERTY_TABINDEX
, m_xComponent
) )
94 // Indices smaller than 0 are treated like 0
95 m_nTabIndex
= std::max(getINT16(m_xComponent
->getPropertyValue( PROPERTY_TABINDEX
)) , sal_Int16(0));
99 bool OGroupComp::operator==( const OGroupComp
& rComp
) const
101 return m_nTabIndex
== rComp
.GetTabIndex() && m_nPos
== rComp
.GetPos();
107 bool operator() (const OGroupComp
& lhs
, const OGroupComp
& rhs
) const
110 // TabIndex of 0 will be added at the end
111 if (lhs
.m_nTabIndex
== rhs
.GetTabIndex())
112 bResult
= lhs
.m_nPos
< rhs
.GetPos();
113 else if (lhs
.m_nTabIndex
&& rhs
.GetTabIndex())
114 bResult
= lhs
.m_nTabIndex
< rhs
.GetTabIndex();
116 bResult
= lhs
.m_nTabIndex
!= 0;
121 OGroup::OGroup( const OUString
& rGroupName
)
122 :m_aGroupName( rGroupName
)
127 void OGroup::InsertComponent( const Reference
<XPropertySet
>& xSet
)
129 OGroupComp
aNewGroupComp( xSet
, m_nInsertPos
);
130 sal_Int32 nPosInserted
= insert_sorted(m_aCompArray
, aNewGroupComp
, OGroupCompLess());
132 OGroupCompAcc
aNewGroupCompAcc( xSet
, m_aCompArray
[nPosInserted
] );
133 insert_sorted(m_aCompAccArray
, aNewGroupCompAcc
, OGroupCompAccLess());
137 void OGroup::RemoveComponent( const Reference
<XPropertySet
>& rxElement
)
139 sal_Int32 nGroupCompAccPos
;
140 OGroupCompAcc
aSearchCompAcc( rxElement
, OGroupComp() );
141 if ( seek_entry(m_aCompAccArray
, aSearchCompAcc
, nGroupCompAccPos
, OGroupCompAccLess()) )
143 OGroupCompAcc
& aGroupCompAcc
= m_aCompAccArray
[nGroupCompAccPos
];
144 const OGroupComp
& aGroupComp
= aGroupCompAcc
.GetGroupComponent();
146 sal_Int32 nGroupCompPos
;
147 if ( seek_entry(m_aCompArray
, aGroupComp
, nGroupCompPos
, OGroupCompLess()) )
149 m_aCompAccArray
.erase( m_aCompAccArray
.begin() + nGroupCompAccPos
);
150 m_aCompArray
.erase( m_aCompArray
.begin() + nGroupCompPos
);
153 * By removing the GroupComp the insertion position has become invalid.
154 * We do not to change it here, however, because it's passed on continuously
155 * and ascending distinctively.
160 OSL_FAIL( "OGroup::RemoveComponent: Component not in Group" );
165 OSL_FAIL( "OGroup::RemoveComponent: Component not in Group" );
169 Sequence
< Reference
<XControlModel
> > OGroup::GetControlModels() const
171 Sequence
<Reference
<XControlModel
> > aControlModelSeq( m_aCompArray
.size() );
172 Reference
<XControlModel
>* pModels
= aControlModelSeq
.getArray();
174 for (auto const& rGroupComp
: m_aCompArray
)
176 *pModels
++ = rGroupComp
.GetControlModel();
178 return aControlModelSeq
;
181 OGroupManager::OGroupManager(const Reference
< XContainer
>& _rxContainer
)
182 :m_pCompGroup( new OGroup( "AllComponentGroup" ) )
183 ,m_xContainer(_rxContainer
)
185 osl_atomic_increment(&m_refCount
);
187 _rxContainer
->addContainerListener(this);
189 osl_atomic_decrement(&m_refCount
);
192 OGroupManager::~OGroupManager()
196 // XPropertyChangeListener
197 void OGroupManager::disposing(const EventObject
& evt
)
199 Reference
<XContainer
> xContainer(evt
.Source
, UNO_QUERY
);
200 if (xContainer
.get() == m_xContainer
.get())
202 m_pCompGroup
.reset();
206 m_xContainer
.clear();
210 void OGroupManager::removeFromGroupMap(const OUString
& _sGroupName
,const Reference
<XPropertySet
>& _xSet
)
212 // remove Component from CompGroup
213 m_pCompGroup
->RemoveComponent( _xSet
);
215 OGroupArr::iterator aFind
= m_aGroupArr
.find(_sGroupName
);
217 if ( aFind
!= m_aGroupArr
.end() )
220 aFind
->second
.RemoveComponent( _xSet
);
222 // If the count of Group elements == 1 -> deactivate Group
223 sal_Int32 nCount
= aFind
->second
.Count();
224 if ( nCount
== 1 || nCount
== 0 )
226 OActiveGroups::iterator aActiveFind
= ::std::find(
227 m_aActiveGroupMap
.begin(),
228 m_aActiveGroupMap
.end(),
231 if ( aActiveFind
!= m_aActiveGroupMap
.end() )
233 // the group is active. Deactivate it if the remaining component
234 // is *no* radio button
235 if ( nCount
== 0 || !isRadioButton( aFind
->second
.GetObject( 0 ) ) )
236 m_aActiveGroupMap
.erase( aActiveFind
);
242 // Deregister as PropertyChangeListener at Component
243 _xSet
->removePropertyChangeListener( PROPERTY_NAME
, this );
244 if (hasProperty(PROPERTY_GROUP_NAME
, _xSet
))
245 _xSet
->removePropertyChangeListener( PROPERTY_GROUP_NAME
, this );
246 if (hasProperty(PROPERTY_TABINDEX
, _xSet
))
247 _xSet
->removePropertyChangeListener( PROPERTY_TABINDEX
, this );
250 void SAL_CALL
OGroupManager::propertyChange(const PropertyChangeEvent
& evt
)
252 Reference
<XPropertySet
> xSet(evt
.Source
, UNO_QUERY
);
254 // remove Component from group
256 if (hasProperty( PROPERTY_GROUP_NAME
, xSet
))
257 xSet
->getPropertyValue( PROPERTY_GROUP_NAME
) >>= sGroupName
;
258 if (evt
.PropertyName
== PROPERTY_NAME
) {
259 if (!sGroupName
.isEmpty())
260 return; // group hasn't changed; ignore this name change.
261 // no GroupName; use Name as GroupName
262 evt
.OldValue
>>= sGroupName
;
264 else if (evt
.PropertyName
== PROPERTY_GROUP_NAME
) {
265 evt
.OldValue
>>= sGroupName
;
266 if (sGroupName
.isEmpty()) {
267 // No prior GroupName; fallback to Name
268 xSet
->getPropertyValue( PROPERTY_NAME
) >>= sGroupName
;
272 sGroupName
= GetGroupName( xSet
);
274 removeFromGroupMap(sGroupName
,xSet
);
276 // Re-insert Component
277 InsertElement( xSet
);
280 // XContainerListener
281 void SAL_CALL
OGroupManager::elementInserted(const ContainerEvent
& Event
)
283 Reference
< XPropertySet
> xProps
;
284 Event
.Element
>>= xProps
;
286 InsertElement( xProps
);
289 void SAL_CALL
OGroupManager::elementRemoved(const ContainerEvent
& Event
)
291 Reference
<XPropertySet
> xProps
;
292 Event
.Element
>>= xProps
;
294 RemoveElement( xProps
);
297 void SAL_CALL
OGroupManager::elementReplaced(const ContainerEvent
& Event
)
299 Reference
<XPropertySet
> xProps
;
300 Event
.ReplacedElement
>>= xProps
;
302 RemoveElement( xProps
);
305 Event
.Element
>>= xProps
;
307 InsertElement( xProps
);
311 Sequence
<Reference
<XControlModel
> > OGroupManager::getControlModels() const
313 return m_pCompGroup
->GetControlModels();
316 sal_Int32
OGroupManager::getGroupCount() const
318 return m_aActiveGroupMap
.size();
321 void OGroupManager::getGroup(sal_Int32 nGroup
, Sequence
< Reference
<XControlModel
> >& _rGroup
, OUString
& _rName
)
323 OSL_ENSURE(nGroup
>= 0 && o3tl::make_unsigned(nGroup
) < m_aActiveGroupMap
.size(),"OGroupManager::getGroup: Invalid group index!");
324 OGroupArr::iterator aGroupPos
= m_aActiveGroupMap
[nGroup
];
325 _rName
= aGroupPos
->second
.GetGroupName();
326 _rGroup
= aGroupPos
->second
.GetControlModels();
329 void OGroupManager::getGroupByName(const OUString
& _rName
, Sequence
< Reference
<XControlModel
> >& _rGroup
)
331 OGroupArr::iterator aFind
= m_aGroupArr
.find(_rName
);
332 if ( aFind
!= m_aGroupArr
.end() )
333 _rGroup
= aFind
->second
.GetControlModels();
336 void OGroupManager::InsertElement( const Reference
<XPropertySet
>& xSet
)
338 // Only ControlModels
339 Reference
<XControlModel
> xControl(xSet
, UNO_QUERY
);
343 // Add Component to CompGroup
344 m_pCompGroup
->InsertComponent( xSet
);
346 // Add Component to Group
347 OUString
sGroupName( GetGroupName( xSet
) );
349 OGroupArr::iterator aFind
= m_aGroupArr
.find(sGroupName
);
351 if ( aFind
== m_aGroupArr
.end() )
353 aFind
= m_aGroupArr
.emplace(sGroupName
,OGroup(sGroupName
)).first
;
356 aFind
->second
.InsertComponent( xSet
);
358 // if we have at least 2 elements in the group, then this is an "active group"
359 bool bActivateGroup
= aFind
->second
.Count() == 2;
361 // Additionally, if the component is a radio button, then it's group becomes active,
362 // too. With this, we ensure that in a container with n radio buttons which all are
363 // in different groups the selection still works reliably (means that all radios can be
364 // clicked independently)
365 if ( aFind
->second
.Count() == 1 )
367 if ( isRadioButton( xSet
) )
368 bActivateGroup
= true;
371 if ( bActivateGroup
)
373 OActiveGroups::iterator aAlreadyExistent
= ::std::find(
374 m_aActiveGroupMap
.begin(),
375 m_aActiveGroupMap
.end(),
378 if ( aAlreadyExistent
== m_aActiveGroupMap
.end() )
379 m_aActiveGroupMap
.push_back( aFind
);
382 // Register as PropertyChangeListener at Component
383 xSet
->addPropertyChangeListener( PROPERTY_NAME
, this );
384 if (hasProperty(PROPERTY_GROUP_NAME
, xSet
))
385 xSet
->addPropertyChangeListener( PROPERTY_GROUP_NAME
, this );
387 // Not everyone needs to support Tabindex
388 if (hasProperty(PROPERTY_TABINDEX
, xSet
))
389 xSet
->addPropertyChangeListener( PROPERTY_TABINDEX
, this );
392 void OGroupManager::RemoveElement( const Reference
<XPropertySet
>& xSet
)
394 // Only ControlModels
395 Reference
<XControlModel
> xControl(xSet
, UNO_QUERY
);
399 // Remove Component from Group
400 OUString
sGroupName( GetGroupName( xSet
) );
402 removeFromGroupMap(sGroupName
,xSet
);
405 OUString
OGroupManager::GetGroupName( const css::uno::Reference
< css::beans::XPropertySet
>& xComponent
)
407 if (!xComponent
.is())
410 if (hasProperty( PROPERTY_GROUP_NAME
, xComponent
)) {
411 xComponent
->getPropertyValue( PROPERTY_GROUP_NAME
) >>= sGroupName
;
412 if (sGroupName
.isEmpty())
413 xComponent
->getPropertyValue( PROPERTY_NAME
) >>= sGroupName
;
416 xComponent
->getPropertyValue( PROPERTY_NAME
) >>= sGroupName
;
421 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */