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