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 #include "precompiled_toolkit.hxx" 25 26 #include "toolkit/awt/animatedimagespeer.hxx" 27 #include "toolkit/helper/property.hxx" 28 29 /** === begin UNO includes === **/ 30 #include <com/sun/star/awt/XAnimatedImages.hpp> 31 #include <com/sun/star/awt/Size.hpp> 32 #include <com/sun/star/graphic/XGraphicProvider.hpp> 33 #include <com/sun/star/beans/XPropertySet.hpp> 34 #include <com/sun/star/graphic/XGraphic.hpp> 35 #include <com/sun/star/awt/ImageScaleMode.hpp> 36 /** === end UNO includes === **/ 37 38 #include <comphelper/componentcontext.hxx> 39 #include <comphelper/namedvaluecollection.hxx> 40 #include <comphelper/processfactory.hxx> 41 #include <rtl/ustrbuf.hxx> 42 #include <tools/diagnose_ex.h> 43 #include <tools/urlobj.hxx> 44 #include <vcl/throbber.hxx> 45 46 #include <limits> 47 48 //...................................................................................................................... 49 namespace toolkit 50 { 51 //...................................................................................................................... 52 53 /** === begin UNO using === **/ 54 using ::com::sun::star::uno::Reference; 55 using ::com::sun::star::uno::XInterface; 56 using ::com::sun::star::uno::UNO_QUERY; 57 using ::com::sun::star::uno::UNO_QUERY_THROW; 58 using ::com::sun::star::uno::UNO_SET_THROW; 59 using ::com::sun::star::uno::Exception; 60 using ::com::sun::star::uno::RuntimeException; 61 using ::com::sun::star::uno::Any; 62 using ::com::sun::star::uno::makeAny; 63 using ::com::sun::star::uno::Sequence; 64 using ::com::sun::star::uno::Type; 65 using ::com::sun::star::lang::EventObject; 66 using ::com::sun::star::container::ContainerEvent; 67 using ::com::sun::star::awt::XAnimatedImages; 68 using ::com::sun::star::awt::Size; 69 using ::com::sun::star::lang::XMultiServiceFactory; 70 using ::com::sun::star::graphic::XGraphicProvider; 71 using ::com::sun::star::beans::XPropertySet; 72 using ::com::sun::star::graphic::XGraphic; 73 /** === end UNO using === **/ 74 namespace ImageScaleMode = ::com::sun::star::awt::ImageScaleMode; 75 76 //================================================================================================================== 77 //= AnimatedImagesPeer_Data 78 //================================================================================================================== 79 struct CachedImage 80 { 81 ::rtl::OUString sImageURL; 82 mutable Reference< XGraphic > xGraphic; 83 CachedImagetoolkit::CachedImage84 CachedImage() 85 :sImageURL() 86 ,xGraphic() 87 { 88 } 89 CachedImagetoolkit::CachedImage90 CachedImage( ::rtl::OUString const& i_imageURL ) 91 :sImageURL( i_imageURL ) 92 ,xGraphic() 93 { 94 } 95 }; 96 97 struct AnimatedImagesPeer_Data 98 { 99 AnimatedImagesPeer& rAntiImpl; 100 ::std::vector< ::std::vector< CachedImage > > aCachedImageSets; 101 AnimatedImagesPeer_Datatoolkit::AnimatedImagesPeer_Data102 AnimatedImagesPeer_Data( AnimatedImagesPeer& i_antiImpl ) 103 :rAntiImpl( i_antiImpl ) 104 ,aCachedImageSets() 105 { 106 } 107 }; 108 109 //================================================================================================================== 110 //= helper 111 //================================================================================================================== 112 namespace 113 { 114 //-------------------------------------------------------------------------------------------------------------- lcl_ensureImage_throw(Reference<XGraphicProvider> const & i_graphicProvider,const bool i_isHighContrast,const CachedImage & i_cachedImage)115 bool lcl_ensureImage_throw( Reference< XGraphicProvider > const& i_graphicProvider, const bool i_isHighContrast, const CachedImage& i_cachedImage ) 116 { 117 if ( !i_cachedImage.xGraphic.is() ) 118 { 119 ::comphelper::NamedValueCollection aMediaProperties; 120 if ( i_isHighContrast ) 121 { 122 // try (to find) the high-contrast version of the graphic first 123 INetURLObject aURL( i_cachedImage.sImageURL ); 124 if ( aURL.GetProtocol() != INET_PROT_PRIV_SOFFICE ) 125 { 126 rtl::OUString sURL( i_cachedImage.sImageURL ); 127 const sal_Int32 separatorPos = sURL.lastIndexOf( '/' ); 128 if ( separatorPos != -1 ) 129 { 130 ::rtl::OUStringBuffer composer; 131 composer.append( sURL.copy( 0, separatorPos ) ); 132 composer.appendAscii( RTL_CONSTASCII_STRINGPARAM( "/hicontrast" ) ); 133 composer.append( sURL.copy( separatorPos ) ); 134 135 aMediaProperties.put( "URL", composer.makeStringAndClear() ); 136 i_cachedImage.xGraphic.set( i_graphicProvider->queryGraphic( aMediaProperties.getPropertyValues() ), UNO_QUERY ); 137 } 138 } 139 } 140 if ( !i_cachedImage.xGraphic.is() ) 141 { 142 aMediaProperties.put( "URL", i_cachedImage.sImageURL ); 143 i_cachedImage.xGraphic.set( i_graphicProvider->queryGraphic( aMediaProperties.getPropertyValues() ), UNO_QUERY ); 144 } 145 } 146 return i_cachedImage.xGraphic.is(); 147 } 148 149 //-------------------------------------------------------------------------------------------------------------- lcl_getGraphicSizePixel(Reference<XGraphic> const & i_graphic)150 Size lcl_getGraphicSizePixel( Reference< XGraphic > const& i_graphic ) 151 { 152 Size aSizePixel; 153 try 154 { 155 if ( i_graphic.is() ) 156 { 157 const Reference< XPropertySet > xGraphicProps( i_graphic, UNO_QUERY_THROW ); 158 OSL_VERIFY( xGraphicProps->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SizePixel" ) ) ) >>= aSizePixel ); 159 } 160 } 161 catch( const Exception& ) 162 { 163 DBG_UNHANDLED_EXCEPTION(); 164 } 165 return aSizePixel; 166 } 167 168 //-------------------------------------------------------------------------------------------------------------- lcl_init(Sequence<::rtl::OUString> const & i_imageURLs,::std::vector<CachedImage> & o_images)169 void lcl_init( Sequence< ::rtl::OUString > const& i_imageURLs, ::std::vector< CachedImage >& o_images ) 170 { 171 o_images.resize(0); 172 size_t count = size_t( i_imageURLs.getLength() ); 173 o_images.reserve( count ); 174 for ( size_t i = 0; i < count; ++i ) 175 { 176 o_images.push_back( CachedImage( i_imageURLs[i] ) ); 177 } 178 } 179 180 //-------------------------------------------------------------------------------------------------------------- lcl_updateImageList_nothrow(AnimatedImagesPeer_Data & i_data)181 void lcl_updateImageList_nothrow( AnimatedImagesPeer_Data& i_data ) 182 { 183 Throbber* pThrobber = dynamic_cast< Throbber* >( i_data.rAntiImpl.GetWindow() ); 184 if ( pThrobber == NULL ) 185 return; 186 187 try 188 { 189 // collect the image sizes of the different image sets 190 const ::comphelper::ComponentContext aContext( ::comphelper::getProcessServiceFactory() ); 191 const Reference< XGraphicProvider > xGraphicProvider( aContext.createComponent( "com.sun.star.graphic.GraphicProvider" ), UNO_QUERY_THROW ); 192 193 const bool isHighContrast = pThrobber->GetSettings().GetStyleSettings().GetHighContrastMode(); 194 195 sal_Int32 nPreferredSet = -1; 196 const size_t nImageSetCount = i_data.aCachedImageSets.size(); 197 if ( nImageSetCount < 2 ) 198 { 199 nPreferredSet = sal_Int32( nImageSetCount ) - 1; 200 } 201 else 202 { 203 ::std::vector< Size > aImageSizes( nImageSetCount ); 204 for ( sal_Int32 nImageSet = 0; size_t( nImageSet ) < nImageSetCount; ++nImageSet ) 205 { 206 ::std::vector< CachedImage > const& rImageSet( i_data.aCachedImageSets[ nImageSet ] ); 207 if ( ( rImageSet.empty() ) 208 || ( !lcl_ensureImage_throw( xGraphicProvider, isHighContrast, rImageSet[0] ) ) 209 ) 210 { 211 aImageSizes[ nImageSet ] = Size( ::std::numeric_limits< long >::max(), ::std::numeric_limits< long >::max() ); 212 } 213 else 214 { 215 aImageSizes[ nImageSet ] = lcl_getGraphicSizePixel( rImageSet[0].xGraphic ); 216 } 217 } 218 219 // find the set with the smallest difference between window size and image size 220 const ::Size aWindowSizePixel = pThrobber->GetSizePixel(); 221 long nMinimalDistance = ::std::numeric_limits< long >::max(); 222 for ( ::std::vector< Size >::const_iterator check = aImageSizes.begin(); 223 check != aImageSizes.end(); 224 ++check 225 ) 226 { 227 if ( ( check->Width > aWindowSizePixel.Width() ) 228 || ( check->Height > aWindowSizePixel.Height() ) 229 ) 230 // do not use an image set which doesn't fit into the window 231 continue; 232 233 const sal_Int64 distance = 234 ( aWindowSizePixel.Width() - check->Width ) * ( aWindowSizePixel.Width() - check->Width ) 235 + ( aWindowSizePixel.Height() - check->Height ) * ( aWindowSizePixel.Height() - check->Height ); 236 if ( distance < nMinimalDistance ) 237 { 238 nMinimalDistance = distance; 239 nPreferredSet = check - aImageSizes.begin(); 240 } 241 } 242 } 243 244 // found a set? 245 Sequence< Reference< XGraphic > > aImages; 246 if ( ( nPreferredSet >= 0 ) && ( size_t( nPreferredSet ) < nImageSetCount ) ) 247 { 248 // => set the images 249 ::std::vector< CachedImage > const& rImageSet( i_data.aCachedImageSets[ nPreferredSet ] ); 250 aImages.realloc( rImageSet.size() ); 251 sal_Int32 imageIndex = 0; 252 for ( ::std::vector< CachedImage >::const_iterator cachedImage = rImageSet.begin(); 253 cachedImage != rImageSet.end(); 254 ++cachedImage, ++imageIndex 255 ) 256 { 257 lcl_ensureImage_throw( xGraphicProvider, isHighContrast, *cachedImage ); 258 aImages[ imageIndex ] = cachedImage->xGraphic; 259 } 260 } 261 pThrobber->setImageList( aImages ); 262 } 263 catch( const Exception& ) 264 { 265 DBG_UNHANDLED_EXCEPTION(); 266 } 267 } 268 269 //-------------------------------------------------------------------------------------------------------------- lcl_updateImageList_nothrow(AnimatedImagesPeer_Data & i_data,const Reference<XAnimatedImages> & i_images)270 void lcl_updateImageList_nothrow( AnimatedImagesPeer_Data& i_data, const Reference< XAnimatedImages >& i_images ) 271 { 272 try 273 { 274 const sal_Int32 nImageSetCount = i_images->getImageSetCount(); 275 i_data.aCachedImageSets.resize(0); 276 for ( sal_Int32 set = 0; set < nImageSetCount; ++set ) 277 { 278 const Sequence< ::rtl::OUString > aImageURLs( i_images->getImageSet( set ) ); 279 ::std::vector< CachedImage > aImages; 280 lcl_init( aImageURLs, aImages ); 281 i_data.aCachedImageSets.push_back( aImages ); 282 } 283 284 lcl_updateImageList_nothrow( i_data ); 285 } 286 catch( const Exception& ) 287 { 288 DBG_UNHANDLED_EXCEPTION(); 289 } 290 } 291 } 292 293 //================================================================================================================== 294 //= AnimatedImagesPeer 295 //================================================================================================================== 296 //------------------------------------------------------------------------------------------------------------------ AnimatedImagesPeer()297 AnimatedImagesPeer::AnimatedImagesPeer() 298 :AnimatedImagesPeer_Base() 299 ,m_pData( new AnimatedImagesPeer_Data( *this ) ) 300 { 301 } 302 303 //------------------------------------------------------------------------------------------------------------------ ~AnimatedImagesPeer()304 AnimatedImagesPeer::~AnimatedImagesPeer() 305 { 306 } 307 308 //------------------------------------------------------------------------------------------------------------------ startAnimation()309 void SAL_CALL AnimatedImagesPeer::startAnimation( ) throw (RuntimeException) 310 { 311 ::vos::OGuard aGuard( GetMutex() ); 312 Throbber* pThrobber( dynamic_cast< Throbber* >( GetWindow() ) ); 313 if ( pThrobber != NULL) 314 pThrobber->start(); 315 } 316 317 //------------------------------------------------------------------------------------------------------------------ stopAnimation()318 void SAL_CALL AnimatedImagesPeer::stopAnimation( ) throw (RuntimeException) 319 { 320 ::vos::OGuard aGuard( GetMutex() ); 321 Throbber* pThrobber( dynamic_cast< Throbber* >( GetWindow() ) ); 322 if ( pThrobber != NULL) 323 pThrobber->stop(); 324 } 325 326 //------------------------------------------------------------------------------------------------------------------ isAnimationRunning()327 ::sal_Bool SAL_CALL AnimatedImagesPeer::isAnimationRunning( ) throw (RuntimeException) 328 { 329 ::vos::OGuard aGuard( GetMutex() ); 330 Throbber* pThrobber( dynamic_cast< Throbber* >( GetWindow() ) ); 331 if ( pThrobber != NULL) 332 return pThrobber->isRunning(); 333 return sal_False; 334 } 335 336 //------------------------------------------------------------------------------------------------------------------ setProperty(const::rtl::OUString & i_propertyName,const Any & i_value)337 void SAL_CALL AnimatedImagesPeer::setProperty( const ::rtl::OUString& i_propertyName, const Any& i_value ) throw(RuntimeException) 338 { 339 ::vos::OGuard aGuard( GetMutex() ); 340 341 Throbber* pThrobber( dynamic_cast< Throbber* >( GetWindow() ) ); 342 if ( pThrobber == NULL ) 343 { 344 VCLXWindow::setProperty( i_propertyName, i_value ); 345 return; 346 } 347 348 const sal_uInt16 nPropertyId = GetPropertyId( i_propertyName ); 349 switch ( nPropertyId ) 350 { 351 case BASEPROPERTY_STEP_TIME: 352 { 353 sal_Int32 nStepTime( 0 ); 354 if ( i_value >>= nStepTime ) 355 pThrobber->setStepTime( nStepTime ); 356 break; 357 } 358 case BASEPROPERTY_AUTO_REPEAT: 359 { 360 sal_Bool bRepeat( sal_True ); 361 if ( i_value >>= bRepeat ) 362 pThrobber->setRepeat( bRepeat ); 363 break; 364 } 365 366 case BASEPROPERTY_IMAGE_SCALE_MODE: 367 { 368 sal_Int16 nScaleMode( ImageScaleMode::ANISOTROPIC ); 369 ImageControl* pImageControl = dynamic_cast< ImageControl* >( GetWindow() ); 370 if ( pImageControl && ( i_value >>= nScaleMode ) ) 371 { 372 pImageControl->SetScaleMode( nScaleMode ); 373 } 374 } 375 break; 376 377 default: 378 AnimatedImagesPeer_Base::setProperty( i_propertyName, i_value ); 379 break; 380 } 381 } 382 383 //------------------------------------------------------------------------------------------------------------------ getProperty(const::rtl::OUString & i_propertyName)384 Any SAL_CALL AnimatedImagesPeer::getProperty( const ::rtl::OUString& i_propertyName ) throw(RuntimeException) 385 { 386 ::vos::OGuard aGuard( GetMutex() ); 387 388 Any aReturn; 389 390 Throbber* pThrobber( dynamic_cast< Throbber* >( GetWindow() ) ); 391 if ( pThrobber == NULL ) 392 return VCLXWindow::getProperty( i_propertyName ); 393 394 const sal_uInt16 nPropertyId = GetPropertyId( i_propertyName ); 395 switch ( nPropertyId ) 396 { 397 case BASEPROPERTY_STEP_TIME: 398 aReturn <<= pThrobber->getStepTime(); 399 break; 400 401 case BASEPROPERTY_AUTO_REPEAT: 402 aReturn <<= pThrobber->getRepeat(); 403 break; 404 405 case BASEPROPERTY_IMAGE_SCALE_MODE: 406 { 407 ImageControl const* pImageControl = dynamic_cast< ImageControl* >( GetWindow() ); 408 aReturn <<= ( pImageControl ? pImageControl->GetScaleMode() : ImageScaleMode::ANISOTROPIC ); 409 } 410 break; 411 412 default: 413 aReturn = AnimatedImagesPeer_Base::getProperty( i_propertyName ); 414 break; 415 } 416 417 return aReturn; 418 } 419 420 //------------------------------------------------------------------------------------------------------------------ ProcessWindowEvent(const VclWindowEvent & i_windowEvent)421 void AnimatedImagesPeer::ProcessWindowEvent( const VclWindowEvent& i_windowEvent ) 422 { 423 switch ( i_windowEvent.GetId() ) 424 { 425 case VCLEVENT_WINDOW_RESIZE: 426 lcl_updateImageList_nothrow( *m_pData ); 427 break; 428 } 429 430 AnimatedImagesPeer_Base::ProcessWindowEvent( i_windowEvent ); 431 } 432 433 //------------------------------------------------------------------------------------------------------------------ impl_updateImages_nolck(const Reference<XInterface> & i_animatedImages)434 void AnimatedImagesPeer::impl_updateImages_nolck( const Reference< XInterface >& i_animatedImages ) 435 { 436 ::vos::OGuard aGuard( GetMutex() ); 437 438 lcl_updateImageList_nothrow( *m_pData, Reference< XAnimatedImages >( i_animatedImages, UNO_QUERY_THROW ) ); 439 } 440 441 //------------------------------------------------------------------------------------------------------------------ elementInserted(const ContainerEvent & i_event)442 void SAL_CALL AnimatedImagesPeer::elementInserted( const ContainerEvent& i_event ) throw (RuntimeException) 443 { 444 ::vos::OGuard aGuard( GetMutex() ); 445 Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW ); 446 447 sal_Int32 nPosition(0); 448 OSL_VERIFY( i_event.Accessor >>= nPosition ); 449 size_t position = size_t( nPosition ); 450 if ( position > m_pData->aCachedImageSets.size() ) 451 { 452 OSL_ENSURE( false, "AnimatedImagesPeer::elementInserted: illegal accessor/index!" ); 453 lcl_updateImageList_nothrow( *m_pData, xAnimatedImages ); 454 } 455 456 Sequence< ::rtl::OUString > aImageURLs; 457 OSL_VERIFY( i_event.Element >>= aImageURLs ); 458 ::std::vector< CachedImage > aImages; 459 lcl_init( aImageURLs, aImages ); 460 m_pData->aCachedImageSets.insert( m_pData->aCachedImageSets.begin() + position, aImages ); 461 lcl_updateImageList_nothrow( *m_pData ); 462 } 463 464 //------------------------------------------------------------------------------------------------------------------ elementRemoved(const ContainerEvent & i_event)465 void SAL_CALL AnimatedImagesPeer::elementRemoved( const ContainerEvent& i_event ) throw (RuntimeException) 466 { 467 ::vos::OGuard aGuard( GetMutex() ); 468 Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW ); 469 470 sal_Int32 nPosition(0); 471 OSL_VERIFY( i_event.Accessor >>= nPosition ); 472 size_t position = size_t( nPosition ); 473 if ( position >= m_pData->aCachedImageSets.size() ) 474 { 475 OSL_ENSURE( false, "AnimatedImagesPeer::elementRemoved: illegal accessor/index!" ); 476 lcl_updateImageList_nothrow( *m_pData, xAnimatedImages ); 477 } 478 479 m_pData->aCachedImageSets.erase( m_pData->aCachedImageSets.begin() + position ); 480 lcl_updateImageList_nothrow( *m_pData ); 481 } 482 483 //------------------------------------------------------------------------------------------------------------------ elementReplaced(const ContainerEvent & i_event)484 void SAL_CALL AnimatedImagesPeer::elementReplaced( const ContainerEvent& i_event ) throw (RuntimeException) 485 { 486 ::vos::OGuard aGuard( GetMutex() ); 487 Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW ); 488 489 sal_Int32 nPosition(0); 490 OSL_VERIFY( i_event.Accessor >>= nPosition ); 491 size_t position = size_t( nPosition ); 492 if ( position >= m_pData->aCachedImageSets.size() ) 493 { 494 OSL_ENSURE( false, "AnimatedImagesPeer::elementReplaced: illegal accessor/index!" ); 495 lcl_updateImageList_nothrow( *m_pData, xAnimatedImages ); 496 } 497 498 Sequence< ::rtl::OUString > aImageURLs; 499 OSL_VERIFY( i_event.Element >>= aImageURLs ); 500 ::std::vector< CachedImage > aImages; 501 lcl_init( aImageURLs, aImages ); 502 m_pData->aCachedImageSets[ position ] = aImages; 503 lcl_updateImageList_nothrow( *m_pData ); 504 } 505 506 //------------------------------------------------------------------------------------------------------------------ disposing(const EventObject & i_event)507 void SAL_CALL AnimatedImagesPeer::disposing( const EventObject& i_event ) throw (RuntimeException) 508 { 509 VCLXWindow::disposing( i_event ); 510 } 511 512 //------------------------------------------------------------------------------------------------------------------ modified(const EventObject & i_event)513 void SAL_CALL AnimatedImagesPeer::modified( const EventObject& i_event ) throw (RuntimeException) 514 { 515 impl_updateImages_nolck( i_event.Source ); 516 } 517 518 //------------------------------------------------------------------------------------------------------------------ dispose()519 void SAL_CALL AnimatedImagesPeer::dispose( ) throw(RuntimeException) 520 { 521 AnimatedImagesPeer_Base::dispose(); 522 ::vos::OGuard aGuard( GetMutex() ); 523 m_pData->aCachedImageSets.resize(0); 524 } 525 526 //...................................................................................................................... 527 } // namespace toolkit 528 //...................................................................................................................... 529