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 #include "imagemanagerimpl.hxx"
21 #include <xml/imagesconfiguration.hxx>
22 #include <uiconfiguration/imagetype.hxx>
23 #include <uiconfiguration/graphicnameaccess.hxx>
24
25 #include <properties.h>
26
27 #include <com/sun/star/frame/theUICommandDescription.hpp>
28 #include <com/sun/star/ui/ConfigurationEvent.hpp>
29 #include <com/sun/star/lang/DisposedException.hpp>
30 #include <com/sun/star/lang/IllegalAccessException.hpp>
31 #include <com/sun/star/beans/XPropertySet.hpp>
32 #include <com/sun/star/beans/PropertyValue.hpp>
33 #include <com/sun/star/embed/ElementModes.hpp>
34 #include <com/sun/star/embed/InvalidStorageException.hpp>
35 #include <com/sun/star/embed/StorageWrappedTargetException.hpp>
36 #include <com/sun/star/io/IOException.hpp>
37 #include <com/sun/star/io/XStream.hpp>
38 #include <com/sun/star/ui/ImageType.hpp>
39 #include <vcl/graph.hxx>
40 #include <vcl/svapp.hxx>
41 #include <o3tl/enumrange.hxx>
42 #include <osl/mutex.hxx>
43 #include <comphelper/sequence.hxx>
44 #include <unotools/ucbstreamhelper.hxx>
45 #include <vcl/filter/PngImageReader.hxx>
46 #include <vcl/pngwrite.hxx>
47 #include <rtl/instance.hxx>
48 #include <memory>
49
50 using ::com::sun::star::uno::Sequence;
51 using ::com::sun::star::uno::XInterface;
52 using ::com::sun::star::uno::RuntimeException;
53 using ::com::sun::star::uno::UNO_QUERY;
54 using ::com::sun::star::uno::Any;
55 using ::com::sun::star::graphic::XGraphic;
56 using namespace ::com::sun::star;
57 using namespace ::com::sun::star::io;
58 using namespace ::com::sun::star::embed;
59 using namespace ::com::sun::star::lang;
60 using namespace ::com::sun::star::container;
61 using namespace ::com::sun::star::beans;
62 using namespace ::com::sun::star::ui;
63 using namespace ::cppu;
64
65 const sal_Int16 MAX_IMAGETYPE_VALUE = css::ui::ImageType::SIZE_32;
66
67 constexpr OUStringLiteral IMAGE_FOLDER = u"images";
68 constexpr OUStringLiteral BITMAPS_FOLDER = u"Bitmaps";
69
70 const o3tl::enumarray<vcl::ImageType, const char*> IMAGELIST_XML_FILE =
71 {
72 "sc_imagelist.xml",
73 "lc_imagelist.xml",
74 "xc_imagelist.xml"
75 };
76
77 const o3tl::enumarray<vcl::ImageType, const char*> BITMAP_FILE_NAMES =
78 {
79 "sc_userimages.png",
80 "lc_userimages.png",
81 "xc_userimages.png"
82 };
83
84 namespace framework
85 {
86
87 static GlobalImageList* pGlobalImageList = nullptr;
88
89 namespace
90 {
91 class theGlobalImageListMutex
92 : public rtl::Static<osl::Mutex, theGlobalImageListMutex> {};
93 }
94
getGlobalImageListMutex()95 static osl::Mutex& getGlobalImageListMutex()
96 {
97 return theGlobalImageListMutex::get();
98 }
99
getGlobalImageList(const uno::Reference<uno::XComponentContext> & rxContext)100 static GlobalImageList* getGlobalImageList( const uno::Reference< uno::XComponentContext >& rxContext )
101 {
102 osl::MutexGuard guard( getGlobalImageListMutex() );
103
104 if ( pGlobalImageList == nullptr )
105 pGlobalImageList = new GlobalImageList( rxContext );
106
107 return pGlobalImageList;
108 }
109
CmdImageList(const uno::Reference<uno::XComponentContext> & rxContext,const OUString & aModuleIdentifier)110 CmdImageList::CmdImageList( const uno::Reference< uno::XComponentContext >& rxContext, const OUString& aModuleIdentifier ) :
111 m_bInitialized(false),
112 m_aModuleIdentifier( aModuleIdentifier ),
113 m_xContext( rxContext )
114 {
115 }
116
~CmdImageList()117 CmdImageList::~CmdImageList()
118 {
119 }
120
initialize()121 void CmdImageList::initialize()
122 {
123 if (m_bInitialized)
124 return;
125
126 const OUString aCommandImageList(UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDIMAGELIST);
127
128 Sequence<OUString> aCommandImageSeq;
129 uno::Reference<XNameAccess> xCommandDesc = frame::theUICommandDescription::get(m_xContext);
130
131 if (!m_aModuleIdentifier.isEmpty())
132 {
133 // If we have a module identifier - use to retrieve the command image name list from it.
134 // Otherwise we will use the global command image list
135 try
136 {
137 xCommandDesc->getByName(m_aModuleIdentifier) >>= xCommandDesc;
138 if (xCommandDesc.is())
139 xCommandDesc->getByName(aCommandImageList) >>= aCommandImageSeq;
140 }
141 catch (const NoSuchElementException&)
142 {
143 // Module unknown we will work with an empty command image list!
144 return;
145 }
146 }
147
148 if (xCommandDesc.is())
149 {
150 try
151 {
152 xCommandDesc->getByName(aCommandImageList) >>= aCommandImageSeq;
153 }
154 catch (const NoSuchElementException&)
155 {
156 }
157 catch (const WrappedTargetException&)
158 {
159 }
160 }
161
162 m_aResolver.registerCommands(aCommandImageSeq);
163
164 m_bInitialized = true;
165 }
166
167
getImageFromCommandURL(vcl::ImageType nImageType,const OUString & rCommandURL)168 Image CmdImageList::getImageFromCommandURL(vcl::ImageType nImageType, const OUString& rCommandURL)
169 {
170 initialize();
171 return m_aResolver.getImageFromCommandURL(nImageType, rCommandURL);
172 }
173
hasImage(vcl::ImageType,const OUString & rCommandURL)174 bool CmdImageList::hasImage(vcl::ImageType /*nImageType*/, const OUString& rCommandURL)
175 {
176 initialize();
177 return m_aResolver.hasImage(rCommandURL);
178 }
179
getImageCommandNames()180 std::vector<OUString>& CmdImageList::getImageCommandNames()
181 {
182 return m_aResolver.getCommandNames();
183 }
184
GlobalImageList(const uno::Reference<uno::XComponentContext> & rxContext)185 GlobalImageList::GlobalImageList( const uno::Reference< uno::XComponentContext >& rxContext ) :
186 CmdImageList( rxContext, OUString() )
187 {
188 }
189
~GlobalImageList()190 GlobalImageList::~GlobalImageList()
191 {
192 osl::MutexGuard guard( getGlobalImageListMutex() );
193 // remove global pointer as we destroy the object now
194 pGlobalImageList = nullptr;
195 }
196
getImageFromCommandURL(vcl::ImageType nImageType,const OUString & rCommandURL)197 Image GlobalImageList::getImageFromCommandURL( vcl::ImageType nImageType, const OUString& rCommandURL )
198 {
199 osl::MutexGuard guard( getGlobalImageListMutex() );
200 return CmdImageList::getImageFromCommandURL( nImageType, rCommandURL );
201 }
202
hasImage(vcl::ImageType nImageType,const OUString & rCommandURL)203 bool GlobalImageList::hasImage( vcl::ImageType nImageType, const OUString& rCommandURL )
204 {
205 osl::MutexGuard guard( getGlobalImageListMutex() );
206 return CmdImageList::hasImage( nImageType, rCommandURL );
207 }
208
getImageCommandNames()209 ::std::vector< OUString >& GlobalImageList::getImageCommandNames()
210 {
211 osl::MutexGuard guard( getGlobalImageListMutex() );
212 return CmdImageList::getImageCommandNames();
213 }
214
implts_checkAndScaleGraphic(uno::Reference<XGraphic> & rOutGraphic,const uno::Reference<XGraphic> & rInGraphic,vcl::ImageType nImageType)215 static bool implts_checkAndScaleGraphic( uno::Reference< XGraphic >& rOutGraphic, const uno::Reference< XGraphic >& rInGraphic, vcl::ImageType nImageType )
216 {
217 if ( !rInGraphic.is() )
218 {
219 rOutGraphic = uno::Reference<graphic::XGraphic>();
220 return false;
221 }
222
223 static const o3tl::enumarray<vcl::ImageType, Size> BITMAP_SIZE =
224 {
225 Size(16, 16), Size(24, 24), Size(32, 32)
226 };
227
228 // Check size and scale it
229 Graphic aImage(rInGraphic);
230 if (BITMAP_SIZE[nImageType] != aImage.GetSizePixel())
231 {
232 BitmapEx aBitmap = aImage.GetBitmapEx();
233 aBitmap.Scale(BITMAP_SIZE[nImageType]);
234 aImage = Graphic(aBitmap);
235 rOutGraphic = aImage.GetXGraphic();
236 }
237 else
238 rOutGraphic = rInGraphic;
239
240 return true;
241 }
242
implts_convertImageTypeToIndex(sal_Int16 nImageType)243 static vcl::ImageType implts_convertImageTypeToIndex( sal_Int16 nImageType )
244 {
245 if (nImageType & css::ui::ImageType::SIZE_LARGE)
246 return vcl::ImageType::Size26;
247 else if (nImageType & css::ui::ImageType::SIZE_32)
248 return vcl::ImageType::Size32;
249 else
250 return vcl::ImageType::Size16;
251 }
252
implts_getUserImageList(vcl::ImageType nImageType)253 ImageList* ImageManagerImpl::implts_getUserImageList( vcl::ImageType nImageType )
254 {
255 SolarMutexGuard g;
256 if ( !m_pUserImageList[nImageType] )
257 implts_loadUserImages( nImageType, m_xUserImageStorage, m_xUserBitmapsStorage );
258
259 return m_pUserImageList[nImageType].get();
260 }
261
implts_initialize()262 void ImageManagerImpl::implts_initialize()
263 {
264 // Initialize the top-level structures with the storage data
265 if ( !m_xUserConfigStorage.is() )
266 return;
267
268 tools::Long nModes = m_bReadOnly ? ElementModes::READ : ElementModes::READWRITE;
269
270 try
271 {
272 m_xUserImageStorage = m_xUserConfigStorage->openStorageElement( IMAGE_FOLDER,
273 nModes );
274 if ( m_xUserImageStorage.is() )
275 {
276 m_xUserBitmapsStorage = m_xUserImageStorage->openStorageElement( BITMAPS_FOLDER,
277 nModes );
278 }
279 }
280 catch ( const css::container::NoSuchElementException& )
281 {
282 }
283 catch ( const css::embed::InvalidStorageException& )
284 {
285 }
286 catch ( const css::lang::IllegalArgumentException& )
287 {
288 }
289 catch ( const css::io::IOException& )
290 {
291 }
292 catch ( const css::embed::StorageWrappedTargetException& )
293 {
294 }
295 }
296
implts_loadUserImages(vcl::ImageType nImageType,const uno::Reference<XStorage> & xUserImageStorage,const uno::Reference<XStorage> & xUserBitmapsStorage)297 void ImageManagerImpl::implts_loadUserImages(
298 vcl::ImageType nImageType,
299 const uno::Reference< XStorage >& xUserImageStorage,
300 const uno::Reference< XStorage >& xUserBitmapsStorage )
301 {
302 SolarMutexGuard g;
303
304 if ( xUserImageStorage.is() && xUserBitmapsStorage.is() )
305 {
306 try
307 {
308 uno::Reference< XStream > xStream = xUserImageStorage->openStreamElement( OUString::createFromAscii( IMAGELIST_XML_FILE[nImageType] ),
309 ElementModes::READ );
310 uno::Reference< XInputStream > xInputStream = xStream->getInputStream();
311
312 ImageItemDescriptorList aUserImageListInfo;
313 ImagesConfiguration::LoadImages( m_xContext,
314 xInputStream,
315 aUserImageListInfo );
316 if ( !aUserImageListInfo.empty() )
317 {
318 sal_Int32 nCount = aUserImageListInfo.size();
319 std::vector< OUString > aUserImagesVector;
320 aUserImagesVector.reserve(nCount);
321 for ( sal_Int32 i=0; i < nCount; i++ )
322 {
323 const ImageItemDescriptor& rItem = aUserImageListInfo[i];
324 aUserImagesVector.push_back( rItem.aCommandURL );
325 }
326
327 uno::Reference< XStream > xBitmapStream = xUserBitmapsStorage->openStreamElement(
328 OUString::createFromAscii( BITMAP_FILE_NAMES[nImageType] ),
329 ElementModes::READ );
330
331 if ( xBitmapStream.is() )
332 {
333 BitmapEx aUserBitmap;
334 {
335 std::unique_ptr<SvStream> pSvStream(utl::UcbStreamHelper::CreateStream( xBitmapStream ));
336 vcl::PngImageReader aPngReader( *pSvStream );
337 aUserBitmap = aPngReader.read();
338 }
339
340 // Delete old image list and create a new one from the read bitmap
341 m_pUserImageList[nImageType].reset(new ImageList());
342 m_pUserImageList[nImageType]->InsertFromHorizontalStrip
343 ( aUserBitmap, aUserImagesVector );
344 return;
345 }
346 }
347 }
348 catch ( const css::container::NoSuchElementException& )
349 {
350 }
351 catch ( const css::embed::InvalidStorageException& )
352 {
353 }
354 catch ( const css::lang::IllegalArgumentException& )
355 {
356 }
357 catch ( const css::io::IOException& )
358 {
359 }
360 catch ( const css::embed::StorageWrappedTargetException& )
361 {
362 }
363 }
364
365 // Destroy old image list - create a new empty one
366 m_pUserImageList[nImageType].reset(new ImageList);
367 }
368
implts_storeUserImages(vcl::ImageType nImageType,const uno::Reference<XStorage> & xUserImageStorage,const uno::Reference<XStorage> & xUserBitmapsStorage)369 bool ImageManagerImpl::implts_storeUserImages(
370 vcl::ImageType nImageType,
371 const uno::Reference< XStorage >& xUserImageStorage,
372 const uno::Reference< XStorage >& xUserBitmapsStorage )
373 {
374 SolarMutexGuard g;
375
376 if ( m_bModified )
377 {
378 ImageList* pImageList = implts_getUserImageList( nImageType );
379 if ( pImageList->GetImageCount() > 0 )
380 {
381 ImageItemDescriptorList aUserImageListInfo;
382
383 for ( sal_uInt16 i=0; i < pImageList->GetImageCount(); i++ )
384 {
385 ImageItemDescriptor aItem;
386 aItem.aCommandURL = pImageList->GetImageName( i );
387 aUserImageListInfo.push_back( aItem );
388 }
389
390 uno::Reference< XTransactedObject > xTransaction;
391 uno::Reference< XOutputStream > xOutputStream;
392 uno::Reference< XStream > xStream = xUserImageStorage->openStreamElement( OUString::createFromAscii( IMAGELIST_XML_FILE[nImageType] ),
393 ElementModes::WRITE|ElementModes::TRUNCATE );
394 if ( xStream.is() )
395 {
396 uno::Reference< XStream > xBitmapStream =
397 xUserBitmapsStorage->openStreamElement( OUString::createFromAscii( BITMAP_FILE_NAMES[nImageType] ),
398 ElementModes::WRITE|ElementModes::TRUNCATE );
399 if ( xBitmapStream.is() )
400 {
401 {
402 std::unique_ptr<SvStream> pSvStream(utl::UcbStreamHelper::CreateStream( xBitmapStream ));
403 vcl::PNGWriter aPngWriter( pImageList->GetAsHorizontalStrip() );
404 aPngWriter.Write( *pSvStream );
405 }
406
407 // Commit user bitmaps storage
408 xTransaction.set( xUserBitmapsStorage, UNO_QUERY );
409 if ( xTransaction.is() )
410 xTransaction->commit();
411 }
412
413 xOutputStream = xStream->getOutputStream();
414 if ( xOutputStream.is() )
415 ImagesConfiguration::StoreImages( m_xContext, xOutputStream, aUserImageListInfo );
416
417 // Commit user image storage
418 xTransaction.set( xUserImageStorage, UNO_QUERY );
419 if ( xTransaction.is() )
420 xTransaction->commit();
421 }
422
423 return true;
424 }
425 else
426 {
427 // Remove the streams from the storage, if we have no data. We have to catch
428 // the NoSuchElementException as it can be possible that there is no stream at all!
429 try
430 {
431 xUserImageStorage->removeElement( OUString::createFromAscii( IMAGELIST_XML_FILE[nImageType] ));
432 }
433 catch ( const css::container::NoSuchElementException& )
434 {
435 }
436
437 try
438 {
439 xUserBitmapsStorage->removeElement( OUString::createFromAscii( BITMAP_FILE_NAMES[nImageType] ));
440 }
441 catch ( const css::container::NoSuchElementException& )
442 {
443 }
444
445 uno::Reference< XTransactedObject > xTransaction;
446
447 // Commit user image storage
448 xTransaction.set( xUserImageStorage, UNO_QUERY );
449 if ( xTransaction.is() )
450 xTransaction->commit();
451
452 // Commit user bitmaps storage
453 xTransaction.set( xUserBitmapsStorage, UNO_QUERY );
454 if ( xTransaction.is() )
455 xTransaction->commit();
456
457 return true;
458 }
459 }
460
461 return false;
462 }
463
implts_getGlobalImageList()464 const rtl::Reference< GlobalImageList >& ImageManagerImpl::implts_getGlobalImageList()
465 {
466 SolarMutexGuard g;
467
468 if ( !m_pGlobalImageList.is() )
469 m_pGlobalImageList = getGlobalImageList( m_xContext );
470 return m_pGlobalImageList;
471 }
472
implts_getDefaultImageList()473 CmdImageList* ImageManagerImpl::implts_getDefaultImageList()
474 {
475 SolarMutexGuard g;
476
477 if ( !m_pDefaultImageList )
478 m_pDefaultImageList.reset(new CmdImageList( m_xContext, m_aModuleIdentifier ));
479
480 return m_pDefaultImageList.get();
481 }
482
ImageManagerImpl(const uno::Reference<uno::XComponentContext> & rxContext,::cppu::OWeakObject * pOwner,bool _bUseGlobal)483 ImageManagerImpl::ImageManagerImpl( const uno::Reference< uno::XComponentContext >& rxContext,::cppu::OWeakObject* pOwner,bool _bUseGlobal ) :
484 m_xContext( rxContext )
485 , m_pOwner(pOwner)
486 , m_aResourceString( "private:resource/images/moduleimages" )
487 , m_aListenerContainer( m_mutex )
488 , m_bUseGlobal(_bUseGlobal)
489 , m_bReadOnly( true )
490 , m_bInitialized( false )
491 , m_bModified( false )
492 , m_bDisposed( false )
493 {
494 for ( vcl::ImageType n : o3tl::enumrange<vcl::ImageType>() )
495 {
496 m_pUserImageList[n] = nullptr;
497 m_bUserImageListModified[n] = false;
498 }
499 }
500
~ImageManagerImpl()501 ImageManagerImpl::~ImageManagerImpl()
502 {
503 clear();
504 }
505
dispose()506 void ImageManagerImpl::dispose()
507 {
508 uno::Reference< uno::XInterface > xOwner(m_pOwner);
509 css::lang::EventObject aEvent( xOwner );
510 m_aListenerContainer.disposeAndClear( aEvent );
511
512 {
513 SolarMutexGuard g;
514 m_xUserConfigStorage.clear();
515 m_xUserImageStorage.clear();
516 m_xUserRootCommit.clear();
517 m_bModified = false;
518 m_bDisposed = true;
519
520 // delete user and default image list on dispose
521 for (auto& n : m_pUserImageList)
522 {
523 n.reset();
524 }
525 m_pDefaultImageList.reset();
526 }
527
528 }
addEventListener(const uno::Reference<XEventListener> & xListener)529 void ImageManagerImpl::addEventListener( const uno::Reference< XEventListener >& xListener )
530 {
531 {
532 SolarMutexGuard g;
533
534 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
535 if ( m_bDisposed )
536 throw DisposedException();
537 }
538
539 m_aListenerContainer.addInterface( cppu::UnoType<XEventListener>::get(), xListener );
540 }
541
removeEventListener(const uno::Reference<XEventListener> & xListener)542 void ImageManagerImpl::removeEventListener( const uno::Reference< XEventListener >& xListener )
543 {
544 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
545 m_aListenerContainer.removeInterface( cppu::UnoType<XEventListener>::get(), xListener );
546 }
547
548 // XInitialization
initialize(const Sequence<Any> & aArguments)549 void ImageManagerImpl::initialize( const Sequence< Any >& aArguments )
550 {
551 SolarMutexGuard g;
552
553 if ( m_bInitialized )
554 return;
555
556 for ( const Any& rArg : aArguments )
557 {
558 PropertyValue aPropValue;
559 if ( rArg >>= aPropValue )
560 {
561 if ( aPropValue.Name == "UserConfigStorage" )
562 {
563 aPropValue.Value >>= m_xUserConfigStorage;
564 }
565 else if ( aPropValue.Name == "ModuleIdentifier" )
566 {
567 aPropValue.Value >>= m_aModuleIdentifier;
568 }
569 else if ( aPropValue.Name == "UserRootCommit" )
570 {
571 aPropValue.Value >>= m_xUserRootCommit;
572 }
573 }
574 }
575
576 if ( m_xUserConfigStorage.is() )
577 {
578 uno::Reference< XPropertySet > xPropSet( m_xUserConfigStorage, UNO_QUERY );
579 if ( xPropSet.is() )
580 {
581 tools::Long nOpenMode = 0;
582 if ( xPropSet->getPropertyValue("OpenMode") >>= nOpenMode )
583 m_bReadOnly = !( nOpenMode & ElementModes::WRITE );
584 }
585 }
586
587 implts_initialize();
588
589 m_bInitialized = true;
590 }
591
592 // XImageManagerImpl
reset()593 void ImageManagerImpl::reset()
594 {
595 SolarMutexGuard g;
596
597 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
598 if ( m_bDisposed )
599 throw DisposedException();
600
601 std::vector< OUString > aUserImageNames;
602
603 for ( vcl::ImageType i : o3tl::enumrange<vcl::ImageType>() )
604 {
605 aUserImageNames.clear();
606 ImageList* pImageList = implts_getUserImageList(i);
607 pImageList->GetImageNames( aUserImageNames );
608
609 Sequence< OUString > aRemoveList( comphelper::containerToSequence(aUserImageNames) );
610
611 // Remove images
612 removeImages( sal_Int16( i ), aRemoveList );
613 m_bUserImageListModified[i] = true;
614 }
615
616 m_bModified = true;
617 }
618
getAllImageNames(::sal_Int16 nImageType)619 Sequence< OUString > ImageManagerImpl::getAllImageNames( ::sal_Int16 nImageType )
620 {
621 SolarMutexGuard g;
622
623 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
624 if ( m_bDisposed )
625 throw DisposedException();
626
627 ImageNameMap aImageCmdNameMap;
628
629 vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType );
630
631 sal_uInt32 i( 0 );
632 if ( m_bUseGlobal )
633 {
634 rtl::Reference< GlobalImageList > rGlobalImageList = implts_getGlobalImageList();
635
636 const std::vector< OUString >& rGlobalImageNameVector = rGlobalImageList->getImageCommandNames();
637 const sal_uInt32 nGlobalCount = rGlobalImageNameVector.size();
638 for ( i = 0; i < nGlobalCount; i++ )
639 aImageCmdNameMap.emplace( rGlobalImageNameVector[i], true );
640
641 const std::vector< OUString >& rModuleImageNameVector = implts_getDefaultImageList()->getImageCommandNames();
642 const sal_uInt32 nModuleCount = rModuleImageNameVector.size();
643 for ( i = 0; i < nModuleCount; i++ )
644 aImageCmdNameMap.emplace( rModuleImageNameVector[i], true );
645 }
646
647 ImageList* pImageList = implts_getUserImageList(nIndex);
648 std::vector< OUString > rUserImageNames;
649 pImageList->GetImageNames( rUserImageNames );
650 const sal_uInt32 nUserCount = rUserImageNames.size();
651 for ( i = 0; i < nUserCount; i++ )
652 aImageCmdNameMap.emplace( rUserImageNames[i], true );
653
654 return comphelper::mapKeysToSequence( aImageCmdNameMap );
655 }
656
hasImage(::sal_Int16 nImageType,const OUString & aCommandURL)657 bool ImageManagerImpl::hasImage( ::sal_Int16 nImageType, const OUString& aCommandURL )
658 {
659 SolarMutexGuard g;
660
661 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
662 if ( m_bDisposed )
663 throw DisposedException();
664
665 if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE ))
666 throw IllegalArgumentException();
667
668 vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType );
669 if ( m_bUseGlobal && implts_getGlobalImageList()->hasImage( nIndex, aCommandURL ))
670 return true;
671 else
672 {
673 if ( m_bUseGlobal && implts_getDefaultImageList()->hasImage( nIndex, aCommandURL ))
674 return true;
675 else
676 {
677 // User layer
678 ImageList* pImageList = implts_getUserImageList(nIndex);
679 if ( pImageList )
680 return ( pImageList->GetImagePos( aCommandURL ) != IMAGELIST_IMAGE_NOTFOUND );
681 }
682 }
683
684 return false;
685 }
686
687 namespace
688 {
GetXGraphic(const Image & rImage)689 css::uno::Reference< css::graphic::XGraphic > GetXGraphic(const Image &rImage)
690 {
691 return Graphic(rImage).GetXGraphic();
692 }
693 }
694
getImages(::sal_Int16 nImageType,const Sequence<OUString> & aCommandURLSequence)695 Sequence< uno::Reference< XGraphic > > ImageManagerImpl::getImages(
696 ::sal_Int16 nImageType,
697 const Sequence< OUString >& aCommandURLSequence )
698 {
699 SolarMutexGuard g;
700
701 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
702 if ( m_bDisposed )
703 throw DisposedException();
704
705 if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE ))
706 throw IllegalArgumentException();
707
708 Sequence< uno::Reference< XGraphic > > aGraphSeq( aCommandURLSequence.getLength() );
709
710 vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType );
711 rtl::Reference< GlobalImageList > rGlobalImageList;
712 CmdImageList* pDefaultImageList = nullptr;
713 if ( m_bUseGlobal )
714 {
715 rGlobalImageList = implts_getGlobalImageList();
716 pDefaultImageList = implts_getDefaultImageList();
717 }
718 ImageList* pUserImageList = implts_getUserImageList(nIndex);
719
720 // We have to search our image list in the following order:
721 // 1. user image list (read/write)
722 // 2. module image list (read)
723 // 3. global image list (read)
724 sal_Int32 n = 0;
725 for ( const OUString& rURL : aCommandURLSequence )
726 {
727 Image aImage = pUserImageList->GetImage( rURL );
728 if ( !aImage && m_bUseGlobal )
729 {
730 aImage = pDefaultImageList->getImageFromCommandURL( nIndex, rURL );
731 if ( !aImage )
732 aImage = rGlobalImageList->getImageFromCommandURL( nIndex, rURL );
733 }
734
735 aGraphSeq[n++] = GetXGraphic(aImage);
736 }
737
738 return aGraphSeq;
739 }
740
replaceImages(::sal_Int16 nImageType,const Sequence<OUString> & aCommandURLSequence,const Sequence<uno::Reference<XGraphic>> & aGraphicsSequence)741 void ImageManagerImpl::replaceImages(
742 ::sal_Int16 nImageType,
743 const Sequence< OUString >& aCommandURLSequence,
744 const Sequence< uno::Reference< XGraphic > >& aGraphicsSequence )
745 {
746 rtl::Reference<GraphicNameAccess> pInsertedImages;
747 rtl::Reference<GraphicNameAccess> pReplacedImages;
748
749 {
750 SolarMutexGuard g;
751
752 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
753 if ( m_bDisposed )
754 throw DisposedException();
755
756 if (( aCommandURLSequence.getLength() != aGraphicsSequence.getLength() ) ||
757 (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE )))
758 throw IllegalArgumentException();
759
760 if ( m_bReadOnly )
761 throw IllegalAccessException();
762
763 vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType );
764 ImageList* pImageList = implts_getUserImageList(nIndex);
765
766 uno::Reference< XGraphic > xGraphic;
767 for ( sal_Int32 i = 0; i < aCommandURLSequence.getLength(); i++ )
768 {
769 // Check size and scale. If we don't have any graphics ignore it
770 if ( !implts_checkAndScaleGraphic( xGraphic, aGraphicsSequence[i], nIndex ))
771 continue;
772
773 sal_uInt16 nPos = pImageList->GetImagePos( aCommandURLSequence[i] );
774 if ( nPos == IMAGELIST_IMAGE_NOTFOUND )
775 {
776 pImageList->AddImage(aCommandURLSequence[i], Image(xGraphic));
777 if ( !pInsertedImages )
778 pInsertedImages = new GraphicNameAccess();
779 pInsertedImages->addElement( aCommandURLSequence[i], xGraphic );
780 }
781 else
782 {
783 pImageList->ReplaceImage(aCommandURLSequence[i], Image(xGraphic));
784 if ( !pReplacedImages )
785 pReplacedImages = new GraphicNameAccess();
786 pReplacedImages->addElement( aCommandURLSequence[i], xGraphic );
787 }
788 }
789
790 if (( pInsertedImages != nullptr ) || ( pReplacedImages != nullptr ))
791 {
792 m_bModified = true;
793 m_bUserImageListModified[nIndex] = true;
794 }
795 }
796
797 uno::Reference< uno::XInterface > xOwner(m_pOwner);
798 // Notify listeners
799 if ( pInsertedImages != nullptr )
800 {
801 ConfigurationEvent aInsertEvent;
802 aInsertEvent.aInfo <<= nImageType;
803 aInsertEvent.Accessor <<= xOwner;
804 aInsertEvent.Source = xOwner;
805 aInsertEvent.ResourceURL = m_aResourceString;
806 aInsertEvent.Element <<= uno::Reference< XNameAccess >(pInsertedImages);
807 implts_notifyContainerListener( aInsertEvent, NotifyOp_Insert );
808 }
809 if ( pReplacedImages != nullptr )
810 {
811 ConfigurationEvent aReplaceEvent;
812 aReplaceEvent.aInfo <<= nImageType;
813 aReplaceEvent.Accessor <<= xOwner;
814 aReplaceEvent.Source = xOwner;
815 aReplaceEvent.ResourceURL = m_aResourceString;
816 aReplaceEvent.ReplacedElement = Any();
817 aReplaceEvent.Element <<= uno::Reference< XNameAccess >(pReplacedImages);
818 implts_notifyContainerListener( aReplaceEvent, NotifyOp_Replace );
819 }
820 }
821
removeImages(::sal_Int16 nImageType,const Sequence<OUString> & aCommandURLSequence)822 void ImageManagerImpl::removeImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence )
823 {
824 rtl::Reference<GraphicNameAccess> pRemovedImages;
825 rtl::Reference<GraphicNameAccess> pReplacedImages;
826
827 {
828 SolarMutexGuard g;
829
830 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
831 if ( m_bDisposed )
832 throw DisposedException();
833
834 if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE ))
835 throw IllegalArgumentException();
836
837 if ( m_bReadOnly )
838 throw IllegalAccessException();
839
840 vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType );
841 rtl::Reference< GlobalImageList > rGlobalImageList;
842 CmdImageList* pDefaultImageList = nullptr;
843 if ( m_bUseGlobal )
844 {
845 rGlobalImageList = implts_getGlobalImageList();
846 pDefaultImageList = implts_getDefaultImageList();
847 }
848 ImageList* pImageList = implts_getUserImageList(nIndex);
849 uno::Reference<XGraphic> xEmptyGraphic;
850
851 for ( const OUString& rURL : aCommandURLSequence )
852 {
853 sal_uInt16 nPos = pImageList->GetImagePos( rURL );
854 if ( nPos != IMAGELIST_IMAGE_NOTFOUND )
855 {
856 sal_uInt16 nId = pImageList->GetImageId( nPos );
857 pImageList->RemoveImage( nId );
858
859 if ( m_bUseGlobal )
860 {
861 // Check, if we have an image in our module/global image list. If we find one =>
862 // this is a replace instead of a remove operation!
863 Image aNewImage = pDefaultImageList->getImageFromCommandURL( nIndex, rURL );
864 if ( !aNewImage )
865 aNewImage = rGlobalImageList->getImageFromCommandURL( nIndex, rURL );
866 if ( !aNewImage )
867 {
868 if ( !pRemovedImages )
869 pRemovedImages = new GraphicNameAccess();
870 pRemovedImages->addElement( rURL, xEmptyGraphic );
871 }
872 else
873 {
874 if ( !pReplacedImages )
875 pReplacedImages = new GraphicNameAccess();
876 pReplacedImages->addElement(rURL, GetXGraphic(aNewImage));
877 }
878 } // if ( m_bUseGlobal )
879 else
880 {
881 if ( !pRemovedImages )
882 pRemovedImages = new GraphicNameAccess();
883 pRemovedImages->addElement( rURL, xEmptyGraphic );
884 }
885 }
886 }
887
888 if (( pReplacedImages != nullptr ) || ( pRemovedImages != nullptr ))
889 {
890 m_bModified = true;
891 m_bUserImageListModified[nIndex] = true;
892 }
893 }
894
895 // Notify listeners
896 uno::Reference< uno::XInterface > xOwner(m_pOwner);
897 if ( pRemovedImages != nullptr )
898 {
899 ConfigurationEvent aRemoveEvent;
900 aRemoveEvent.aInfo <<= nImageType;
901 aRemoveEvent.Accessor <<= xOwner;
902 aRemoveEvent.Source = xOwner;
903 aRemoveEvent.ResourceURL = m_aResourceString;
904 aRemoveEvent.Element <<= uno::Reference< XNameAccess >(pRemovedImages);
905 implts_notifyContainerListener( aRemoveEvent, NotifyOp_Remove );
906 }
907 if ( pReplacedImages != nullptr )
908 {
909 ConfigurationEvent aReplaceEvent;
910 aReplaceEvent.aInfo <<= nImageType;
911 aReplaceEvent.Accessor <<= xOwner;
912 aReplaceEvent.Source = xOwner;
913 aReplaceEvent.ResourceURL = m_aResourceString;
914 aReplaceEvent.ReplacedElement = Any();
915 aReplaceEvent.Element <<= uno::Reference< XNameAccess >(pReplacedImages);
916 implts_notifyContainerListener( aReplaceEvent, NotifyOp_Replace );
917 }
918 }
919
insertImages(::sal_Int16 nImageType,const Sequence<OUString> & aCommandURLSequence,const Sequence<uno::Reference<XGraphic>> & aGraphicSequence)920 void ImageManagerImpl::insertImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence, const Sequence< uno::Reference< XGraphic > >& aGraphicSequence )
921 {
922 replaceImages(nImageType,aCommandURLSequence,aGraphicSequence);
923 }
924
925 // XUIConfigurationPersistence
reload()926 void ImageManagerImpl::reload()
927 {
928 SolarMutexResettableGuard aGuard;
929
930 if ( m_bDisposed )
931 throw DisposedException();
932
933 CommandMap aOldUserCmdImageSet;
934 std::vector< OUString > aNewUserCmdImageSet;
935
936 if ( !m_bModified )
937 return;
938
939 for ( vcl::ImageType i : o3tl::enumrange<vcl::ImageType>() )
940 {
941 if ( !m_bDisposed && m_bUserImageListModified[i] )
942 {
943 std::vector< OUString > aOldUserCmdImageVector;
944 ImageList* pImageList = implts_getUserImageList(i);
945 pImageList->GetImageNames( aOldUserCmdImageVector );
946
947 // Fill hash map to speed up search afterwards
948 sal_uInt32 j( 0 );
949 const sal_uInt32 nOldCount = aOldUserCmdImageVector.size();
950 for ( j = 0; j < nOldCount; j++ )
951 aOldUserCmdImageSet.emplace( aOldUserCmdImageVector[j], false );
952
953 // Attention: This can make the old image list pointer invalid!
954 implts_loadUserImages( i, m_xUserImageStorage, m_xUserBitmapsStorage );
955 pImageList = implts_getUserImageList(i);
956 pImageList->GetImageNames( aNewUserCmdImageSet );
957
958 rtl::Reference<GraphicNameAccess> pInsertedImages;
959 rtl::Reference<GraphicNameAccess> pReplacedImages;
960 rtl::Reference<GraphicNameAccess> pRemovedImages;
961
962 for (auto const& newUserCmdImage : aNewUserCmdImageSet)
963 {
964 CommandMap::iterator pIter = aOldUserCmdImageSet.find(newUserCmdImage);
965 if ( pIter != aOldUserCmdImageSet.end() )
966 {
967 pIter->second = true; // mark entry as replaced
968 if ( !pReplacedImages )
969 pReplacedImages = new GraphicNameAccess();
970 pReplacedImages->addElement( newUserCmdImage,
971 GetXGraphic(pImageList->GetImage(newUserCmdImage)) );
972 }
973 else
974 {
975 if ( !pInsertedImages )
976 pInsertedImages = new GraphicNameAccess();
977 pInsertedImages->addElement( newUserCmdImage,
978 GetXGraphic(pImageList->GetImage(newUserCmdImage)) );
979 }
980 }
981
982 // Search map for unmarked entries => they have been removed from the user list
983 // through this reload operation.
984 // We have to search the module and global image list!
985 rtl::Reference< GlobalImageList > rGlobalImageList;
986 CmdImageList* pDefaultImageList = nullptr;
987 if ( m_bUseGlobal )
988 {
989 rGlobalImageList = implts_getGlobalImageList();
990 pDefaultImageList = implts_getDefaultImageList();
991 }
992 uno::Reference<XGraphic> xEmptyGraphic;
993 for (auto const& oldUserCmdImage : aOldUserCmdImageSet)
994 {
995 if ( !oldUserCmdImage.second )
996 {
997 if ( m_bUseGlobal )
998 {
999 Image aImage = pDefaultImageList->getImageFromCommandURL( i, oldUserCmdImage.first );
1000 if ( !aImage )
1001 aImage = rGlobalImageList->getImageFromCommandURL( i, oldUserCmdImage.first );
1002
1003 if ( !aImage )
1004 {
1005 // No image in the module/global image list => remove user image
1006 if ( !pRemovedImages )
1007 pRemovedImages = new GraphicNameAccess();
1008 pRemovedImages->addElement( oldUserCmdImage.first, xEmptyGraphic );
1009 }
1010 else
1011 {
1012 // Image has been found in the module/global image list => replace user image
1013 if ( !pReplacedImages )
1014 pReplacedImages = new GraphicNameAccess();
1015 pReplacedImages->addElement(oldUserCmdImage.first, GetXGraphic(aImage));
1016 }
1017 } // if ( m_bUseGlobal )
1018 else
1019 {
1020 // No image in the user image list => remove user image
1021 if ( !pRemovedImages )
1022 pRemovedImages = new GraphicNameAccess();
1023 pRemovedImages->addElement( oldUserCmdImage.first, xEmptyGraphic );
1024 }
1025 }
1026 }
1027
1028 aGuard.clear();
1029
1030 // Now notify our listeners. Unlock mutex to prevent deadlocks
1031 uno::Reference< uno::XInterface > xOwner(m_pOwner);
1032 if ( pInsertedImages != nullptr )
1033 {
1034 ConfigurationEvent aInsertEvent;
1035 aInsertEvent.aInfo <<=static_cast<sal_uInt16>(i);
1036 aInsertEvent.Accessor <<= xOwner;
1037 aInsertEvent.Source = xOwner;
1038 aInsertEvent.ResourceURL = m_aResourceString;
1039 aInsertEvent.Element <<= uno::Reference< XNameAccess >( pInsertedImages );
1040 implts_notifyContainerListener( aInsertEvent, NotifyOp_Insert );
1041 }
1042 if ( pReplacedImages != nullptr )
1043 {
1044 ConfigurationEvent aReplaceEvent;
1045 aReplaceEvent.aInfo <<= static_cast<sal_uInt16>(i);
1046 aReplaceEvent.Accessor <<= xOwner;
1047 aReplaceEvent.Source = xOwner;
1048 aReplaceEvent.ResourceURL = m_aResourceString;
1049 aReplaceEvent.ReplacedElement = Any();
1050 aReplaceEvent.Element <<= uno::Reference< XNameAccess >( pReplacedImages );
1051 implts_notifyContainerListener( aReplaceEvent, NotifyOp_Replace );
1052 }
1053 if ( pRemovedImages != nullptr )
1054 {
1055 ConfigurationEvent aRemoveEvent;
1056 aRemoveEvent.aInfo <<= static_cast<sal_uInt16>(i);
1057 aRemoveEvent.Accessor <<= xOwner;
1058 aRemoveEvent.Source = xOwner;
1059 aRemoveEvent.ResourceURL = m_aResourceString;
1060 aRemoveEvent.Element <<= uno::Reference< XNameAccess >( pRemovedImages );
1061 implts_notifyContainerListener( aRemoveEvent, NotifyOp_Remove );
1062 }
1063
1064 aGuard.reset();
1065 }
1066 }
1067 }
1068
store()1069 void ImageManagerImpl::store()
1070 {
1071 SolarMutexGuard g;
1072
1073 if ( m_bDisposed )
1074 throw DisposedException();
1075
1076 if ( !m_bModified )
1077 return;
1078
1079 bool bWritten( false );
1080 for ( vcl::ImageType i : o3tl::enumrange<vcl::ImageType>() )
1081 {
1082 bool bSuccess = implts_storeUserImages(i, m_xUserImageStorage, m_xUserBitmapsStorage );
1083 if ( bSuccess )
1084 bWritten = true;
1085 m_bUserImageListModified[i] = false;
1086 }
1087
1088 if ( bWritten &&
1089 m_xUserConfigStorage.is() )
1090 {
1091 uno::Reference< XTransactedObject > xUserConfigStorageCommit( m_xUserConfigStorage, UNO_QUERY );
1092 if ( xUserConfigStorageCommit.is() )
1093 xUserConfigStorageCommit->commit();
1094 if ( m_xUserRootCommit.is() )
1095 m_xUserRootCommit->commit();
1096 }
1097
1098 m_bModified = false;
1099 }
1100
storeToStorage(const uno::Reference<XStorage> & Storage)1101 void ImageManagerImpl::storeToStorage( const uno::Reference< XStorage >& Storage )
1102 {
1103 SolarMutexGuard g;
1104
1105 if ( m_bDisposed )
1106 throw DisposedException();
1107
1108 if ( !(m_bModified && Storage.is()) )
1109 return;
1110
1111 tools::Long nModes = ElementModes::READWRITE;
1112
1113 uno::Reference< XStorage > xUserImageStorage = Storage->openStorageElement( IMAGE_FOLDER,
1114 nModes );
1115 if ( !xUserImageStorage.is() )
1116 return;
1117
1118 uno::Reference< XStorage > xUserBitmapsStorage = xUserImageStorage->openStorageElement( BITMAPS_FOLDER,
1119 nModes );
1120 for ( vcl::ImageType i : o3tl::enumrange<vcl::ImageType>() )
1121 {
1122 implts_getUserImageList(i);
1123 implts_storeUserImages( i, xUserImageStorage, xUserBitmapsStorage );
1124 }
1125
1126 uno::Reference< XTransactedObject > xTransaction( Storage, UNO_QUERY );
1127 if ( xTransaction.is() )
1128 xTransaction->commit();
1129 }
1130
isModified() const1131 bool ImageManagerImpl::isModified() const
1132 {
1133 SolarMutexGuard g;
1134 return m_bModified;
1135 }
1136
isReadOnly() const1137 bool ImageManagerImpl::isReadOnly() const
1138 {
1139 SolarMutexGuard g;
1140 return m_bReadOnly;
1141 }
1142 // XUIConfiguration
addConfigurationListener(const uno::Reference<css::ui::XUIConfigurationListener> & xListener)1143 void ImageManagerImpl::addConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener )
1144 {
1145 {
1146 SolarMutexGuard g;
1147
1148 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
1149 if ( m_bDisposed )
1150 throw DisposedException();
1151 }
1152
1153 m_aListenerContainer.addInterface( cppu::UnoType<XUIConfigurationListener>::get(), xListener );
1154 }
1155
removeConfigurationListener(const uno::Reference<css::ui::XUIConfigurationListener> & xListener)1156 void ImageManagerImpl::removeConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener )
1157 {
1158 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
1159 m_aListenerContainer.removeInterface( cppu::UnoType<XUIConfigurationListener>::get(), xListener );
1160 }
1161
implts_notifyContainerListener(const ConfigurationEvent & aEvent,NotifyOp eOp)1162 void ImageManagerImpl::implts_notifyContainerListener( const ConfigurationEvent& aEvent, NotifyOp eOp )
1163 {
1164 ::cppu::OInterfaceContainerHelper* pContainer = m_aListenerContainer.getContainer(
1165 cppu::UnoType<css::ui::XUIConfigurationListener>::get());
1166 if ( pContainer == nullptr )
1167 return;
1168
1169 ::cppu::OInterfaceIteratorHelper pIterator( *pContainer );
1170 while ( pIterator.hasMoreElements() )
1171 {
1172 try
1173 {
1174 switch ( eOp )
1175 {
1176 case NotifyOp_Replace:
1177 static_cast< css::ui::XUIConfigurationListener*>(pIterator.next())->elementReplaced( aEvent );
1178 break;
1179 case NotifyOp_Insert:
1180 static_cast< css::ui::XUIConfigurationListener*>(pIterator.next())->elementInserted( aEvent );
1181 break;
1182 case NotifyOp_Remove:
1183 static_cast< css::ui::XUIConfigurationListener*>(pIterator.next())->elementRemoved( aEvent );
1184 break;
1185 }
1186 }
1187 catch( const css::uno::RuntimeException& )
1188 {
1189 pIterator.remove();
1190 }
1191 }
1192 }
clear()1193 void ImageManagerImpl::clear()
1194 {
1195 SolarMutexGuard g;
1196
1197 for (auto & n : m_pUserImageList)
1198 {
1199 n.reset();
1200 }
1201 }
1202 } // namespace framework
1203
1204 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1205