xref: /trunk/main/forms/source/component/GroupManager.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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