xref: /trunk/main/extensions/source/propctrlr/propertycomposer.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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_extensions.hxx"
26 #include "propertycomposer.hxx"
27 
28 /** === begin UNO includes === **/
29 #include <com/sun/star/lang/NullPointerException.hpp>
30 #include <com/sun/star/lang/IllegalArgumentException.hpp>
31 /** === end UNO includes === **/
32 #include <osl/diagnose.h>
33 #include <tools/diagnose_ex.h>
34 
35 #include <functional>
36 #include <algorithm>
37 #include <map>
38 #include <iterator>
39 
40 //........................................................................
41 namespace pcr
42 {
43 //........................................................................
44 
45     using namespace ::com::sun::star::uno;
46     using namespace ::com::sun::star::beans;
47     using namespace ::com::sun::star::lang;
48     using namespace ::com::sun::star::inspection;
49 
50     //====================================================================
51     //= helper
52     //====================================================================
53     namespace
54     {
55         //----------------------------------------------------------------
56         struct SetPropertyValue : public ::std::unary_function< Reference< XPropertyHandler >, void >
57         {
58             ::rtl::OUString sPropertyName;
59             const Any&      rValue;
SetPropertyValuepcr::__anon9034b33a0111::SetPropertyValue60             SetPropertyValue( const ::rtl::OUString& _rPropertyName, const Any& _rValue ) : sPropertyName( _rPropertyName ), rValue( _rValue ) { }
operator ()pcr::__anon9034b33a0111::SetPropertyValue61             void operator()( const Reference< XPropertyHandler >& _rHandler )
62             {
63                 _rHandler->setPropertyValue( sPropertyName, rValue );
64             }
65         };
66 
67         //----------------------------------------------------------------
68         template < class BagType >
putIntoBag(const Sequence<typename BagType::value_type> & _rArray,BagType & _rBag)69         void putIntoBag( const Sequence< typename BagType::value_type >& _rArray, BagType& /* [out] */ _rBag )
70         {
71             ::std::copy( _rArray.getConstArray(), _rArray.getConstArray() + _rArray.getLength(),
72                 ::std::insert_iterator< BagType >( _rBag, _rBag.begin() ) );
73         }
74 
75         //----------------------------------------------------------------
76         template < class BagType >
copyBagToArray(const BagType & _rBag,Sequence<typename BagType::value_type> & _rArray)77         void copyBagToArray( const BagType& /* [out] */ _rBag, Sequence< typename BagType::value_type >& _rArray )
78         {
79             _rArray.realloc( _rBag.size() );
80             ::std::copy( _rBag.begin(), _rBag.end(), _rArray.getArray() );
81         }
82     }
83 
84     //====================================================================
85     //= PropertyComposer
86     //====================================================================
87 
88     // TODO: there are various places where we determine the first handler in our array which
89     // supports a given property id. This is, at the moment, done with searching all handlers,
90     // which is O( n * k ) at worst (n being the number of handlers, k being the maximum number
91     // of supported properties per handler). Shouldn't we cache this? So that it is O( log k )?
92 
93     //--------------------------------------------------------------------
PropertyComposer(const::std::vector<Reference<XPropertyHandler>> & _rSlaveHandlers)94     PropertyComposer::PropertyComposer( const ::std::vector< Reference< XPropertyHandler > >& _rSlaveHandlers )
95         :PropertyComposer_Base          ( m_aMutex          )
96         ,m_aSlaveHandlers               ( _rSlaveHandlers   )
97         ,m_aPropertyListeners           ( m_aMutex          )
98         ,m_bSupportedPropertiesAreKnown ( false             )
99     {
100         if ( m_aSlaveHandlers.empty() )
101             throw IllegalArgumentException();
102 
103         osl_incrementInterlockedCount( &m_refCount );
104         {
105             Reference< XPropertyChangeListener > xMeMyselfAndI( this );
106             for (   HandlerArray::const_iterator loop = m_aSlaveHandlers.begin();
107                     loop != m_aSlaveHandlers.end();
108                     ++loop
109                 )
110             {
111                 if ( !loop->is() )
112                     throw NullPointerException();
113                 (*loop)->addPropertyChangeListener( xMeMyselfAndI );
114             }
115         }
116         osl_decrementInterlockedCount( &m_refCount );
117     }
118 
119     //--------------------------------------------------------------------
inspect(const Reference<XInterface> & _rxIntrospectee)120     void SAL_CALL PropertyComposer::inspect( const Reference< XInterface >& _rxIntrospectee )
121     {
122         MethodGuard aGuard( *this );
123 
124         for ( HandlerArray::const_iterator loop = m_aSlaveHandlers.begin();
125               loop != m_aSlaveHandlers.end();
126               ++loop
127             )
128         {
129             (*loop)->inspect( _rxIntrospectee );
130         }
131     }
132 
133     //--------------------------------------------------------------------
getPropertyValue(const::rtl::OUString & _rPropertyName)134     Any SAL_CALL PropertyComposer::getPropertyValue( const ::rtl::OUString& _rPropertyName )
135     {
136         MethodGuard aGuard( *this );
137         return m_aSlaveHandlers[0]->getPropertyValue( _rPropertyName );
138     }
139 
140     //--------------------------------------------------------------------
setPropertyValue(const::rtl::OUString & _rPropertyName,const Any & _rValue)141     void SAL_CALL PropertyComposer::setPropertyValue( const ::rtl::OUString& _rPropertyName, const Any& _rValue )
142     {
143         MethodGuard aGuard( *this );
144         ::std::for_each( m_aSlaveHandlers.begin(), m_aSlaveHandlers.end(), SetPropertyValue( _rPropertyName, _rValue ) );
145     }
146 
147     //--------------------------------------------------------------------
convertToPropertyValue(const::rtl::OUString & _rPropertyName,const Any & _rControlValue)148     Any SAL_CALL PropertyComposer::convertToPropertyValue( const ::rtl::OUString& _rPropertyName, const Any& _rControlValue )
149     {
150         MethodGuard aGuard( *this );
151         return m_aSlaveHandlers[0]->convertToPropertyValue( _rPropertyName, _rControlValue );
152     }
153 
154     //--------------------------------------------------------------------
convertToControlValue(const::rtl::OUString & _rPropertyName,const Any & _rPropertyValue,const Type & _rControlValueType)155     Any SAL_CALL PropertyComposer::convertToControlValue( const ::rtl::OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType )
156     {
157         MethodGuard aGuard( *this );
158         return m_aSlaveHandlers[0]->convertToControlValue( _rPropertyName, _rPropertyValue, _rControlValueType );
159     }
160 
161     //--------------------------------------------------------------------
getPropertyState(const::rtl::OUString & _rPropertyName)162     PropertyState SAL_CALL PropertyComposer::getPropertyState( const ::rtl::OUString& _rPropertyName )
163     {
164         MethodGuard aGuard( *this );
165 
166         // assume DIRECT for the moment. This will stay this way if *all* slaves
167         // tell the property has DIRECT state, and if *all* values equal
168         PropertyState eState = PropertyState_DIRECT_VALUE;
169 
170         // check the master state
171         Reference< XPropertyHandler > xPrimary( *m_aSlaveHandlers.begin() );
172         Any aPrimaryValue = xPrimary->getPropertyValue( _rPropertyName );
173         eState = xPrimary->getPropertyState( _rPropertyName );
174 
175         // loop through the secondary sets
176         PropertyState eSecondaryState = PropertyState_DIRECT_VALUE;
177         for ( HandlerArray::const_iterator loop = ( m_aSlaveHandlers.begin() + 1 );
178               loop != m_aSlaveHandlers.end();
179               ++loop
180             )
181         {
182             // the secondary state
183             eSecondaryState = (*loop)->getPropertyState( _rPropertyName );
184 
185             // the secondary value
186             Any aSecondaryValue( (*loop)->getPropertyValue( _rPropertyName ) );
187 
188             if  (   ( PropertyState_AMBIGUOUS_VALUE == eSecondaryState )    // secondary is ambiguous
189                 ||  ( aPrimaryValue != aSecondaryValue )                    // unequal values
190                 )
191             {
192                 eState = PropertyState_AMBIGUOUS_VALUE;
193                 break;
194             }
195         }
196 
197         return eState;
198     }
199 
200     //--------------------------------------------------------------------
addPropertyChangeListener(const Reference<XPropertyChangeListener> & _rxListener)201     void SAL_CALL PropertyComposer::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
202     {
203         MethodGuard aGuard( *this );
204         m_aPropertyListeners.addListener( _rxListener );
205     }
206 
207     //--------------------------------------------------------------------
removePropertyChangeListener(const Reference<XPropertyChangeListener> & _rxListener)208     void SAL_CALL PropertyComposer::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
209     {
210         MethodGuard aGuard( *this );
211         m_aPropertyListeners.removeListener( _rxListener );
212     }
213 
214     //--------------------------------------------------------------------
getSupportedProperties()215     Sequence< Property > SAL_CALL PropertyComposer::getSupportedProperties()
216     {
217         MethodGuard aGuard( *this );
218 
219         if ( !m_bSupportedPropertiesAreKnown )
220         {
221             // we support a property if and only if all of our slaves support it
222 
223             // initially, use all the properties of an arbitrary handler (we take the first one)
224             putIntoBag( (*m_aSlaveHandlers.begin())->getSupportedProperties(), m_aSupportedProperties );
225 
226             // now intersect with the properties of *all* other handlers
227             for ( HandlerArray::const_iterator loop = ( m_aSlaveHandlers.begin() + 1 );
228                 loop != m_aSlaveHandlers.end();
229                 ++loop
230                 )
231             {
232                 // the properties supported by the current handler
233                 PropertyBag aThisRound;
234                 putIntoBag( (*loop)->getSupportedProperties(), aThisRound );
235 
236                 // the intersection of those properties with all we already have
237                 PropertyBag aIntersection;
238                 ::std::set_intersection( aThisRound.begin(), aThisRound.end(), m_aSupportedProperties.begin(), m_aSupportedProperties.end(),
239                     ::std::insert_iterator< PropertyBag >( aIntersection, aIntersection.begin() ), PropertyLessByName() );
240 
241                 m_aSupportedProperties.swap( aIntersection );
242                 if ( m_aSupportedProperties.empty() )
243                     break;
244             }
245 
246             // remove those properties which are not composable
247             for (   PropertyBag::iterator check = m_aSupportedProperties.begin();
248                     check != m_aSupportedProperties.end();
249                 )
250             {
251                 sal_Bool bIsComposable = isComposable( check->Name );
252                 if ( !bIsComposable )
253                 {
254                     PropertyBag::iterator next = check; ++next;
255                     m_aSupportedProperties.erase( check );
256                     check = next;
257                 }
258                 else
259                     ++check;
260             }
261 
262             m_bSupportedPropertiesAreKnown = true;
263         }
264 
265         Sequence< Property > aSurvived;
266         copyBagToArray( m_aSupportedProperties, aSurvived );
267         return aSurvived;
268     }
269 
270     //--------------------------------------------------------------------
uniteStringArrays(const PropertyComposer::HandlerArray & _rHandlers,Sequence<::rtl::OUString> (SAL_CALL XPropertyHandler::* pGetter)(void),Sequence<::rtl::OUString> & _rUnion)271     void uniteStringArrays( const PropertyComposer::HandlerArray& _rHandlers, Sequence< ::rtl::OUString > (SAL_CALL XPropertyHandler::*pGetter)( void ),
272         Sequence< ::rtl::OUString >& /* [out] */ _rUnion )
273     {
274         ::std::set< ::rtl::OUString > aUnitedBag;
275 
276         Sequence< ::rtl::OUString > aThisRound;
277         for ( PropertyComposer::HandlerArray::const_iterator loop = _rHandlers.begin();
278               loop != _rHandlers.end();
279               ++loop
280             )
281         {
282             aThisRound = (loop->get()->*pGetter)();
283             putIntoBag( aThisRound, aUnitedBag );
284         }
285 
286         copyBagToArray( aUnitedBag, _rUnion );
287     }
288 
289     //--------------------------------------------------------------------
getSupersededProperties()290     Sequence< ::rtl::OUString > SAL_CALL PropertyComposer::getSupersededProperties( )
291     {
292         MethodGuard aGuard( *this );
293 
294         // we supersede those properties which are superseded by at least one of our slaves
295         Sequence< ::rtl::OUString > aSuperseded;
296         uniteStringArrays( m_aSlaveHandlers, &XPropertyHandler::getSupersededProperties, aSuperseded );
297         return aSuperseded;
298     }
299 
300     //--------------------------------------------------------------------
getActuatingProperties()301     Sequence< ::rtl::OUString > SAL_CALL PropertyComposer::getActuatingProperties( )
302     {
303         MethodGuard aGuard( *this );
304 
305         // we're interested in those properties which at least one handler wants to have
306         Sequence< ::rtl::OUString > aActuating;
307         uniteStringArrays( m_aSlaveHandlers, &XPropertyHandler::getActuatingProperties, aActuating );
308         return aActuating;
309     }
310 
311     //--------------------------------------------------------------------
describePropertyLine(const::rtl::OUString & _rPropertyName,const Reference<XPropertyControlFactory> & _rxControlFactory)312     LineDescriptor SAL_CALL PropertyComposer::describePropertyLine( const ::rtl::OUString& _rPropertyName,
313         const Reference< XPropertyControlFactory >& _rxControlFactory )
314     {
315         MethodGuard aGuard( *this );
316         return m_aSlaveHandlers[0]->describePropertyLine( _rPropertyName, _rxControlFactory );
317     }
318 
319     //--------------------------------------------------------------------
isComposable(const::rtl::OUString & _rPropertyName)320     ::sal_Bool SAL_CALL PropertyComposer::isComposable( const ::rtl::OUString& _rPropertyName )
321     {
322         MethodGuard aGuard( *this );
323         return m_aSlaveHandlers[0]->isComposable( _rPropertyName );
324     }
325 
326     //--------------------------------------------------------------------
onInteractivePropertySelection(const::rtl::OUString & _rPropertyName,sal_Bool _bPrimary,Any & _rData,const Reference<XObjectInspectorUI> & _rxInspectorUI)327     InteractiveSelectionResult SAL_CALL PropertyComposer::onInteractivePropertySelection( const ::rtl::OUString& _rPropertyName, sal_Bool _bPrimary, Any& _rData, const Reference< XObjectInspectorUI >& _rxInspectorUI )
328     {
329         if ( !_rxInspectorUI.is() )
330             throw NullPointerException();
331 
332         MethodGuard aGuard( *this );
333 
334         impl_ensureUIRequestComposer( _rxInspectorUI );
335         ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
336 
337         // ask the first of the handlers
338         InteractiveSelectionResult eResult = (*m_aSlaveHandlers.begin())->onInteractivePropertySelection(
339                 _rPropertyName,
340                 _bPrimary,
341                 _rData,
342                 m_pUIRequestComposer->getUIForPropertyHandler( *m_aSlaveHandlers.begin() )
343             );
344 
345         switch ( eResult )
346         {
347         case InteractiveSelectionResult_Cancelled:
348             // fine
349             break;
350 
351         case InteractiveSelectionResult_Success:
352         case InteractiveSelectionResult_Pending:
353             OSL_ENSURE( false, "PropertyComposer::onInteractivePropertySelection: no chance to forward the new value to the other handlers!" );
354             // This means that we cannot know the new property value, which either has already been set
355             // at the first component ("Success"), or will be set later on once the asynchronous input
356             // is finished ("Pending"). So, we also cannot forward this new property value to the other
357             // handlers.
358             // We would need to be a listener at the property at the first component, but even this wouldn't
359             // be sufficient, since the property handler is free to change *any* property during a dedicated
360             // property UI.
361             eResult = InteractiveSelectionResult_Cancelled;
362             break;
363 
364         case InteractiveSelectionResult_ObtainedValue:
365             // OK. Our own caller will pass this as setPropertyValue, and we will then pass it to
366             // all slave handlers
367             break;
368 
369         default:
370             OSL_ENSURE( false, "OPropertyBrowserController::onInteractivePropertySelection: unknown result value!" );
371             break;
372         }
373 
374         return eResult;
375     }
376 
377     //--------------------------------------------------------------------
impl_ensureUIRequestComposer(const Reference<XObjectInspectorUI> & _rxInspectorUI)378     void PropertyComposer::impl_ensureUIRequestComposer( const Reference< XObjectInspectorUI >& _rxInspectorUI )
379     {
380         OSL_ENSURE( !m_pUIRequestComposer.get() || m_pUIRequestComposer->getDelegatorUI().get() == _rxInspectorUI.get(),
381             "PropertyComposer::impl_ensureUIRequestComposer: somebody's changing the horse in the mid of the race!" );
382 
383         if ( !m_pUIRequestComposer.get() )
384             m_pUIRequestComposer.reset( new ComposedPropertyUIUpdate( _rxInspectorUI, this ) );
385     }
386 
387     //--------------------------------------------------------------------
actuatingPropertyChanged(const::rtl::OUString & _rActuatingPropertyName,const Any & _rNewValue,const Any & _rOldValue,const Reference<XObjectInspectorUI> & _rxInspectorUI,sal_Bool _bFirstTimeInit)388     void SAL_CALL PropertyComposer::actuatingPropertyChanged( const ::rtl::OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& _rOldValue, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit )
389     {
390         if ( !_rxInspectorUI.is() )
391             throw NullPointerException();
392 
393         MethodGuard aGuard( *this );
394 
395         impl_ensureUIRequestComposer( _rxInspectorUI );
396         ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
397 
398         // ask all handlers which expressed interest in this particular property, and "compose" their
399         // commands for the UIUpdater
400         for (   HandlerArray::const_iterator loop = m_aSlaveHandlers.begin();
401                 loop != m_aSlaveHandlers.end();
402                 ++loop
403             )
404         {
405             // TODO: make this cheaper (cache it?)
406             const StlSyntaxSequence< ::rtl::OUString > aThisHandlersActuatingProps = (*loop)->getActuatingProperties();
407             for (   StlSyntaxSequence< ::rtl::OUString >::const_iterator loopProps = aThisHandlersActuatingProps.begin();
408                     loopProps != aThisHandlersActuatingProps.end();
409                     ++loopProps
410                 )
411             {
412                 if ( *loopProps == _rActuatingPropertyName )
413                 {
414                     (*loop)->actuatingPropertyChanged( _rActuatingPropertyName, _rNewValue, _rOldValue,
415                         m_pUIRequestComposer->getUIForPropertyHandler( *loop ),
416                         _bFirstTimeInit );
417                     break;
418                 }
419             }
420         }
421     }
422 
423     //--------------------------------------------------------------------
IMPLEMENT_FORWARD_XCOMPONENT(PropertyComposer,PropertyComposer_Base)424     IMPLEMENT_FORWARD_XCOMPONENT( PropertyComposer, PropertyComposer_Base )
425 
426     //--------------------------------------------------------------------
427     void SAL_CALL PropertyComposer::disposing()
428     {
429         MethodGuard aGuard( *this );
430 
431         // dispose our slave handlers
432         for ( PropertyComposer::HandlerArray::const_iterator loop = m_aSlaveHandlers.begin();
433               loop != m_aSlaveHandlers.end();
434               ++loop
435             )
436         {
437             (*loop)->removePropertyChangeListener( this );
438             (*loop)->dispose();
439         }
440 
441         clearContainer( m_aSlaveHandlers );
442 
443         if ( m_pUIRequestComposer.get() )
444             m_pUIRequestComposer->dispose();
445         m_pUIRequestComposer.reset();
446     }
447 
448     //--------------------------------------------------------------------
propertyChange(const PropertyChangeEvent & evt)449     void SAL_CALL PropertyComposer::propertyChange( const PropertyChangeEvent& evt )
450     {
451         if ( !impl_isSupportedProperty_nothrow( evt.PropertyName ) )
452             // A slave handler might fire events for more properties than we support. Ignore those.
453             return;
454 
455         PropertyChangeEvent aTranslatedEvent( evt );
456         try
457         {
458             aTranslatedEvent.NewValue = getPropertyValue( evt.PropertyName );
459         }
460         catch( const Exception& )
461         {
462             DBG_UNHANDLED_EXCEPTION();
463         }
464         m_aPropertyListeners.notify( aTranslatedEvent, &XPropertyChangeListener::propertyChange );
465     }
466 
467     //--------------------------------------------------------------------
disposing(const EventObject & Source)468     void SAL_CALL PropertyComposer::disposing( const EventObject& Source )
469     {
470         MethodGuard aGuard( *this );
471         m_aPropertyListeners.disposing( Source );
472     }
473 
474     //--------------------------------------------------------------------
suspend(sal_Bool _bSuspend)475     sal_Bool SAL_CALL PropertyComposer::suspend( sal_Bool _bSuspend )
476     {
477         MethodGuard aGuard( *this );
478         for ( PropertyComposer::HandlerArray::const_iterator loop = m_aSlaveHandlers.begin();
479               loop != m_aSlaveHandlers.end();
480               ++loop
481             )
482         {
483             if ( !(*loop)->suspend( _bSuspend ) )
484             {
485                 if ( _bSuspend && ( loop != m_aSlaveHandlers.begin() ) )
486                 {
487                     // if we tried to suspend, but one of the slave handlers vetoed,
488                     // re-activate the handlers which actually did *not* veto
489                     // the suspension
490                     do
491                     {
492                         --loop;
493                         (*loop)->suspend( sal_False );
494                     }
495                     while ( loop != m_aSlaveHandlers.begin() );
496                 }
497                 return false;
498             }
499         }
500         return true;
501     }
502 
503     //--------------------------------------------------------------------
hasPropertyByName(const::rtl::OUString & _rName)504     sal_Bool SAL_CALL PropertyComposer::hasPropertyByName( const ::rtl::OUString& _rName )
505     {
506         return impl_isSupportedProperty_nothrow( _rName );
507     }
508 
509 //........................................................................
510 } // namespace pcr
511 //........................................................................
512