1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <awt/animatedimagespeer.hxx>
22 #include <helper/property.hxx>
24 #include <com/sun/star/awt/Size.hpp>
25 #include <com/sun/star/graphic/GraphicProvider.hpp>
26 #include <com/sun/star/graphic/XGraphicProvider.hpp>
27 #include <com/sun/star/beans/XPropertySet.hpp>
28 #include <com/sun/star/awt/ImageScaleMode.hpp>
30 #include <comphelper/namedvaluecollection.hxx>
31 #include <comphelper/processfactory.hxx>
32 #include <o3tl/safeint.hxx>
33 #include <comphelper/diagnose_ex.hxx>
34 #include <tools/urlobj.hxx>
35 #include <vcl/toolkit/throbber.hxx>
36 #include <vcl/svapp.hxx>
37 #include <vcl/settings.hxx>
40 #include <string_view>
46 using ::com::sun::star::uno::XComponentContext
;
47 using ::com::sun::star::uno::Reference
;
48 using ::com::sun::star::uno::XInterface
;
49 using ::com::sun::star::uno::UNO_QUERY_THROW
;
50 using ::com::sun::star::uno::Exception
;
51 using ::com::sun::star::uno::Any
;
52 using ::com::sun::star::uno::Sequence
;
53 using ::com::sun::star::lang::EventObject
;
54 using ::com::sun::star::container::ContainerEvent
;
55 using ::com::sun::star::awt::XAnimatedImages
;
56 using ::com::sun::star::awt::Size
;
57 using ::com::sun::star::graphic::XGraphicProvider
;
58 using ::com::sun::star::beans::XPropertySet
;
59 using ::com::sun::star::graphic::XGraphic
;
61 namespace ImageScaleMode
= ::com::sun::star::awt::ImageScaleMode
;
68 OUString
lcl_getHighContrastURL( OUString
const& i_imageURL
)
70 INetURLObject
aURL( i_imageURL
);
71 if ( aURL
.GetProtocol() != INetProtocol::PrivSoffice
)
73 OSL_VERIFY( aURL
.insertName( u
"sifr", false, 0 ) );
74 return aURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
76 // the private: scheme is not considered to be hierarchical by INetURLObject, so manually insert the
78 const sal_Int32 separatorPos
= i_imageURL
.indexOf( '/' );
79 ENSURE_OR_RETURN( separatorPos
!= -1, "lcl_getHighContrastURL: unsupported URL scheme - cannot automatically determine HC version!", i_imageURL
);
81 OUString composer
= OUString::Concat(i_imageURL
.subView(0, separatorPos
)) + "/sifr" +
82 i_imageURL
.subView(separatorPos
);
87 bool lcl_ensureImage_throw( Reference
< XGraphicProvider
> const& i_graphicProvider
, const bool i_isHighContrast
, const AnimatedImagesPeer::CachedImage
& i_cachedImage
)
89 if ( !i_cachedImage
.xGraphic
.is() )
91 ::comphelper::NamedValueCollection aMediaProperties
;
92 if ( i_isHighContrast
)
94 // try (to find) the high-contrast version of the graphic first
95 aMediaProperties
.put( u
"URL"_ustr
, lcl_getHighContrastURL( i_cachedImage
.sImageURL
) );
96 i_cachedImage
.xGraphic
= i_graphicProvider
->queryGraphic( aMediaProperties
.getPropertyValues() );
98 if ( !i_cachedImage
.xGraphic
.is() )
100 aMediaProperties
.put( u
"URL"_ustr
, i_cachedImage
.sImageURL
);
101 i_cachedImage
.xGraphic
= i_graphicProvider
->queryGraphic( aMediaProperties
.getPropertyValues() );
104 return i_cachedImage
.xGraphic
.is();
108 Size
lcl_getGraphicSizePixel( Reference
< XGraphic
> const& i_graphic
)
113 if ( i_graphic
.is() )
115 const Reference
< XPropertySet
> xGraphicProps( i_graphic
, UNO_QUERY_THROW
);
116 OSL_VERIFY( xGraphicProps
->getPropertyValue(u
"SizePixel"_ustr
) >>= aSizePixel
);
119 catch( const Exception
& )
121 DBG_UNHANDLED_EXCEPTION("toolkit");
127 void lcl_init( Sequence
< OUString
> const& i_imageURLs
, ::std::vector
< AnimatedImagesPeer::CachedImage
>& o_images
)
130 size_t count
= size_t( i_imageURLs
.getLength() );
131 o_images
.reserve( count
);
132 for ( const auto& rImageURL
: i_imageURLs
)
134 o_images
.emplace_back( AnimatedImagesPeer::CachedImage
{ rImageURL
, nullptr } );
142 //= AnimatedImagesPeer
145 AnimatedImagesPeer::AnimatedImagesPeer()
150 AnimatedImagesPeer::~AnimatedImagesPeer()
155 void SAL_CALL
AnimatedImagesPeer::startAnimation()
157 SolarMutexGuard aGuard
;
158 VclPtr
<Throbber
> pThrobber
= GetAsDynamic
<Throbber
>();
163 void SAL_CALL
AnimatedImagesPeer::stopAnimation()
165 SolarMutexGuard aGuard
;
166 VclPtr
<Throbber
> pThrobber
= GetAsDynamic
<Throbber
>();
171 sal_Bool SAL_CALL
AnimatedImagesPeer::isAnimationRunning()
173 SolarMutexGuard aGuard
;
174 VclPtr
<Throbber
> pThrobber
= GetAsDynamic
<Throbber
>();
176 return pThrobber
->isRunning();
180 void SAL_CALL
AnimatedImagesPeer::setProperty( const OUString
& i_propertyName
, const Any
& i_value
)
182 SolarMutexGuard aGuard
;
184 VclPtr
<Throbber
> pThrobber
= GetAsDynamic
<Throbber
>();
187 VCLXWindow::setProperty( i_propertyName
, i_value
);
191 const sal_uInt16 nPropertyId
= GetPropertyId( i_propertyName
);
192 switch ( nPropertyId
)
194 case BASEPROPERTY_STEP_TIME
:
196 sal_Int32
nStepTime( 0 );
197 if ( i_value
>>= nStepTime
)
198 pThrobber
->setStepTime( nStepTime
);
201 case BASEPROPERTY_AUTO_REPEAT
:
203 bool bRepeat( true );
204 if ( i_value
>>= bRepeat
)
205 pThrobber
->setRepeat( bRepeat
);
209 case BASEPROPERTY_IMAGE_SCALE_MODE
:
211 sal_Int16
nScaleMode( ImageScaleMode::ANISOTROPIC
);
212 VclPtr
<ImageControl
> pImageControl
= GetAsDynamic
< ImageControl
>();
213 if ( pImageControl
&& ( i_value
>>= nScaleMode
) )
214 pImageControl
->SetScaleMode( nScaleMode
);
219 AnimatedImagesPeer_Base::setProperty( i_propertyName
, i_value
);
225 Any SAL_CALL
AnimatedImagesPeer::getProperty( const OUString
& i_propertyName
)
227 SolarMutexGuard aGuard
;
231 VclPtr
<Throbber
> pThrobber
= GetAsDynamic
<Throbber
>();
233 return VCLXWindow::getProperty( i_propertyName
);
235 const sal_uInt16 nPropertyId
= GetPropertyId( i_propertyName
);
236 switch ( nPropertyId
)
238 case BASEPROPERTY_STEP_TIME
:
239 aReturn
<<= pThrobber
->getStepTime();
242 case BASEPROPERTY_AUTO_REPEAT
:
243 aReturn
<<= pThrobber
->getRepeat();
246 case BASEPROPERTY_IMAGE_SCALE_MODE
:
248 VclPtr
<ImageControl
> pImageControl
= GetAsDynamic
<ImageControl
>();
249 aReturn
<<= ( pImageControl
? pImageControl
->GetScaleMode() : ImageScaleMode::ANISOTROPIC
);
254 aReturn
= AnimatedImagesPeer_Base::getProperty( i_propertyName
);
262 void AnimatedImagesPeer::ProcessWindowEvent( const VclWindowEvent
& i_windowEvent
)
264 if ( i_windowEvent
.GetId() == VclEventId::WindowResize
)
266 updateImageList_nothrow();
269 AnimatedImagesPeer_Base::ProcessWindowEvent( i_windowEvent
);
273 void AnimatedImagesPeer::impl_updateImages_nolck( const Reference
< XInterface
>& i_animatedImages
)
275 SolarMutexGuard aGuard
;
277 updateImageList_nothrow( Reference
< XAnimatedImages
>( i_animatedImages
, UNO_QUERY_THROW
) );
281 void SAL_CALL
AnimatedImagesPeer::elementInserted( const ContainerEvent
& i_event
)
283 SolarMutexGuard aGuard
;
284 Reference
< XAnimatedImages
> xAnimatedImages( i_event
.Source
, UNO_QUERY_THROW
);
286 sal_Int32
nPosition(0);
287 OSL_VERIFY( i_event
.Accessor
>>= nPosition
);
288 size_t position
= size_t( nPosition
);
289 if ( position
> maCachedImageSets
.size() )
291 OSL_ENSURE( false, "AnimatedImagesPeer::elementInserted: illegal accessor/index!" );
292 updateImageList_nothrow( xAnimatedImages
);
295 Sequence
< OUString
> aImageURLs
;
296 OSL_VERIFY( i_event
.Element
>>= aImageURLs
);
297 ::std::vector
< CachedImage
> aImages
;
298 lcl_init( aImageURLs
, aImages
);
299 maCachedImageSets
.insert( maCachedImageSets
.begin() + position
, aImages
);
300 updateImageList_nothrow();
304 void SAL_CALL
AnimatedImagesPeer::elementRemoved( const ContainerEvent
& i_event
)
306 SolarMutexGuard aGuard
;
307 Reference
< XAnimatedImages
> xAnimatedImages( i_event
.Source
, UNO_QUERY_THROW
);
309 sal_Int32
nPosition(0);
310 OSL_VERIFY( i_event
.Accessor
>>= nPosition
);
311 size_t position
= size_t( nPosition
);
312 if ( position
>= maCachedImageSets
.size() )
314 OSL_ENSURE( false, "AnimatedImagesPeer::elementRemoved: illegal accessor/index!" );
315 updateImageList_nothrow( xAnimatedImages
);
318 maCachedImageSets
.erase( maCachedImageSets
.begin() + position
);
319 updateImageList_nothrow();
323 void SAL_CALL
AnimatedImagesPeer::elementReplaced( const ContainerEvent
& i_event
)
325 SolarMutexGuard aGuard
;
326 Reference
< XAnimatedImages
> xAnimatedImages( i_event
.Source
, UNO_QUERY_THROW
);
328 sal_Int32
nPosition(0);
329 OSL_VERIFY( i_event
.Accessor
>>= nPosition
);
330 size_t position
= size_t( nPosition
);
331 if ( position
>= maCachedImageSets
.size() )
333 OSL_ENSURE( false, "AnimatedImagesPeer::elementReplaced: illegal accessor/index!" );
334 updateImageList_nothrow( xAnimatedImages
);
337 Sequence
< OUString
> aImageURLs
;
338 OSL_VERIFY( i_event
.Element
>>= aImageURLs
);
339 ::std::vector
< CachedImage
> aImages
;
340 lcl_init( aImageURLs
, aImages
);
341 maCachedImageSets
[ position
] = std::move(aImages
);
342 updateImageList_nothrow();
346 void SAL_CALL
AnimatedImagesPeer::disposing( const EventObject
& i_event
)
348 VCLXWindow::disposing( i_event
);
352 void SAL_CALL
AnimatedImagesPeer::modified( const EventObject
& i_event
)
354 impl_updateImages_nolck( i_event
.Source
);
358 void SAL_CALL
AnimatedImagesPeer::dispose( )
360 AnimatedImagesPeer_Base::dispose();
361 SolarMutexGuard aGuard
;
362 maCachedImageSets
.resize(0);
365 void AnimatedImagesPeer::updateImageList_nothrow()
367 VclPtr
<Throbber
> pThrobber
= GetAsDynamic
<Throbber
>();
373 // collect the image sizes of the different image sets
374 const Reference
< XComponentContext
>& xContext( ::comphelper::getProcessComponentContext() );
375 const Reference
< XGraphicProvider
> xGraphicProvider( css::graphic::GraphicProvider::create(xContext
) );
377 const bool isHighContrast
= pThrobber
->GetSettings().GetStyleSettings().GetHighContrastMode();
379 sal_Int32 nPreferredSet
= -1;
380 const size_t nImageSetCount
= maCachedImageSets
.size();
381 if ( nImageSetCount
< 2 )
383 nPreferredSet
= sal_Int32( nImageSetCount
) - 1;
387 ::std::vector
< Size
> aImageSizes( nImageSetCount
);
388 for ( size_t nImageSet
= 0; nImageSet
< nImageSetCount
; ++nImageSet
)
390 ::std::vector
< CachedImage
> const& rImageSet( maCachedImageSets
[ nImageSet
] );
391 if ( ( rImageSet
.empty() )
392 || ( !lcl_ensureImage_throw( xGraphicProvider
, isHighContrast
, rImageSet
[0] ) )
395 aImageSizes
[ nImageSet
] = Size( SAL_MAX_INT32
, SAL_MAX_INT32
);
399 aImageSizes
[ nImageSet
] = lcl_getGraphicSizePixel( rImageSet
[0].xGraphic
);
403 // find the set with the smallest difference between window size and image size
404 const ::Size aWindowSizePixel
= pThrobber
->GetSizePixel();
405 tools::Long nMinimalDistance
= ::std::numeric_limits
< tools::Long
>::max();
406 for ( ::std::vector
< Size
>::const_iterator check
= aImageSizes
.begin();
407 check
!= aImageSizes
.end();
411 if ( ( check
->Width
> aWindowSizePixel
.Width() )
412 || ( check
->Height
> aWindowSizePixel
.Height() )
414 // do not use an image set which doesn't fit into the window
417 const sal_Int64 distance
=
418 ( aWindowSizePixel
.Width() - check
->Width
) * ( aWindowSizePixel
.Width() - check
->Width
)
419 + ( aWindowSizePixel
.Height() - check
->Height
) * ( aWindowSizePixel
.Height() - check
->Height
);
420 if ( distance
< nMinimalDistance
)
422 nMinimalDistance
= distance
;
423 nPreferredSet
= check
- aImageSizes
.begin();
429 std::vector
< Image
> aImages
;
430 if ( ( nPreferredSet
>= 0 ) && ( o3tl::make_unsigned( nPreferredSet
) < nImageSetCount
) )
433 ::std::vector
< CachedImage
> const& rImageSet( maCachedImageSets
[ nPreferredSet
] );
434 aImages
.resize( rImageSet
.size() );
435 sal_Int32 imageIndex
= 0;
436 for ( const auto& rCachedImage
: rImageSet
)
438 lcl_ensureImage_throw( xGraphicProvider
, isHighContrast
, rCachedImage
);
439 aImages
[ imageIndex
++ ] = Image(rCachedImage
.xGraphic
);
442 pThrobber
->setImageList( std::move(aImages
) );
444 catch( const Exception
& )
446 DBG_UNHANDLED_EXCEPTION("toolkit");
451 void AnimatedImagesPeer::updateImageList_nothrow( const Reference
< XAnimatedImages
>& i_images
)
455 const sal_Int32 nImageSetCount
= i_images
->getImageSetCount();
456 maCachedImageSets
.resize(0);
457 for ( sal_Int32 set
= 0; set
< nImageSetCount
; ++set
)
459 const Sequence
< OUString
> aImageURLs( i_images
->getImageSet( set
) );
460 ::std::vector
< CachedImage
> aImages
;
461 lcl_init( aImageURLs
, aImages
);
462 maCachedImageSets
.push_back( aImages
);
465 updateImageList_nothrow();
467 catch( const Exception
& )
469 DBG_UNHANDLED_EXCEPTION("toolkit");
473 } // namespace toolkit
476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */