LibreOffice Module toolkit (master) 1
animatedimagespeer.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20
22#include <helper/property.hxx>
23
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>
29
32#include <o3tl/safeint.hxx>
34#include <tools/urlobj.hxx>
36#include <vcl/svapp.hxx>
37#include <vcl/settings.hxx>
38
39#include <limits>
40#include <string_view>
41
42namespace toolkit
43{
44
45
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;
60
61 namespace ImageScaleMode = ::com::sun::star::awt::ImageScaleMode;
62
63 //= helper
64
65 namespace
66 {
67
68 OUString lcl_getHighContrastURL( OUString const& i_imageURL )
69 {
70 INetURLObject aURL( i_imageURL );
71 if ( aURL.GetProtocol() != INetProtocol::PrivSoffice )
72 {
73 OSL_VERIFY( aURL.insertName( u"sifr", false, 0 ) );
74 return aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
75 }
76 // the private: scheme is not considered to be hierarchical by INetURLObject, so manually insert the
77 // segment
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 );
80
81 OUString composer = OUString::Concat(i_imageURL.subView(0, separatorPos)) + "/sifr" +
82 i_imageURL.subView(separatorPos);
83 return composer;
84 }
85
86
87 bool lcl_ensureImage_throw( Reference< XGraphicProvider > const& i_graphicProvider, const bool i_isHighContrast, const AnimatedImagesPeer::CachedImage& i_cachedImage )
88 {
89 if ( !i_cachedImage.xGraphic.is() )
90 {
92 if ( i_isHighContrast )
93 {
94 // try (to find) the high-contrast version of the graphic first
95 aMediaProperties.put( "URL", lcl_getHighContrastURL( i_cachedImage.sImageURL ) );
96 i_cachedImage.xGraphic = i_graphicProvider->queryGraphic( aMediaProperties.getPropertyValues() );
97 }
98 if ( !i_cachedImage.xGraphic.is() )
99 {
100 aMediaProperties.put( "URL", i_cachedImage.sImageURL );
101 i_cachedImage.xGraphic = i_graphicProvider->queryGraphic( aMediaProperties.getPropertyValues() );
102 }
103 }
104 return i_cachedImage.xGraphic.is();
105 }
106
107
108 Size lcl_getGraphicSizePixel( Reference< XGraphic > const& i_graphic )
109 {
110 Size aSizePixel;
111 try
112 {
113 if ( i_graphic.is() )
114 {
115 const Reference< XPropertySet > xGraphicProps( i_graphic, UNO_QUERY_THROW );
116 OSL_VERIFY( xGraphicProps->getPropertyValue("SizePixel") >>= aSizePixel );
117 }
118 }
119 catch( const Exception& )
120 {
121 DBG_UNHANDLED_EXCEPTION("toolkit");
122 }
123 return aSizePixel;
124 }
125
126
127 void lcl_init( Sequence< OUString > const& i_imageURLs, ::std::vector< AnimatedImagesPeer::CachedImage >& o_images )
128 {
129 o_images.resize(0);
130 size_t count = size_t( i_imageURLs.getLength() );
131 o_images.reserve( count );
132 for ( const auto& rImageURL : i_imageURLs )
133 {
134 o_images.emplace_back( AnimatedImagesPeer::CachedImage{ rImageURL, nullptr } );
135 }
136 }
137
138
139 }
140
141
142 //= AnimatedImagesPeer
143
144
146 {
147 }
148
149
151 {
152 }
153
154
156 {
157 SolarMutexGuard aGuard;
158 VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>();
159 if (pThrobber)
160 pThrobber->start();
161 }
162
164 {
165 SolarMutexGuard aGuard;
166 VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>();
167 if (pThrobber)
168 pThrobber->stop();
169 }
170
172 {
173 SolarMutexGuard aGuard;
174 VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>();
175 if (pThrobber)
176 return pThrobber->isRunning();
177 return false;
178 }
179
180 void SAL_CALL AnimatedImagesPeer::setProperty( const OUString& i_propertyName, const Any& i_value )
181 {
182 SolarMutexGuard aGuard;
183
184 VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>();
185 if ( pThrobber )
186 {
187 VCLXWindow::setProperty( i_propertyName, i_value );
188 return;
189 }
190
191 const sal_uInt16 nPropertyId = GetPropertyId( i_propertyName );
192 switch ( nPropertyId )
193 {
195 {
196 sal_Int32 nStepTime( 0 );
197 if ( i_value >>= nStepTime )
198 pThrobber->setStepTime( nStepTime );
199 break;
200 }
202 {
203 bool bRepeat( true );
204 if ( i_value >>= bRepeat )
205 pThrobber->setRepeat( bRepeat );
206 break;
207 }
208
210 {
211 sal_Int16 nScaleMode( ImageScaleMode::ANISOTROPIC );
212 VclPtr<ImageControl> pImageControl = GetAsDynamic< ImageControl >();
213 if ( pImageControl && ( i_value >>= nScaleMode ) )
214 pImageControl->SetScaleMode( nScaleMode );
215 }
216 break;
217
218 default:
219 AnimatedImagesPeer_Base::setProperty( i_propertyName, i_value );
220 break;
221 }
222 }
223
224
225 Any SAL_CALL AnimatedImagesPeer::getProperty( const OUString& i_propertyName )
226 {
227 SolarMutexGuard aGuard;
228
229 Any aReturn;
230
231 VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>();
232 if ( !pThrobber )
233 return VCLXWindow::getProperty( i_propertyName );
234
235 const sal_uInt16 nPropertyId = GetPropertyId( i_propertyName );
236 switch ( nPropertyId )
237 {
239 aReturn <<= pThrobber->getStepTime();
240 break;
241
243 aReturn <<= pThrobber->getRepeat();
244 break;
245
247 {
248 VclPtr<ImageControl> pImageControl = GetAsDynamic<ImageControl>();
249 aReturn <<= ( pImageControl ? pImageControl->GetScaleMode() : ImageScaleMode::ANISOTROPIC );
250 }
251 break;
252
253 default:
254 aReturn = AnimatedImagesPeer_Base::getProperty( i_propertyName );
255 break;
256 }
257
258 return aReturn;
259 }
260
261
263 {
264 if ( i_windowEvent.GetId() == VclEventId::WindowResize )
265 {
267 }
268
269 AnimatedImagesPeer_Base::ProcessWindowEvent( i_windowEvent );
270 }
271
272
273 void AnimatedImagesPeer::impl_updateImages_nolck( const Reference< XInterface >& i_animatedImages )
274 {
275 SolarMutexGuard aGuard;
276
277 updateImageList_nothrow( Reference< XAnimatedImages >( i_animatedImages, UNO_QUERY_THROW ) );
278 }
279
280
281 void SAL_CALL AnimatedImagesPeer::elementInserted( const ContainerEvent& i_event )
282 {
283 SolarMutexGuard aGuard;
284 Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW );
285
286 sal_Int32 nPosition(0);
287 OSL_VERIFY( i_event.Accessor >>= nPosition );
288 size_t position = size_t( nPosition );
289 if ( position > maCachedImageSets.size() )
290 {
291 OSL_ENSURE( false, "AnimatedImagesPeer::elementInserted: illegal accessor/index!" );
292 updateImageList_nothrow( xAnimatedImages );
293 }
294
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 );
301 }
302
303
304 void SAL_CALL AnimatedImagesPeer::elementRemoved( const ContainerEvent& i_event )
305 {
306 SolarMutexGuard aGuard;
307 Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW );
308
309 sal_Int32 nPosition(0);
310 OSL_VERIFY( i_event.Accessor >>= nPosition );
311 size_t position = size_t( nPosition );
312 if ( position >= maCachedImageSets.size() )
313 {
314 OSL_ENSURE( false, "AnimatedImagesPeer::elementRemoved: illegal accessor/index!" );
315 updateImageList_nothrow( xAnimatedImages );
316 }
317
320 }
321
322
323 void SAL_CALL AnimatedImagesPeer::elementReplaced( const ContainerEvent& i_event )
324 {
325 SolarMutexGuard aGuard;
326 Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW );
327
328 sal_Int32 nPosition(0);
329 OSL_VERIFY( i_event.Accessor >>= nPosition );
330 size_t position = size_t( nPosition );
331 if ( position >= maCachedImageSets.size() )
332 {
333 OSL_ENSURE( false, "AnimatedImagesPeer::elementReplaced: illegal accessor/index!" );
334 updateImageList_nothrow( xAnimatedImages );
335 }
336
337 Sequence< OUString > aImageURLs;
338 OSL_VERIFY( i_event.Element >>= aImageURLs );
339 ::std::vector< CachedImage > aImages;
340 lcl_init( aImageURLs, aImages );
341 maCachedImageSets[ position ] = aImages;
343 }
344
345
346 void SAL_CALL AnimatedImagesPeer::disposing( const EventObject& i_event )
347 {
348 VCLXWindow::disposing( i_event );
349 }
350
351
352 void SAL_CALL AnimatedImagesPeer::modified( const EventObject& i_event )
353 {
354 impl_updateImages_nolck( i_event.Source );
355 }
356
357
359 {
360 AnimatedImagesPeer_Base::dispose();
361 SolarMutexGuard aGuard;
362 maCachedImageSets.resize(0);
363 }
364
366 {
367 VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>();
368 if ( !pThrobber )
369 return;
370
371 try
372 {
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) );
376
377 const bool isHighContrast = pThrobber->GetSettings().GetStyleSettings().GetHighContrastMode();
378
379 sal_Int32 nPreferredSet = -1;
380 const size_t nImageSetCount = maCachedImageSets.size();
381 if ( nImageSetCount < 2 )
382 {
383 nPreferredSet = sal_Int32( nImageSetCount ) - 1;
384 }
385 else
386 {
387 ::std::vector< Size > aImageSizes( nImageSetCount );
388 for ( size_t nImageSet = 0; nImageSet < nImageSetCount; ++nImageSet )
389 {
390 ::std::vector< CachedImage > const& rImageSet( maCachedImageSets[ nImageSet ] );
391 if ( ( rImageSet.empty() )
392 || ( !lcl_ensureImage_throw( xGraphicProvider, isHighContrast, rImageSet[0] ) )
393 )
394 {
395 aImageSizes[ nImageSet ] = Size( SAL_MAX_INT32, SAL_MAX_INT32 );
396 }
397 else
398 {
399 aImageSizes[ nImageSet ] = lcl_getGraphicSizePixel( rImageSet[0].xGraphic );
400 }
401 }
402
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();
408 ++check
409 )
410 {
411 if ( ( check->Width > aWindowSizePixel.Width() )
412 || ( check->Height > aWindowSizePixel.Height() )
413 )
414 // do not use an image set which doesn't fit into the window
415 continue;
416
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 )
421 {
422 nMinimalDistance = distance;
423 nPreferredSet = check - aImageSizes.begin();
424 }
425 }
426 }
427
428 // found a set?
429 std::vector< Image > aImages;
430 if ( ( nPreferredSet >= 0 ) && ( o3tl::make_unsigned( nPreferredSet ) < nImageSetCount ) )
431 {
432 // => set the images
433 ::std::vector< CachedImage > const& rImageSet( maCachedImageSets[ nPreferredSet ] );
434 aImages.resize( rImageSet.size() );
435 sal_Int32 imageIndex = 0;
436 for ( const auto& rCachedImage : rImageSet )
437 {
438 lcl_ensureImage_throw( xGraphicProvider, isHighContrast, rCachedImage );
439 aImages[ imageIndex++ ] = Image(rCachedImage.xGraphic);
440 }
441 }
442 pThrobber->setImageList( std::move(aImages) );
443 }
444 catch( const Exception& )
445 {
446 DBG_UNHANDLED_EXCEPTION("toolkit");
447 }
448 }
449
450
451 void AnimatedImagesPeer::updateImageList_nothrow( const Reference< XAnimatedImages >& i_images )
452 {
453 try
454 {
455 const sal_Int32 nImageSetCount = i_images->getImageSetCount();
456 maCachedImageSets.resize(0);
457 for ( sal_Int32 set = 0; set < nImageSetCount; ++set )
458 {
459 const Sequence< OUString > aImageURLs( i_images->getImageSet( set ) );
460 ::std::vector< CachedImage > aImages;
461 lcl_init( aImageURLs, aImages );
462 maCachedImageSets.push_back( aImages );
463 }
464
466 }
467 catch( const Exception& )
468 {
469 DBG_UNHANDLED_EXCEPTION("toolkit");
470 }
471 }
472
473} // namespace toolkit
474
475
476/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
PropFlags nPropertyId
css::uno::Any SAL_CALL getProperty(const OUString &PropertyName) override
void SAL_CALL setProperty(const OUString &PropertyName, const css::uno::Any &Value) override
virtual void SAL_CALL disposing(const css::lang::EventObject &Source) override
VclEventId GetId() const
bool put(const OUString &_rValueName, const VALUE_TYPE &_rValue)
css::uno::Sequence< css::beans::PropertyValue > getPropertyValues() const
virtual css::uno::Any SAL_CALL getProperty(const OUString &PropertyName) override
std::vector< std::vector< CachedImage > > maCachedImageSets
void impl_updateImages_nolck(const css::uno::Reference< css::uno::XInterface > &i_animatedImages)
updates our images with the ones from the given XAnimatedImages component
virtual void SAL_CALL startAnimation() override
virtual void SAL_CALL modified(const css::lang::EventObject &i_event) override
virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent &Event) override
virtual sal_Bool SAL_CALL isAnimationRunning() override
virtual void SAL_CALL setProperty(const OUString &PropertyName, const css::uno::Any &Value) override
virtual void SAL_CALL stopAnimation() override
virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent &Event) override
void SAL_CALL dispose() override
virtual ~AnimatedImagesPeer() override
virtual void SAL_CALL elementInserted(const css::container::ContainerEvent &Event) override
virtual void SAL_CALL disposing(const css::lang::EventObject &i_event) override
void ProcessWindowEvent(const VclWindowEvent &i_windowEvent) override
#define ENSURE_OR_RETURN(c, m, r)
#define DBG_UNHANDLED_EXCEPTION(...)
URL aURL
float u
double distance
def position(n=-1)
void set(css::uno::UnoInterfaceReference const &value)
@ Exception
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > > check(dp_misc::DescriptionInfoset const &infoset)
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
long Long
sal_uInt16 GetPropertyId(const OUString &rPropertyName)
Definition: property.cxx:278
#define BASEPROPERTY_STEP_TIME
Definition: property.hxx:151
#define BASEPROPERTY_AUTO_REPEAT
Definition: property.hxx:193
#define BASEPROPERTY_IMAGE_SCALE_MODE
Definition: property.hxx:172
css::uno::Reference< css::graphic::XGraphic > xGraphic
#define SAL_MAX_INT32
unsigned char sal_Bool