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 
84         CachedImage()
85             :sImageURL()
86             ,xGraphic()
87         {
88         }
89 
90         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 
102         AnimatedImagesPeer_Data( AnimatedImagesPeer& i_antiImpl )
103             :rAntiImpl( i_antiImpl )
104             ,aCachedImageSets()
105         {
106         }
107     };
108 
109 	//==================================================================================================================
110 	//= helper
111 	//==================================================================================================================
112     namespace
113     {
114         //--------------------------------------------------------------------------------------------------------------
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 	    //--------------------------------------------------------------------------------------------------------------
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 	    //--------------------------------------------------------------------------------------------------------------
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 	    //--------------------------------------------------------------------------------------------------------------
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 	    //--------------------------------------------------------------------------------------------------------------
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 	//------------------------------------------------------------------------------------------------------------------
297     AnimatedImagesPeer::AnimatedImagesPeer()
298         :AnimatedImagesPeer_Base()
299         ,m_pData( new AnimatedImagesPeer_Data( *this ) )
300     {
301     }
302 
303 	//------------------------------------------------------------------------------------------------------------------
304     AnimatedImagesPeer::~AnimatedImagesPeer()
305     {
306     }
307 
308 	//------------------------------------------------------------------------------------------------------------------
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 	//------------------------------------------------------------------------------------------------------------------
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 	//------------------------------------------------------------------------------------------------------------------
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 	//------------------------------------------------------------------------------------------------------------------
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 	//------------------------------------------------------------------------------------------------------------------
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     //------------------------------------------------------------------------------------------------------------------
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     //------------------------------------------------------------------------------------------------------------------
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     //------------------------------------------------------------------------------------------------------------------
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     //------------------------------------------------------------------------------------------------------------------
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     //------------------------------------------------------------------------------------------------------------------
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     //------------------------------------------------------------------------------------------------------------------
507     void SAL_CALL AnimatedImagesPeer::disposing( const EventObject& i_event ) throw (RuntimeException)
508     {
509         VCLXWindow::disposing( i_event );
510     }
511 
512     //------------------------------------------------------------------------------------------------------------------
513     void SAL_CALL AnimatedImagesPeer::modified( const EventObject& i_event ) throw (RuntimeException)
514     {
515         impl_updateImages_nolck( i_event.Source );
516     }
517 
518     //------------------------------------------------------------------------------------------------------------------
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