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