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