1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_forms.hxx"
26 #include "GroupManager.hxx"
27 #include <com/sun/star/beans/XFastPropertySet.hpp>
28 #include <com/sun/star/form/FormComponentType.hpp>
29 #include <comphelper/property.hxx>
30 #include <comphelper/uno3.hxx>
31 #include <tools/solar.h>
32 #include <tools/debug.hxx>
33 
34 #include "property.hrc"
35 
36 #include <algorithm>
37 
38 //.........................................................................
39 namespace frm
40 {
41 //.........................................................................
42 
43 using namespace ::com::sun::star::uno;
44 using namespace ::com::sun::star::sdbc;
45 using namespace ::com::sun::star::beans;
46 using namespace ::com::sun::star::container;
47 using namespace ::com::sun::star::form;
48 using namespace ::com::sun::star::awt;
49 using namespace ::com::sun::star::lang;
50 using namespace ::com::sun::star::form;
51 
52 namespace
53 {
isRadioButton(const Reference<XPropertySet> & _rxComponent)54     bool isRadioButton( const Reference< XPropertySet >& _rxComponent )
55     {
56         bool bIs = false;
57 	    if ( hasProperty( PROPERTY_CLASSID, _rxComponent ) )
58         {
59             sal_Int16 nClassId = FormComponentType::CONTROL;
60             _rxComponent->getPropertyValue( PROPERTY_CLASSID ) >>= nClassId;
61             if ( nClassId == FormComponentType::RADIOBUTTON )
62                 bIs = true;
63         }
64         return bIs;
65     }
66 }
67 
68 //========================================================================
69 // class OGroupCompAcc
70 //========================================================================
71 //------------------------------------------------------------------
OGroupCompAcc(const Reference<XPropertySet> & rxElement,const OGroupComp & _rGroupComp)72 OGroupCompAcc::OGroupCompAcc(const Reference<XPropertySet>& rxElement, const OGroupComp& _rGroupComp )
73 			   :m_xComponent( rxElement )
74 			   ,m_aGroupComp( _rGroupComp )
75 {
76 }
77 
78 //------------------------------------------------------------------
operator ==(const OGroupCompAcc & rCompAcc) const79 sal_Bool OGroupCompAcc::operator==( const OGroupCompAcc& rCompAcc ) const
80 {
81 	return (m_xComponent == rCompAcc.GetComponent());
82 }
83 
84 //------------------------------------------------------------------
85 class OGroupCompAccLess : public ::std::binary_function<OGroupCompAcc, OGroupCompAcc, sal_Bool>
86 {
87 public:
operator ()(const OGroupCompAcc & lhs,const OGroupCompAcc & rhs) const88 	sal_Bool operator() (const OGroupCompAcc& lhs, const OGroupCompAcc& rhs) const
89 	{
90 		return
91 			reinterpret_cast<sal_Int64>(lhs.m_xComponent.get())
92 		<	reinterpret_cast<sal_Int64>(rhs.m_xComponent.get());
93 	}
94 };
95 
96 //========================================================================
97 // class OGroupComp
98 //========================================================================
99 
100 //------------------------------------------------------------------
OGroupComp()101 OGroupComp::OGroupComp()
102     :m_nPos( -1 )
103     ,m_nTabIndex( 0 )
104 {
105 }
106 
107 //------------------------------------------------------------------
OGroupComp(const OGroupComp & _rSource)108 OGroupComp::OGroupComp(const OGroupComp& _rSource)
109     :m_aName( _rSource.m_aName )
110     ,m_xComponent( _rSource.m_xComponent )
111     ,m_xControlModel(_rSource.m_xControlModel)
112     ,m_nPos( _rSource.m_nPos )
113     ,m_nTabIndex( _rSource.m_nTabIndex )
114 {
115 }
116 
117 //------------------------------------------------------------------
OGroupComp(const Reference<XPropertySet> & rxSet,sal_Int32 nInsertPos)118 OGroupComp::OGroupComp(const Reference<XPropertySet>& rxSet, sal_Int32 nInsertPos )
119     :m_xComponent( rxSet )
120     ,m_xControlModel(rxSet,UNO_QUERY)
121     ,m_nPos( nInsertPos )
122     ,m_nTabIndex(0)
123 {
124 	if (m_xComponent.is())
125 	{
126 		if (hasProperty( PROPERTY_TABINDEX, m_xComponent ) )
127 			// Indices kleiner 0 werden wie 0 behandelt
128 			m_nTabIndex = Max(getINT16(m_xComponent->getPropertyValue( PROPERTY_TABINDEX )) , sal_Int16(0));
129 
130 		m_xComponent->getPropertyValue( PROPERTY_NAME ) >>= m_aName;
131 	}
132 }
133 
134 //------------------------------------------------------------------
operator ==(const OGroupComp & rComp) const135 sal_Bool OGroupComp::operator==( const OGroupComp& rComp ) const
136 {
137 	return m_nTabIndex == rComp.GetTabIndex() && m_nPos == rComp.GetPos();
138 }
139 
140 //------------------------------------------------------------------
141 class OGroupCompLess : public ::std::binary_function<OGroupComp, OGroupComp, sal_Bool>
142 {
143 public:
operator ()(const OGroupComp & lhs,const OGroupComp & rhs) const144 	sal_Bool operator() (const OGroupComp& lhs, const OGroupComp& rhs) const
145 	{
146 		sal_Bool bResult;
147 		// TabIndex von 0 wird hinten einsortiert
148 		if (lhs.m_nTabIndex == rhs.GetTabIndex())
149 			bResult = lhs.m_nPos < rhs.GetPos();
150 		else if (lhs.m_nTabIndex && rhs.GetTabIndex())
151 			bResult = lhs.m_nTabIndex < rhs.GetTabIndex();
152 		else
153 			bResult = lhs.m_nTabIndex != 0;
154 		return bResult;
155 	}
156 };
157 
158 //========================================================================
159 // class OGroup
160 //========================================================================
161 
DBG_NAME(OGroup)162 DBG_NAME(OGroup)
163 //------------------------------------------------------------------
164 OGroup::OGroup( const ::rtl::OUString& rGroupName )
165 		:m_aGroupName( rGroupName )
166 		,m_nInsertPos(0)
167 {
168 	DBG_CTOR(OGroup,NULL);
169 }
170 
171 #ifdef DBG_UTIL
172 //------------------------------------------------------------------
OGroup(const OGroup & _rSource)173 OGroup::OGroup( const OGroup& _rSource )
174 :m_aCompArray(_rSource.m_aCompArray)
175 	,m_aCompAccArray(_rSource.m_aCompAccArray)
176 	,m_aGroupName(_rSource.m_aGroupName)
177 	,m_nInsertPos(_rSource.m_nInsertPos)
178 {
179 	DBG_CTOR(OGroup,NULL);
180 }
181 #endif
182 
183 //------------------------------------------------------------------
~OGroup()184 OGroup::~OGroup()
185 {
186 	DBG_DTOR(OGroup,NULL);
187 }
188 
189 //------------------------------------------------------------------
InsertComponent(const Reference<XPropertySet> & xSet)190 void OGroup::InsertComponent( const Reference<XPropertySet>& xSet )
191 {
192 	OGroupComp aNewGroupComp( xSet, m_nInsertPos );
193 	sal_Int32 nPosInserted = insert_sorted(m_aCompArray, aNewGroupComp, OGroupCompLess());
194 
195 	OGroupCompAcc aNewGroupCompAcc( xSet, m_aCompArray[nPosInserted] );
196 	insert_sorted(m_aCompAccArray, aNewGroupCompAcc, OGroupCompAccLess());
197 	m_nInsertPos++;
198 }
199 
200 //------------------------------------------------------------------
RemoveComponent(const Reference<XPropertySet> & rxElement)201 void OGroup::RemoveComponent( const Reference<XPropertySet>& rxElement )
202 {
203 	sal_Int32 nGroupCompAccPos;
204 	OGroupCompAcc aSearchCompAcc( rxElement, OGroupComp() );
205 	if ( seek_entry(m_aCompAccArray, aSearchCompAcc, nGroupCompAccPos, OGroupCompAccLess()) )
206 	{
207 		OGroupCompAcc& aGroupCompAcc = m_aCompAccArray[nGroupCompAccPos];
208 		const OGroupComp& aGroupComp = aGroupCompAcc.GetGroupComponent();
209 
210 		sal_Int32 nGroupCompPos;
211 		if ( seek_entry(m_aCompArray, aGroupComp, nGroupCompPos, OGroupCompLess()) )
212 		{
213 			m_aCompAccArray.erase( m_aCompAccArray.begin() + nGroupCompAccPos );
214 			m_aCompArray.erase( m_aCompArray.begin() + nGroupCompPos );
215 
216 			/*============================================================
217 			Durch das Entfernen der GroupComp ist die Einfuegeposition
218 			ungueltig geworden. Sie braucht hier aber nicht angepasst werden,
219 			da sie fortlaufend vergeben wird und damit immer
220 			aufsteigend eindeutig ist.
221 			============================================================*/
222 		}
223 		else
224 		{
225 			DBG_ERROR( "OGroup::RemoveComponent: Component nicht in Gruppe" );
226 		}
227 	}
228 	else
229 	{
230 		DBG_ERROR( "OGroup::RemoveComponent: Component nicht in Gruppe" );
231 	}
232 }
233 
234 //------------------------------------------------------------------
operator ==(const OGroup & rGroup) const235 sal_Bool OGroup::operator==( const OGroup& rGroup ) const
236 {
237 	return m_aGroupName.equals(rGroup.GetGroupName());
238 }
239 
240 //------------------------------------------------------------------
241 class OGroupLess : public ::std::binary_function<OGroup, OGroup, sal_Bool>
242 {
243 public:
operator ()(const OGroup & lhs,const OGroup & rhs) const244 	sal_Bool operator() (const OGroup& lhs, const OGroup& rhs) const
245 	{
246 		return lhs.m_aGroupName < rhs.m_aGroupName;
247 	}
248 };
249 
250 //------------------------------------------------------------------
GetControlModels() const251 Sequence< Reference<XControlModel>  > OGroup::GetControlModels() const
252 {
253 	sal_Int32 nLen = m_aCompArray.size();
254 	Sequence<Reference<XControlModel> > aControlModelSeq( nLen );
255 	Reference<XControlModel>* pModels = aControlModelSeq.getArray();
256 
257 	ConstOGroupCompArrIterator aGroupComps = m_aCompArray.begin();
258 	for (sal_Int32 i = 0; i < nLen; ++i, ++pModels, ++aGroupComps)
259 	{
260 		*pModels = aGroupComps->GetControlModel();
261 	}
262 	return aControlModelSeq;
263 }
264 
265 DBG_NAME(OGroupManager);
266 //------------------------------------------------------------------
OGroupManager(const Reference<XContainer> & _rxContainer)267 OGroupManager::OGroupManager(const Reference< XContainer >& _rxContainer)
268 	:m_pCompGroup( new OGroup( ::rtl::OUString::createFromAscii( "AllComponentGroup" ) ) )
269 	,m_xContainer(_rxContainer)
270 {
271 	DBG_CTOR(OGroupManager,NULL);
272 
273 	increment(m_refCount);
274 	{
275 		_rxContainer->addContainerListener(this);
276 	}
277 	decrement(m_refCount);
278 }
279 
280 //------------------------------------------------------------------
~OGroupManager()281 OGroupManager::~OGroupManager()
282 {
283 	DBG_DTOR(OGroupManager,NULL);
284 	// Alle Components und CompGroup loeschen
285 	delete m_pCompGroup;
286 }
287 
288 // XPropertyChangeListener
289 //------------------------------------------------------------------
disposing(const EventObject & evt)290 void OGroupManager::disposing(const EventObject& evt) throw( RuntimeException )
291 {
292 	Reference<XContainer>  xContainer(evt.Source, UNO_QUERY);
293 	if (xContainer.get() == m_xContainer.get())
294 	{
295 		DELETEZ(m_pCompGroup);
296 
297 		////////////////////////////////////////////////////////////////
298 		// Gruppen loeschen
299 		m_aGroupArr.clear();
300 		m_xContainer.clear();
301 	}
302 }
303 // -----------------------------------------------------------------------------
removeFromGroupMap(const::rtl::OUString & _sGroupName,const Reference<XPropertySet> & _xSet)304 void OGroupManager::removeFromGroupMap(const ::rtl::OUString& _sGroupName,const Reference<XPropertySet>& _xSet)
305 {
306 	// Component aus CompGroup entfernen
307 	m_pCompGroup->RemoveComponent( _xSet );
308 
309 	OGroupArr::iterator aFind = m_aGroupArr.find(_sGroupName);
310 
311 	if ( aFind != m_aGroupArr.end() )
312 	{
313 		// Gruppe vorhanden
314 		aFind->second.RemoveComponent( _xSet );
315 
316 		// Wenn Anzahl der Gruppenelemente == 1 ist, Gruppe deaktivieren
317 		if ( aFind->second.Count() == 1 )
318 		{
319 			OActiveGroups::iterator aActiveFind = ::std::find(
320                 m_aActiveGroupMap.begin(),
321                 m_aActiveGroupMap.end(),
322                 aFind
323             );
324 			if ( aActiveFind != m_aActiveGroupMap.end() )
325             {
326                 // the group is active. Deactivate it if the remaining component
327                 // is *no* radio button
328                 if ( !isRadioButton( aFind->second.GetObject( 0 ) ) )
329 				    m_aActiveGroupMap.erase( aActiveFind );
330             }
331 		}
332 	}
333 
334 
335 	// Bei Component als PropertyChangeListener abmelden
336 	_xSet->removePropertyChangeListener( PROPERTY_NAME, this );
337 	if (hasProperty(PROPERTY_TABINDEX, _xSet))
338 		_xSet->removePropertyChangeListener( PROPERTY_TABINDEX, this );
339 }
340 //------------------------------------------------------------------
propertyChange(const PropertyChangeEvent & evt)341 void SAL_CALL OGroupManager::propertyChange(const PropertyChangeEvent& evt) throw ( ::com::sun::star::uno::RuntimeException)
342 {
343 	Reference<XPropertySet>  xSet(evt.Source, UNO_QUERY);
344 
345 	// Component aus Gruppe entfernen
346 	::rtl::OUString		sGroupName;
347 	if (evt.PropertyName == PROPERTY_NAME)
348 		evt.OldValue >>= sGroupName;
349 	else
350 		xSet->getPropertyValue( PROPERTY_NAME ) >>= sGroupName;
351 
352 	removeFromGroupMap(sGroupName,xSet);
353 
354 	// Component neu einordnen
355 	InsertElement( xSet );
356 }
357 
358 // XContainerListener
359 //------------------------------------------------------------------
elementInserted(const ContainerEvent & Event)360 void SAL_CALL OGroupManager::elementInserted(const ContainerEvent& Event) throw ( ::com::sun::star::uno::RuntimeException)
361 {
362 	Reference< XPropertySet > xProps;
363 	Event.Element >>= xProps;
364 	if ( xProps.is() )
365 		InsertElement( xProps );
366 }
367 
368 //------------------------------------------------------------------
elementRemoved(const ContainerEvent & Event)369 void SAL_CALL OGroupManager::elementRemoved(const ContainerEvent& Event) throw ( ::com::sun::star::uno::RuntimeException)
370 {
371 	Reference<XPropertySet> xProps;
372 	Event.Element >>= xProps;
373 	if ( xProps.is() )
374 		RemoveElement( xProps );
375 }
376 
377 //------------------------------------------------------------------
elementReplaced(const ContainerEvent & Event)378 void SAL_CALL OGroupManager::elementReplaced(const ContainerEvent& Event) throw ( ::com::sun::star::uno::RuntimeException)
379 {
380 	Reference<XPropertySet> xProps;
381 	Event.ReplacedElement >>= xProps;
382 	if ( xProps.is() )
383 		RemoveElement( xProps );
384 
385 	xProps.clear();
386 	Event.Element >>= xProps;
387 	if ( xProps.is() )
388 		InsertElement( xProps );
389 }
390 
391 // Other functions
392 //------------------------------------------------------------------
getControlModels()393 Sequence<Reference<XControlModel> > OGroupManager::getControlModels()
394 {
395 	return m_pCompGroup->GetControlModels();
396 }
397 
398 //------------------------------------------------------------------
getGroupCount()399 sal_Int32 OGroupManager::getGroupCount()
400 {
401 	return m_aActiveGroupMap.size();
402 }
403 
404 //------------------------------------------------------------------
getGroup(sal_Int32 nGroup,Sequence<Reference<XControlModel>> & _rGroup,::rtl::OUString & _rName)405 void OGroupManager::getGroup(sal_Int32 nGroup, Sequence< Reference<XControlModel> >& _rGroup, ::rtl::OUString& _rName)
406 {
407 	OSL_ENSURE(nGroup >= 0 && (size_t)nGroup < m_aActiveGroupMap.size(),"OGroupManager::getGroup: Invalid group index!");
408 	OGroupArr::iterator aGroupPos	= m_aActiveGroupMap[nGroup];
409 	_rName							= aGroupPos->second.GetGroupName();
410 	_rGroup							= aGroupPos->second.GetControlModels();
411 }
412 
413 //------------------------------------------------------------------
getGroupByName(const::rtl::OUString & _rName,Sequence<Reference<XControlModel>> & _rGroup)414 void OGroupManager::getGroupByName(const ::rtl::OUString& _rName, Sequence< Reference<XControlModel>  >& _rGroup)
415 {
416 	OGroupArr::iterator aFind = m_aGroupArr.find(_rName);
417 	if ( aFind != m_aGroupArr.end() )
418 		_rGroup = aFind->second.GetControlModels();
419 }
420 
421 //------------------------------------------------------------------
InsertElement(const Reference<XPropertySet> & xSet)422 void OGroupManager::InsertElement( const Reference<XPropertySet>& xSet )
423 {
424 	// Nur ControlModels
425 	Reference<XControlModel>  xControl(xSet, UNO_QUERY);
426 	if (!xControl.is() )
427 		return;
428 
429 	// Component in CompGroup aufnehmen
430 	m_pCompGroup->InsertComponent( xSet );
431 
432 	// Component in Gruppe aufnehmen
433 	::rtl::OUString sGroupName;
434 	xSet->getPropertyValue( PROPERTY_NAME ) >>= sGroupName;
435 
436 	OGroupArr::iterator aFind = m_aGroupArr.find(sGroupName);
437 
438 	if ( aFind == m_aGroupArr.end() )
439 	{
440 		aFind = m_aGroupArr.insert(OGroupArr::value_type(sGroupName,OGroup(sGroupName))).first;
441 	}
442 
443 	aFind->second.InsertComponent( xSet );
444 
445 	// if we have at least 2 elements in the group, then this is an "active group"
446     bool bActivateGroup = aFind->second.Count() == 2;
447 
448     // Additionally, if the component is a radio button, then it's group becomes active,
449     // too. With this, we ensure that in a container with n radio buttons which all are
450     // in different groups the selection still works reliably (means that all radios can be
451     // clicked independently)
452     if ( aFind->second.Count() == 1 )
453     {
454         if ( isRadioButton( xSet ) )
455             bActivateGroup = true;
456     }
457 
458 	if ( bActivateGroup )
459 	{
460 		OActiveGroups::iterator aAlreadyExistent = ::std::find(
461             m_aActiveGroupMap.begin(),
462             m_aActiveGroupMap.end(),
463             aFind
464         );
465         if ( aAlreadyExistent == m_aActiveGroupMap.end() )
466     		m_aActiveGroupMap.push_back(  aFind );
467 	}
468 
469 
470 	// Bei Component als PropertyChangeListener anmelden
471 	xSet->addPropertyChangeListener( PROPERTY_NAME, this );
472 
473     // Tabindex muss nicht jeder unterstuetzen
474 	if (hasProperty(PROPERTY_TABINDEX, xSet))
475 		xSet->addPropertyChangeListener( PROPERTY_TABINDEX, this );
476 
477 }
478 
479 //------------------------------------------------------------------
RemoveElement(const Reference<XPropertySet> & xSet)480 void OGroupManager::RemoveElement( const Reference<XPropertySet>& xSet )
481 {
482 	// Nur ControlModels
483 	Reference<XControlModel>  xControl(xSet, UNO_QUERY);
484 	if (!xControl.is() )
485 		return;
486 
487 	// Component aus Gruppe entfernen
488 	::rtl::OUString		sGroupName;
489 	xSet->getPropertyValue( PROPERTY_NAME ) >>= sGroupName;
490 
491 	removeFromGroupMap(sGroupName,xSet);
492 }
493 
494 //.........................................................................
495 }	// namespace frm
496 //.........................................................................
497 
498