LibreOffice Module forms (master) 1
GroupManager.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include "GroupManager.hxx"
21#include <com/sun/star/form/FormComponentType.hpp>
23#include <comphelper/types.hxx>
24#include <o3tl/safeint.hxx>
25#include <osl/diagnose.h>
26
27#include <frm_strings.hxx>
28
29#include <algorithm>
30#include <utility>
31
32namespace frm
33{
34using namespace ::com::sun::star::uno;
35using namespace ::com::sun::star::beans;
36using namespace ::com::sun::star::container;
37using namespace ::com::sun::star::form;
38using namespace ::com::sun::star::awt;
39using namespace ::com::sun::star::lang;
40using namespace ::comphelper;
41
42namespace
43{
44 bool isRadioButton( const Reference< XPropertySet >& _rxComponent )
45 {
46 bool bIs = false;
47 if ( hasProperty( PROPERTY_CLASSID, _rxComponent ) )
48 {
49 sal_Int16 nClassId = FormComponentType::CONTROL;
50 _rxComponent->getPropertyValue( PROPERTY_CLASSID ) >>= nClassId;
51 if ( nClassId == FormComponentType::RADIOBUTTON )
52 bIs = true;
53 }
54 return bIs;
55 }
56}
57
58OGroupCompAcc::OGroupCompAcc(const Reference<XPropertySet>& rxElement, OGroupComp _aGroupComp )
59 :m_xComponent( rxElement )
60 ,m_aGroupComp(std::move( _aGroupComp ))
61{
62}
63
64bool OGroupCompAcc::operator==( const OGroupCompAcc& rCompAcc ) const
65{
66 return m_xComponent == rCompAcc.m_xComponent;
67}
68
70{
71public:
72 bool operator() (const OGroupCompAcc& lhs, const OGroupCompAcc& rhs) const
73 {
74 return
75 reinterpret_cast<sal_Int64>(lhs.m_xComponent.get())
76 < reinterpret_cast<sal_Int64>(rhs.m_xComponent.get());
77 }
78};
79
81 :m_nPos( -1 )
82 ,m_nTabIndex( 0 )
83{
84}
85
86OGroupComp::OGroupComp(const Reference<XPropertySet>& rxSet, sal_Int32 nInsertPos )
87 : m_xComponent( rxSet )
88 , m_xControlModel(rxSet,UNO_QUERY)
89 , m_nPos( nInsertPos )
90 , m_nTabIndex(0)
91{
92 if (m_xComponent.is())
93 {
95 // Indices smaller than 0 are treated like 0
96 m_nTabIndex = std::max(getINT16(m_xComponent->getPropertyValue( PROPERTY_TABINDEX )) , sal_Int16(0));
97 }
98}
99
100bool OGroupComp::operator==( const OGroupComp& rComp ) const
101{
102 return m_nTabIndex == rComp.GetTabIndex() && m_nPos == rComp.GetPos();
103}
104
106{
107public:
108 bool operator() (const OGroupComp& lhs, const OGroupComp& rhs) const
109 {
110 bool bResult;
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();
116 else
117 bResult = lhs.m_nTabIndex != 0;
118 return bResult;
119 }
120};
121
122OGroup::OGroup( OUString sGroupName )
123 :m_aGroupName(std::move( sGroupName ))
124 ,m_nInsertPos(0)
125{
126}
127
128void OGroup::InsertComponent( const Reference<XPropertySet>& xSet )
129{
130 OGroupComp aNewGroupComp( xSet, m_nInsertPos );
131 sal_Int32 nPosInserted = insert_sorted(m_aCompArray, aNewGroupComp, OGroupCompLess());
132
133 OGroupCompAcc aNewGroupCompAcc( xSet, m_aCompArray[nPosInserted] );
134 insert_sorted(m_aCompAccArray, aNewGroupCompAcc, OGroupCompAccLess());
135 m_nInsertPos++;
136}
137
138void OGroup::RemoveComponent( const Reference<XPropertySet>& rxElement )
139{
140 sal_Int32 nGroupCompAccPos;
141 OGroupCompAcc aSearchCompAcc( rxElement, OGroupComp() );
142 if ( seek_entry(m_aCompAccArray, aSearchCompAcc, nGroupCompAccPos, OGroupCompAccLess()) )
143 {
144 OGroupCompAcc& aGroupCompAcc = m_aCompAccArray[nGroupCompAccPos];
145 const OGroupComp& aGroupComp = aGroupCompAcc.GetGroupComponent();
146
147 sal_Int32 nGroupCompPos;
148 if ( seek_entry(m_aCompArray, aGroupComp, nGroupCompPos, OGroupCompLess()) )
149 {
150 m_aCompAccArray.erase( m_aCompAccArray.begin() + nGroupCompAccPos );
151 m_aCompArray.erase( m_aCompArray.begin() + nGroupCompPos );
152
153 /*
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.
157 */
158 }
159 else
160 {
161 OSL_FAIL( "OGroup::RemoveComponent: Component not in Group" );
162 }
163 }
164 else
165 {
166 OSL_FAIL( "OGroup::RemoveComponent: Component not in Group" );
167 }
168}
169
170Sequence< Reference<XControlModel> > OGroup::GetControlModels() const
171{
172 Sequence<Reference<XControlModel> > aControlModelSeq( m_aCompArray.size() );
173 Reference<XControlModel>* pModels = aControlModelSeq.getArray();
174
175 for (auto const& rGroupComp : m_aCompArray)
176 {
177 *pModels++ = rGroupComp.GetControlModel();
178 }
179 return aControlModelSeq;
180}
181
182OGroupManager::OGroupManager(const Reference< XContainer >& _rxContainer)
183 :m_pCompGroup( new OGroup( "AllComponentGroup" ) )
184 ,m_xContainer(_rxContainer)
185{
186 osl_atomic_increment(&m_refCount);
187 {
188 _rxContainer->addContainerListener(this);
189 }
190 osl_atomic_decrement(&m_refCount);
191}
192
194{
195}
196
197// XPropertyChangeListener
198void OGroupManager::disposing(const EventObject& evt)
199{
200 Reference<XContainer> xContainer(evt.Source, UNO_QUERY);
201 if (xContainer.get() == m_xContainer.get())
202 {
203 m_pCompGroup.reset();
204
205 // delete group
206 m_aGroupArr.clear();
207 m_xContainer.clear();
208 }
209}
210
211void OGroupManager::removeFromGroupMap(const OUString& _sGroupName,const Reference<XPropertySet>& _xSet)
212{
213 // remove Component from CompGroup
214 m_pCompGroup->RemoveComponent( _xSet );
215
216 OGroupArr::iterator aFind = m_aGroupArr.find(_sGroupName);
217
218 if ( aFind != m_aGroupArr.end() )
219 {
220 // group exists
221 aFind->second.RemoveComponent( _xSet );
222
223 // If the count of Group elements == 1 -> deactivate Group
224 sal_Int32 nCount = aFind->second.Count();
225 if ( nCount == 1 || nCount == 0 )
226 {
227 OActiveGroups::iterator aActiveFind = ::std::find(
228 m_aActiveGroupMap.begin(),
229 m_aActiveGroupMap.end(),
230 aFind
231 );
232 if ( aActiveFind != m_aActiveGroupMap.end() )
233 {
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 );
238 }
239 }
240 }
241
242
243 // Deregister as PropertyChangeListener at Component
244 _xSet->removePropertyChangeListener( PROPERTY_NAME, this );
246 _xSet->removePropertyChangeListener( PROPERTY_GROUP_NAME, this );
247 if (hasProperty(PROPERTY_TABINDEX, _xSet))
248 _xSet->removePropertyChangeListener( PROPERTY_TABINDEX, this );
249}
250
251void SAL_CALL OGroupManager::propertyChange(const PropertyChangeEvent& evt)
252{
253 Reference<XPropertySet> xSet(evt.Source, UNO_QUERY);
254
255 // remove Component from group
256 OUString sGroupName;
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;
264 }
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;
270 }
271 }
272 else
273 sGroupName = GetGroupName( xSet );
274
275 removeFromGroupMap(sGroupName,xSet);
276
277 // Re-insert Component
278 InsertElement( xSet );
279}
280
281// XContainerListener
282void SAL_CALL OGroupManager::elementInserted(const ContainerEvent& Event)
283{
284 Reference< XPropertySet > xProps;
285 Event.Element >>= xProps;
286 if ( xProps.is() )
287 InsertElement( xProps );
288}
289
290void SAL_CALL OGroupManager::elementRemoved(const ContainerEvent& Event)
291{
292 Reference<XPropertySet> xProps;
293 Event.Element >>= xProps;
294 if ( xProps.is() )
295 RemoveElement( xProps );
296}
297
298void SAL_CALL OGroupManager::elementReplaced(const ContainerEvent& Event)
299{
300 Reference<XPropertySet> xProps;
301 Event.ReplacedElement >>= xProps;
302 if ( xProps.is() )
303 RemoveElement( xProps );
304
305 xProps.clear();
306 Event.Element >>= xProps;
307 if ( xProps.is() )
308 InsertElement( xProps );
309}
310
311// Other functions
312Sequence<Reference<XControlModel> > OGroupManager::getControlModels() const
313{
314 return m_pCompGroup->GetControlModels();
315}
316
318{
319 return m_aActiveGroupMap.size();
320}
321
322void OGroupManager::getGroup(sal_Int32 nGroup, Sequence< Reference<XControlModel> >& _rGroup, OUString& _rName)
323{
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();
328}
329
330void OGroupManager::getGroupByName(const OUString& _rName, Sequence< Reference<XControlModel> >& _rGroup)
331{
332 OGroupArr::iterator aFind = m_aGroupArr.find(_rName);
333 if ( aFind != m_aGroupArr.end() )
334 _rGroup = aFind->second.GetControlModels();
335}
336
337void OGroupManager::InsertElement( const Reference<XPropertySet>& xSet )
338{
339 // Only ControlModels
340 Reference<XControlModel> xControl(xSet, UNO_QUERY);
341 if (!xControl.is() )
342 return;
343
344 // Add Component to CompGroup
345 m_pCompGroup->InsertComponent( xSet );
346
347 // Add Component to Group
348 OUString sGroupName( GetGroupName( xSet ) );
349
350 OGroupArr::iterator aFind = m_aGroupArr.find(sGroupName);
351
352 if ( aFind == m_aGroupArr.end() )
353 {
354 aFind = m_aGroupArr.emplace(sGroupName,OGroup(sGroupName)).first;
355 }
356
357 aFind->second.InsertComponent( xSet );
358
359 // if we have at least 2 elements in the group, then this is an "active group"
360 bool bActivateGroup = aFind->second.Count() == 2;
361
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 )
367 {
368 if ( isRadioButton( xSet ) )
369 bActivateGroup = true;
370 }
371
372 if ( bActivateGroup )
373 {
374 OActiveGroups::iterator aAlreadyExistent = ::std::find(
375 m_aActiveGroupMap.begin(),
376 m_aActiveGroupMap.end(),
377 aFind
378 );
379 if ( aAlreadyExistent == m_aActiveGroupMap.end() )
380 m_aActiveGroupMap.push_back( aFind );
381 }
382
383 // Register as PropertyChangeListener at Component
384 xSet->addPropertyChangeListener( PROPERTY_NAME, this );
386 xSet->addPropertyChangeListener( PROPERTY_GROUP_NAME, this );
387
388 // Not everyone needs to support Tabindex
390 xSet->addPropertyChangeListener( PROPERTY_TABINDEX, this );
391}
392
393void OGroupManager::RemoveElement( const Reference<XPropertySet>& xSet )
394{
395 // Only ControlModels
396 Reference<XControlModel> xControl(xSet, UNO_QUERY);
397 if (!xControl.is() )
398 return;
399
400 // Remove Component from Group
401 OUString sGroupName( GetGroupName( xSet ) );
402
403 removeFromGroupMap(sGroupName,xSet);
404}
405
406OUString OGroupManager::GetGroupName( const css::uno::Reference< css::beans::XPropertySet>& xComponent )
407{
408 if (!xComponent.is())
409 return OUString();
410 OUString sGroupName;
411 if (hasProperty( PROPERTY_GROUP_NAME, xComponent )) {
412 xComponent->getPropertyValue( PROPERTY_GROUP_NAME ) >>= sGroupName;
413 if (sGroupName.isEmpty())
414 xComponent->getPropertyValue( PROPERTY_NAME ) >>= sGroupName;
415 }
416 else
417 xComponent->getPropertyValue( PROPERTY_NAME ) >>= sGroupName;
418 return sGroupName;
419}
420} // namespace frm
421
422/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool operator()(const OGroupCompAcc &lhs, const OGroupCompAcc &rhs) const
const OGroupComp & GetGroupComponent() const
OGroupCompAcc(const css::uno::Reference< css::beans::XPropertySet > &rxElement, OGroupComp _aGroupComp)
bool operator==(const OGroupCompAcc &rCompAcc) const
css::uno::Reference< css::beans::XPropertySet > m_xComponent
bool operator()(const OGroupComp &lhs, const OGroupComp &rhs) const
sal_Int16 m_nTabIndex
sal_Int32 GetPos() const
sal_Int32 m_nPos
bool operator==(const OGroupComp &rComp) const
css::uno::Reference< css::beans::XPropertySet > m_xComponent
sal_Int16 GetTabIndex() const
std::unique_ptr< OGroup > m_pCompGroup
virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent &evt) override
OGroupManager(const css::uno::Reference< css::container::XContainer > &_rxContainer)
css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > getControlModels() const
css::uno::Reference< css::container::XContainer > m_xContainer
static OUString GetGroupName(const css::uno::Reference< css::beans::XPropertySet > &xComponent)
void getGroupByName(const OUString &Name, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > &_rGroup)
virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent &_rEvent) override
void getGroup(sal_Int32 nGroup, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > &_rGroup, OUString &Name)
virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent &_rEvent) override
virtual ~OGroupManager() override
void InsertElement(const css::uno::Reference< css::beans::XPropertySet > &rxElement)
void RemoveElement(const css::uno::Reference< css::beans::XPropertySet > &rxElement)
virtual void SAL_CALL elementInserted(const css::container::ContainerEvent &_rEvent) override
virtual void SAL_CALL disposing(const css::lang::EventObject &_rSource) override
void removeFromGroupMap(const OUString &_sGroupName, const css::uno::Reference< css::beans::XPropertySet > &_xSet)
sal_Int32 getGroupCount() const
OActiveGroups m_aActiveGroupMap
void RemoveComponent(const css::uno::Reference< css::beans::XPropertySet > &rxElement)
void InsertComponent(const css::uno::Reference< css::beans::XPropertySet > &rxElement)
std::vector< OGroupCompAcc > m_aCompAccArray
css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > GetControlModels() const
std::vector< OGroupComp > m_aCompArray
sal_uInt16 m_nInsertPos
OGroup(OUString sGroupName)
int nCount
ULONG m_refCount
size_t m_nPos
constexpr OUStringLiteral PROPERTY_TABINDEX
Definition: frm_strings.hxx:26
constexpr OUStringLiteral PROPERTY_NAME
Definition: frm_strings.hxx:28
constexpr OUStringLiteral PROPERTY_GROUP_NAME
Definition: frm_strings.hxx:29
constexpr OUStringLiteral PROPERTY_CLASSID
Definition: frm_strings.hxx:30
bool hasProperty(const OUString &_rName, const Reference< XPropertySet > &_rxSet)
sal_Int16 getINT16(const Any &_rAny)
ListBox is a bit confusing / different from other form components, so here are a few notes:
Definition: BaseListBox.hxx:25
sal_Int32 insert_sorted(::std::vector< ELEMENT > &_rArray, const ELEMENT &_rNewElement, const LESS_COMPARE &_rCompareOp)
bool seek_entry(const ::std::vector< ELEMENT > &_rArray, const ELEMENT &_rNewElement, sal_Int32 &nPos, const LESS_COMPARE &_rCompareOp)
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
Reference< XNameAccess > m_xContainer