1 /*************************************************************************
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3  *
4  * Copyright 2000, 2010 Oracle and/or its affiliates.
5  *
6  * OpenOffice.org - a multi-platform office productivity suite
7  *
8  * This file is part of OpenOffice.org.
9  *
10  * OpenOffice.org is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU Lesser General Public License version 3
12  * only, as published by the Free Software Foundation.
13  *
14  * OpenOffice.org is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License version 3 for more details
18  * (a copy is included in the LICENSE file that accompanied this code).
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * version 3 along with OpenOffice.org.  If not, see
22  * <http://www.openoffice.org/license.html>
23  * for a copy of the LGPLv3 License.
24  *
25  ************************************************************************/
26 
27 #include "precompiled_toolkit.hxx"
28 
29 #include "toolkit/awt/animatedimagespeer.hxx"
30 #include "toolkit/helper/property.hxx"
31 
32 /** === begin UNO includes === **/
33 #include <com/sun/star/awt/XAnimatedImages.hpp>
34 #include <com/sun/star/awt/Size.hpp>
35 #include <com/sun/star/graphic/XGraphicProvider.hpp>
36 #include <com/sun/star/beans/XPropertySet.hpp>
37 #include <com/sun/star/graphic/XGraphic.hpp>
38 #include <com/sun/star/awt/ImageScaleMode.hpp>
39 /** === end UNO includes === **/
40 
41 #include <comphelper/componentcontext.hxx>
42 #include <comphelper/namedvaluecollection.hxx>
43 #include <comphelper/processfactory.hxx>
44 #include <rtl/ustrbuf.hxx>
45 #include <tools/diagnose_ex.h>
46 #include <tools/urlobj.hxx>
47 #include <vcl/throbber.hxx>
48 
49 #include <limits>
50 
51 //......................................................................................................................
52 namespace toolkit
53 {
54 //......................................................................................................................
55 
56 	/** === begin UNO using === **/
57 	using ::com::sun::star::uno::Reference;
58 	using ::com::sun::star::uno::XInterface;
59 	using ::com::sun::star::uno::UNO_QUERY;
60 	using ::com::sun::star::uno::UNO_QUERY_THROW;
61 	using ::com::sun::star::uno::UNO_SET_THROW;
62 	using ::com::sun::star::uno::Exception;
63 	using ::com::sun::star::uno::RuntimeException;
64 	using ::com::sun::star::uno::Any;
65 	using ::com::sun::star::uno::makeAny;
66 	using ::com::sun::star::uno::Sequence;
67 	using ::com::sun::star::uno::Type;
68     using ::com::sun::star::lang::EventObject;
69     using ::com::sun::star::container::ContainerEvent;
70     using ::com::sun::star::awt::XAnimatedImages;
71     using ::com::sun::star::awt::Size;
72     using ::com::sun::star::lang::XMultiServiceFactory;
73     using ::com::sun::star::graphic::XGraphicProvider;
74     using ::com::sun::star::beans::XPropertySet;
75     using ::com::sun::star::graphic::XGraphic;
76 	/** === end UNO using === **/
77     namespace ImageScaleMode = ::com::sun::star::awt::ImageScaleMode;
78 
79 	//==================================================================================================================
80 	//= AnimatedImagesPeer_Data
81 	//==================================================================================================================
82     struct CachedImage
83     {
84         ::rtl::OUString                 sImageURL;
85         mutable Reference< XGraphic >   xGraphic;
86 
87         CachedImage()
88             :sImageURL()
89             ,xGraphic()
90         {
91         }
92 
93         CachedImage( ::rtl::OUString const& i_imageURL )
94             :sImageURL( i_imageURL )
95             ,xGraphic()
96         {
97         }
98     };
99 
100     struct AnimatedImagesPeer_Data
101     {
102         AnimatedImagesPeer&                             rAntiImpl;
103         ::std::vector< ::std::vector< CachedImage > >   aCachedImageSets;
104 
105         AnimatedImagesPeer_Data( AnimatedImagesPeer& i_antiImpl )
106             :rAntiImpl( i_antiImpl )
107             ,aCachedImageSets()
108         {
109         }
110     };
111 
112 	//==================================================================================================================
113 	//= helper
114 	//==================================================================================================================
115     namespace
116     {
117 	    //--------------------------------------------------------------------------------------------------------------
118         ::rtl::OUString lcl_getHighContrastURL( ::rtl::OUString const& i_imageURL )
119         {
120             INetURLObject aURL( i_imageURL );
121             if ( aURL.GetProtocol() != INET_PROT_PRIV_SOFFICE )
122             {
123                 OSL_VERIFY( aURL.insertName( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "hicontrast" ) ), false, 0 ) );
124                 return aURL.GetMainURL( INetURLObject::NO_DECODE );
125             }
126             // the private: scheme is not considered to be hierarchical by INetURLObject, so manually insert the
127             // segment
128             const sal_Int32 separatorPos = i_imageURL.indexOf( '/' );
129             ENSURE_OR_RETURN( separatorPos != -1, "lcl_getHighContrastURL: unsipported URL scheme - cannot automatically determine HC version!", i_imageURL );
130 
131             ::rtl::OUStringBuffer composer;
132             composer.append( i_imageURL.copy( 0, separatorPos ) );
133             composer.appendAscii( "/hicontrast" );
134             composer.append( i_imageURL.copy( separatorPos ) );
135             return composer.makeStringAndClear();
136         }
137 
138 	    //--------------------------------------------------------------------------------------------------------------
139         bool lcl_ensureImage_throw( Reference< XGraphicProvider > const& i_graphicProvider, const bool i_isHighContrast, const CachedImage& i_cachedImage )
140         {
141             if ( !i_cachedImage.xGraphic.is() )
142             {
143                 ::comphelper::NamedValueCollection aMediaProperties;
144                 if ( i_isHighContrast )
145                 {
146                     // try (to find) the high-contrast version of the graphic first
147                     aMediaProperties.put( "URL", lcl_getHighContrastURL( i_cachedImage.sImageURL ) );
148                     i_cachedImage.xGraphic.set( i_graphicProvider->queryGraphic( aMediaProperties.getPropertyValues() ), UNO_QUERY );
149                 }
150                 if ( !i_cachedImage.xGraphic.is() )
151                 {
152                     aMediaProperties.put( "URL", i_cachedImage.sImageURL );
153                     i_cachedImage.xGraphic.set( i_graphicProvider->queryGraphic( aMediaProperties.getPropertyValues() ), UNO_QUERY );
154                 }
155             }
156             return i_cachedImage.xGraphic.is();
157         }
158 
159 	    //--------------------------------------------------------------------------------------------------------------
160         Size lcl_getGraphicSizePixel( Reference< XGraphic > const& i_graphic )
161         {
162             Size aSizePixel;
163             try
164             {
165                 if ( i_graphic.is() )
166                 {
167                     const Reference< XPropertySet > xGraphicProps( i_graphic, UNO_QUERY_THROW );
168                     OSL_VERIFY( xGraphicProps->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SizePixel" ) ) ) >>= aSizePixel );
169                 }
170             }
171             catch( const Exception& )
172             {
173             	DBG_UNHANDLED_EXCEPTION();
174             }
175             return aSizePixel;
176         }
177 
178 	    //--------------------------------------------------------------------------------------------------------------
179         void lcl_init( Sequence< ::rtl::OUString > const& i_imageURLs, ::std::vector< CachedImage >& o_images )
180         {
181             o_images.resize(0);
182             size_t count = size_t( i_imageURLs.getLength() );
183             o_images.reserve( count );
184             for ( size_t i = 0; i < count; ++i )
185             {
186                 o_images.push_back( CachedImage( i_imageURLs[i] ) );
187             }
188         }
189 
190 	    //--------------------------------------------------------------------------------------------------------------
191         void lcl_updateImageList_nothrow( AnimatedImagesPeer_Data& i_data )
192         {
193             Throbber* pThrobber = dynamic_cast< Throbber* >( i_data.rAntiImpl.GetWindow() );
194             if ( pThrobber == NULL )
195                 return;
196 
197             try
198             {
199                 // collect the image sizes of the different image sets
200                 const ::comphelper::ComponentContext aContext( ::comphelper::getProcessServiceFactory() );
201                 const Reference< XGraphicProvider > xGraphicProvider( aContext.createComponent( "com.sun.star.graphic.GraphicProvider" ), UNO_QUERY_THROW );
202 
203                 const bool isHighContrast = pThrobber->GetSettings().GetStyleSettings().GetHighContrastMode();
204 
205                 sal_Int32 nPreferredSet = -1;
206                 const size_t nImageSetCount = i_data.aCachedImageSets.size();
207                 if ( nImageSetCount < 2 )
208                 {
209                     nPreferredSet = sal_Int32( nImageSetCount ) - 1;
210                 }
211                 else
212                 {
213                     ::std::vector< Size > aImageSizes( nImageSetCount );
214                     for ( sal_Int32 nImageSet = 0; size_t( nImageSet ) < nImageSetCount; ++nImageSet )
215                     {
216                         ::std::vector< CachedImage > const& rImageSet( i_data.aCachedImageSets[ nImageSet ] );
217                         if  (   ( rImageSet.empty() )
218                             ||  ( !lcl_ensureImage_throw( xGraphicProvider, isHighContrast, rImageSet[0] ) )
219                             )
220                         {
221                             aImageSizes[ nImageSet ] = Size( ::std::numeric_limits< long >::max(), ::std::numeric_limits< long >::max() );
222                         }
223                         else
224                         {
225                             aImageSizes[ nImageSet ] = lcl_getGraphicSizePixel( rImageSet[0].xGraphic );
226                         }
227                     }
228 
229                     // find the set with the smallest difference between window size and image size
230                     const ::Size aWindowSizePixel = pThrobber->GetSizePixel();
231                     long nMinimalDistance = ::std::numeric_limits< long >::max();
232                     for (   ::std::vector< Size >::const_iterator check = aImageSizes.begin();
233                             check != aImageSizes.end();
234                             ++check
235                         )
236                     {
237                         if  (   ( check->Width > aWindowSizePixel.Width() )
238                             ||  ( check->Height > aWindowSizePixel.Height() )
239                             )
240                             // do not use an image set which doesn't fit into the window
241                             continue;
242 
243                         const sal_Int64 distance =
244                                 ( aWindowSizePixel.Width() - check->Width ) * ( aWindowSizePixel.Width() - check->Width )
245                             +   ( aWindowSizePixel.Height() - check->Height ) * ( aWindowSizePixel.Height() - check->Height );
246                         if ( distance < nMinimalDistance )
247                         {
248                             nMinimalDistance = distance;
249                             nPreferredSet = check - aImageSizes.begin();
250                         }
251                     }
252                 }
253 
254                 // found a set?
255                 Sequence< Reference< XGraphic > > aImages;
256                 if ( ( nPreferredSet >= 0 ) && ( size_t( nPreferredSet ) < nImageSetCount ) )
257                 {
258                     // => set the images
259                     ::std::vector< CachedImage > const& rImageSet( i_data.aCachedImageSets[ nPreferredSet ] );
260                     aImages.realloc( rImageSet.size() );
261                     sal_Int32 imageIndex = 0;
262                     for (   ::std::vector< CachedImage >::const_iterator cachedImage = rImageSet.begin();
263                             cachedImage != rImageSet.end();
264                             ++cachedImage, ++imageIndex
265                         )
266                     {
267                         lcl_ensureImage_throw( xGraphicProvider, isHighContrast, *cachedImage );
268                         aImages[ imageIndex ] = cachedImage->xGraphic;
269                     }
270                 }
271                 pThrobber->setImageList( aImages );
272             }
273             catch( const Exception& )
274             {
275             	DBG_UNHANDLED_EXCEPTION();
276             }
277         }
278 
279 	    //--------------------------------------------------------------------------------------------------------------
280         void lcl_updateImageList_nothrow( AnimatedImagesPeer_Data& i_data, const Reference< XAnimatedImages >& i_images )
281         {
282             try
283             {
284                 const sal_Int32 nImageSetCount = i_images->getImageSetCount();
285                 i_data.aCachedImageSets.resize(0);
286                 for ( sal_Int32 set = 0;  set < nImageSetCount; ++set )
287                 {
288                     const Sequence< ::rtl::OUString > aImageURLs( i_images->getImageSet( set ) );
289                     ::std::vector< CachedImage > aImages;
290                     lcl_init( aImageURLs, aImages );
291                     i_data.aCachedImageSets.push_back( aImages );
292                 }
293 
294                 lcl_updateImageList_nothrow( i_data );
295             }
296             catch( const Exception& )
297             {
298             	DBG_UNHANDLED_EXCEPTION();
299             }
300         }
301     }
302 
303 	//==================================================================================================================
304 	//= AnimatedImagesPeer
305 	//==================================================================================================================
306 	//------------------------------------------------------------------------------------------------------------------
307     AnimatedImagesPeer::AnimatedImagesPeer()
308         :AnimatedImagesPeer_Base()
309         ,m_pData( new AnimatedImagesPeer_Data( *this ) )
310     {
311     }
312 
313 	//------------------------------------------------------------------------------------------------------------------
314     AnimatedImagesPeer::~AnimatedImagesPeer()
315     {
316     }
317 
318 	//------------------------------------------------------------------------------------------------------------------
319     void SAL_CALL AnimatedImagesPeer::startAnimation(  ) throw (RuntimeException)
320     {
321         ::vos::OGuard aGuard( GetMutex() );
322         Throbber* pThrobber( dynamic_cast< Throbber* >( GetWindow() ) );
323         if ( pThrobber != NULL)
324             pThrobber->start();
325     }
326 
327 	//------------------------------------------------------------------------------------------------------------------
328     void SAL_CALL AnimatedImagesPeer::stopAnimation(  ) throw (RuntimeException)
329     {
330         ::vos::OGuard aGuard( GetMutex() );
331         Throbber* pThrobber( dynamic_cast< Throbber* >( GetWindow() ) );
332         if ( pThrobber != NULL)
333             pThrobber->stop();
334     }
335 
336 	//------------------------------------------------------------------------------------------------------------------
337     ::sal_Bool SAL_CALL AnimatedImagesPeer::isAnimationRunning(  ) throw (RuntimeException)
338     {
339         ::vos::OGuard aGuard( GetMutex() );
340         Throbber* pThrobber( dynamic_cast< Throbber* >( GetWindow() ) );
341         if ( pThrobber != NULL)
342             return pThrobber->isRunning();
343         return sal_False;
344     }
345 
346 	//------------------------------------------------------------------------------------------------------------------
347     void SAL_CALL AnimatedImagesPeer::setProperty( const ::rtl::OUString& i_propertyName, const Any& i_value ) throw(RuntimeException)
348     {
349 	    ::vos::OGuard aGuard( GetMutex() );
350 
351         Throbber* pThrobber( dynamic_cast< Throbber* >( GetWindow() ) );
352         if ( pThrobber == NULL )
353         {
354             VCLXWindow::setProperty( i_propertyName, i_value );
355             return;
356         }
357 
358         const sal_uInt16 nPropertyId = GetPropertyId( i_propertyName );
359         switch ( nPropertyId )
360         {
361             case BASEPROPERTY_STEP_TIME:
362             {
363                 sal_Int32 nStepTime( 0 );
364                 if ( i_value >>= nStepTime )
365                     pThrobber->setStepTime( nStepTime );
366                 break;
367             }
368             case BASEPROPERTY_AUTO_REPEAT:
369             {
370                 sal_Bool bRepeat( sal_True );
371                 if ( i_value >>= bRepeat )
372                     pThrobber->setRepeat( bRepeat );
373                 break;
374             }
375 
376             case BASEPROPERTY_IMAGE_SCALE_MODE:
377             {
378                 sal_Int16 nScaleMode( ImageScaleMode::Anisotropic );
379                 ImageControl* pImageControl = dynamic_cast< ImageControl* >( GetWindow() );
380                 if ( pImageControl && ( i_value >>= nScaleMode ) )
381                 {
382                     pImageControl->SetScaleMode( nScaleMode );
383                 }
384             }
385             break;
386 
387             default:
388                 AnimatedImagesPeer_Base::setProperty( i_propertyName, i_value );
389                 break;
390         }
391     }
392 
393 	//------------------------------------------------------------------------------------------------------------------
394     Any SAL_CALL AnimatedImagesPeer::getProperty( const ::rtl::OUString& i_propertyName ) throw(RuntimeException)
395     {
396         ::vos::OGuard aGuard( GetMutex() );
397 
398         Any aReturn;
399 
400         Throbber* pThrobber( dynamic_cast< Throbber* >( GetWindow() ) );
401         if ( pThrobber == NULL )
402             return VCLXWindow::getProperty( i_propertyName );
403 
404         const sal_uInt16 nPropertyId = GetPropertyId( i_propertyName );
405         switch ( nPropertyId )
406         {
407         case BASEPROPERTY_STEP_TIME:
408             aReturn <<= pThrobber->getStepTime();
409             break;
410 
411         case BASEPROPERTY_AUTO_REPEAT:
412             aReturn <<= pThrobber->getRepeat();
413             break;
414 
415         case BASEPROPERTY_IMAGE_SCALE_MODE:
416             {
417                 ImageControl const* pImageControl = dynamic_cast< ImageControl* >( GetWindow() );
418                 aReturn <<= ( pImageControl ? pImageControl->GetScaleMode() : ImageScaleMode::Anisotropic );
419             }
420             break;
421 
422         default:
423             aReturn = AnimatedImagesPeer_Base::getProperty( i_propertyName );
424             break;
425         }
426 
427         return aReturn;
428     }
429 
430     //------------------------------------------------------------------------------------------------------------------
431     void AnimatedImagesPeer::ProcessWindowEvent( const VclWindowEvent& i_windowEvent )
432     {
433         switch ( i_windowEvent.GetId() )
434         {
435         case VCLEVENT_WINDOW_RESIZE:
436             lcl_updateImageList_nothrow( *m_pData );
437             break;
438         }
439 
440         AnimatedImagesPeer_Base::ProcessWindowEvent( i_windowEvent );
441     }
442 
443     //------------------------------------------------------------------------------------------------------------------
444     void AnimatedImagesPeer::impl_updateImages_nolck( const Reference< XInterface >& i_animatedImages )
445     {
446         ::vos::OGuard aGuard( GetMutex() );
447 
448         lcl_updateImageList_nothrow( *m_pData, Reference< XAnimatedImages >( i_animatedImages, UNO_QUERY_THROW ) );
449     }
450 
451     //------------------------------------------------------------------------------------------------------------------
452     void SAL_CALL AnimatedImagesPeer::elementInserted( const ContainerEvent& i_event ) throw (RuntimeException)
453     {
454         ::vos::OGuard aGuard( GetMutex() );
455         Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW );
456 
457         sal_Int32 nPosition(0);
458         OSL_VERIFY( i_event.Accessor >>= nPosition );
459         size_t position = size_t( nPosition );
460         if ( position > m_pData->aCachedImageSets.size() )
461         {
462             OSL_ENSURE( false, "AnimatedImagesPeer::elementInserted: illegal accessor/index!" );
463             lcl_updateImageList_nothrow( *m_pData, xAnimatedImages );
464         }
465 
466         Sequence< ::rtl::OUString > aImageURLs;
467         OSL_VERIFY( i_event.Element >>= aImageURLs );
468         ::std::vector< CachedImage > aImages;
469         lcl_init( aImageURLs, aImages );
470         m_pData->aCachedImageSets.insert( m_pData->aCachedImageSets.begin() + position, aImages );
471         lcl_updateImageList_nothrow( *m_pData );
472     }
473 
474     //------------------------------------------------------------------------------------------------------------------
475     void SAL_CALL AnimatedImagesPeer::elementRemoved( const ContainerEvent& i_event ) throw (RuntimeException)
476     {
477         ::vos::OGuard aGuard( GetMutex() );
478         Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW );
479 
480         sal_Int32 nPosition(0);
481         OSL_VERIFY( i_event.Accessor >>= nPosition );
482         size_t position = size_t( nPosition );
483         if ( position >= m_pData->aCachedImageSets.size() )
484         {
485             OSL_ENSURE( false, "AnimatedImagesPeer::elementRemoved: illegal accessor/index!" );
486             lcl_updateImageList_nothrow( *m_pData, xAnimatedImages );
487         }
488 
489         m_pData->aCachedImageSets.erase( m_pData->aCachedImageSets.begin() + position );
490         lcl_updateImageList_nothrow( *m_pData );
491     }
492 
493     //------------------------------------------------------------------------------------------------------------------
494     void SAL_CALL AnimatedImagesPeer::elementReplaced( const ContainerEvent& i_event ) throw (RuntimeException)
495     {
496         ::vos::OGuard aGuard( GetMutex() );
497         Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW );
498 
499         sal_Int32 nPosition(0);
500         OSL_VERIFY( i_event.Accessor >>= nPosition );
501         size_t position = size_t( nPosition );
502         if ( position >= m_pData->aCachedImageSets.size() )
503         {
504             OSL_ENSURE( false, "AnimatedImagesPeer::elementReplaced: illegal accessor/index!" );
505             lcl_updateImageList_nothrow( *m_pData, xAnimatedImages );
506         }
507 
508         Sequence< ::rtl::OUString > aImageURLs;
509         OSL_VERIFY( i_event.Element >>= aImageURLs );
510         ::std::vector< CachedImage > aImages;
511         lcl_init( aImageURLs, aImages );
512         m_pData->aCachedImageSets[ position ] = aImages;
513         lcl_updateImageList_nothrow( *m_pData );
514     }
515 
516     //------------------------------------------------------------------------------------------------------------------
517     void SAL_CALL AnimatedImagesPeer::disposing( const EventObject& i_event ) throw (RuntimeException)
518     {
519         VCLXWindow::disposing( i_event );
520     }
521 
522     //------------------------------------------------------------------------------------------------------------------
523     void SAL_CALL AnimatedImagesPeer::modified( const EventObject& i_event ) throw (RuntimeException)
524     {
525         impl_updateImages_nolck( i_event.Source );
526     }
527 
528     //------------------------------------------------------------------------------------------------------------------
529     void SAL_CALL AnimatedImagesPeer::dispose(  ) throw(RuntimeException)
530     {
531         AnimatedImagesPeer_Base::dispose();
532         ::vos::OGuard aGuard( GetMutex() );
533         m_pData->aCachedImageSets.resize(0);
534     }
535 
536 //......................................................................................................................
537 } // namespace toolkit
538 //......................................................................................................................
539