| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_extensions.hxx" |
| #include "propertycomposer.hxx" |
| |
| /** === begin UNO includes === **/ |
| #include <com/sun/star/lang/NullPointerException.hpp> |
| #include <com/sun/star/lang/IllegalArgumentException.hpp> |
| /** === end UNO includes === **/ |
| #include <osl/diagnose.h> |
| #include <tools/diagnose_ex.h> |
| |
| #include <functional> |
| #include <algorithm> |
| #include <map> |
| |
| //........................................................................ |
| namespace pcr |
| { |
| //........................................................................ |
| |
| using namespace ::com::sun::star::uno; |
| using namespace ::com::sun::star::beans; |
| using namespace ::com::sun::star::lang; |
| using namespace ::com::sun::star::inspection; |
| |
| //==================================================================== |
| //= helper |
| //==================================================================== |
| namespace |
| { |
| //---------------------------------------------------------------- |
| struct SetPropertyValue : public ::std::unary_function< Reference< XPropertyHandler >, void > |
| { |
| ::rtl::OUString sPropertyName; |
| const Any& rValue; |
| SetPropertyValue( const ::rtl::OUString& _rPropertyName, const Any& _rValue ) : sPropertyName( _rPropertyName ), rValue( _rValue ) { } |
| void operator()( const Reference< XPropertyHandler >& _rHandler ) |
| { |
| _rHandler->setPropertyValue( sPropertyName, rValue ); |
| } |
| }; |
| |
| //---------------------------------------------------------------- |
| template < class BagType > |
| void putIntoBag( const Sequence< typename BagType::value_type >& _rArray, BagType& /* [out] */ _rBag ) |
| { |
| ::std::copy( _rArray.getConstArray(), _rArray.getConstArray() + _rArray.getLength(), |
| ::std::insert_iterator< BagType >( _rBag, _rBag.begin() ) ); |
| } |
| |
| //---------------------------------------------------------------- |
| template < class BagType > |
| void copyBagToArray( const BagType& /* [out] */ _rBag, Sequence< typename BagType::value_type >& _rArray ) |
| { |
| _rArray.realloc( _rBag.size() ); |
| ::std::copy( _rBag.begin(), _rBag.end(), _rArray.getArray() ); |
| } |
| } |
| |
| //==================================================================== |
| //= PropertyComposer |
| //==================================================================== |
| |
| // TODO: there are various places where we determine the first handler in our array which |
| // supports a given property id. This is, at the moment, done with searching all handlers, |
| // which is O( n * k ) at worst (n being the number of handlers, k being the maximum number |
| // of supported properties per handler). Shouldn't we cache this? So that it is O( log k )? |
| |
| //-------------------------------------------------------------------- |
| PropertyComposer::PropertyComposer( const ::std::vector< Reference< XPropertyHandler > >& _rSlaveHandlers ) |
| :PropertyComposer_Base ( m_aMutex ) |
| ,m_aSlaveHandlers ( _rSlaveHandlers ) |
| ,m_aPropertyListeners ( m_aMutex ) |
| ,m_bSupportedPropertiesAreKnown ( false ) |
| { |
| if ( m_aSlaveHandlers.empty() ) |
| throw IllegalArgumentException(); |
| |
| osl_incrementInterlockedCount( &m_refCount ); |
| { |
| Reference< XPropertyChangeListener > xMeMyselfAndI( this ); |
| for ( HandlerArray::const_iterator loop = m_aSlaveHandlers.begin(); |
| loop != m_aSlaveHandlers.end(); |
| ++loop |
| ) |
| { |
| if ( !loop->is() ) |
| throw NullPointerException(); |
| (*loop)->addPropertyChangeListener( xMeMyselfAndI ); |
| } |
| } |
| osl_decrementInterlockedCount( &m_refCount ); |
| } |
| |
| //-------------------------------------------------------------------- |
| void SAL_CALL PropertyComposer::inspect( const Reference< XInterface >& _rxIntrospectee ) throw (RuntimeException, NullPointerException) |
| { |
| MethodGuard aGuard( *this ); |
| |
| for ( HandlerArray::const_iterator loop = m_aSlaveHandlers.begin(); |
| loop != m_aSlaveHandlers.end(); |
| ++loop |
| ) |
| { |
| (*loop)->inspect( _rxIntrospectee ); |
| } |
| } |
| |
| //-------------------------------------------------------------------- |
| Any SAL_CALL PropertyComposer::getPropertyValue( const ::rtl::OUString& _rPropertyName ) throw (UnknownPropertyException, RuntimeException) |
| { |
| MethodGuard aGuard( *this ); |
| return m_aSlaveHandlers[0]->getPropertyValue( _rPropertyName ); |
| } |
| |
| //-------------------------------------------------------------------- |
| void SAL_CALL PropertyComposer::setPropertyValue( const ::rtl::OUString& _rPropertyName, const Any& _rValue ) throw (UnknownPropertyException, RuntimeException) |
| { |
| MethodGuard aGuard( *this ); |
| ::std::for_each( m_aSlaveHandlers.begin(), m_aSlaveHandlers.end(), SetPropertyValue( _rPropertyName, _rValue ) ); |
| } |
| |
| //-------------------------------------------------------------------- |
| Any SAL_CALL PropertyComposer::convertToPropertyValue( const ::rtl::OUString& _rPropertyName, const Any& _rControlValue ) throw (UnknownPropertyException, RuntimeException) |
| { |
| MethodGuard aGuard( *this ); |
| return m_aSlaveHandlers[0]->convertToPropertyValue( _rPropertyName, _rControlValue ); |
| } |
| |
| //-------------------------------------------------------------------- |
| Any SAL_CALL PropertyComposer::convertToControlValue( const ::rtl::OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType ) throw (UnknownPropertyException, RuntimeException) |
| { |
| MethodGuard aGuard( *this ); |
| return m_aSlaveHandlers[0]->convertToControlValue( _rPropertyName, _rPropertyValue, _rControlValueType ); |
| } |
| |
| //-------------------------------------------------------------------- |
| PropertyState SAL_CALL PropertyComposer::getPropertyState( const ::rtl::OUString& _rPropertyName ) throw (UnknownPropertyException, RuntimeException) |
| { |
| MethodGuard aGuard( *this ); |
| |
| // assume DIRECT for the moment. This will stay this way if *all* slaves |
| // tell the property has DIRECT state, and if *all* values equal |
| PropertyState eState = PropertyState_DIRECT_VALUE; |
| |
| // check the master state |
| Reference< XPropertyHandler > xPrimary( *m_aSlaveHandlers.begin() ); |
| Any aPrimaryValue = xPrimary->getPropertyValue( _rPropertyName ); |
| eState = xPrimary->getPropertyState( _rPropertyName ); |
| |
| // loop through the secondary sets |
| PropertyState eSecondaryState = PropertyState_DIRECT_VALUE; |
| for ( HandlerArray::const_iterator loop = ( m_aSlaveHandlers.begin() + 1 ); |
| loop != m_aSlaveHandlers.end(); |
| ++loop |
| ) |
| { |
| // the secondary state |
| eSecondaryState = (*loop)->getPropertyState( _rPropertyName ); |
| |
| // the secondary value |
| Any aSecondaryValue( (*loop)->getPropertyValue( _rPropertyName ) ); |
| |
| if ( ( PropertyState_AMBIGUOUS_VALUE == eSecondaryState ) // secondary is ambiguous |
| || ( aPrimaryValue != aSecondaryValue ) // unequal values |
| ) |
| { |
| eState = PropertyState_AMBIGUOUS_VALUE; |
| break; |
| } |
| } |
| |
| return eState; |
| } |
| |
| //-------------------------------------------------------------------- |
| void SAL_CALL PropertyComposer::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) throw (RuntimeException) |
| { |
| MethodGuard aGuard( *this ); |
| m_aPropertyListeners.addListener( _rxListener ); |
| } |
| |
| //-------------------------------------------------------------------- |
| void SAL_CALL PropertyComposer::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) throw (RuntimeException) |
| { |
| MethodGuard aGuard( *this ); |
| m_aPropertyListeners.removeListener( _rxListener ); |
| } |
| |
| //-------------------------------------------------------------------- |
| Sequence< Property > SAL_CALL PropertyComposer::getSupportedProperties() throw (RuntimeException) |
| { |
| MethodGuard aGuard( *this ); |
| |
| if ( !m_bSupportedPropertiesAreKnown ) |
| { |
| // we support a property if and only if all of our slaves support it |
| |
| // initially, use all the properties of an arbitrary handler (we take the first one) |
| putIntoBag( (*m_aSlaveHandlers.begin())->getSupportedProperties(), m_aSupportedProperties ); |
| |
| // now intersect with the properties of *all* other handlers |
| for ( HandlerArray::const_iterator loop = ( m_aSlaveHandlers.begin() + 1 ); |
| loop != m_aSlaveHandlers.end(); |
| ++loop |
| ) |
| { |
| // the properties supported by the current handler |
| PropertyBag aThisRound; |
| putIntoBag( (*loop)->getSupportedProperties(), aThisRound ); |
| |
| // the intersection of those properties with all we already have |
| PropertyBag aIntersection; |
| ::std::set_intersection( aThisRound.begin(), aThisRound.end(), m_aSupportedProperties.begin(), m_aSupportedProperties.end(), |
| ::std::insert_iterator< PropertyBag >( aIntersection, aIntersection.begin() ), PropertyLessByName() ); |
| |
| m_aSupportedProperties.swap( aIntersection ); |
| if ( m_aSupportedProperties.empty() ) |
| break; |
| } |
| |
| // remove those properties which are not composable |
| for ( PropertyBag::iterator check = m_aSupportedProperties.begin(); |
| check != m_aSupportedProperties.end(); |
| ) |
| { |
| sal_Bool bIsComposable = isComposable( check->Name ); |
| if ( !bIsComposable ) |
| { |
| PropertyBag::iterator next = check; ++next; |
| m_aSupportedProperties.erase( check ); |
| check = next; |
| } |
| else |
| ++check; |
| } |
| |
| m_bSupportedPropertiesAreKnown = true; |
| } |
| |
| Sequence< Property > aSurvived; |
| copyBagToArray( m_aSupportedProperties, aSurvived ); |
| return aSurvived; |
| } |
| |
| //-------------------------------------------------------------------- |
| void uniteStringArrays( const PropertyComposer::HandlerArray& _rHandlers, Sequence< ::rtl::OUString > (SAL_CALL XPropertyHandler::*pGetter)( void ), |
| Sequence< ::rtl::OUString >& /* [out] */ _rUnion ) |
| { |
| ::std::set< ::rtl::OUString > aUnitedBag; |
| |
| Sequence< ::rtl::OUString > aThisRound; |
| for ( PropertyComposer::HandlerArray::const_iterator loop = _rHandlers.begin(); |
| loop != _rHandlers.end(); |
| ++loop |
| ) |
| { |
| aThisRound = (loop->get()->*pGetter)(); |
| putIntoBag( aThisRound, aUnitedBag ); |
| } |
| |
| copyBagToArray( aUnitedBag, _rUnion ); |
| } |
| |
| //-------------------------------------------------------------------- |
| Sequence< ::rtl::OUString > SAL_CALL PropertyComposer::getSupersededProperties( ) throw (RuntimeException) |
| { |
| MethodGuard aGuard( *this ); |
| |
| // we supersede those properties which are superseded by at least one of our slaves |
| Sequence< ::rtl::OUString > aSuperseded; |
| uniteStringArrays( m_aSlaveHandlers, &XPropertyHandler::getSupersededProperties, aSuperseded ); |
| return aSuperseded; |
| } |
| |
| //-------------------------------------------------------------------- |
| Sequence< ::rtl::OUString > SAL_CALL PropertyComposer::getActuatingProperties( ) throw (RuntimeException) |
| { |
| MethodGuard aGuard( *this ); |
| |
| // we're interested in those properties which at least one handler wants to have |
| Sequence< ::rtl::OUString > aActuating; |
| uniteStringArrays( m_aSlaveHandlers, &XPropertyHandler::getActuatingProperties, aActuating ); |
| return aActuating; |
| } |
| |
| //-------------------------------------------------------------------- |
| LineDescriptor SAL_CALL PropertyComposer::describePropertyLine( const ::rtl::OUString& _rPropertyName, |
| const Reference< XPropertyControlFactory >& _rxControlFactory ) |
| throw (UnknownPropertyException, NullPointerException, RuntimeException) |
| { |
| MethodGuard aGuard( *this ); |
| return m_aSlaveHandlers[0]->describePropertyLine( _rPropertyName, _rxControlFactory ); |
| } |
| |
| //-------------------------------------------------------------------- |
| ::sal_Bool SAL_CALL PropertyComposer::isComposable( const ::rtl::OUString& _rPropertyName ) throw (UnknownPropertyException, RuntimeException) |
| { |
| MethodGuard aGuard( *this ); |
| return m_aSlaveHandlers[0]->isComposable( _rPropertyName ); |
| } |
| |
| //-------------------------------------------------------------------- |
| InteractiveSelectionResult SAL_CALL PropertyComposer::onInteractivePropertySelection( const ::rtl::OUString& _rPropertyName, sal_Bool _bPrimary, Any& _rData, const Reference< XObjectInspectorUI >& _rxInspectorUI ) throw (UnknownPropertyException, NullPointerException, RuntimeException) |
| { |
| if ( !_rxInspectorUI.is() ) |
| throw NullPointerException(); |
| |
| MethodGuard aGuard( *this ); |
| |
| impl_ensureUIRequestComposer( _rxInspectorUI ); |
| ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer ); |
| |
| // ask the first of the handlers |
| InteractiveSelectionResult eResult = (*m_aSlaveHandlers.begin())->onInteractivePropertySelection( |
| _rPropertyName, |
| _bPrimary, |
| _rData, |
| m_pUIRequestComposer->getUIForPropertyHandler( *m_aSlaveHandlers.begin() ) |
| ); |
| |
| switch ( eResult ) |
| { |
| case InteractiveSelectionResult_Cancelled: |
| // fine |
| break; |
| |
| case InteractiveSelectionResult_Success: |
| case InteractiveSelectionResult_Pending: |
| OSL_ENSURE( false, "PropertyComposer::onInteractivePropertySelection: no chance to forward the new value to the other handlers!" ); |
| // This means that we cannot know the new property value, which either has already been set |
| // at the first component ("Success"), or will be set later on once the asynchronous input |
| // is finished ("Pending"). So, we also cannot forward this new property value to the other |
| // handlers. |
| // We would need to be a listener at the property at the first component, but even this wouldn't |
| // be sufficient, since the property handler is free to change *any* property during a dedicated |
| // property UI. |
| eResult = InteractiveSelectionResult_Cancelled; |
| break; |
| |
| case InteractiveSelectionResult_ObtainedValue: |
| // OK. Our own caller will pass this as setPropertyValue, and we will then pass it to |
| // all slave handlers |
| break; |
| |
| default: |
| OSL_ENSURE( false, "OPropertyBrowserController::onInteractivePropertySelection: unknown result value!" ); |
| break; |
| } |
| |
| return eResult; |
| } |
| |
| //-------------------------------------------------------------------- |
| void PropertyComposer::impl_ensureUIRequestComposer( const Reference< XObjectInspectorUI >& _rxInspectorUI ) |
| { |
| OSL_ENSURE( !m_pUIRequestComposer.get() || m_pUIRequestComposer->getDelegatorUI().get() == _rxInspectorUI.get(), |
| "PropertyComposer::impl_ensureUIRequestComposer: somebody's changing the horse in the mid of the race!" ); |
| |
| if ( !m_pUIRequestComposer.get() ) |
| m_pUIRequestComposer.reset( new ComposedPropertyUIUpdate( _rxInspectorUI, this ) ); |
| } |
| |
| //-------------------------------------------------------------------- |
| void SAL_CALL PropertyComposer::actuatingPropertyChanged( const ::rtl::OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& _rOldValue, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) throw (NullPointerException, RuntimeException) |
| { |
| if ( !_rxInspectorUI.is() ) |
| throw NullPointerException(); |
| |
| MethodGuard aGuard( *this ); |
| |
| impl_ensureUIRequestComposer( _rxInspectorUI ); |
| ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer ); |
| |
| // ask all handlers which expressed interest in this particular property, and "compose" their |
| // commands for the UIUpdater |
| for ( HandlerArray::const_iterator loop = m_aSlaveHandlers.begin(); |
| loop != m_aSlaveHandlers.end(); |
| ++loop |
| ) |
| { |
| // TODO: make this cheaper (cache it?) |
| const StlSyntaxSequence< ::rtl::OUString > aThisHandlersActuatingProps = (*loop)->getActuatingProperties(); |
| for ( StlSyntaxSequence< ::rtl::OUString >::const_iterator loopProps = aThisHandlersActuatingProps.begin(); |
| loopProps != aThisHandlersActuatingProps.end(); |
| ++loopProps |
| ) |
| { |
| if ( *loopProps == _rActuatingPropertyName ) |
| { |
| (*loop)->actuatingPropertyChanged( _rActuatingPropertyName, _rNewValue, _rOldValue, |
| m_pUIRequestComposer->getUIForPropertyHandler( *loop ), |
| _bFirstTimeInit ); |
| break; |
| } |
| } |
| } |
| } |
| |
| //-------------------------------------------------------------------- |
| IMPLEMENT_FORWARD_XCOMPONENT( PropertyComposer, PropertyComposer_Base ) |
| |
| //-------------------------------------------------------------------- |
| void SAL_CALL PropertyComposer::disposing() |
| { |
| MethodGuard aGuard( *this ); |
| |
| // dispose our slave handlers |
| for ( PropertyComposer::HandlerArray::const_iterator loop = m_aSlaveHandlers.begin(); |
| loop != m_aSlaveHandlers.end(); |
| ++loop |
| ) |
| { |
| (*loop)->removePropertyChangeListener( this ); |
| (*loop)->dispose(); |
| } |
| |
| clearContainer( m_aSlaveHandlers ); |
| |
| if ( m_pUIRequestComposer.get() ) |
| m_pUIRequestComposer->dispose(); |
| m_pUIRequestComposer.reset(); |
| } |
| |
| //-------------------------------------------------------------------- |
| void SAL_CALL PropertyComposer::propertyChange( const PropertyChangeEvent& evt ) throw (RuntimeException) |
| { |
| if ( !impl_isSupportedProperty_nothrow( evt.PropertyName ) ) |
| // A slave handler might fire events for more properties than we support. Ignore those. |
| return; |
| |
| PropertyChangeEvent aTranslatedEvent( evt ); |
| try |
| { |
| aTranslatedEvent.NewValue = getPropertyValue( evt.PropertyName ); |
| } |
| catch( const Exception& ) |
| { |
| DBG_UNHANDLED_EXCEPTION(); |
| } |
| m_aPropertyListeners.notify( aTranslatedEvent, &XPropertyChangeListener::propertyChange ); |
| } |
| |
| //-------------------------------------------------------------------- |
| void SAL_CALL PropertyComposer::disposing( const EventObject& Source ) throw (RuntimeException) |
| { |
| MethodGuard aGuard( *this ); |
| m_aPropertyListeners.disposing( Source ); |
| } |
| |
| //-------------------------------------------------------------------- |
| sal_Bool SAL_CALL PropertyComposer::suspend( sal_Bool _bSuspend ) throw (RuntimeException) |
| { |
| MethodGuard aGuard( *this ); |
| for ( PropertyComposer::HandlerArray::const_iterator loop = m_aSlaveHandlers.begin(); |
| loop != m_aSlaveHandlers.end(); |
| ++loop |
| ) |
| { |
| if ( !(*loop)->suspend( _bSuspend ) ) |
| { |
| if ( _bSuspend && ( loop != m_aSlaveHandlers.begin() ) ) |
| { |
| // if we tried to suspend, but one of the slave handlers vetoed, |
| // re-activate the handlers which actually did *not* veto |
| // the suspension |
| do |
| { |
| --loop; |
| (*loop)->suspend( sal_False ); |
| } |
| while ( loop != m_aSlaveHandlers.begin() ); |
| } |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| //-------------------------------------------------------------------- |
| sal_Bool SAL_CALL PropertyComposer::hasPropertyByName( const ::rtl::OUString& _rName ) throw (RuntimeException) |
| { |
| return impl_isSupportedProperty_nothrow( _rName ); |
| } |
| |
| //........................................................................ |
| } // namespace pcr |
| //........................................................................ |
| |