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 <toolkit/awt/animatedimagespeer.hxx>
22 #include <toolkit/helper/property.hxx>
24 #include <com/sun/star/awt/XAnimatedImages.hpp>
25 #include <com/sun/star/awt/Size.hpp>
26 #include <com/sun/star/graphic/GraphicProvider.hpp>
27 #include <com/sun/star/graphic/XGraphicProvider.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/graphic/XGraphic.hpp>
30 #include <com/sun/star/awt/ImageScaleMode.hpp>
32 #include <comphelper/namedvaluecollection.hxx>
33 #include <comphelper/processfactory.hxx>
34 #include <rtl/ustrbuf.hxx>
35 #include <tools/diagnose_ex.h>
36 #include <tools/urlobj.hxx>
37 #include <vcl/throbber.hxx>
38 #include <vcl/svapp.hxx>
39 #include <vcl/settings.hxx>
42 #include <string_view>
48 using ::com::sun::star::uno::XComponentContext
;
49 using ::com::sun::star::uno::Reference
;
50 using ::com::sun::star::uno::XInterface
;
51 using ::com::sun::star::uno::UNO_QUERY
;
52 using ::com::sun::star::uno::UNO_QUERY_THROW
;
53 using ::com::sun::star::uno::Exception
;
54 using ::com::sun::star::uno::RuntimeException
;
55 using ::com::sun::star::uno::Any
;
56 using ::com::sun::star::uno::Sequence
;
57 using ::com::sun::star::lang::EventObject
;
58 using ::com::sun::star::container::ContainerEvent
;
59 using ::com::sun::star::awt::XAnimatedImages
;
60 using ::com::sun::star::awt::Size
;
61 using ::com::sun::star::graphic::XGraphicProvider
;
62 using ::com::sun::star::beans::XPropertySet
;
63 using ::com::sun::star::graphic::XGraphic
;
65 namespace ImageScaleMode
= ::com::sun::star::awt::ImageScaleMode
;
68 //= AnimatedImagesPeer_Data
73 mutable Reference
< XGraphic
> xGraphic
;
81 explicit CachedImage( OUString
const& i_imageURL
)
82 :sImageURL( i_imageURL
)
88 struct AnimatedImagesPeer_Data
90 AnimatedImagesPeer
& rAntiImpl
;
91 ::std::vector
< ::std::vector
< CachedImage
> > aCachedImageSets
;
93 explicit AnimatedImagesPeer_Data( AnimatedImagesPeer
& i_antiImpl
)
94 :rAntiImpl( i_antiImpl
)
106 OUString
lcl_getHighContrastURL( OUString
const& i_imageURL
)
108 INetURLObject
aURL( i_imageURL
);
109 if ( aURL
.GetProtocol() != INetProtocol::PrivSoffice
)
111 OSL_VERIFY( aURL
.insertName( "sifr", false, 0 ) );
112 return aURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
114 // the private: scheme is not considered to be hierarchical by INetURLObject, so manually insert the
116 const sal_Int32 separatorPos
= i_imageURL
.indexOf( '/' );
117 ENSURE_OR_RETURN( separatorPos
!= -1, "lcl_getHighContrastURL: unsupported URL scheme - cannot automatically determine HC version!", i_imageURL
);
119 OUStringBuffer composer
;
120 composer
.append( std::u16string_view(i_imageURL
).substr(0, separatorPos
) );
121 composer
.append( "/sifr" );
122 composer
.append( std::u16string_view(i_imageURL
).substr(separatorPos
) );
123 return composer
.makeStringAndClear();
127 bool lcl_ensureImage_throw( Reference
< XGraphicProvider
> const& i_graphicProvider
, const bool i_isHighContrast
, const CachedImage
& i_cachedImage
)
129 if ( !i_cachedImage
.xGraphic
.is() )
131 ::comphelper::NamedValueCollection aMediaProperties
;
132 if ( i_isHighContrast
)
134 // try (to find) the high-contrast version of the graphic first
135 aMediaProperties
.put( "URL", lcl_getHighContrastURL( i_cachedImage
.sImageURL
) );
136 i_cachedImage
.xGraphic
.set( i_graphicProvider
->queryGraphic( aMediaProperties
.getPropertyValues() ), UNO_QUERY
);
138 if ( !i_cachedImage
.xGraphic
.is() )
140 aMediaProperties
.put( "URL", i_cachedImage
.sImageURL
);
141 i_cachedImage
.xGraphic
.set( i_graphicProvider
->queryGraphic( aMediaProperties
.getPropertyValues() ), UNO_QUERY
);
144 return i_cachedImage
.xGraphic
.is();
148 Size
lcl_getGraphicSizePixel( Reference
< XGraphic
> const& i_graphic
)
153 if ( i_graphic
.is() )
155 const Reference
< XPropertySet
> xGraphicProps( i_graphic
, UNO_QUERY_THROW
);
156 OSL_VERIFY( xGraphicProps
->getPropertyValue("SizePixel") >>= aSizePixel
);
159 catch( const Exception
& )
161 DBG_UNHANDLED_EXCEPTION("toolkit");
167 void lcl_init( Sequence
< OUString
> const& i_imageURLs
, ::std::vector
< CachedImage
>& o_images
)
170 size_t count
= size_t( i_imageURLs
.getLength() );
171 o_images
.reserve( count
);
172 for ( size_t i
= 0; i
< count
; ++i
)
174 o_images
.emplace_back( i_imageURLs
[i
] );
179 void lcl_updateImageList_nothrow( AnimatedImagesPeer_Data
& i_data
)
181 VclPtr
<Throbber
> pThrobber
= i_data
.rAntiImpl
.GetAsDynamic
<Throbber
>();
187 // collect the image sizes of the different image sets
188 const Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
189 const Reference
< XGraphicProvider
> xGraphicProvider( css::graphic::GraphicProvider::create(xContext
) );
191 const bool isHighContrast
= pThrobber
->GetSettings().GetStyleSettings().GetHighContrastMode();
193 sal_Int32 nPreferredSet
= -1;
194 const size_t nImageSetCount
= i_data
.aCachedImageSets
.size();
195 if ( nImageSetCount
< 2 )
197 nPreferredSet
= sal_Int32( nImageSetCount
) - 1;
201 ::std::vector
< Size
> aImageSizes( nImageSetCount
);
202 for ( size_t nImageSet
= 0; nImageSet
< nImageSetCount
; ++nImageSet
)
204 ::std::vector
< CachedImage
> const& rImageSet( i_data
.aCachedImageSets
[ nImageSet
] );
205 if ( ( rImageSet
.empty() )
206 || ( !lcl_ensureImage_throw( xGraphicProvider
, isHighContrast
, rImageSet
[0] ) )
209 aImageSizes
[ nImageSet
] = Size( SAL_MAX_INT32
, SAL_MAX_INT32
);
213 aImageSizes
[ nImageSet
] = lcl_getGraphicSizePixel( rImageSet
[0].xGraphic
);
217 // find the set with the smallest difference between window size and image size
218 const ::Size aWindowSizePixel
= pThrobber
->GetSizePixel();
219 long nMinimalDistance
= ::std::numeric_limits
< long >::max();
220 for ( ::std::vector
< Size
>::const_iterator check
= aImageSizes
.begin();
221 check
!= aImageSizes
.end();
225 if ( ( check
->Width
> aWindowSizePixel
.Width() )
226 || ( check
->Height
> aWindowSizePixel
.Height() )
228 // do not use an image set which doesn't fit into the window
231 const sal_Int64 distance
=
232 ( aWindowSizePixel
.Width() - check
->Width
) * ( aWindowSizePixel
.Width() - check
->Width
)
233 + ( aWindowSizePixel
.Height() - check
->Height
) * ( aWindowSizePixel
.Height() - check
->Height
);
234 if ( distance
< nMinimalDistance
)
236 nMinimalDistance
= distance
;
237 nPreferredSet
= check
- aImageSizes
.begin();
243 std::vector
< Image
> aImages
;
244 if ( ( nPreferredSet
>= 0 ) && ( size_t( nPreferredSet
) < nImageSetCount
) )
247 ::std::vector
< CachedImage
> const& rImageSet( i_data
.aCachedImageSets
[ nPreferredSet
] );
248 aImages
.resize( rImageSet
.size() );
249 sal_Int32 imageIndex
= 0;
250 for ( const auto& rCachedImage
: rImageSet
)
252 lcl_ensureImage_throw( xGraphicProvider
, isHighContrast
, rCachedImage
);
253 aImages
[ imageIndex
++ ] = Image(rCachedImage
.xGraphic
);
256 pThrobber
->setImageList( aImages
);
258 catch( const Exception
& )
260 DBG_UNHANDLED_EXCEPTION("toolkit");
265 void lcl_updateImageList_nothrow( AnimatedImagesPeer_Data
& i_data
, const Reference
< XAnimatedImages
>& i_images
)
269 const sal_Int32 nImageSetCount
= i_images
->getImageSetCount();
270 i_data
.aCachedImageSets
.resize(0);
271 for ( sal_Int32 set
= 0; set
< nImageSetCount
; ++set
)
273 const Sequence
< OUString
> aImageURLs( i_images
->getImageSet( set
) );
274 ::std::vector
< CachedImage
> aImages
;
275 lcl_init( aImageURLs
, aImages
);
276 i_data
.aCachedImageSets
.push_back( aImages
);
279 lcl_updateImageList_nothrow( i_data
);
281 catch( const Exception
& )
283 DBG_UNHANDLED_EXCEPTION("toolkit");
289 //= AnimatedImagesPeer
292 AnimatedImagesPeer::AnimatedImagesPeer()
293 :AnimatedImagesPeer_Base()
294 ,m_xData( new AnimatedImagesPeer_Data( *this ) )
299 AnimatedImagesPeer::~AnimatedImagesPeer()
304 void SAL_CALL
AnimatedImagesPeer::startAnimation()
306 SolarMutexGuard aGuard
;
307 VclPtr
<Throbber
> pThrobber
= GetAsDynamic
<Throbber
>();
312 void SAL_CALL
AnimatedImagesPeer::stopAnimation()
314 SolarMutexGuard aGuard
;
315 VclPtr
<Throbber
> pThrobber
= GetAsDynamic
<Throbber
>();
320 sal_Bool SAL_CALL
AnimatedImagesPeer::isAnimationRunning()
322 SolarMutexGuard aGuard
;
323 VclPtr
<Throbber
> pThrobber
= GetAsDynamic
<Throbber
>();
325 return pThrobber
->isRunning();
329 void SAL_CALL
AnimatedImagesPeer::setProperty( const OUString
& i_propertyName
, const Any
& i_value
)
331 SolarMutexGuard aGuard
;
333 VclPtr
<Throbber
> pThrobber
= GetAsDynamic
<Throbber
>();
336 VCLXWindow::setProperty( i_propertyName
, i_value
);
340 const sal_uInt16 nPropertyId
= GetPropertyId( i_propertyName
);
341 switch ( nPropertyId
)
343 case BASEPROPERTY_STEP_TIME
:
345 sal_Int32
nStepTime( 0 );
346 if ( i_value
>>= nStepTime
)
347 pThrobber
->setStepTime( nStepTime
);
350 case BASEPROPERTY_AUTO_REPEAT
:
352 bool bRepeat( true );
353 if ( i_value
>>= bRepeat
)
354 pThrobber
->setRepeat( bRepeat
);
358 case BASEPROPERTY_IMAGE_SCALE_MODE
:
360 sal_Int16
nScaleMode( ImageScaleMode::ANISOTROPIC
);
361 VclPtr
<ImageControl
> pImageControl
= GetAsDynamic
< ImageControl
>();
362 if ( pImageControl
&& ( i_value
>>= nScaleMode
) )
363 pImageControl
->SetScaleMode( nScaleMode
);
368 AnimatedImagesPeer_Base::setProperty( i_propertyName
, i_value
);
374 Any SAL_CALL
AnimatedImagesPeer::getProperty( const OUString
& i_propertyName
)
376 SolarMutexGuard aGuard
;
380 VclPtr
<Throbber
> pThrobber
= GetAsDynamic
<Throbber
>();
382 return VCLXWindow::getProperty( i_propertyName
);
384 const sal_uInt16 nPropertyId
= GetPropertyId( i_propertyName
);
385 switch ( nPropertyId
)
387 case BASEPROPERTY_STEP_TIME
:
388 aReturn
<<= pThrobber
->getStepTime();
391 case BASEPROPERTY_AUTO_REPEAT
:
392 aReturn
<<= pThrobber
->getRepeat();
395 case BASEPROPERTY_IMAGE_SCALE_MODE
:
397 VclPtr
<ImageControl
> pImageControl
= GetAsDynamic
<ImageControl
>();
398 aReturn
<<= ( pImageControl
? pImageControl
->GetScaleMode() : ImageScaleMode::ANISOTROPIC
);
403 aReturn
= AnimatedImagesPeer_Base::getProperty( i_propertyName
);
411 void AnimatedImagesPeer::ProcessWindowEvent( const VclWindowEvent
& i_windowEvent
)
413 if ( i_windowEvent
.GetId() == VclEventId::WindowResize
)
415 lcl_updateImageList_nothrow( *m_xData
);
418 AnimatedImagesPeer_Base::ProcessWindowEvent( i_windowEvent
);
422 void AnimatedImagesPeer::impl_updateImages_nolck( const Reference
< XInterface
>& i_animatedImages
)
424 SolarMutexGuard aGuard
;
426 lcl_updateImageList_nothrow( *m_xData
, Reference
< XAnimatedImages
>( i_animatedImages
, UNO_QUERY_THROW
) );
430 void SAL_CALL
AnimatedImagesPeer::elementInserted( const ContainerEvent
& i_event
)
432 SolarMutexGuard aGuard
;
433 Reference
< XAnimatedImages
> xAnimatedImages( i_event
.Source
, UNO_QUERY_THROW
);
435 sal_Int32
nPosition(0);
436 OSL_VERIFY( i_event
.Accessor
>>= nPosition
);
437 size_t position
= size_t( nPosition
);
438 if ( position
> m_xData
->aCachedImageSets
.size() )
440 OSL_ENSURE( false, "AnimatedImagesPeer::elementInserted: illegal accessor/index!" );
441 lcl_updateImageList_nothrow( *m_xData
, xAnimatedImages
);
444 Sequence
< OUString
> aImageURLs
;
445 OSL_VERIFY( i_event
.Element
>>= aImageURLs
);
446 ::std::vector
< CachedImage
> aImages
;
447 lcl_init( aImageURLs
, aImages
);
448 m_xData
->aCachedImageSets
.insert( m_xData
->aCachedImageSets
.begin() + position
, aImages
);
449 lcl_updateImageList_nothrow( *m_xData
);
453 void SAL_CALL
AnimatedImagesPeer::elementRemoved( const ContainerEvent
& i_event
)
455 SolarMutexGuard aGuard
;
456 Reference
< XAnimatedImages
> xAnimatedImages( i_event
.Source
, UNO_QUERY_THROW
);
458 sal_Int32
nPosition(0);
459 OSL_VERIFY( i_event
.Accessor
>>= nPosition
);
460 size_t position
= size_t( nPosition
);
461 if ( position
>= m_xData
->aCachedImageSets
.size() )
463 OSL_ENSURE( false, "AnimatedImagesPeer::elementRemoved: illegal accessor/index!" );
464 lcl_updateImageList_nothrow( *m_xData
, xAnimatedImages
);
467 m_xData
->aCachedImageSets
.erase( m_xData
->aCachedImageSets
.begin() + position
);
468 lcl_updateImageList_nothrow( *m_xData
);
472 void SAL_CALL
AnimatedImagesPeer::elementReplaced( const ContainerEvent
& i_event
)
474 SolarMutexGuard aGuard
;
475 Reference
< XAnimatedImages
> xAnimatedImages( i_event
.Source
, UNO_QUERY_THROW
);
477 sal_Int32
nPosition(0);
478 OSL_VERIFY( i_event
.Accessor
>>= nPosition
);
479 size_t position
= size_t( nPosition
);
480 if ( position
>= m_xData
->aCachedImageSets
.size() )
482 OSL_ENSURE( false, "AnimatedImagesPeer::elementReplaced: illegal accessor/index!" );
483 lcl_updateImageList_nothrow( *m_xData
, xAnimatedImages
);
486 Sequence
< OUString
> aImageURLs
;
487 OSL_VERIFY( i_event
.Element
>>= aImageURLs
);
488 ::std::vector
< CachedImage
> aImages
;
489 lcl_init( aImageURLs
, aImages
);
490 m_xData
->aCachedImageSets
[ position
] = aImages
;
491 lcl_updateImageList_nothrow( *m_xData
);
495 void SAL_CALL
AnimatedImagesPeer::disposing( const EventObject
& i_event
)
497 VCLXWindow::disposing( i_event
);
501 void SAL_CALL
AnimatedImagesPeer::modified( const EventObject
& i_event
)
503 impl_updateImages_nolck( i_event
.Source
);
507 void SAL_CALL
AnimatedImagesPeer::dispose( )
509 AnimatedImagesPeer_Base::dispose();
510 SolarMutexGuard aGuard
;
511 m_xData
->aCachedImageSets
.resize(0);
515 } // namespace toolkit
518 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */