diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /framework/source/uiconfiguration | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'framework/source/uiconfiguration')
15 files changed, 7622 insertions, 0 deletions
diff --git a/framework/source/uiconfiguration/CommandImageResolver.cxx b/framework/source/uiconfiguration/CommandImageResolver.cxx new file mode 100644 index 000000000..b44393668 --- /dev/null +++ b/framework/source/uiconfiguration/CommandImageResolver.cxx @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "CommandImageResolver.hxx" +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <rtl/ustrbuf.hxx> +#include <tools/urlobj.hxx> + +using css::uno::Sequence; + +namespace vcl +{ + +namespace +{ + +static const o3tl::enumarray<ImageType, const char*> ImageType_Prefixes = +{ + "cmd/sc_", + "cmd/lc_", + "cmd/32/" +}; + +OUString lclConvertToCanonicalName(const OUString& rFileName) +{ + bool bRemoveSlash(true); + sal_Int32 nLength = rFileName.getLength(); + const sal_Unicode* pString = rFileName.getStr(); + + OUStringBuffer aBuffer(nLength*2); + + for (sal_Int32 i = 0; i < nLength; i++) + { + const sal_Unicode cCurrentChar = pString[i]; + switch (cCurrentChar) + { + // map forbidden characters to escape + case '/': + if (!bRemoveSlash) + aBuffer.append("%2f"); + break; + case '\\': aBuffer.append("%5c"); bRemoveSlash = false; break; + case ':': aBuffer.append("%3a"); bRemoveSlash = false; break; + case '*': aBuffer.append("%2a"); bRemoveSlash = false; break; + case '?': aBuffer.append("%3f"); bRemoveSlash = false; break; + case '<': aBuffer.append("%3c"); bRemoveSlash = false; break; + case '>': aBuffer.append("%3e"); bRemoveSlash = false; break; + case '|': aBuffer.append("%7c"); bRemoveSlash = false; break; + default: + aBuffer.append(cCurrentChar); bRemoveSlash = false; break; + } + } + return aBuffer.makeStringAndClear(); +} + +} // end anonymous namespace + +CommandImageResolver::CommandImageResolver() +{ +} + +CommandImageResolver::~CommandImageResolver() +{ +} + +void CommandImageResolver::registerCommands(Sequence<OUString>& aCommandSequence) +{ + sal_Int32 nSequenceSize = aCommandSequence.getLength(); + + m_aImageCommandNameVector.resize(nSequenceSize); + m_aImageNameVector.resize(nSequenceSize); + + for (sal_Int32 i = 0; i < nSequenceSize; ++i) + { + OUString aCommandName(aCommandSequence[i]); + OUString aImageName; + + m_aImageCommandNameVector[i] = aCommandName; + + if (aCommandName.indexOf(".uno:") != 0) + { + INetURLObject aUrlObject(aCommandName, INetURLObject::EncodeMechanism::All); + aImageName = aUrlObject.GetURLPath(); + aImageName = lclConvertToCanonicalName(aImageName); + } + else + { + // just remove the schema + if (aCommandName.getLength() > 5) + aImageName = aCommandName.copy(5); + + // Search for query part. + if (aImageName.indexOf('?') != -1) + aImageName = lclConvertToCanonicalName(aImageName); + } + + // Image names are not case-dependent. Always use lower case characters to + // reflect this. + aImageName = aImageName.toAsciiLowerCase() + ".png"; + + m_aImageNameVector[i] = aImageName; + m_aCommandToImageNameMap[aCommandName] = aImageName; + } +} + +bool CommandImageResolver::hasImage(const OUString& rCommandURL) +{ + CommandToImageNameMap::const_iterator pIterator = m_aCommandToImageNameMap.find(rCommandURL); + return pIterator != m_aCommandToImageNameMap.end(); +} + +ImageList* CommandImageResolver::getImageList(ImageType nImageType) +{ + const OUString sIconTheme = Application::GetSettings().GetStyleSettings().DetermineIconTheme(); + + if (sIconTheme != m_sIconTheme) + { + m_sIconTheme = sIconTheme; + for (auto& rp : m_pImageList) + rp.reset(); + } + + if (!m_pImageList[nImageType]) + { + OUString sIconPath = OUString::createFromAscii(ImageType_Prefixes[nImageType]); + m_pImageList[nImageType].reset( new ImageList(m_aImageNameVector, sIconPath) ); + } + + return m_pImageList[nImageType].get(); +} + +Image CommandImageResolver::getImageFromCommandURL(ImageType nImageType, const OUString& rCommandURL) +{ + CommandToImageNameMap::const_iterator pIterator = m_aCommandToImageNameMap.find(rCommandURL); + if (pIterator != m_aCommandToImageNameMap.end()) + { + ImageList* pImageList = getImageList(nImageType); + return pImageList->GetImage(pIterator->second); + } + return Image(); +} + +} // end namespace vcl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/CommandImageResolver.hxx b/framework/source/uiconfiguration/CommandImageResolver.hxx new file mode 100644 index 000000000..fe9a4ed95 --- /dev/null +++ b/framework/source/uiconfiguration/CommandImageResolver.hxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_VCL_COMMANDICONRESOLVER_HXX +#define INCLUDED_VCL_COMMANDICONRESOLVER_HXX + +#include <vcl/image.hxx> +#include <o3tl/enumarray.hxx> + +#include <com/sun/star/uno/Sequence.hxx> + +#include "ImageList.hxx" + +#include <memory> +#include <unordered_map> +#include <vector> + +namespace vcl +{ + +class CommandImageResolver final +{ +private: + typedef std::unordered_map<OUString, OUString > CommandToImageNameMap; + + CommandToImageNameMap m_aCommandToImageNameMap; + std::vector<OUString> m_aImageCommandNameVector; + std::vector<OUString> m_aImageNameVector; + + o3tl::enumarray<ImageType, std::unique_ptr<ImageList>> m_pImageList; + OUString m_sIconTheme; + + ImageList* getImageList(ImageType nImageType); + +public: + CommandImageResolver(); + ~CommandImageResolver(); + + void registerCommands(css::uno::Sequence<OUString>& aCommandSequence); + Image getImageFromCommandURL(ImageType nImageType, const OUString& rCommandURL); + + std::vector<OUString>& getCommandNames() + { + return m_aImageCommandNameVector; + } + + bool hasImage(const OUString& rCommandURL); +}; + +} // end namespace vcl + +#endif // INCLUDED_VCL_COMMANDICONRESOLVER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/ImageList.cxx b/framework/source/uiconfiguration/ImageList.cxx new file mode 100644 index 000000000..8e775aeda --- /dev/null +++ b/framework/source/uiconfiguration/ImageList.cxx @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/log.hxx> +#include <tools/debug.hxx> +#include <vcl/image.hxx> +#include "ImageList.hxx" + +ImageList::ImageList() +{ +} + +ImageList::ImageList(const std::vector< OUString >& rNameVector, + const OUString& rPrefix) +{ + SAL_INFO( "vcl", "vcl: ImageList::ImageList(const vector< OUString >& ..." ); + + maImages.reserve( rNameVector.size() ); + + maPrefix = rPrefix; + for( size_t i = 0; i < rNameVector.size(); ++i ) + ImplAddImage( rPrefix, rNameVector[ i ], static_cast< sal_uInt16 >( i ) + 1, Image() ); +} + +// FIXME: Rather a performance hazard +BitmapEx ImageList::GetAsHorizontalStrip() const +{ + sal_uInt16 nCount = maImages.size(); + if( !nCount ) + return BitmapEx(); + + BitmapEx aTempl = maImages[ 0 ]->maImage.GetBitmapEx(); + Size aImageSize(aTempl.GetSizePixel()); + Size aSize(aImageSize.Width() * nCount, aImageSize.Height()); + BitmapEx aResult( aTempl, Point(), aSize ); + + tools::Rectangle aSrcRect( Point( 0, 0 ), aImageSize ); + for (sal_uInt16 nIdx = 0; nIdx < nCount; nIdx++) + { + tools::Rectangle aDestRect( Point( nIdx * aImageSize.Width(), 0 ), aImageSize ); + ImageAryData *pData = maImages[ nIdx ].get(); + BitmapEx aTmp = pData->maImage.GetBitmapEx(); + aResult.CopyPixel( aDestRect, aSrcRect, &aTmp); + } + + return aResult; +} + +void ImageList::InsertFromHorizontalStrip( const BitmapEx &rBitmapEx, + const std::vector< OUString > &rNameVector ) +{ + sal_uInt16 nItems = sal::static_int_cast< sal_uInt16 >( rNameVector.size() ); + + if (!nItems) + return; + + Size aSize( rBitmapEx.GetSizePixel() ); + DBG_ASSERT (rBitmapEx.GetSizePixel().Width() % nItems == 0, + "ImageList::InsertFromHorizontalStrip - very odd size"); + aSize.setWidth( aSize.Width() / nItems ); + maImages.clear(); + maNameHash.clear(); + maImages.reserve( nItems ); + maPrefix.clear(); + + for (sal_uInt16 nIdx = 0; nIdx < nItems; nIdx++) + { + BitmapEx aBitmap( rBitmapEx, Point( nIdx * aSize.Width(), 0 ), aSize ); + ImplAddImage( maPrefix, rNameVector[ nIdx ], nIdx + 1, Image( aBitmap ) ); + } +} + +sal_uInt16 ImageList::ImplGetImageId( const OUString& rImageName ) const +{ + auto it = maNameHash.find( rImageName ); + if (it == maNameHash.end()) + return 0; + return it->second->mnId; +} + +void ImageList::AddImage( const OUString& rImageName, const Image& rImage ) +{ + SAL_WARN_IF( GetImagePos( rImageName ) != IMAGELIST_IMAGE_NOTFOUND, "vcl", "ImageList::AddImage() - ImageName already exists" ); + + ImplAddImage( maPrefix, rImageName, GetImageCount() + 1, rImage ); +} + +void ImageList::ReplaceImage( const OUString& rImageName, const Image& rImage ) +{ + const sal_uInt16 nId = ImplGetImageId( rImageName ); + + if( nId ) + { + // Just replace the bitmap rather than doing RemoveImage / AddImage + // which breaks index-based iteration. + ImageAryData *pImg = maNameHash[ rImageName ]; + pImg->maImage = rImage; + } +} + +void ImageList::RemoveImage( sal_uInt16 nId ) +{ + for( size_t i = 0; i < maImages.size(); ++i ) + { + if( maImages[ i ]->mnId == nId ) + { + ImplRemoveImage( static_cast< sal_uInt16 >( i ) ); + break; + } + } +} + +Image ImageList::GetImage( const OUString& rImageName ) const +{ + auto it = maNameHash.find( rImageName ); + if (it == maNameHash.end()) + return Image(); + return it->second->maImage; +} + +sal_uInt16 ImageList::GetImageCount() const +{ + return static_cast< sal_uInt16 >( maImages.size() ); +} + +sal_uInt16 ImageList::GetImagePos( const OUString& rImageName ) const +{ + if( !rImageName.isEmpty() ) + { + for( size_t i = 0; i < maImages.size(); i++ ) + { + if (maImages[i]->maName == rImageName) + return static_cast< sal_uInt16 >( i ); + } + } + + return IMAGELIST_IMAGE_NOTFOUND; +} + +sal_uInt16 ImageList::GetImageId( sal_uInt16 nPos ) const +{ + return maImages[ nPos ]->mnId; +} + +OUString ImageList::GetImageName( sal_uInt16 nPos ) const +{ + return maImages[ nPos ]->maName; +} + +void ImageList::GetImageNames( std::vector< OUString >& rNames ) const +{ + SAL_INFO( "vcl", "vcl: ImageList::GetImageNames" ); + + rNames = std::vector< OUString >(); + + for(auto const & pImage : maImages) + { + const OUString& rName( pImage->maName ); + if( !rName.isEmpty()) + rNames.push_back( rName ); + } +} + +void ImageList::ImplAddImage( const OUString &aPrefix, const OUString &aName, + sal_uInt16 nId, const Image &aImage ) +{ + Image aInsert = aImage; + if (!aInsert) + aInsert = Image( "private:graphicrepository/" + aPrefix + aName ); + + ImageAryData *pImg = new ImageAryData{ aName, nId, aInsert }; + maImages.emplace_back( pImg ); + if( !aName.isEmpty() ) + maNameHash [ aName ] = pImg; +} + +void ImageList::ImplRemoveImage( sal_uInt16 nPos ) +{ + ImageAryData *pImg = maImages[ nPos ].get(); + if( !pImg->maName.isEmpty() ) + maNameHash.erase( pImg->maName ); + maImages.erase( maImages.begin() + nPos ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/ImageList.hxx b/framework/source/uiconfiguration/ImageList.hxx new file mode 100644 index 000000000..946805f6c --- /dev/null +++ b/framework/source/uiconfiguration/ImageList.hxx @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef FRAMEWORK_SOURCE_UICONFIGURATION_IMAGELIST_HXX +#define FRAMEWORK_SOURCE_UICONFIGURATION_IMAGELIST_HXX + +#include <vcl/image.hxx> +#include <unordered_map> +#include <vector> + +// Images identified by either name, or by id +struct ImageAryData +{ + OUString maName; + sal_uInt16 mnId; + Image maImage; +}; + + +class ImageList +{ +public: + explicit ImageList(); + ImageList( const std::vector<OUString>& rNameVector, + const OUString& rPrefix); + + void InsertFromHorizontalStrip( const BitmapEx &rBitmapEx, + const std::vector< OUString > &rNameVector ); + BitmapEx GetAsHorizontalStrip() const; + sal_uInt16 GetImageCount() const; + + void AddImage( const OUString& rImageName, const Image& rImage ); + + void ReplaceImage( const OUString& rImageName, const Image& rImage ); + + void RemoveImage( sal_uInt16 nId ); + + Image GetImage( const OUString& rImageName ) const; + + sal_uInt16 GetImagePos( const OUString& rImageName ) const; + + sal_uInt16 GetImageId( sal_uInt16 nPos ) const; + + OUString GetImageName( sal_uInt16 nPos ) const; + void GetImageNames( ::std::vector< OUString >& rNames ) const; + +private: + + std::vector< std::unique_ptr<ImageAryData> > maImages; + std::unordered_map< OUString, ImageAryData * > maNameHash; + OUString maPrefix; + + sal_uInt16 ImplGetImageId( const OUString& rImageName ) const; + void ImplAddImage( const OUString &aPrefix, const OUString &aName, sal_uInt16 nId, const Image &aImage ); + void ImplRemoveImage( sal_uInt16 nPos ); +}; + +#endif // INCLUDED_VCL_IMAGE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/globalsettings.cxx b/framework/source/uiconfiguration/globalsettings.cxx new file mode 100644 index 000000000..f8a492299 --- /dev/null +++ b/framework/source/uiconfiguration/globalsettings.cxx @@ -0,0 +1,272 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uiconfiguration/globalsettings.hxx> +#include <services.h> + +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XEventListener.hpp> + +#include <rtl/instance.hxx> +#include <comphelper/propertysequence.hxx> +#include <cppuhelper/implbase.hxx> + +// Defines + +using namespace ::com::sun::star; + +// Namespace + +namespace framework +{ + +// Configuration access class for WindowState supplier implementation + +namespace { + +class GlobalSettings_Access : public ::cppu::WeakImplHelper< + css::lang::XComponent, + css::lang::XEventListener> +{ + public: + explicit GlobalSettings_Access( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + // XComponent + virtual void SAL_CALL dispose() override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // settings access + bool HasToolbarStatesInfo(); + bool GetToolbarStateInfo( GlobalSettings::StateInfo eStateInfo, css::uno::Any& aValue ); + + private: + void impl_initConfigAccess(); + + osl::Mutex m_mutex; + bool m_bDisposed : 1, + m_bConfigRead : 1; + OUString m_aNodeRefStates; + OUString m_aPropStatesEnabled; + OUString m_aPropLocked; + OUString m_aPropDocked; + css::uno::Reference< css::container::XNameAccess > m_xConfigAccess; + css::uno::Reference< css::uno::XComponentContext> m_xContext; +}; + +} + +GlobalSettings_Access::GlobalSettings_Access( const css::uno::Reference< css::uno::XComponentContext >& rxContext ) : + m_bDisposed( false ), + m_bConfigRead( false ), + m_aNodeRefStates( "States" ), + m_aPropStatesEnabled( "StatesEnabled" ), + m_aPropLocked( "Locked" ), + m_aPropDocked( "Docked" ), + m_xContext( rxContext ) +{ +} + +// XComponent +void SAL_CALL GlobalSettings_Access::dispose() +{ + osl::MutexGuard g(m_mutex); + m_xConfigAccess.clear(); + m_bDisposed = true; +} + +void SAL_CALL GlobalSettings_Access::addEventListener( const css::uno::Reference< css::lang::XEventListener >& ) +{ +} + +void SAL_CALL GlobalSettings_Access::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& ) +{ +} + +// XEventListener +void SAL_CALL GlobalSettings_Access::disposing( const css::lang::EventObject& ) +{ + osl::MutexGuard g(m_mutex); + m_xConfigAccess.clear(); +} + +// settings access +bool GlobalSettings_Access::HasToolbarStatesInfo() +{ + osl::MutexGuard g(m_mutex); + + if ( m_bDisposed ) + return false; + + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + impl_initConfigAccess(); + } + + if ( m_xConfigAccess.is() ) + { + try + { + css::uno::Any a; + bool bValue; + a = m_xConfigAccess->getByName( m_aPropStatesEnabled ); + if ( a >>= bValue ) + return bValue; + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::uno::Exception& ) + { + } + } + + return false; +} + +bool GlobalSettings_Access::GetToolbarStateInfo( GlobalSettings::StateInfo eStateInfo, css::uno::Any& aValue ) +{ + osl::MutexGuard g(m_mutex); + + if ( m_bDisposed ) + return false; + + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + impl_initConfigAccess(); + } + + if ( m_xConfigAccess.is() ) + { + try + { + css::uno::Any a = m_xConfigAccess->getByName( m_aNodeRefStates ); + css::uno::Reference< css::container::XNameAccess > xNameAccess; + if ( a >>= xNameAccess ) + { + if ( eStateInfo == GlobalSettings::STATEINFO_LOCKED ) + a = xNameAccess->getByName( m_aPropLocked ); + else if ( eStateInfo == GlobalSettings::STATEINFO_DOCKED ) + a = xNameAccess->getByName( m_aPropDocked ); + + aValue = a; + return true; + } + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::uno::Exception& ) + { + } + } + + return false; +} + +void GlobalSettings_Access::impl_initConfigAccess() +{ + try + { + if ( m_xContext.is() ) + { + css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider = + css::configuration::theDefaultProvider::get( m_xContext ); + + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", uno::Any(OUString("/org.openoffice.Office.UI.GlobalSettings/Toolbars"))} + })); + m_xConfigAccess.set(xConfigProvider->createInstanceWithArguments( + SERVICENAME_CFGREADACCESS, aArgs ), + css::uno::UNO_QUERY ); + + css::uno::Reference< css::lang::XComponent >( + xConfigProvider, css::uno::UNO_QUERY_THROW )->addEventListener( + css::uno::Reference< css::lang::XEventListener >( + static_cast< cppu::OWeakObject* >( this ), + css::uno::UNO_QUERY )); + } + } + catch ( const css::lang::WrappedTargetException& ) + { + } + catch ( const css::uno::Exception& ) + { + } +} + +// global class + +namespace { + +struct mutexGlobalSettings : public rtl::Static< osl::Mutex, mutexGlobalSettings > {}; + +} + +static GlobalSettings_Access* pStaticSettings = nullptr; + +static GlobalSettings_Access* GetGlobalSettings( const css::uno::Reference< css::uno::XComponentContext >& rxContext ) +{ + osl::MutexGuard aGuard(mutexGlobalSettings::get()); + if ( !pStaticSettings ) + pStaticSettings = new GlobalSettings_Access( rxContext ); + return pStaticSettings; +} + +GlobalSettings::GlobalSettings( const css::uno::Reference< css::uno::XComponentContext >& rxContext ) : + m_xContext( rxContext ) +{ +} + +GlobalSettings::~GlobalSettings() +{ +} + +// settings access +bool GlobalSettings::HasToolbarStatesInfo() const +{ + GlobalSettings_Access* pSettings( GetGlobalSettings( m_xContext )); + + if ( pSettings ) + return pSettings->HasToolbarStatesInfo(); + else + return false; +} + +bool GlobalSettings::GetToolbarStateInfo( StateInfo eStateInfo, css::uno::Any& aValue ) +{ + GlobalSettings_Access* pSettings( GetGlobalSettings( m_xContext )); + + if ( pSettings ) + return pSettings->GetToolbarStateInfo( eStateInfo, aValue ); + else + return false; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/graphicnameaccess.cxx b/framework/source/uiconfiguration/graphicnameaccess.cxx new file mode 100644 index 000000000..809cc88b8 --- /dev/null +++ b/framework/source/uiconfiguration/graphicnameaccess.cxx @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uiconfiguration/graphicnameaccess.hxx> + +#include <comphelper/sequence.hxx> + +using namespace ::com::sun::star; + +namespace framework +{ + +GraphicNameAccess::GraphicNameAccess() +{ +} + +GraphicNameAccess::~GraphicNameAccess() +{ +} + +void GraphicNameAccess::addElement( const OUString& rName, const uno::Reference< graphic::XGraphic >& rElement ) +{ + m_aNameToElementMap.emplace( rName, rElement ); +} + +// XNameAccess +uno::Any SAL_CALL GraphicNameAccess::getByName( const OUString& aName ) +{ + NameGraphicHashMap::const_iterator pIter = m_aNameToElementMap.find( aName ); + if ( pIter == m_aNameToElementMap.end() ) + throw container::NoSuchElementException(); + return uno::makeAny( pIter->second ); +} + +uno::Sequence< OUString > SAL_CALL GraphicNameAccess::getElementNames() +{ + if ( !m_aSeq.hasElements() ) + { + m_aSeq = comphelper::mapKeysToSequence(m_aNameToElementMap); + } + + return m_aSeq; +} + +sal_Bool SAL_CALL GraphicNameAccess::hasByName( const OUString& aName ) +{ + NameGraphicHashMap::const_iterator pIter = m_aNameToElementMap.find( aName ); + return ( pIter != m_aNameToElementMap.end() ); +} + +// XElementAccess +sal_Bool SAL_CALL GraphicNameAccess::hasElements() +{ + return ( !m_aNameToElementMap.empty() ); +} + +uno::Type SAL_CALL GraphicNameAccess::getElementType() +{ + return cppu::UnoType<graphic::XGraphic>::get(); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/imagemanager.cxx b/framework/source/uiconfiguration/imagemanager.cxx new file mode 100644 index 000000000..b39c77b69 --- /dev/null +++ b/framework/source/uiconfiguration/imagemanager.cxx @@ -0,0 +1,173 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uiconfiguration/imagemanager.hxx> +#include "imagemanagerimpl.hxx" + +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <vcl/svapp.hxx> + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::uno::Any; +using ::com::sun::star::graphic::XGraphic; +using namespace ::com::sun::star; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; + +namespace framework +{ + +ImageManager::ImageManager( const uno::Reference< uno::XComponentContext >& rxContext ) : + m_pImpl( new ImageManagerImpl(rxContext, this, false) ) +{ +} + +ImageManager::~ImageManager() +{ + m_pImpl->clear(); +} + +// XComponent +void SAL_CALL ImageManager::dispose() +{ + m_pImpl->dispose(); +} + +void SAL_CALL ImageManager::addEventListener( const uno::Reference< XEventListener >& xListener ) +{ + m_pImpl->addEventListener(xListener); +} + +void SAL_CALL ImageManager::removeEventListener( const uno::Reference< XEventListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_pImpl->removeEventListener(xListener); +} + +// Non-UNO methods +void ImageManager::setStorage( const uno::Reference< XStorage >& Storage ) +{ + SolarMutexGuard g; + + m_pImpl->m_xUserConfigStorage = Storage; + m_pImpl->implts_initialize(); +} + +// XInitialization +void SAL_CALL ImageManager::initialize( const Sequence< Any >& aArguments ) +{ + m_pImpl->initialize(aArguments); +} + +// XImageManager +void SAL_CALL ImageManager::reset() +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_pImpl->reset(); +} + +Sequence< OUString > SAL_CALL ImageManager::getAllImageNames( ::sal_Int16 nImageType ) +{ + return m_pImpl->getAllImageNames( nImageType ); +} + +sal_Bool SAL_CALL ImageManager::hasImage( ::sal_Int16 nImageType, const OUString& aCommandURL ) +{ + return m_pImpl->hasImage(nImageType,aCommandURL); +} + +Sequence< uno::Reference< XGraphic > > SAL_CALL ImageManager::getImages( + ::sal_Int16 nImageType, + const Sequence< OUString >& aCommandURLSequence ) +{ + return m_pImpl->getImages(nImageType,aCommandURLSequence); +} + +void SAL_CALL ImageManager::replaceImages( + ::sal_Int16 nImageType, + const Sequence< OUString >& aCommandURLSequence, + const Sequence< uno::Reference< XGraphic > >& aGraphicsSequence ) +{ + m_pImpl->replaceImages(nImageType,aCommandURLSequence,aGraphicsSequence); +} + +void SAL_CALL ImageManager::removeImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence ) +{ + m_pImpl->removeImages(nImageType,aCommandURLSequence); +} + +void SAL_CALL ImageManager::insertImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence, const Sequence< uno::Reference< XGraphic > >& aGraphicSequence ) +{ + m_pImpl->insertImages(nImageType,aCommandURLSequence,aGraphicSequence); +} + +// XUIConfiguration +void SAL_CALL ImageManager::addConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + m_pImpl->addConfigurationListener(xListener); +} + +void SAL_CALL ImageManager::removeConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_pImpl->removeConfigurationListener(xListener); +} + +// XUIConfigurationPersistence +void SAL_CALL ImageManager::reload() +{ + m_pImpl->reload(); +} + +void SAL_CALL ImageManager::store() +{ + m_pImpl->store(); +} + +void SAL_CALL ImageManager::storeToStorage( const uno::Reference< XStorage >& Storage ) +{ + m_pImpl->storeToStorage(Storage); +} + +sal_Bool SAL_CALL ImageManager::isModified() +{ + return m_pImpl->isModified(); +} + +sal_Bool SAL_CALL ImageManager::isReadOnly() +{ + return m_pImpl->isReadOnly(); +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ImageManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new framework::ImageManager(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/imagemanagerimpl.cxx b/framework/source/uiconfiguration/imagemanagerimpl.cxx new file mode 100644 index 000000000..297def1bb --- /dev/null +++ b/framework/source/uiconfiguration/imagemanagerimpl.cxx @@ -0,0 +1,1211 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "imagemanagerimpl.hxx" +#include <xml/imagesconfiguration.hxx> +#include <uiconfiguration/imagetype.hxx> +#include <uiconfiguration/graphicnameaccess.hxx> + +#include <properties.h> + +#include <com/sun/star/frame/theUICommandDescription.hpp> +#include <com/sun/star/ui/ConfigurationEvent.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalAccessException.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/InvalidStorageException.hpp> +#include <com/sun/star/embed/StorageWrappedTargetException.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/ui/ImageType.hpp> +#include <vcl/graph.hxx> +#include <vcl/svapp.hxx> +#include <o3tl/enumrange.hxx> +#include <osl/mutex.hxx> +#include <comphelper/sequence.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <vcl/pngread.hxx> +#include <vcl/pngwrite.hxx> +#include <rtl/instance.hxx> +#include <memory> + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::Any; +using ::com::sun::star::graphic::XGraphic; +using namespace ::com::sun::star; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::ui; +using namespace ::cppu; + +const sal_Int16 MAX_IMAGETYPE_VALUE = css::ui::ImageType::SIZE_32; + +static const char IMAGE_FOLDER[] = "images"; +static const char BITMAPS_FOLDER[] = "Bitmaps"; + +static const o3tl::enumarray<vcl::ImageType, const char*> IMAGELIST_XML_FILE = +{ + "sc_imagelist.xml", + "lc_imagelist.xml", + "xc_imagelist.xml" +}; + +static const o3tl::enumarray<vcl::ImageType, const char*> BITMAP_FILE_NAMES = +{ + "sc_userimages.png", + "lc_userimages.png", + "xc_userimages.png" +}; + +namespace framework +{ + +static GlobalImageList* pGlobalImageList = nullptr; + +namespace +{ + class theGlobalImageListMutex + : public rtl::Static<osl::Mutex, theGlobalImageListMutex> {}; +} + +static osl::Mutex& getGlobalImageListMutex() +{ + return theGlobalImageListMutex::get(); +} + +static GlobalImageList* getGlobalImageList( const uno::Reference< uno::XComponentContext >& rxContext ) +{ + osl::MutexGuard guard( getGlobalImageListMutex() ); + + if ( pGlobalImageList == nullptr ) + pGlobalImageList = new GlobalImageList( rxContext ); + + return pGlobalImageList; +} + +CmdImageList::CmdImageList( const uno::Reference< uno::XComponentContext >& rxContext, const OUString& aModuleIdentifier ) : + m_bInitialized(false), + m_aModuleIdentifier( aModuleIdentifier ), + m_xContext( rxContext ) +{ +} + +CmdImageList::~CmdImageList() +{ +} + +void CmdImageList::initialize() +{ + if (m_bInitialized) + return; + + const OUString aCommandImageList(UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDIMAGELIST); + + Sequence<OUString> aCommandImageSeq; + uno::Reference<XNameAccess> xCommandDesc = frame::theUICommandDescription::get(m_xContext); + + if (!m_aModuleIdentifier.isEmpty()) + { + // If we have a module identifier - use to retrieve the command image name list from it. + // Otherwise we will use the global command image list + try + { + xCommandDesc->getByName(m_aModuleIdentifier) >>= xCommandDesc; + if (xCommandDesc.is()) + xCommandDesc->getByName(aCommandImageList) >>= aCommandImageSeq; + } + catch (const NoSuchElementException&) + { + // Module unknown we will work with an empty command image list! + return; + } + } + + if (xCommandDesc.is()) + { + try + { + xCommandDesc->getByName(aCommandImageList) >>= aCommandImageSeq; + } + catch (const NoSuchElementException&) + { + } + catch (const WrappedTargetException&) + { + } + } + + m_aResolver.registerCommands(aCommandImageSeq); + + m_bInitialized = true; +} + + +Image CmdImageList::getImageFromCommandURL(vcl::ImageType nImageType, const OUString& rCommandURL) +{ + initialize(); + return m_aResolver.getImageFromCommandURL(nImageType, rCommandURL); +} + +bool CmdImageList::hasImage(vcl::ImageType /*nImageType*/, const OUString& rCommandURL) +{ + initialize(); + return m_aResolver.hasImage(rCommandURL); +} + +std::vector<OUString>& CmdImageList::getImageCommandNames() +{ + return m_aResolver.getCommandNames(); +} + +GlobalImageList::GlobalImageList( const uno::Reference< uno::XComponentContext >& rxContext ) : + CmdImageList( rxContext, OUString() ) +{ +} + +GlobalImageList::~GlobalImageList() +{ + osl::MutexGuard guard( getGlobalImageListMutex() ); + // remove global pointer as we destroy the object now + pGlobalImageList = nullptr; +} + +Image GlobalImageList::getImageFromCommandURL( vcl::ImageType nImageType, const OUString& rCommandURL ) +{ + osl::MutexGuard guard( getGlobalImageListMutex() ); + return CmdImageList::getImageFromCommandURL( nImageType, rCommandURL ); +} + +bool GlobalImageList::hasImage( vcl::ImageType nImageType, const OUString& rCommandURL ) +{ + osl::MutexGuard guard( getGlobalImageListMutex() ); + return CmdImageList::hasImage( nImageType, rCommandURL ); +} + +::std::vector< OUString >& GlobalImageList::getImageCommandNames() +{ + osl::MutexGuard guard( getGlobalImageListMutex() ); + return CmdImageList::getImageCommandNames(); +} + +static bool implts_checkAndScaleGraphic( uno::Reference< XGraphic >& rOutGraphic, const uno::Reference< XGraphic >& rInGraphic, vcl::ImageType nImageType ) +{ + if ( !rInGraphic.is() ) + { + rOutGraphic = uno::Reference<graphic::XGraphic>(); + return false; + } + + static const o3tl::enumarray<vcl::ImageType, Size> BITMAP_SIZE = + { + Size(16, 16), Size(24, 24), Size(32, 32) + }; + + // Check size and scale it + Graphic aImage(rInGraphic); + if (BITMAP_SIZE[nImageType] != aImage.GetSizePixel()) + { + BitmapEx aBitmap = aImage.GetBitmapEx(); + aBitmap.Scale(BITMAP_SIZE[nImageType]); + aImage = Graphic(aBitmap); + rOutGraphic = aImage.GetXGraphic(); + } + else + rOutGraphic = rInGraphic; + + return true; +} + +static vcl::ImageType implts_convertImageTypeToIndex( sal_Int16 nImageType ) +{ + if (nImageType & css::ui::ImageType::SIZE_LARGE) + return vcl::ImageType::Size26; + else if (nImageType & css::ui::ImageType::SIZE_32) + return vcl::ImageType::Size32; + else + return vcl::ImageType::Size16; +} + +ImageList* ImageManagerImpl::implts_getUserImageList( vcl::ImageType nImageType ) +{ + SolarMutexGuard g; + if ( !m_pUserImageList[nImageType] ) + implts_loadUserImages( nImageType, m_xUserImageStorage, m_xUserBitmapsStorage ); + + return m_pUserImageList[nImageType].get(); +} + +void ImageManagerImpl::implts_initialize() +{ + // Initialize the top-level structures with the storage data + if ( !m_xUserConfigStorage.is() ) + return; + + long nModes = m_bReadOnly ? ElementModes::READ : ElementModes::READWRITE; + + try + { + m_xUserImageStorage = m_xUserConfigStorage->openStorageElement( IMAGE_FOLDER, + nModes ); + if ( m_xUserImageStorage.is() ) + { + m_xUserBitmapsStorage = m_xUserImageStorage->openStorageElement( BITMAPS_FOLDER, + nModes ); + } + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::io::IOException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } +} + +void ImageManagerImpl::implts_loadUserImages( + vcl::ImageType nImageType, + const uno::Reference< XStorage >& xUserImageStorage, + const uno::Reference< XStorage >& xUserBitmapsStorage ) +{ + SolarMutexGuard g; + + if ( xUserImageStorage.is() && xUserBitmapsStorage.is() ) + { + try + { + uno::Reference< XStream > xStream = xUserImageStorage->openStreamElement( OUString::createFromAscii( IMAGELIST_XML_FILE[nImageType] ), + ElementModes::READ ); + uno::Reference< XInputStream > xInputStream = xStream->getInputStream(); + + ImageItemDescriptorList aUserImageListInfo; + ImagesConfiguration::LoadImages( m_xContext, + xInputStream, + aUserImageListInfo ); + if ( !aUserImageListInfo.empty() ) + { + sal_Int32 nCount = aUserImageListInfo.size(); + std::vector< OUString > aUserImagesVector; + aUserImagesVector.reserve(nCount); + for ( sal_Int32 i=0; i < nCount; i++ ) + { + const ImageItemDescriptor& rItem = aUserImageListInfo[i]; + aUserImagesVector.push_back( rItem.aCommandURL ); + } + + uno::Reference< XStream > xBitmapStream = xUserBitmapsStorage->openStreamElement( + OUString::createFromAscii( BITMAP_FILE_NAMES[nImageType] ), + ElementModes::READ ); + + if ( xBitmapStream.is() ) + { + BitmapEx aUserBitmap; + { + std::unique_ptr<SvStream> pSvStream(utl::UcbStreamHelper::CreateStream( xBitmapStream )); + vcl::PNGReader aPngReader( *pSvStream ); + aUserBitmap = aPngReader.Read(); + } + + // Delete old image list and create a new one from the read bitmap + m_pUserImageList[nImageType].reset(new ImageList()); + m_pUserImageList[nImageType]->InsertFromHorizontalStrip + ( aUserBitmap, aUserImagesVector ); + return; + } + } + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::io::IOException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } + } + + // Destroy old image list - create a new empty one + m_pUserImageList[nImageType].reset(new ImageList); +} + +bool ImageManagerImpl::implts_storeUserImages( + vcl::ImageType nImageType, + const uno::Reference< XStorage >& xUserImageStorage, + const uno::Reference< XStorage >& xUserBitmapsStorage ) +{ + SolarMutexGuard g; + + if ( m_bModified ) + { + ImageList* pImageList = implts_getUserImageList( nImageType ); + if ( pImageList->GetImageCount() > 0 ) + { + ImageItemDescriptorList aUserImageListInfo; + + for ( sal_uInt16 i=0; i < pImageList->GetImageCount(); i++ ) + { + ImageItemDescriptor aItem; + aItem.aCommandURL = pImageList->GetImageName( i ); + aUserImageListInfo.push_back( aItem ); + } + + uno::Reference< XTransactedObject > xTransaction; + uno::Reference< XOutputStream > xOutputStream; + uno::Reference< XStream > xStream = xUserImageStorage->openStreamElement( OUString::createFromAscii( IMAGELIST_XML_FILE[nImageType] ), + ElementModes::WRITE|ElementModes::TRUNCATE ); + if ( xStream.is() ) + { + uno::Reference< XStream > xBitmapStream = + xUserBitmapsStorage->openStreamElement( OUString::createFromAscii( BITMAP_FILE_NAMES[nImageType] ), + ElementModes::WRITE|ElementModes::TRUNCATE ); + if ( xBitmapStream.is() ) + { + { + std::unique_ptr<SvStream> pSvStream(utl::UcbStreamHelper::CreateStream( xBitmapStream )); + vcl::PNGWriter aPngWriter( pImageList->GetAsHorizontalStrip() ); + aPngWriter.Write( *pSvStream ); + } + + // Commit user bitmaps storage + xTransaction.set( xUserBitmapsStorage, UNO_QUERY ); + if ( xTransaction.is() ) + xTransaction->commit(); + } + + xOutputStream = xStream->getOutputStream(); + if ( xOutputStream.is() ) + ImagesConfiguration::StoreImages( m_xContext, xOutputStream, aUserImageListInfo ); + + // Commit user image storage + xTransaction.set( xUserImageStorage, UNO_QUERY ); + if ( xTransaction.is() ) + xTransaction->commit(); + } + + return true; + } + else + { + // Remove the streams from the storage, if we have no data. We have to catch + // the NoSuchElementException as it can be possible that there is no stream at all! + try + { + xUserImageStorage->removeElement( OUString::createFromAscii( IMAGELIST_XML_FILE[nImageType] )); + } + catch ( const css::container::NoSuchElementException& ) + { + } + + try + { + xUserBitmapsStorage->removeElement( OUString::createFromAscii( BITMAP_FILE_NAMES[nImageType] )); + } + catch ( const css::container::NoSuchElementException& ) + { + } + + uno::Reference< XTransactedObject > xTransaction; + + // Commit user image storage + xTransaction.set( xUserImageStorage, UNO_QUERY ); + if ( xTransaction.is() ) + xTransaction->commit(); + + // Commit user bitmaps storage + xTransaction.set( xUserBitmapsStorage, UNO_QUERY ); + if ( xTransaction.is() ) + xTransaction->commit(); + + return true; + } + } + + return false; +} + +const rtl::Reference< GlobalImageList >& ImageManagerImpl::implts_getGlobalImageList() +{ + SolarMutexGuard g; + + if ( !m_pGlobalImageList.is() ) + m_pGlobalImageList = getGlobalImageList( m_xContext ); + return m_pGlobalImageList; +} + +CmdImageList* ImageManagerImpl::implts_getDefaultImageList() +{ + SolarMutexGuard g; + + if ( !m_pDefaultImageList ) + m_pDefaultImageList.reset(new CmdImageList( m_xContext, m_aModuleIdentifier )); + + return m_pDefaultImageList.get(); +} + +ImageManagerImpl::ImageManagerImpl( const uno::Reference< uno::XComponentContext >& rxContext,::cppu::OWeakObject* pOwner,bool _bUseGlobal ) : + m_xContext( rxContext ) + , m_pOwner(pOwner) + , m_aResourceString( "private:resource/images/moduleimages" ) + , m_aListenerContainer( m_mutex ) + , m_bUseGlobal(_bUseGlobal) + , m_bReadOnly( true ) + , m_bInitialized( false ) + , m_bModified( false ) + , m_bDisposed( false ) +{ + for ( vcl::ImageType n : o3tl::enumrange<vcl::ImageType>() ) + { + m_pUserImageList[n] = nullptr; + m_bUserImageListModified[n] = false; + } +} + +ImageManagerImpl::~ImageManagerImpl() +{ + clear(); +} + +void ImageManagerImpl::dispose() +{ + uno::Reference< uno::XInterface > xOwner(m_pOwner); + css::lang::EventObject aEvent( xOwner ); + m_aListenerContainer.disposeAndClear( aEvent ); + + { + SolarMutexGuard g; + m_xUserConfigStorage.clear(); + m_xUserImageStorage.clear(); + m_xUserRootCommit.clear(); + m_bModified = false; + m_bDisposed = true; + + // delete user and default image list on dispose + for (auto& n : m_pUserImageList) + { + n.reset(); + } + m_pDefaultImageList.reset(); + } + +} +void ImageManagerImpl::addEventListener( const uno::Reference< XEventListener >& xListener ) +{ + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + } + + m_aListenerContainer.addInterface( cppu::UnoType<XEventListener>::get(), xListener ); +} + +void ImageManagerImpl::removeEventListener( const uno::Reference< XEventListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_aListenerContainer.removeInterface( cppu::UnoType<XEventListener>::get(), xListener ); +} + +// XInitialization +void ImageManagerImpl::initialize( const Sequence< Any >& aArguments ) +{ + SolarMutexGuard g; + + if ( m_bInitialized ) + return; + + for ( const Any& rArg : aArguments ) + { + PropertyValue aPropValue; + if ( rArg >>= aPropValue ) + { + if ( aPropValue.Name == "UserConfigStorage" ) + { + aPropValue.Value >>= m_xUserConfigStorage; + } + else if ( aPropValue.Name == "ModuleIdentifier" ) + { + aPropValue.Value >>= m_aModuleIdentifier; + } + else if ( aPropValue.Name == "UserRootCommit" ) + { + aPropValue.Value >>= m_xUserRootCommit; + } + } + } + + if ( m_xUserConfigStorage.is() ) + { + uno::Reference< XPropertySet > xPropSet( m_xUserConfigStorage, UNO_QUERY ); + if ( xPropSet.is() ) + { + long nOpenMode = 0; + if ( xPropSet->getPropertyValue("OpenMode") >>= nOpenMode ) + m_bReadOnly = !( nOpenMode & ElementModes::WRITE ); + } + } + + implts_initialize(); + + m_bInitialized = true; +} + +// XImageManagerImpl +void ImageManagerImpl::reset() +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + std::vector< OUString > aUserImageNames; + + for ( vcl::ImageType i : o3tl::enumrange<vcl::ImageType>() ) + { + aUserImageNames.clear(); + ImageList* pImageList = implts_getUserImageList(i); + pImageList->GetImageNames( aUserImageNames ); + + Sequence< OUString > aRemoveList( comphelper::containerToSequence(aUserImageNames) ); + + // Remove images + removeImages( sal_Int16( i ), aRemoveList ); + m_bUserImageListModified[i] = true; + } + + m_bModified = true; +} + +Sequence< OUString > ImageManagerImpl::getAllImageNames( ::sal_Int16 nImageType ) +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + ImageNameMap aImageCmdNameMap; + + vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType ); + + sal_uInt32 i( 0 ); + if ( m_bUseGlobal ) + { + rtl::Reference< GlobalImageList > rGlobalImageList = implts_getGlobalImageList(); + + const std::vector< OUString >& rGlobalImageNameVector = rGlobalImageList->getImageCommandNames(); + const sal_uInt32 nGlobalCount = rGlobalImageNameVector.size(); + for ( i = 0; i < nGlobalCount; i++ ) + aImageCmdNameMap.emplace( rGlobalImageNameVector[i], true ); + + const std::vector< OUString >& rModuleImageNameVector = implts_getDefaultImageList()->getImageCommandNames(); + const sal_uInt32 nModuleCount = rModuleImageNameVector.size(); + for ( i = 0; i < nModuleCount; i++ ) + aImageCmdNameMap.emplace( rModuleImageNameVector[i], true ); + } + + ImageList* pImageList = implts_getUserImageList(nIndex); + std::vector< OUString > rUserImageNames; + pImageList->GetImageNames( rUserImageNames ); + const sal_uInt32 nUserCount = rUserImageNames.size(); + for ( i = 0; i < nUserCount; i++ ) + aImageCmdNameMap.emplace( rUserImageNames[i], true ); + + return comphelper::mapKeysToSequence( aImageCmdNameMap ); +} + +bool ImageManagerImpl::hasImage( ::sal_Int16 nImageType, const OUString& aCommandURL ) +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE )) + throw IllegalArgumentException(); + + vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType ); + if ( m_bUseGlobal && implts_getGlobalImageList()->hasImage( nIndex, aCommandURL )) + return true; + else + { + if ( m_bUseGlobal && implts_getDefaultImageList()->hasImage( nIndex, aCommandURL )) + return true; + else + { + // User layer + ImageList* pImageList = implts_getUserImageList(nIndex); + if ( pImageList ) + return ( pImageList->GetImagePos( aCommandURL ) != IMAGELIST_IMAGE_NOTFOUND ); + } + } + + return false; +} + +namespace +{ + css::uno::Reference< css::graphic::XGraphic > GetXGraphic(const Image &rImage) + { + return Graphic(rImage).GetXGraphic(); + } +} + +Sequence< uno::Reference< XGraphic > > ImageManagerImpl::getImages( + ::sal_Int16 nImageType, + const Sequence< OUString >& aCommandURLSequence ) +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE )) + throw IllegalArgumentException(); + + Sequence< uno::Reference< XGraphic > > aGraphSeq( aCommandURLSequence.getLength() ); + + vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType ); + rtl::Reference< GlobalImageList > rGlobalImageList; + CmdImageList* pDefaultImageList = nullptr; + if ( m_bUseGlobal ) + { + rGlobalImageList = implts_getGlobalImageList(); + pDefaultImageList = implts_getDefaultImageList(); + } + ImageList* pUserImageList = implts_getUserImageList(nIndex); + + // We have to search our image list in the following order: + // 1. user image list (read/write) + // 2. module image list (read) + // 3. global image list (read) + sal_Int32 n = 0; + for ( const OUString& rURL : aCommandURLSequence ) + { + Image aImage = pUserImageList->GetImage( rURL ); + if ( !aImage && m_bUseGlobal ) + { + aImage = pDefaultImageList->getImageFromCommandURL( nIndex, rURL ); + if ( !aImage ) + aImage = rGlobalImageList->getImageFromCommandURL( nIndex, rURL ); + } + + aGraphSeq[n++] = GetXGraphic(aImage); + } + + return aGraphSeq; +} + +void ImageManagerImpl::replaceImages( + ::sal_Int16 nImageType, + const Sequence< OUString >& aCommandURLSequence, + const Sequence< uno::Reference< XGraphic > >& aGraphicsSequence ) +{ + GraphicNameAccess* pInsertedImages( nullptr ); + GraphicNameAccess* pReplacedImages( nullptr ); + + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + if (( aCommandURLSequence.getLength() != aGraphicsSequence.getLength() ) || + (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE ))) + throw IllegalArgumentException(); + + if ( m_bReadOnly ) + throw IllegalAccessException(); + + vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType ); + ImageList* pImageList = implts_getUserImageList(nIndex); + + uno::Reference< XGraphic > xGraphic; + for ( sal_Int32 i = 0; i < aCommandURLSequence.getLength(); i++ ) + { + // Check size and scale. If we don't have any graphics ignore it + if ( !implts_checkAndScaleGraphic( xGraphic, aGraphicsSequence[i], nIndex )) + continue; + + sal_uInt16 nPos = pImageList->GetImagePos( aCommandURLSequence[i] ); + if ( nPos == IMAGELIST_IMAGE_NOTFOUND ) + { + pImageList->AddImage(aCommandURLSequence[i], Image(xGraphic)); + if ( !pInsertedImages ) + pInsertedImages = new GraphicNameAccess(); + pInsertedImages->addElement( aCommandURLSequence[i], xGraphic ); + } + else + { + pImageList->ReplaceImage(aCommandURLSequence[i], Image(xGraphic)); + if ( !pReplacedImages ) + pReplacedImages = new GraphicNameAccess(); + pReplacedImages->addElement( aCommandURLSequence[i], xGraphic ); + } + } + + if (( pInsertedImages != nullptr ) || ( pReplacedImages != nullptr )) + { + m_bModified = true; + m_bUserImageListModified[nIndex] = true; + } + } + + uno::Reference< uno::XInterface > xOwner(m_pOwner); + // Notify listeners + if ( pInsertedImages != nullptr ) + { + ConfigurationEvent aInsertEvent; + aInsertEvent.aInfo <<= nImageType; + aInsertEvent.Accessor <<= xOwner; + aInsertEvent.Source = xOwner; + aInsertEvent.ResourceURL = m_aResourceString; + aInsertEvent.Element <<= uno::Reference< XNameAccess >( + static_cast< OWeakObject *>( pInsertedImages ), UNO_QUERY ); + implts_notifyContainerListener( aInsertEvent, NotifyOp_Insert ); + } + if ( pReplacedImages != nullptr ) + { + ConfigurationEvent aReplaceEvent; + aReplaceEvent.aInfo <<= nImageType; + aReplaceEvent.Accessor <<= xOwner; + aReplaceEvent.Source = xOwner; + aReplaceEvent.ResourceURL = m_aResourceString; + aReplaceEvent.ReplacedElement = Any(); + aReplaceEvent.Element <<= uno::Reference< XNameAccess >( + static_cast< OWeakObject *>( pReplacedImages ), UNO_QUERY ); + implts_notifyContainerListener( aReplaceEvent, NotifyOp_Replace ); + } +} + +void ImageManagerImpl::removeImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence ) +{ + GraphicNameAccess* pRemovedImages( nullptr ); + GraphicNameAccess* pReplacedImages( nullptr ); + + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE )) + throw IllegalArgumentException(); + + if ( m_bReadOnly ) + throw IllegalAccessException(); + + vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType ); + rtl::Reference< GlobalImageList > rGlobalImageList; + CmdImageList* pDefaultImageList = nullptr; + if ( m_bUseGlobal ) + { + rGlobalImageList = implts_getGlobalImageList(); + pDefaultImageList = implts_getDefaultImageList(); + } + ImageList* pImageList = implts_getUserImageList(nIndex); + uno::Reference<XGraphic> xEmptyGraphic; + + for ( const OUString& rURL : aCommandURLSequence ) + { + sal_uInt16 nPos = pImageList->GetImagePos( rURL ); + if ( nPos != IMAGELIST_IMAGE_NOTFOUND ) + { + sal_uInt16 nId = pImageList->GetImageId( nPos ); + pImageList->RemoveImage( nId ); + + if ( m_bUseGlobal ) + { + // Check, if we have an image in our module/global image list. If we find one => + // this is a replace instead of a remove operation! + Image aNewImage = pDefaultImageList->getImageFromCommandURL( nIndex, rURL ); + if ( !aNewImage ) + aNewImage = rGlobalImageList->getImageFromCommandURL( nIndex, rURL ); + if ( !aNewImage ) + { + if ( !pRemovedImages ) + pRemovedImages = new GraphicNameAccess(); + pRemovedImages->addElement( rURL, xEmptyGraphic ); + } + else + { + if ( !pReplacedImages ) + pReplacedImages = new GraphicNameAccess(); + pReplacedImages->addElement(rURL, GetXGraphic(aNewImage)); + } + } // if ( m_bUseGlobal ) + else + { + if ( !pRemovedImages ) + pRemovedImages = new GraphicNameAccess(); + pRemovedImages->addElement( rURL, xEmptyGraphic ); + } + } + } + + if (( pReplacedImages != nullptr ) || ( pRemovedImages != nullptr )) + { + m_bModified = true; + m_bUserImageListModified[nIndex] = true; + } + } + + // Notify listeners + uno::Reference< uno::XInterface > xOwner(m_pOwner); + if ( pRemovedImages != nullptr ) + { + ConfigurationEvent aRemoveEvent; + aRemoveEvent.aInfo <<= nImageType; + aRemoveEvent.Accessor <<= xOwner; + aRemoveEvent.Source = xOwner; + aRemoveEvent.ResourceURL = m_aResourceString; + aRemoveEvent.Element <<= uno::Reference< XNameAccess >( + static_cast< OWeakObject *>( pRemovedImages ), UNO_QUERY ); + implts_notifyContainerListener( aRemoveEvent, NotifyOp_Remove ); + } + if ( pReplacedImages != nullptr ) + { + ConfigurationEvent aReplaceEvent; + aReplaceEvent.aInfo <<= nImageType; + aReplaceEvent.Accessor <<= xOwner; + aReplaceEvent.Source = xOwner; + aReplaceEvent.ResourceURL = m_aResourceString; + aReplaceEvent.ReplacedElement = Any(); + aReplaceEvent.Element <<= uno::Reference< XNameAccess >( + static_cast< OWeakObject *>( pReplacedImages ), UNO_QUERY ); + implts_notifyContainerListener( aReplaceEvent, NotifyOp_Replace ); + } +} + +void ImageManagerImpl::insertImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence, const Sequence< uno::Reference< XGraphic > >& aGraphicSequence ) +{ + replaceImages(nImageType,aCommandURLSequence,aGraphicSequence); +} + +// XUIConfigurationPersistence +void ImageManagerImpl::reload() +{ + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + CommandMap aOldUserCmdImageSet; + std::vector< OUString > aNewUserCmdImageSet; + + if ( !m_bModified ) + return; + + for ( vcl::ImageType i : o3tl::enumrange<vcl::ImageType>() ) + { + if ( !m_bDisposed && m_bUserImageListModified[i] ) + { + std::vector< OUString > aOldUserCmdImageVector; + ImageList* pImageList = implts_getUserImageList(i); + pImageList->GetImageNames( aOldUserCmdImageVector ); + + // Fill hash map to speed up search afterwards + sal_uInt32 j( 0 ); + const sal_uInt32 nOldCount = aOldUserCmdImageVector.size(); + for ( j = 0; j < nOldCount; j++ ) + aOldUserCmdImageSet.emplace( aOldUserCmdImageVector[j], false ); + + // Attention: This can make the old image list pointer invalid! + implts_loadUserImages( i, m_xUserImageStorage, m_xUserBitmapsStorage ); + pImageList = implts_getUserImageList(i); + pImageList->GetImageNames( aNewUserCmdImageSet ); + + GraphicNameAccess* pInsertedImages( nullptr ); + GraphicNameAccess* pReplacedImages( nullptr ); + GraphicNameAccess* pRemovedImages( nullptr ); + + for (auto const& newUserCmdImage : aNewUserCmdImageSet) + { + CommandMap::iterator pIter = aOldUserCmdImageSet.find(newUserCmdImage); + if ( pIter != aOldUserCmdImageSet.end() ) + { + pIter->second = true; // mark entry as replaced + if ( !pReplacedImages ) + pReplacedImages = new GraphicNameAccess(); + pReplacedImages->addElement( newUserCmdImage, + GetXGraphic(pImageList->GetImage(newUserCmdImage)) ); + } + else + { + if ( !pInsertedImages ) + pInsertedImages = new GraphicNameAccess(); + pInsertedImages->addElement( newUserCmdImage, + GetXGraphic(pImageList->GetImage(newUserCmdImage)) ); + } + } + + // Search map for unmarked entries => they have been removed from the user list + // through this reload operation. + // We have to search the module and global image list! + rtl::Reference< GlobalImageList > rGlobalImageList; + CmdImageList* pDefaultImageList = nullptr; + if ( m_bUseGlobal ) + { + rGlobalImageList = implts_getGlobalImageList(); + pDefaultImageList = implts_getDefaultImageList(); + } + uno::Reference<XGraphic> xEmptyGraphic; + for (auto const& oldUserCmdImage : aOldUserCmdImageSet) + { + if ( !oldUserCmdImage.second ) + { + if ( m_bUseGlobal ) + { + Image aImage = pDefaultImageList->getImageFromCommandURL( i, oldUserCmdImage.first ); + if ( !aImage ) + aImage = rGlobalImageList->getImageFromCommandURL( i, oldUserCmdImage.first ); + + if ( !aImage ) + { + // No image in the module/global image list => remove user image + if ( !pRemovedImages ) + pRemovedImages = new GraphicNameAccess(); + pRemovedImages->addElement( oldUserCmdImage.first, xEmptyGraphic ); + } + else + { + // Image has been found in the module/global image list => replace user image + if ( !pReplacedImages ) + pReplacedImages = new GraphicNameAccess(); + pReplacedImages->addElement(oldUserCmdImage.first, GetXGraphic(aImage)); + } + } // if ( m_bUseGlobal ) + else + { + // No image in the user image list => remove user image + if ( !pRemovedImages ) + pRemovedImages = new GraphicNameAccess(); + pRemovedImages->addElement( oldUserCmdImage.first, xEmptyGraphic ); + } + } + } + + aGuard.clear(); + + // Now notify our listeners. Unlock mutex to prevent deadlocks + uno::Reference< uno::XInterface > xOwner(m_pOwner); + if ( pInsertedImages != nullptr ) + { + ConfigurationEvent aInsertEvent; + aInsertEvent.aInfo <<=static_cast<sal_uInt16>(i); + aInsertEvent.Accessor <<= xOwner; + aInsertEvent.Source = xOwner; + aInsertEvent.ResourceURL = m_aResourceString; + aInsertEvent.Element <<= uno::Reference< XNameAccess >( + static_cast< OWeakObject *>( pInsertedImages ), UNO_QUERY ); + implts_notifyContainerListener( aInsertEvent, NotifyOp_Insert ); + } + if ( pReplacedImages != nullptr ) + { + ConfigurationEvent aReplaceEvent; + aReplaceEvent.aInfo <<= static_cast<sal_uInt16>(i); + aReplaceEvent.Accessor <<= xOwner; + aReplaceEvent.Source = xOwner; + aReplaceEvent.ResourceURL = m_aResourceString; + aReplaceEvent.ReplacedElement = Any(); + aReplaceEvent.Element <<= uno::Reference< XNameAccess >( + static_cast< OWeakObject *>( pReplacedImages ), UNO_QUERY ); + implts_notifyContainerListener( aReplaceEvent, NotifyOp_Replace ); + } + if ( pRemovedImages != nullptr ) + { + ConfigurationEvent aRemoveEvent; + aRemoveEvent.aInfo <<= static_cast<sal_uInt16>(i); + aRemoveEvent.Accessor <<= xOwner; + aRemoveEvent.Source = xOwner; + aRemoveEvent.ResourceURL = m_aResourceString; + aRemoveEvent.Element <<= uno::Reference< XNameAccess >( + static_cast< OWeakObject *>( pRemovedImages ), UNO_QUERY ); + implts_notifyContainerListener( aRemoveEvent, NotifyOp_Remove ); + } + + aGuard.clear(); + } + } +} + +void ImageManagerImpl::store() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_bModified ) + return; + + bool bWritten( false ); + for ( vcl::ImageType i : o3tl::enumrange<vcl::ImageType>() ) + { + bool bSuccess = implts_storeUserImages(i, m_xUserImageStorage, m_xUserBitmapsStorage ); + if ( bSuccess ) + bWritten = true; + m_bUserImageListModified[i] = false; + } + + if ( bWritten && + m_xUserConfigStorage.is() ) + { + uno::Reference< XTransactedObject > xUserConfigStorageCommit( m_xUserConfigStorage, UNO_QUERY ); + if ( xUserConfigStorageCommit.is() ) + xUserConfigStorageCommit->commit(); + if ( m_xUserRootCommit.is() ) + m_xUserRootCommit->commit(); + } + + m_bModified = false; +} + +void ImageManagerImpl::storeToStorage( const uno::Reference< XStorage >& Storage ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !(m_bModified && Storage.is()) ) + return; + + long nModes = ElementModes::READWRITE; + + uno::Reference< XStorage > xUserImageStorage = Storage->openStorageElement( IMAGE_FOLDER, + nModes ); + if ( !xUserImageStorage.is() ) + return; + + uno::Reference< XStorage > xUserBitmapsStorage = xUserImageStorage->openStorageElement( BITMAPS_FOLDER, + nModes ); + for ( vcl::ImageType i : o3tl::enumrange<vcl::ImageType>() ) + { + implts_getUserImageList(i); + implts_storeUserImages( i, xUserImageStorage, xUserBitmapsStorage ); + } + + uno::Reference< XTransactedObject > xTransaction( Storage, UNO_QUERY ); + if ( xTransaction.is() ) + xTransaction->commit(); +} + +bool ImageManagerImpl::isModified() const +{ + SolarMutexGuard g; + return m_bModified; +} + +bool ImageManagerImpl::isReadOnly() const +{ + SolarMutexGuard g; + return m_bReadOnly; +} +// XUIConfiguration +void ImageManagerImpl::addConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + } + + m_aListenerContainer.addInterface( cppu::UnoType<XUIConfigurationListener>::get(), xListener ); +} + +void ImageManagerImpl::removeConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_aListenerContainer.removeInterface( cppu::UnoType<XUIConfigurationListener>::get(), xListener ); +} + +void ImageManagerImpl::implts_notifyContainerListener( const ConfigurationEvent& aEvent, NotifyOp eOp ) +{ + ::cppu::OInterfaceContainerHelper* pContainer = m_aListenerContainer.getContainer( + cppu::UnoType<css::ui::XUIConfigurationListener>::get()); + if ( pContainer == nullptr ) + return; + + ::cppu::OInterfaceIteratorHelper pIterator( *pContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + switch ( eOp ) + { + case NotifyOp_Replace: + static_cast< css::ui::XUIConfigurationListener*>(pIterator.next())->elementReplaced( aEvent ); + break; + case NotifyOp_Insert: + static_cast< css::ui::XUIConfigurationListener*>(pIterator.next())->elementInserted( aEvent ); + break; + case NotifyOp_Remove: + static_cast< css::ui::XUIConfigurationListener*>(pIterator.next())->elementRemoved( aEvent ); + break; + } + } + catch( const css::uno::RuntimeException& ) + { + pIterator.remove(); + } + } +} +void ImageManagerImpl::clear() +{ + SolarMutexGuard g; + + for (auto & n : m_pUserImageList) + { + n.reset(); + } +} +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/imagemanagerimpl.hxx b/framework/source/uiconfiguration/imagemanagerimpl.hxx new file mode 100644 index 000000000..fa224e09f --- /dev/null +++ b/framework/source/uiconfiguration/imagemanagerimpl.hxx @@ -0,0 +1,188 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_FRAMEWORK_SOURCE_UICONFIGURATION_IMAGEMANAGERIMPL_HXX +#define INCLUDED_FRAMEWORK_SOURCE_UICONFIGURATION_IMAGEMANAGERIMPL_HXX + +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/lang/XEventListener.hpp> +#include <com/sun/star/ui/ConfigurationEvent.hpp> +#include <com/sun/star/ui/XUIConfigurationListener.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> + +#include <cppuhelper/weak.hxx> +#include <cppuhelper/interfacecontainer.hxx> +#include <rtl/ustring.hxx> + +#include <rtl/ref.hxx> +#include <salhelper/simplereferenceobject.hxx> + +#include <unordered_map> +#include <vector> + +#include "CommandImageResolver.hxx" + +namespace framework +{ + class CmdImageList + { + public: + CmdImageList(const css::uno::Reference< css::uno::XComponentContext >& rxContext, const OUString& aModuleIdentifier); + virtual ~CmdImageList(); + + virtual Image getImageFromCommandURL(vcl::ImageType nImageType, const OUString& rCommandURL); + virtual bool hasImage(vcl::ImageType nImageType, const OUString& rCommandURL); + virtual std::vector<OUString>& getImageCommandNames(); + + protected: + void initialize(); + + private: + bool m_bInitialized; + vcl::CommandImageResolver m_aResolver; + + OUString m_aModuleIdentifier; + css::uno::Reference<css::uno::XComponentContext> m_xContext; + }; + + class GlobalImageList : public CmdImageList, public salhelper::SimpleReferenceObject + { + public: + explicit GlobalImageList(const css::uno::Reference< css::uno::XComponentContext >& rxContext); + virtual ~GlobalImageList() override; + + virtual Image getImageFromCommandURL( vcl::ImageType nImageType, const OUString& rCommandURL ) override; + virtual bool hasImage( vcl::ImageType nImageType, const OUString& rCommandURL ) override; + virtual ::std::vector< OUString >& getImageCommandNames() override; + }; + + class ImageManagerImpl + { + public: + ImageManagerImpl(const css::uno::Reference< css::uno::XComponentContext >& rxContext + ,::cppu::OWeakObject *pOwner + ,bool _bUseGlobal); + ~ImageManagerImpl(); + + void dispose(); + void initialize( const css::uno::Sequence< css::uno::Any >& aArguments ); + /// @throws css::uno::RuntimeException + void addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ); + /// @throws css::uno::RuntimeException + void removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ); + + // XImageManager + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalAccessException + void reset(); + /// @throws css::uno::RuntimeException + css::uno::Sequence< OUString > getAllImageNames( ::sal_Int16 nImageType ); + /// @throws css::lang::IllegalArgumentException + /// @throws css::uno::RuntimeException + bool hasImage( ::sal_Int16 nImageType, const OUString& aCommandURL ); + /// @throws css::lang::IllegalArgumentException + /// @throws css::uno::RuntimeException + css::uno::Sequence< css::uno::Reference< css::graphic::XGraphic > > getImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aCommandURLSequence ); + /// @throws css::lang::IllegalArgumentException + /// @throws css::lang::IllegalAccessException + /// @throws css::uno::RuntimeException + void replaceImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aCommandURLSequence, const css::uno::Sequence< css::uno::Reference< css::graphic::XGraphic > >& aGraphicsSequence ); + /// @throws css::lang::IllegalArgumentException + /// @throws css::lang::IllegalAccessException + /// @throws css::uno::RuntimeException + void removeImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aResourceURLSequence ); + /// @throws css::container::ElementExistException + /// @throws css::lang::IllegalArgumentException + /// @throws css::lang::IllegalAccessException + /// @throws css::uno::RuntimeException + void insertImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aCommandURLSequence, const css::uno::Sequence< css::uno::Reference< css::graphic::XGraphic > >& aGraphicSequence ); + + // XUIConfiguration + /// @throws css::uno::RuntimeException + void addConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ); + /// @throws css::uno::RuntimeException + void removeConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ); + + // XUIConfigurationPersistence + /// @throws css::uno::Exception + /// @throws css::uno::RuntimeException + void reload(); + /// @throws css::uno::Exception + /// @throws css::uno::RuntimeException + void store(); + /// @throws css::uno::Exception + /// @throws css::uno::RuntimeException + void storeToStorage( const css::uno::Reference< css::embed::XStorage >& Storage ); + /// @throws css::uno::RuntimeException + bool isModified() const; + /// @throws css::uno::RuntimeException + bool isReadOnly() const; + + void clear(); + + typedef std::unordered_map< OUString, + sal_Bool > ImageNameMap; + + enum NotifyOp + { + NotifyOp_Remove, + NotifyOp_Insert, + NotifyOp_Replace + }; + + typedef ::std::vector< css::ui::ConfigurationEvent > ConfigEventNotifyContainer; + + void implts_initialize(); + void implts_notifyContainerListener( const css::ui::ConfigurationEvent& aEvent, NotifyOp eOp ); + ImageList* implts_getUserImageList( vcl::ImageType nImageType ); + void implts_loadUserImages( vcl::ImageType nImageType, + const css::uno::Reference< css::embed::XStorage >& xUserImageStorage, + const css::uno::Reference< css::embed::XStorage >& xUserBitmapsStorage ); + bool implts_storeUserImages( vcl::ImageType nImageType, + const css::uno::Reference< css::embed::XStorage >& xUserImageStorage, + const css::uno::Reference< css::embed::XStorage >& xUserBitmapsStorage ); + const rtl::Reference< GlobalImageList >& implts_getGlobalImageList(); + CmdImageList* implts_getDefaultImageList(); + + css::uno::Reference< css::embed::XStorage > m_xUserConfigStorage; + css::uno::Reference< css::embed::XStorage > m_xUserImageStorage; + css::uno::Reference< css::embed::XStorage > m_xUserBitmapsStorage; + css::uno::Reference< css::embed::XTransactedObject > m_xUserRootCommit; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + ::cppu::OWeakObject* m_pOwner; + rtl::Reference< GlobalImageList > m_pGlobalImageList; + std::unique_ptr<CmdImageList> m_pDefaultImageList; + OUString m_aModuleIdentifier; + OUString m_aResourceString; + osl::Mutex m_mutex; + ::cppu::OMultiTypeInterfaceContainerHelper m_aListenerContainer; /// container for ALL Listener + o3tl::enumarray<vcl::ImageType,std::unique_ptr<ImageList>> m_pUserImageList; + o3tl::enumarray<vcl::ImageType,bool> m_bUserImageListModified; + bool m_bUseGlobal; + bool m_bReadOnly; + bool m_bInitialized; + bool m_bModified; + bool m_bDisposed; + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/moduleimagemanager.cxx b/framework/source/uiconfiguration/moduleimagemanager.cxx new file mode 100644 index 000000000..05041fe0f --- /dev/null +++ b/framework/source/uiconfiguration/moduleimagemanager.cxx @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uiconfiguration/moduleimagemanager.hxx> +#include "imagemanagerimpl.hxx" + +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <sal/log.hxx> + +// namespaces + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Any; +using ::com::sun::star::graphic::XGraphic; +using namespace ::com::sun::star; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::ui; + +namespace framework +{ +ModuleImageManager::ModuleImageManager( const uno::Reference< uno::XComponentContext >& xContext ) : + m_pImpl( new ImageManagerImpl(xContext,static_cast< OWeakObject* >(this),true) ) +{ +} + +ModuleImageManager::~ModuleImageManager() +{ +} + +// XComponent +void SAL_CALL ModuleImageManager::dispose() +{ + m_pImpl->dispose(); +} + +void SAL_CALL ModuleImageManager::addEventListener( const uno::Reference< XEventListener >& xListener ) +{ + m_pImpl->addEventListener(xListener); +} + +void SAL_CALL ModuleImageManager::removeEventListener( const uno::Reference< XEventListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_pImpl->removeEventListener(xListener); +} + +// XInitialization +void SAL_CALL ModuleImageManager::initialize( const Sequence< Any >& aArguments ) +{ + m_pImpl->initialize(aArguments); +} + +// XImageManager +void SAL_CALL ModuleImageManager::reset() +{ + m_pImpl->reset(); +} + +Sequence< OUString > SAL_CALL ModuleImageManager::getAllImageNames( ::sal_Int16 nImageType ) +{ + return m_pImpl->getAllImageNames( nImageType ); +} + +sal_Bool SAL_CALL ModuleImageManager::hasImage( ::sal_Int16 nImageType, const OUString& aCommandURL ) +{ + return m_pImpl->hasImage(nImageType,aCommandURL); +} + +Sequence< uno::Reference< XGraphic > > SAL_CALL ModuleImageManager::getImages( + ::sal_Int16 nImageType, + const Sequence< OUString >& aCommandURLSequence ) +{ + SAL_INFO( "fwk", "framework: ModuleImageManager::getImages" ); + return m_pImpl->getImages(nImageType,aCommandURLSequence); +} + +void SAL_CALL ModuleImageManager::replaceImages( + ::sal_Int16 nImageType, + const Sequence< OUString >& aCommandURLSequence, + const Sequence< uno::Reference< XGraphic > >& aGraphicsSequence ) +{ + m_pImpl->replaceImages(nImageType,aCommandURLSequence,aGraphicsSequence); +} + +void SAL_CALL ModuleImageManager::removeImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence ) +{ + m_pImpl->removeImages(nImageType,aCommandURLSequence); +} + +void SAL_CALL ModuleImageManager::insertImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence, const Sequence< uno::Reference< XGraphic > >& aGraphicSequence ) +{ + m_pImpl->insertImages(nImageType,aCommandURLSequence,aGraphicSequence); +} + +// XUIConfiguration +void SAL_CALL ModuleImageManager::addConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + m_pImpl->addConfigurationListener(xListener); +} + +void SAL_CALL ModuleImageManager::removeConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + m_pImpl->removeConfigurationListener(xListener); +} + +// XUIConfigurationPersistence +void SAL_CALL ModuleImageManager::reload() +{ + m_pImpl->reload(); +} + +void SAL_CALL ModuleImageManager::store() +{ + m_pImpl->store(); +} + +void SAL_CALL ModuleImageManager::storeToStorage( const uno::Reference< XStorage >& Storage ) +{ + m_pImpl->storeToStorage(Storage); +} + +sal_Bool SAL_CALL ModuleImageManager::isModified() +{ + return m_pImpl->isModified(); +} + +sal_Bool SAL_CALL ModuleImageManager::isReadOnly() +{ + return m_pImpl->isReadOnly(); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/moduleuicfgsupplier.cxx b/framework/source/uiconfiguration/moduleuicfgsupplier.cxx new file mode 100644 index 000000000..31d61d8d0 --- /dev/null +++ b/framework/source/uiconfiguration/moduleuicfgsupplier.cxx @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <stdtypes.h> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/ui/ModuleUIConfigurationManager.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/ui/XModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/XUIConfigurationManager.hpp> +#include <com/sun/star/ui/XModuleUIConfigurationManager2.hpp> +#include <com/sun/star/frame/XModuleManager2.hpp> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <unordered_map> + +using namespace com::sun::star::uno; +using namespace com::sun::star::io; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::beans; +using namespace com::sun::star::embed; +using namespace ::com::sun::star::ui; +using namespace ::com::sun::star::frame; +using namespace framework; + +namespace { + +typedef cppu::WeakComponentImplHelper< + css::lang::XServiceInfo, + css::ui::XModuleUIConfigurationManagerSupplier > + ModuleUIConfigurationManagerSupplier_BASE; + +class ModuleUIConfigurationManagerSupplier : private cppu::BaseMutex, + public ModuleUIConfigurationManagerSupplier_BASE +{ +public: + explicit ModuleUIConfigurationManagerSupplier( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~ModuleUIConfigurationManagerSupplier() override; + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.ModuleUIConfigurationManagerSupplier"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.ModuleUIConfigurationManagerSupplier"}; + } + + // XModuleUIConfigurationManagerSupplier + virtual css::uno::Reference< css::ui::XUIConfigurationManager > SAL_CALL getUIConfigurationManager( const OUString& ModuleIdentifier ) override; + +private: + virtual void SAL_CALL disposing() final override; + + typedef std::unordered_map< OUString, css::uno::Reference< css::ui::XModuleUIConfigurationManager2 > > ModuleToModuleCfgMgr; + +//TODO_AS void impl_initStorages(); + + ModuleToModuleCfgMgr m_aModuleToModuleUICfgMgrMap; + css::uno::Reference< css::frame::XModuleManager2 > m_xModuleMgr; + css::uno::Reference< css::uno::XComponentContext > m_xContext; +}; + +ModuleUIConfigurationManagerSupplier::ModuleUIConfigurationManagerSupplier( const Reference< XComponentContext >& xContext ) : + ModuleUIConfigurationManagerSupplier_BASE(m_aMutex) + , m_xModuleMgr( ModuleManager::create( xContext ) ) + , m_xContext( xContext ) +{ + try + { + // Retrieve known modules and insert them into our unordered_map to speed-up access time. + Reference< XNameAccess > xNameAccess( m_xModuleMgr, UNO_QUERY_THROW ); + const Sequence< OUString > aNameSeq = xNameAccess->getElementNames(); + for ( const OUString& rName : aNameSeq ) + m_aModuleToModuleUICfgMgrMap.emplace( rName, Reference< XModuleUIConfigurationManager2 >() ); + } + catch(...) + { + } +} + +ModuleUIConfigurationManagerSupplier::~ModuleUIConfigurationManagerSupplier() +{ + disposing(); +} + +void SAL_CALL ModuleUIConfigurationManagerSupplier::disposing() +{ + osl::MutexGuard g(rBHelper.rMutex); + + // dispose all our module user interface configuration managers + for (auto const& elem : m_aModuleToModuleUICfgMgrMap) + { + Reference< XComponent > xComponent( elem.second, UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + } + m_aModuleToModuleUICfgMgrMap.clear(); + m_xModuleMgr.clear(); +} + +// XModuleUIConfigurationManagerSupplier +Reference< XUIConfigurationManager > SAL_CALL ModuleUIConfigurationManagerSupplier::getUIConfigurationManager( const OUString& sModuleIdentifier ) +{ + osl::MutexGuard g(rBHelper.rMutex); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + ModuleToModuleCfgMgr::iterator pIter = m_aModuleToModuleUICfgMgrMap.find( sModuleIdentifier ); + if ( pIter == m_aModuleToModuleUICfgMgrMap.end() ) + throw NoSuchElementException(); +//TODO_AS impl_initStorages(); + + // Create instance on demand + if ( !pIter->second.is() ) + { + OUString sShort; + try + { + Sequence< PropertyValue > lProps; + m_xModuleMgr->getByName(sModuleIdentifier) >>= lProps; + for (PropertyValue const & rProp : std::as_const(lProps)) + { + if ( rProp.Name == "ooSetupFactoryShortName" ) + { + rProp.Value >>= sShort; + break; + } + } + } + catch( const Exception& ) + { + sShort.clear(); + } + + if (sShort.isEmpty()) + throw NoSuchElementException(); + + pIter->second = css::ui::ModuleUIConfigurationManager::createDefault(m_xContext, sShort, sModuleIdentifier); + } + + return pIter->second; +} + +struct Instance { + explicit Instance( + css::uno::Reference<css::uno::XComponentContext> const & context): + instance(static_cast<cppu::OWeakObject *>( + new ModuleUIConfigurationManagerSupplier(context))) + { + } + + css::uno::Reference<css::uno::XInterface> instance; +}; + +struct Singleton: + public rtl::StaticWithArg< + Instance, css::uno::Reference<css::uno::XComponentContext>, Singleton> +{}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ModuleUIConfigurationManagerSupplier_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(static_cast<cppu::OWeakObject *>( + Singleton::get(context).instance.get())); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/moduleuiconfigurationmanager.cxx b/framework/source/uiconfiguration/moduleuiconfigurationmanager.cxx new file mode 100644 index 000000000..793d8ed18 --- /dev/null +++ b/framework/source/uiconfiguration/moduleuiconfigurationmanager.cxx @@ -0,0 +1,1653 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <accelerators/presethandler.hxx> +#include <uiconfiguration/moduleimagemanager.hxx> +#include <uielement/constitemcontainer.hxx> +#include <uielement/rootitemcontainer.hxx> +#include <uielement/uielementtypenames.hxx> +#include <menuconfiguration.hxx> +#include <toolboxconfiguration.hxx> + +#include <statusbarconfiguration.hxx> + +#include <com/sun/star/ui/UIElementType.hpp> +#include <com/sun/star/ui/ConfigurationEvent.hpp> +#include <com/sun/star/ui/ModuleAcceleratorConfiguration.hpp> +#include <com/sun/star/ui/XModuleUIConfigurationManager2.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalAccessException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/InvalidStorageException.hpp> +#include <com/sun/star/embed/StorageWrappedTargetException.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/container/ElementExistException.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XComponent.hpp> + +#include <comphelper/propertysequence.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <vcl/svapp.hxx> +#include <sal/log.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/servicehelper.hxx> +#include <memory> + +using namespace css; +using namespace com::sun::star::uno; +using namespace com::sun::star::io; +using namespace com::sun::star::embed; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::beans; +using namespace framework; + +#define RESOURCETYPE_MENUBAR "menubar" +#define RESOURCETYPE_TOOLBAR "toolbar" +#define RESOURCETYPE_STATUSBAR "statusbar" +#define RESOURCETYPE_POPUPMENU "popupmenu" + +namespace { + +class ModuleUIConfigurationManager : public cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::lang::XComponent, + css::ui::XModuleUIConfigurationManager2 > +{ +public: + ModuleUIConfigurationManager( + const css::uno::Reference< css::uno::XComponentContext >& xServiceManager, + const css::uno::Sequence< css::uno::Any >& aArguments); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.ModuleUIConfigurationManager"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.ModuleUIConfigurationManager"}; + } + + // XComponent + virtual void SAL_CALL dispose() override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + // XUIConfiguration + virtual void SAL_CALL addConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override; + virtual void SAL_CALL removeConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override; + + // XUIConfigurationManager + virtual void SAL_CALL reset() override; + virtual css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL getUIElementsInfo( sal_Int16 ElementType ) override; + virtual css::uno::Reference< css::container::XIndexContainer > SAL_CALL createSettings( ) override; + virtual sal_Bool SAL_CALL hasSettings( const OUString& ResourceURL ) override; + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getSettings( const OUString& ResourceURL, sal_Bool bWriteable ) override; + virtual void SAL_CALL replaceSettings( const OUString& ResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override; + virtual void SAL_CALL removeSettings( const OUString& ResourceURL ) override; + virtual void SAL_CALL insertSettings( const OUString& NewResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getImageManager() override; + virtual css::uno::Reference< css::ui::XAcceleratorConfiguration > SAL_CALL getShortCutManager() override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getEventsManager() override; + + // XModuleUIConfigurationManager + virtual sal_Bool SAL_CALL isDefaultSettings( const OUString& ResourceURL ) override; + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getDefaultSettings( const OUString& ResourceURL ) override; + + // XUIConfigurationPersistence + virtual void SAL_CALL reload() override; + virtual void SAL_CALL store() override; + virtual void SAL_CALL storeToStorage( const css::uno::Reference< css::embed::XStorage >& Storage ) override; + virtual sal_Bool SAL_CALL isModified() override; + virtual sal_Bool SAL_CALL isReadOnly() override; + +private: + // private data types + enum Layer + { + LAYER_DEFAULT, + LAYER_USERDEFINED, + LAYER_COUNT + }; + + enum NotifyOp + { + NotifyOp_Remove, + NotifyOp_Insert, + NotifyOp_Replace + }; + + struct UIElementInfo + { + UIElementInfo( const OUString& rResourceURL, const OUString& rUIName ) : + aResourceURL( rResourceURL), aUIName( rUIName ) {} + OUString aResourceURL; + OUString aUIName; + }; + + struct UIElementData + { + UIElementData() : bModified( false ), bDefault( true ), bDefaultNode( true ) {}; + + OUString aResourceURL; + OUString aName; + bool bModified; // has been changed since last storing + bool bDefault; // default settings + bool bDefaultNode; // this is a default layer element data + css::uno::Reference< css::container::XIndexAccess > xSettings; + }; + + typedef std::unordered_map< OUString, UIElementData > UIElementDataHashMap; + + struct UIElementType + { + UIElementType() : bModified( false ), + bLoaded( false ), + nElementType( css::ui::UIElementType::UNKNOWN ) {} + + bool bModified; + bool bLoaded; + sal_Int16 nElementType; + UIElementDataHashMap aElementsHashMap; + css::uno::Reference< css::embed::XStorage > xStorage; + }; + + typedef std::vector< UIElementType > UIElementTypesVector; + typedef std::vector< css::ui::ConfigurationEvent > ConfigEventNotifyContainer; + typedef std::unordered_map< OUString, UIElementInfo > UIElementInfoHashMap; + + void impl_Initialize(); + void implts_notifyContainerListener( const css::ui::ConfigurationEvent& aEvent, NotifyOp eOp ); + void impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType ); + void impl_preloadUIElementTypeList( Layer eLayer, sal_Int16 nElementType ); + UIElementData* impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad = true ); + void impl_requestUIElementData( sal_Int16 nElementType, Layer eLayer, UIElementData& aUIElementData ); + void impl_storeElementTypeData( const css::uno::Reference< css::embed::XStorage >& xStorage, UIElementType& rElementType, bool bResetModifyState = true ); + void impl_resetElementTypeData( UIElementType& rUserElementType, UIElementType const & rDefaultElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer, ConfigEventNotifyContainer& rReplaceNotifyContainer ); + void impl_reloadElementTypeData( UIElementType& rUserElementType, UIElementType const & rDefaultElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer, ConfigEventNotifyContainer& rReplaceNotifyContainer ); + + UIElementTypesVector m_aUIElements[LAYER_COUNT]; + std::unique_ptr<PresetHandler> m_pStorageHandler[css::ui::UIElementType::COUNT]; + css::uno::Reference< css::embed::XStorage > m_xDefaultConfigStorage; + css::uno::Reference< css::embed::XStorage > m_xUserConfigStorage; + bool m_bReadOnly; + bool m_bModified; + bool m_bDisposed; + OUString m_aXMLPostfix; + OUString m_aPropUIName; + OUString m_aModuleIdentifier; + css::uno::Reference< css::embed::XTransactedObject > m_xUserRootCommit; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + osl::Mutex m_mutex; + ::cppu::OMultiTypeInterfaceContainerHelper m_aListenerContainer; /// container for ALL Listener + css::uno::Reference< css::lang::XComponent > m_xModuleImageManager; + css::uno::Reference< css::ui::XAcceleratorConfiguration > m_xModuleAcceleratorManager; +}; + +// important: The order and position of the elements must match the constant +// definition of "css::ui::UIElementType" +static OUStringLiteral UIELEMENTTYPENAMES[] = +{ + "", // Dummy value for unknown! + UIELEMENTTYPE_MENUBAR_NAME, + UIELEMENTTYPE_POPUPMENU_NAME, + UIELEMENTTYPE_TOOLBAR_NAME, + UIELEMENTTYPE_STATUSBAR_NAME, + UIELEMENTTYPE_FLOATINGWINDOW_NAME, + UIELEMENTTYPE_PROGRESSBAR_NAME, + UIELEMENTTYPE_TOOLPANEL_NAME +}; + +static const char RESOURCEURL_PREFIX[] = "private:resource/"; +static const sal_Int32 RESOURCEURL_PREFIX_SIZE = strlen(RESOURCEURL_PREFIX); + +sal_Int16 RetrieveTypeFromResourceURL( const OUString& aResourceURL ) +{ + + if (( aResourceURL.startsWith( RESOURCEURL_PREFIX ) ) && + ( aResourceURL.getLength() > RESOURCEURL_PREFIX_SIZE )) + { + OUString aTmpStr = aResourceURL.copy( RESOURCEURL_PREFIX_SIZE ); + sal_Int32 nIndex = aTmpStr.indexOf( '/' ); + if (( nIndex > 0 ) && ( aTmpStr.getLength() > nIndex )) + { + OUString aTypeStr( aTmpStr.copy( 0, nIndex )); + for ( int i = 0; i < ui::UIElementType::COUNT; i++ ) + { + if ( aTypeStr == UIELEMENTTYPENAMES[i] ) + return sal_Int16( i ); + } + } + } + + return ui::UIElementType::UNKNOWN; +} + +OUString RetrieveNameFromResourceURL( const OUString& aResourceURL ) +{ + if (( aResourceURL.startsWith( RESOURCEURL_PREFIX ) ) && + ( aResourceURL.getLength() > RESOURCEURL_PREFIX_SIZE )) + { + sal_Int32 nIndex = aResourceURL.lastIndexOf( '/' ); + if (( nIndex > 0 ) && (( nIndex+1 ) < aResourceURL.getLength())) + return aResourceURL.copy( nIndex+1 ); + } + + return OUString(); +} + +void ModuleUIConfigurationManager::impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType ) +{ + // preload list of element types on demand + impl_preloadUIElementTypeList( LAYER_USERDEFINED, nElementType ); + impl_preloadUIElementTypeList( LAYER_DEFAULT, nElementType ); + + UIElementDataHashMap& rUserElements = m_aUIElements[LAYER_USERDEFINED][nElementType].aElementsHashMap; + + OUString aCustomUrlPrefix( "custom_" ); + for (auto const& userElement : rUserElements) + { + sal_Int32 nIndex = userElement.second.aResourceURL.indexOf( aCustomUrlPrefix, RESOURCEURL_PREFIX_SIZE ); + if ( nIndex > RESOURCEURL_PREFIX_SIZE ) + { + // Performance: Retrieve user interface name only for custom user interface elements. + // It's only used by them! + UIElementData* pDataSettings = impl_findUIElementData( userElement.second.aResourceURL, nElementType ); + if ( pDataSettings ) + { + // Retrieve user interface name from XPropertySet interface + OUString aUIName; + Reference< XPropertySet > xPropSet( pDataSettings->xSettings, UNO_QUERY ); + if ( xPropSet.is() ) + { + Any a = xPropSet->getPropertyValue( m_aPropUIName ); + a >>= aUIName; + } + + UIElementInfo aInfo( userElement.second.aResourceURL, aUIName ); + aUIElementInfoCollection.emplace( userElement.second.aResourceURL, aInfo ); + } + } + else + { + // The user interface name for standard user interface elements is stored in the WindowState.xcu file + UIElementInfo aInfo( userElement.second.aResourceURL, OUString() ); + aUIElementInfoCollection.emplace( userElement.second.aResourceURL, aInfo ); + } + } + + UIElementDataHashMap& rDefaultElements = m_aUIElements[LAYER_DEFAULT][nElementType].aElementsHashMap; + + for (auto const& defaultElement : rDefaultElements) + { + UIElementInfoHashMap::const_iterator pIterInfo = aUIElementInfoCollection.find( defaultElement.second.aResourceURL ); + if ( pIterInfo == aUIElementInfoCollection.end() ) + { + sal_Int32 nIndex = defaultElement.second.aResourceURL.indexOf( aCustomUrlPrefix, RESOURCEURL_PREFIX_SIZE ); + if ( nIndex > RESOURCEURL_PREFIX_SIZE ) + { + // Performance: Retrieve user interface name only for custom user interface elements. + // It's only used by them! + UIElementData* pDataSettings = impl_findUIElementData( defaultElement.second.aResourceURL, nElementType ); + if ( pDataSettings ) + { + // Retrieve user interface name from XPropertySet interface + OUString aUIName; + Reference< XPropertySet > xPropSet( pDataSettings->xSettings, UNO_QUERY ); + if ( xPropSet.is() ) + { + Any a = xPropSet->getPropertyValue( m_aPropUIName ); + a >>= aUIName; + } + UIElementInfo aInfo( defaultElement.second.aResourceURL, aUIName ); + aUIElementInfoCollection.emplace( defaultElement.second.aResourceURL, aInfo ); + } + } + else + { + // The user interface name for standard user interface elements is stored in the WindowState.xcu file + UIElementInfo aInfo( defaultElement.second.aResourceURL, OUString() ); + aUIElementInfoCollection.emplace( defaultElement.second.aResourceURL, aInfo ); + } + } + } +} + +void ModuleUIConfigurationManager::impl_preloadUIElementTypeList( Layer eLayer, sal_Int16 nElementType ) +{ + UIElementType& rElementTypeData = m_aUIElements[eLayer][nElementType]; + + if ( rElementTypeData.bLoaded ) + return; + + Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage; + if ( !xElementTypeStorage.is() ) + return; + + OUString aResURLPrefix = + RESOURCEURL_PREFIX + + UIELEMENTTYPENAMES[ nElementType ] + + "/"; + + UIElementDataHashMap& rHashMap = rElementTypeData.aElementsHashMap; + const Sequence< OUString > aUIElementNames = xElementTypeStorage->getElementNames(); + for ( OUString const & rElementName : aUIElementNames ) + { + UIElementData aUIElementData; + + // Resource name must be without ".xml" + sal_Int32 nIndex = rElementName.lastIndexOf( '.' ); + if (( nIndex > 0 ) && ( nIndex < rElementName.getLength() )) + { + OUString aExtension( rElementName.copy( nIndex+1 )); + OUString aUIElementName( rElementName.copy( 0, nIndex )); + + if (!aUIElementName.isEmpty() && + ( aExtension.equalsIgnoreAsciiCase("xml"))) + { + aUIElementData.aResourceURL = aResURLPrefix + aUIElementName; + aUIElementData.aName = rElementName; + + if ( eLayer == LAYER_USERDEFINED ) + { + aUIElementData.bModified = false; + aUIElementData.bDefault = false; + aUIElementData.bDefaultNode = false; + } + + // Create std::unordered_map entries for all user interface elements inside the storage. We don't load the + // settings to speed up the process. + rHashMap.emplace( aUIElementData.aResourceURL, aUIElementData ); + } + } + rElementTypeData.bLoaded = true; + } + +} + +void ModuleUIConfigurationManager::impl_requestUIElementData( sal_Int16 nElementType, Layer eLayer, UIElementData& aUIElementData ) +{ + UIElementType& rElementTypeData = m_aUIElements[eLayer][nElementType]; + + Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage; + if ( xElementTypeStorage.is() && !aUIElementData.aName.isEmpty() ) + { + try + { + Reference< XStream > xStream = xElementTypeStorage->openStreamElement( aUIElementData.aName, ElementModes::READ ); + Reference< XInputStream > xInputStream = xStream->getInputStream(); + + if ( xInputStream.is() ) + { + switch ( nElementType ) + { + case css::ui::UIElementType::UNKNOWN: + break; + + case css::ui::UIElementType::MENUBAR: + case css::ui::UIElementType::POPUPMENU: + { + try + { + MenuConfiguration aMenuCfg( m_xContext ); + Reference< XIndexAccess > xContainer( aMenuCfg.CreateMenuBarConfigurationFromXML( xInputStream )); + auto pRootItemContainer = comphelper::getUnoTunnelImplementation<RootItemContainer>( xContainer ); + if ( pRootItemContainer ) + aUIElementData.xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer( pRootItemContainer, true ) ), UNO_QUERY ); + else + aUIElementData.xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer( xContainer, true ) ), UNO_QUERY ); + return; + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + case css::ui::UIElementType::TOOLBAR: + { + try + { + Reference< XIndexContainer > xIndexContainer( static_cast< OWeakObject * >( new RootItemContainer() ), UNO_QUERY ); + ToolBoxConfiguration::LoadToolBox( m_xContext, xInputStream, xIndexContainer ); + auto pRootItemContainer = comphelper::getUnoTunnelImplementation<RootItemContainer>( xIndexContainer ); + aUIElementData.xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer( pRootItemContainer, true ) ), UNO_QUERY ); + return; + } + catch ( const css::lang::WrappedTargetException& ) + { + } + + break; + } + + case css::ui::UIElementType::STATUSBAR: + { + try + { + Reference< XIndexContainer > xIndexContainer( static_cast< OWeakObject * >( new RootItemContainer() ), UNO_QUERY ); + StatusBarConfiguration::LoadStatusBar( m_xContext, xInputStream, xIndexContainer ); + auto pRootItemContainer = comphelper::getUnoTunnelImplementation<RootItemContainer>( xIndexContainer ); + aUIElementData.xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer( pRootItemContainer, true ) ), UNO_QUERY ); + return; + } + catch ( const css::lang::WrappedTargetException& ) + { + } + + break; + } + + case css::ui::UIElementType::FLOATINGWINDOW: + { + break; + } + } + } + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::io::IOException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } + } + + // At least we provide an empty settings container! + aUIElementData.xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer() ), UNO_QUERY ); +} + +ModuleUIConfigurationManager::UIElementData* ModuleUIConfigurationManager::impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad ) +{ + // preload list of element types on demand + impl_preloadUIElementTypeList( LAYER_USERDEFINED, nElementType ); + impl_preloadUIElementTypeList( LAYER_DEFAULT, nElementType ); + + // first try to look into our user-defined vector/unordered_map combination + UIElementDataHashMap& rUserHashMap = m_aUIElements[LAYER_USERDEFINED][nElementType].aElementsHashMap; + UIElementDataHashMap::iterator pIter = rUserHashMap.find( aResourceURL ); + if ( pIter != rUserHashMap.end() ) + { + // Default data settings data must be retrieved from the default layer! + if ( !pIter->second.bDefault ) + { + if ( !pIter->second.xSettings.is() && bLoad ) + impl_requestUIElementData( nElementType, LAYER_USERDEFINED, pIter->second ); + return &(pIter->second); + } + } + + // Not successful, we have to look into our default vector/unordered_map combination + UIElementDataHashMap& rDefaultHashMap = m_aUIElements[LAYER_DEFAULT][nElementType].aElementsHashMap; + pIter = rDefaultHashMap.find( aResourceURL ); + if ( pIter != rDefaultHashMap.end() ) + { + if ( !pIter->second.xSettings.is() && bLoad ) + impl_requestUIElementData( nElementType, LAYER_DEFAULT, pIter->second ); + return &(pIter->second); + } + + // Nothing has been found! + return nullptr; +} + +void ModuleUIConfigurationManager::impl_storeElementTypeData( const Reference< XStorage >& xStorage, UIElementType& rElementType, bool bResetModifyState ) +{ + UIElementDataHashMap& rHashMap = rElementType.aElementsHashMap; + + for (auto & elem : rHashMap) + { + UIElementData& rElement = elem.second; + if ( rElement.bModified ) + { + if ( rElement.bDefault ) + { + xStorage->removeElement( rElement.aName ); + rElement.bModified = false; // mark as not modified + } + else + { + Reference< XStream > xStream = xStorage->openStreamElement( rElement.aName, ElementModes::WRITE|ElementModes::TRUNCATE ); + Reference< XOutputStream > xOutputStream( xStream->getOutputStream() ); + + if ( xOutputStream.is() ) + { + switch( rElementType.nElementType ) + { + case css::ui::UIElementType::MENUBAR: + case css::ui::UIElementType::POPUPMENU: + { + try + { + MenuConfiguration aMenuCfg( m_xContext ); + aMenuCfg.StoreMenuBarConfigurationToXML( + rElement.xSettings, xOutputStream, rElementType.nElementType == css::ui::UIElementType::MENUBAR ); + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + case css::ui::UIElementType::TOOLBAR: + { + try + { + ToolBoxConfiguration::StoreToolBox( m_xContext, xOutputStream, rElement.xSettings ); + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + case css::ui::UIElementType::STATUSBAR: + { + try + { + StatusBarConfiguration::StoreStatusBar( m_xContext, xOutputStream, rElement.xSettings ); + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + default: + break; + } + } + + // mark as not modified if we store to our own storage + if ( bResetModifyState ) + rElement.bModified = false; + } + } + } + + // commit element type storage + Reference< XTransactedObject > xTransactedObject( xStorage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); + + // mark UIElementType as not modified if we store to our own storage + if ( bResetModifyState ) + rElementType.bModified = false; +} + +// This is only allowed to be called on the LAYER_USER_DEFINED! +void ModuleUIConfigurationManager::impl_resetElementTypeData( + UIElementType& rUserElementType, + UIElementType const & rDefaultElementType, + ConfigEventNotifyContainer& rRemoveNotifyContainer, + ConfigEventNotifyContainer& rReplaceNotifyContainer ) +{ + UIElementDataHashMap& rHashMap = rUserElementType.aElementsHashMap; + + Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY ); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + sal_Int16 nType = rUserElementType.nElementType; + + // Make copies of the event structures to be thread-safe. We have to unlock our mutex before calling + // our listeners! + for (auto & elem : rHashMap) + { + UIElementData& rElement = elem.second; + if ( !rElement.bDefault ) + { + if ( rDefaultElementType.xStorage->hasByName( rElement.aName )) + { + // Replace settings with data from default layer + Reference< XIndexAccess > xOldSettings( rElement.xSettings ); + impl_requestUIElementData( nType, LAYER_DEFAULT, rElement ); + + ui::ConfigurationEvent aReplaceEvent; + aReplaceEvent.ResourceURL = rElement.aResourceURL; + aReplaceEvent.Accessor <<= xThis; + aReplaceEvent.Source = xIfac; + aReplaceEvent.ReplacedElement <<= xOldSettings; + aReplaceEvent.Element <<= rElement.xSettings; + + rReplaceNotifyContainer.push_back( aReplaceEvent ); + + // Mark element as default and not modified. That means "not active" + // in the user layer anymore. + rElement.bModified = false; + rElement.bDefault = true; + } + else + { + // Remove user-defined settings from user layer + ui::ConfigurationEvent aEvent; + aEvent.ResourceURL = rElement.aResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.Element <<= rElement.xSettings; + + rRemoveNotifyContainer.push_back( aEvent ); + + // Mark element as default and not modified. That means "not active" + // in the user layer anymore. + rElement.bModified = false; + rElement.bDefault = true; + } + } + } + + // Remove all settings from our user interface elements + rHashMap.clear(); +} + +void ModuleUIConfigurationManager::impl_reloadElementTypeData( + UIElementType& rUserElementType, + UIElementType const & rDefaultElementType, + ConfigEventNotifyContainer& rRemoveNotifyContainer, + ConfigEventNotifyContainer& rReplaceNotifyContainer ) +{ + UIElementDataHashMap& rHashMap = rUserElementType.aElementsHashMap; + + Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY ); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + sal_Int16 nType = rUserElementType.nElementType; + + for (auto & elem : rHashMap) + { + UIElementData& rElement = elem.second; + if ( rElement.bModified ) + { + if ( rUserElementType.xStorage->hasByName( rElement.aName )) + { + // Replace settings with data from user layer + Reference< XIndexAccess > xOldSettings( rElement.xSettings ); + + impl_requestUIElementData( nType, LAYER_USERDEFINED, rElement ); + + ui::ConfigurationEvent aReplaceEvent; + + aReplaceEvent.ResourceURL = rElement.aResourceURL; + aReplaceEvent.Accessor <<= xThis; + aReplaceEvent.Source = xIfac; + aReplaceEvent.ReplacedElement <<= xOldSettings; + aReplaceEvent.Element <<= rElement.xSettings; + rReplaceNotifyContainer.push_back( aReplaceEvent ); + + rElement.bModified = false; + } + else if ( rDefaultElementType.xStorage->hasByName( rElement.aName )) + { + // Replace settings with data from default layer + Reference< XIndexAccess > xOldSettings( rElement.xSettings ); + + impl_requestUIElementData( nType, LAYER_DEFAULT, rElement ); + + ui::ConfigurationEvent aReplaceEvent; + + aReplaceEvent.ResourceURL = rElement.aResourceURL; + aReplaceEvent.Accessor <<= xThis; + aReplaceEvent.Source = xIfac; + aReplaceEvent.ReplacedElement <<= xOldSettings; + aReplaceEvent.Element <<= rElement.xSettings; + rReplaceNotifyContainer.push_back( aReplaceEvent ); + + // Mark element as default and not modified. That means "not active" + // in the user layer anymore. + rElement.bModified = false; + rElement.bDefault = true; + } + else + { + // Element settings are not in any storage => remove + ui::ConfigurationEvent aRemoveEvent; + + aRemoveEvent.ResourceURL = rElement.aResourceURL; + aRemoveEvent.Accessor <<= xThis; + aRemoveEvent.Source = xIfac; + aRemoveEvent.Element <<= rElement.xSettings; + + rRemoveNotifyContainer.push_back( aRemoveEvent ); + + // Mark element as default and not modified. That means "not active" + // in the user layer anymore. + rElement.bModified = false; + rElement.bDefault = true; + } + } + } + + rUserElementType.bModified = false; +} + +void ModuleUIConfigurationManager::impl_Initialize() +{ + // Initialize the top-level structures with the storage data + if ( m_xUserConfigStorage.is() ) + { + // Try to access our module sub folder + for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT; + i++ ) + { + Reference< XStorage > xElementTypeStorage; + try + { + if ( m_pStorageHandler[i] ) + xElementTypeStorage = m_pStorageHandler[i]->getWorkingStorageUser(); + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::io::IOException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } + + m_aUIElements[LAYER_USERDEFINED][i].nElementType = i; + m_aUIElements[LAYER_USERDEFINED][i].bModified = false; + m_aUIElements[LAYER_USERDEFINED][i].xStorage = xElementTypeStorage; + } + } + + if ( !m_xDefaultConfigStorage.is() ) + return; + + Reference< XNameAccess > xNameAccess( m_xDefaultConfigStorage, UNO_QUERY_THROW ); + + // Try to access our module sub folder + for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT; + i++ ) + { + Reference< XStorage > xElementTypeStorage; + try + { + const OUString sName( UIELEMENTTYPENAMES[i] ); + if( xNameAccess->hasByName( sName ) ) + xNameAccess->getByName( sName ) >>= xElementTypeStorage; + } + catch ( const css::container::NoSuchElementException& ) + { + } + + m_aUIElements[LAYER_DEFAULT][i].nElementType = i; + m_aUIElements[LAYER_DEFAULT][i].bModified = false; + m_aUIElements[LAYER_DEFAULT][i].xStorage = xElementTypeStorage; + } +} + +ModuleUIConfigurationManager::ModuleUIConfigurationManager( + const Reference< XComponentContext >& xContext, + const css::uno::Sequence< css::uno::Any >& aArguments) + : m_bReadOnly( true ) + , m_bModified( false ) + , m_bDisposed( false ) + , m_aXMLPostfix( ".xml" ) + , m_aPropUIName( "UIName" ) + , m_xContext( xContext ) + , m_aListenerContainer( m_mutex ) +{ + // Make sure we have a default initialized entry for every layer and user interface element type! + // The following code depends on this! + m_aUIElements[LAYER_DEFAULT].resize( css::ui::UIElementType::COUNT ); + m_aUIElements[LAYER_USERDEFINED].resize( css::ui::UIElementType::COUNT ); + + SolarMutexGuard g; + + OUString aModuleShortName; + if( aArguments.getLength() == 2 && (aArguments[0] >>= aModuleShortName) && (aArguments[1] >>= m_aModuleIdentifier)) + { + } + else + { + ::comphelper::SequenceAsHashMap lArgs(aArguments); + aModuleShortName = lArgs.getUnpackedValueOrDefault("ModuleShortName", OUString()); + m_aModuleIdentifier = lArgs.getUnpackedValueOrDefault("ModuleIdentifier", OUString()); + } + + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + OUString aResourceType; + if ( i == css::ui::UIElementType::MENUBAR ) + aResourceType = RESOURCETYPE_MENUBAR; + else if ( i == css::ui::UIElementType::TOOLBAR ) + aResourceType = RESOURCETYPE_TOOLBAR; + else if ( i == css::ui::UIElementType::STATUSBAR ) + aResourceType = RESOURCETYPE_STATUSBAR; + else if ( i == css::ui::UIElementType::POPUPMENU ) + aResourceType = RESOURCETYPE_POPUPMENU; + + if ( !aResourceType.isEmpty() ) + { + m_pStorageHandler[i].reset( new PresetHandler( m_xContext ) ); + m_pStorageHandler[i]->connectToResource( PresetHandler::E_MODULES, + aResourceType, // this path won't be used later... see next lines! + aModuleShortName, + css::uno::Reference< css::embed::XStorage >()); // no document root used here! + } + } + + // initialize root storages for all resource types + m_xUserRootCommit.set( m_pStorageHandler[css::ui::UIElementType::MENUBAR]->getOrCreateRootStorageUser(), css::uno::UNO_QUERY); // can be empty + m_xDefaultConfigStorage = m_pStorageHandler[css::ui::UIElementType::MENUBAR]->getParentStorageShare(); + m_xUserConfigStorage = m_pStorageHandler[css::ui::UIElementType::MENUBAR]->getParentStorageUser(); + + if ( m_xUserConfigStorage.is() ) + { + Reference< XPropertySet > xPropSet( m_xUserConfigStorage, UNO_QUERY ); + if ( xPropSet.is() ) + { + long nOpenMode = 0; + Any a = xPropSet->getPropertyValue("OpenMode"); + if ( a >>= nOpenMode ) + m_bReadOnly = !( nOpenMode & ElementModes::WRITE ); + } + } + + impl_Initialize(); +} + +// XComponent +void SAL_CALL ModuleUIConfigurationManager::dispose() +{ + Reference< XComponent > xThis( static_cast< OWeakObject* >(this), UNO_QUERY ); + + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aGuard; + Reference< XComponent > xModuleImageManager( m_xModuleImageManager ); + m_xModuleImageManager.clear(); + m_xModuleAcceleratorManager.clear(); + m_aUIElements[LAYER_USERDEFINED].clear(); + m_aUIElements[LAYER_DEFAULT].clear(); + m_xDefaultConfigStorage.clear(); + m_xUserConfigStorage.clear(); + m_xUserRootCommit.clear(); + m_bModified = false; + m_bDisposed = true; + aGuard.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + try + { + if ( xModuleImageManager.is() ) + xModuleImageManager->dispose(); + } + catch ( const Exception& ) + { + } +} + +void SAL_CALL ModuleUIConfigurationManager::addEventListener( const Reference< XEventListener >& xListener ) +{ + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + } + + m_aListenerContainer.addInterface( cppu::UnoType<XEventListener>::get(), xListener ); +} + +void SAL_CALL ModuleUIConfigurationManager::removeEventListener( const Reference< XEventListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_aListenerContainer.removeInterface( cppu::UnoType<XEventListener>::get(), xListener ); +} + +// XUIConfiguration +void SAL_CALL ModuleUIConfigurationManager::addConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + } + + m_aListenerContainer.addInterface( cppu::UnoType<ui::XUIConfigurationListener>::get(), xListener ); +} + +void SAL_CALL ModuleUIConfigurationManager::removeConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_aListenerContainer.removeInterface( cppu::UnoType<ui::XUIConfigurationListener>::get(), xListener ); +} + +// XUIConfigurationManager +void SAL_CALL ModuleUIConfigurationManager::reset() +{ + SolarMutexClearableGuard aGuard; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + if ( isReadOnly() ) + return; + + // Remove all elements from our user-defined storage! + try + { + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][i]; + + if ( rElementType.xStorage.is() ) + { + bool bCommitSubStorage( false ); + const Sequence< OUString > aUIElementStreamNames = rElementType.xStorage->getElementNames(); + for ( OUString const & rName : aUIElementStreamNames ) + { + rElementType.xStorage->removeElement( rName ); + bCommitSubStorage = true; + } + + if ( bCommitSubStorage ) + { + Reference< XTransactedObject > xTransactedObject( rElementType.xStorage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); + m_pStorageHandler[i]->commitUserChanges(); + } + } + } + + // remove settings from user defined layer and notify listener about removed settings data! + ConfigEventNotifyContainer aRemoveEventNotifyContainer; + ConfigEventNotifyContainer aReplaceEventNotifyContainer; + for ( sal_Int16 j = 1; j < css::ui::UIElementType::COUNT; j++ ) + { + try + { + UIElementType& rUserElementType = m_aUIElements[LAYER_USERDEFINED][j]; + UIElementType& rDefaultElementType = m_aUIElements[LAYER_DEFAULT][j]; + + impl_resetElementTypeData( rUserElementType, rDefaultElementType, aRemoveEventNotifyContainer, aReplaceEventNotifyContainer ); + rUserElementType.bModified = false; + } + catch (const Exception&) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "ModuleUIConfigurationManager::reset exception", + css::uno::Reference<css::uno::XInterface>(*this), anyEx); + } + } + + m_bModified = false; + + // Unlock mutex before notify our listeners + aGuard.clear(); + + // Notify our listeners + for ( auto const & k: aRemoveEventNotifyContainer ) + implts_notifyContainerListener( k, NotifyOp_Remove ); + for ( auto const & k: aReplaceEventNotifyContainer ) + implts_notifyContainerListener( k, NotifyOp_Replace ); + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } +} + +Sequence< Sequence< PropertyValue > > SAL_CALL ModuleUIConfigurationManager::getUIElementsInfo( sal_Int16 ElementType ) +{ + if (( ElementType < 0 ) || ( ElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + + SolarMutexGuard g; + if ( m_bDisposed ) + throw DisposedException(); + + std::vector< Sequence< PropertyValue > > aElementInfoSeq; + UIElementInfoHashMap aUIElementInfoCollection; + + if ( ElementType == css::ui::UIElementType::UNKNOWN ) + { + for ( sal_Int16 i = 0; i < css::ui::UIElementType::COUNT; i++ ) + impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, i ); + } + else + impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, ElementType ); + + Sequence< PropertyValue > aUIElementInfo( 2 ); + aUIElementInfo[0].Name = "ResourceURL"; + aUIElementInfo[1].Name = m_aPropUIName; + + aElementInfoSeq.resize( aUIElementInfoCollection.size() ); + + sal_Int32 n = 0; + for (auto const& elem : aUIElementInfoCollection) + { + aUIElementInfo[0].Value <<= elem.second.aResourceURL; + aUIElementInfo[1].Value <<= elem.second.aUIName; + aElementInfoSeq[n++] = aUIElementInfo; + } + + return comphelper::containerToSequence(aElementInfoSeq); +} + +Reference< XIndexContainer > SAL_CALL ModuleUIConfigurationManager::createSettings() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + // Creates an empty item container which can be filled from outside + return Reference< XIndexContainer >( static_cast< OWeakObject * >( new RootItemContainer() ), UNO_QUERY ); +} + +sal_Bool SAL_CALL ModuleUIConfigurationManager::hasSettings( const OUString& ResourceURL ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType, false ); + if ( pDataSettings ) + return true; + + return false; +} + +Reference< XIndexAccess > SAL_CALL ModuleUIConfigurationManager::getSettings( const OUString& ResourceURL, sal_Bool bWriteable ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType ); + if ( pDataSettings ) + { + // Create a copy of our data if someone wants to change the data. + if ( bWriteable ) + return Reference< XIndexAccess >( static_cast< OWeakObject * >( new RootItemContainer( pDataSettings->xSettings ) ), UNO_QUERY ); + else + return pDataSettings->xSettings; + } + + throw NoSuchElementException(); +} + +void SAL_CALL ModuleUIConfigurationManager::replaceSettings( const OUString& ResourceURL, const Reference< css::container::XIndexAccess >& aNewData ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + else if ( m_bReadOnly ) + throw IllegalAccessException(); + else + { + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType ); + if ( !pDataSettings ) + throw NoSuchElementException(); + if ( !pDataSettings->bDefaultNode ) + { + // we have a settings entry in our user-defined layer - replace + Reference< XIndexAccess > xOldSettings = pDataSettings->xSettings; + + // Create a copy of the data if the container is not const + Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY ); + if ( xReplace.is() ) + pDataSettings->xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer( aNewData ) ), UNO_QUERY ); + else + pDataSettings->xSettings = aNewData; + pDataSettings->bDefault = false; + pDataSettings->bModified = true; + m_bModified = true; + + // Modify type container + UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType]; + rElementType.bModified = true; + + Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY ); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + + // Create event to notify listener about replaced element settings + ui::ConfigurationEvent aEvent; + aEvent.ResourceURL = ResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.ReplacedElement <<= xOldSettings; + aEvent.Element <<= pDataSettings->xSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Replace ); + } + else + { + // we have no settings in our user-defined layer - insert + UIElementData aUIElementData; + + aUIElementData.bDefault = false; + aUIElementData.bDefaultNode = false; + aUIElementData.bModified = true; + + // Create a copy of the data if the container is not const + Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY ); + if ( xReplace.is() ) + aUIElementData.xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer( aNewData ) ), UNO_QUERY ); + else + aUIElementData.xSettings = aNewData; + aUIElementData.aName = RetrieveNameFromResourceURL( ResourceURL ) + m_aXMLPostfix; + aUIElementData.aResourceURL = ResourceURL; + m_bModified = true; + + // Modify type container + UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType]; + rElementType.bModified = true; + + UIElementDataHashMap& rElements = rElementType.aElementsHashMap; + + // Check our user element settings hash map as it can already contain settings that have been set to default! + // If no node can be found, we have to insert it. + UIElementDataHashMap::iterator pIter = rElements.find( ResourceURL ); + if ( pIter != rElements.end() ) + pIter->second = aUIElementData; + else + rElements.emplace( ResourceURL, aUIElementData ); + + Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY ); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + + // Create event to notify listener about replaced element settings + ui::ConfigurationEvent aEvent; + + aEvent.ResourceURL = ResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.ReplacedElement <<= pDataSettings->xSettings; + aEvent.Element <<= aUIElementData.xSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Replace ); + } + } +} + +void SAL_CALL ModuleUIConfigurationManager::removeSettings( const OUString& ResourceURL ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException( "The ResourceURL is not valid or " + "describes an unknown type. " + "ResourceURL: " + ResourceURL, nullptr, 0 ); + else if ( m_bReadOnly ) + throw IllegalAccessException( "The configuration manager is read-only. " + "ResourceURL: " + ResourceURL, nullptr ); + else + { + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException( "The configuration manager has been disposed, " + "and can't uphold its method specification anymore. " + "ResourceURL: " + ResourceURL, nullptr ); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType ); + if ( !pDataSettings ) + throw NoSuchElementException( "The settings data cannot be found. " + "ResourceURL: " + ResourceURL, nullptr ); + // If element settings are default, we don't need to change anything! + if ( pDataSettings->bDefault ) + return; + else + { + Reference< XIndexAccess > xRemovedSettings = pDataSettings->xSettings; + pDataSettings->bDefault = true; + + // check if this is a default layer node + if ( !pDataSettings->bDefaultNode ) + pDataSettings->bModified = true; // we have to remove this node from the user layer! + pDataSettings->xSettings.clear(); + m_bModified = true; // user layer must be written + + // Modify type container + UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType]; + rElementType.bModified = true; + + Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY ); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + + // Check if we have settings in the default layer which replaces the user-defined one! + UIElementData* pDefaultDataSettings = impl_findUIElementData( ResourceURL, nElementType ); + if ( pDefaultDataSettings ) + { + // Create event to notify listener about replaced element settings + ui::ConfigurationEvent aEvent; + + aEvent.ResourceURL = ResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.Element <<= xRemovedSettings; + aEvent.ReplacedElement <<= pDefaultDataSettings->xSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Replace ); + } + else + { + // Create event to notify listener about removed element settings + ui::ConfigurationEvent aEvent; + + aEvent.ResourceURL = ResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.Element <<= xRemovedSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Remove ); + } + } + } +} + +void SAL_CALL ModuleUIConfigurationManager::insertSettings( const OUString& NewResourceURL, const Reference< XIndexAccess >& aNewData ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( NewResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + else if ( m_bReadOnly ) + throw IllegalAccessException(); + else + { + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + UIElementData* pDataSettings = impl_findUIElementData( NewResourceURL, nElementType ); + if ( !(!pDataSettings) ) + throw ElementExistException(); + UIElementData aUIElementData; + + aUIElementData.bDefault = false; + aUIElementData.bDefaultNode = false; + aUIElementData.bModified = true; + + // Create a copy of the data if the container is not const + Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY ); + if ( xReplace.is() ) + aUIElementData.xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer( aNewData ) ), UNO_QUERY ); + else + aUIElementData.xSettings = aNewData; + aUIElementData.aName = RetrieveNameFromResourceURL( NewResourceURL ) + m_aXMLPostfix; + aUIElementData.aResourceURL = NewResourceURL; + m_bModified = true; + + UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType]; + rElementType.bModified = true; + + UIElementDataHashMap& rElements = rElementType.aElementsHashMap; + rElements.emplace( NewResourceURL, aUIElementData ); + + Reference< XIndexAccess > xInsertSettings( aUIElementData.xSettings ); + Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY ); + + // Create event to notify listener about removed element settings + ui::ConfigurationEvent aEvent; + + aEvent.ResourceURL = NewResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xThis; + aEvent.Element <<= xInsertSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Insert ); + } +} + +Reference< XInterface > SAL_CALL ModuleUIConfigurationManager::getImageManager() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xModuleImageManager.is() ) + { + m_xModuleImageManager.set( static_cast< cppu::OWeakObject *>( new ModuleImageManager( m_xContext )), + UNO_QUERY ); + Reference< XInitialization > xInit( m_xModuleImageManager, UNO_QUERY ); + + uno::Sequence<uno::Any> aPropSeq(comphelper::InitAnyPropertySequence( + { + {"UserConfigStorage", uno::Any(m_xUserConfigStorage)}, + {"ModuleIdentifier", uno::Any(m_aModuleIdentifier)}, + {"UserRootCommit", uno::Any(m_xUserRootCommit)}, + })); + xInit->initialize( aPropSeq ); + } + + return Reference< XInterface >( m_xModuleImageManager, UNO_QUERY ); +} + +Reference< ui::XAcceleratorConfiguration > SAL_CALL ModuleUIConfigurationManager::getShortCutManager() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xModuleAcceleratorManager.is() ) try + { + m_xModuleAcceleratorManager = ui::ModuleAcceleratorConfiguration:: + createWithModuleIdentifier(m_xContext, m_aModuleIdentifier); + } + catch ( const css::uno::DeploymentException& ) + { + SAL_WARN("fwk.uiconfiguration", "ModuleAcceleratorConfiguration" + " not available. This should happen only on mobile platforms."); + } + + return m_xModuleAcceleratorManager; +} + +Reference< XInterface > SAL_CALL ModuleUIConfigurationManager::getEventsManager() +{ + return Reference< XInterface >(); +} + +// XModuleUIConfigurationManager +sal_Bool SAL_CALL ModuleUIConfigurationManager::isDefaultSettings( const OUString& ResourceURL ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType, false ); + if ( pDataSettings && pDataSettings->bDefaultNode ) + return true; + + return false; +} + +Reference< XIndexAccess > SAL_CALL ModuleUIConfigurationManager::getDefaultSettings( const OUString& ResourceURL ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + // preload list of element types on demand + impl_preloadUIElementTypeList( LAYER_DEFAULT, nElementType ); + + // Look into our default vector/unordered_map combination + UIElementDataHashMap& rDefaultHashMap = m_aUIElements[LAYER_DEFAULT][nElementType].aElementsHashMap; + UIElementDataHashMap::iterator pIter = rDefaultHashMap.find( ResourceURL ); + if ( pIter != rDefaultHashMap.end() ) + { + if ( !pIter->second.xSettings.is() ) + impl_requestUIElementData( nElementType, LAYER_DEFAULT, pIter->second ); + return pIter->second.xSettings; + } + + // Nothing has been found! + throw NoSuchElementException(); +} + +// XUIConfigurationPersistence +void SAL_CALL ModuleUIConfigurationManager::reload() +{ + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !(m_xUserConfigStorage.is() && m_bModified && !m_bReadOnly) ) + return; + + // Try to access our module sub folder + ConfigEventNotifyContainer aRemoveNotifyContainer; + ConfigEventNotifyContainer aReplaceNotifyContainer; + for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + try + { + UIElementType& rUserElementType = m_aUIElements[LAYER_USERDEFINED][i]; + + if ( rUserElementType.bModified ) + { + UIElementType& rDefaultElementType = m_aUIElements[LAYER_DEFAULT][i]; + impl_reloadElementTypeData( rUserElementType, rDefaultElementType, aRemoveNotifyContainer, aReplaceNotifyContainer ); + } + } + catch ( const Exception& ) + { + throw IOException(); + } + } + + m_bModified = false; + + // Unlock mutex before notify our listeners + aGuard.clear(); + + // Notify our listeners + for (const ui::ConfigurationEvent & j : aRemoveNotifyContainer) + implts_notifyContainerListener( j, NotifyOp_Remove ); + for (const ui::ConfigurationEvent & k : aReplaceNotifyContainer) + implts_notifyContainerListener( k, NotifyOp_Replace ); +} + +void SAL_CALL ModuleUIConfigurationManager::store() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !(m_xUserConfigStorage.is() && m_bModified && !m_bReadOnly) ) + return; + + // Try to access our module sub folder + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + try + { + UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][i]; + + if ( rElementType.bModified && rElementType.xStorage.is() ) + { + impl_storeElementTypeData( rElementType.xStorage, rElementType ); + m_pStorageHandler[i]->commitUserChanges(); + } + } + catch ( const Exception& ) + { + throw IOException(); + } + } + + m_bModified = false; +} + +void SAL_CALL ModuleUIConfigurationManager::storeToStorage( const Reference< XStorage >& Storage ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !(m_xUserConfigStorage.is() && m_bModified && !m_bReadOnly) ) + return; + + // Try to access our module sub folder + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + try + { + Reference< XStorage > xElementTypeStorage( Storage->openStorageElement( + UIELEMENTTYPENAMES[i], ElementModes::READWRITE )); + UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][i]; + + if ( rElementType.bModified && xElementTypeStorage.is() ) + impl_storeElementTypeData( xElementTypeStorage, rElementType, false ); // store data to storage, but don't reset modify flag! + } + catch ( const Exception& ) + { + throw IOException(); + } + } + + Reference< XTransactedObject > xTransactedObject( Storage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); +} + +sal_Bool SAL_CALL ModuleUIConfigurationManager::isModified() +{ + SolarMutexGuard g; + + return m_bModified; +} + +sal_Bool SAL_CALL ModuleUIConfigurationManager::isReadOnly() +{ + SolarMutexGuard g; + + return m_bReadOnly; +} + +void ModuleUIConfigurationManager::implts_notifyContainerListener( const ui::ConfigurationEvent& aEvent, NotifyOp eOp ) +{ + ::cppu::OInterfaceContainerHelper* pContainer = m_aListenerContainer.getContainer( cppu::UnoType<css::ui::XUIConfigurationListener>::get()); + if ( pContainer == nullptr ) + return; + + ::cppu::OInterfaceIteratorHelper pIterator( *pContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + switch ( eOp ) + { + case NotifyOp_Replace: + static_cast< css::ui::XUIConfigurationListener*>(pIterator.next())->elementReplaced( aEvent ); + break; + case NotifyOp_Insert: + static_cast< css::ui::XUIConfigurationListener*>(pIterator.next())->elementInserted( aEvent ); + break; + case NotifyOp_Remove: + static_cast< css::ui::XUIConfigurationListener*>(pIterator.next())->elementRemoved( aEvent ); + break; + } + } + catch( const css::uno::RuntimeException& ) + { + pIterator.remove(); + } + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ModuleUIConfigurationManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &arguments) +{ + return cppu::acquire(new ModuleUIConfigurationManager(context, arguments)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/uicategorydescription.cxx b/framework/source/uiconfiguration/uicategorydescription.cxx new file mode 100644 index 000000000..517a8d47f --- /dev/null +++ b/framework/source/uiconfiguration/uicategorydescription.cxx @@ -0,0 +1,406 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uielement/uicommanddescription.hxx> + +#include <helper/mischelper.hxx> + +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <sal/log.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <comphelper/propertysequence.hxx> + +#include <unordered_map> + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::configuration; +using namespace com::sun::star::container; +using namespace framework; + +namespace { + +class ConfigurationAccess_UICategory : public ::cppu::WeakImplHelper<XNameAccess,XContainerListener> +{ + osl::Mutex aMutex; + public: + ConfigurationAccess_UICategory( const OUString& aModuleName, const Reference< XNameAccess >& xGenericUICommands, const Reference< XComponentContext >& rxContext ); + virtual ~ConfigurationAccess_UICategory() override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + + virtual sal_Bool SAL_CALL hasElements() override; + + // container.XContainerListener + virtual void SAL_CALL elementInserted( const ContainerEvent& aEvent ) override; + virtual void SAL_CALL elementRemoved ( const ContainerEvent& aEvent ) override; + virtual void SAL_CALL elementReplaced( const ContainerEvent& aEvent ) override; + + // lang.XEventListener + virtual void SAL_CALL disposing( const EventObject& aEvent ) override; + + protected: + Any getUINameFromID( const OUString& rId ); + Any getUINameFromCache( const OUString& rId ); + Sequence< OUString > getAllIds(); + void fillCache(); + + private: + typedef std::unordered_map< OUString, + OUString > IdToInfoCache; + + void initializeConfigAccess(); + + OUString m_aConfigCategoryAccess; + OUString m_aPropUIName; + Reference< XNameAccess > m_xGenericUICategories; + Reference< XMultiServiceFactory > m_xConfigProvider; + Reference< XNameAccess > m_xConfigAccess; + Reference< XContainerListener > m_xConfigListener; + bool m_bConfigAccessInitialized; + bool m_bCacheFilled; + IdToInfoCache m_aIdCache; +}; + +// XInterface, XTypeProvider + +ConfigurationAccess_UICategory::ConfigurationAccess_UICategory( const OUString& aModuleName, const Reference< XNameAccess >& rGenericUICategories, const Reference< XComponentContext >& rxContext ) : + // Create configuration hierarchical access name + m_aConfigCategoryAccess( "/org.openoffice.Office.UI." + aModuleName + "/Commands/Categories"), + m_aPropUIName( "Name" ), + m_xGenericUICategories( rGenericUICategories ), + m_xConfigProvider(theDefaultProvider::get( rxContext )), + m_bConfigAccessInitialized( false ), + m_bCacheFilled( false ) +{ +} + +ConfigurationAccess_UICategory::~ConfigurationAccess_UICategory() +{ + // SAFE + osl::MutexGuard g(aMutex); + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + xContainer->removeContainerListener(m_xConfigListener); +} + +// XNameAccess +Any SAL_CALL ConfigurationAccess_UICategory::getByName( const OUString& rId ) +{ + osl::MutexGuard g(aMutex); + if ( !m_bConfigAccessInitialized ) + { + initializeConfigAccess(); + m_bConfigAccessInitialized = true; + fillCache(); + } + + // SAFE + Any a = getUINameFromID( rId ); + + if ( !a.hasValue() ) + throw NoSuchElementException(); + + return a; +} + +Sequence< OUString > SAL_CALL ConfigurationAccess_UICategory::getElementNames() +{ + return getAllIds(); +} + +sal_Bool SAL_CALL ConfigurationAccess_UICategory::hasByName( const OUString& rId ) +{ + return getByName( rId ).hasValue(); +} + +// XElementAccess +Type SAL_CALL ConfigurationAccess_UICategory::getElementType() +{ + return cppu::UnoType<OUString>::get(); +} + +sal_Bool SAL_CALL ConfigurationAccess_UICategory::hasElements() +{ + // There must be global categories! + return true; +} + +void ConfigurationAccess_UICategory::fillCache() +{ + SAL_INFO( "fwk", "framework (cd100003) ::ConfigurationAccess_UICategory::fillCache" ); + + if ( m_bCacheFilled ) + return; + + OUString aUIName; + const Sequence< OUString > aNameSeq = m_xConfigAccess->getElementNames(); + + for ( OUString const & rName : aNameSeq ) + { + try + { + Reference< XNameAccess > xNameAccess(m_xConfigAccess->getByName( rName ),UNO_QUERY); + if ( xNameAccess.is() ) + { + xNameAccess->getByName( m_aPropUIName ) >>= aUIName; + + m_aIdCache.emplace( rName, aUIName ); + } + } + catch ( const css::lang::WrappedTargetException& ) + { + } + catch ( const css::container::NoSuchElementException& ) + { + } + } + + m_bCacheFilled = true; +} + +Any ConfigurationAccess_UICategory::getUINameFromID( const OUString& rId ) +{ + Any a; + + try + { + a = getUINameFromCache( rId ); + if ( !a.hasValue() ) + { + // Try to ask our global commands configuration access + if ( m_xGenericUICategories.is() ) + { + try + { + return m_xGenericUICategories->getByName( rId ); + } + catch ( const css::lang::WrappedTargetException& ) + { + } + catch ( const css::container::NoSuchElementException& ) + { + } + } + } + } + catch( const css::container::NoSuchElementException& ) + { + } + catch ( const css::lang::WrappedTargetException& ) + { + } + + return a; +} + +Any ConfigurationAccess_UICategory::getUINameFromCache( const OUString& rId ) +{ + Any a; + + IdToInfoCache::const_iterator pIter = m_aIdCache.find( rId ); + if ( pIter != m_aIdCache.end() ) + a <<= pIter->second; + + return a; +} + +Sequence< OUString > ConfigurationAccess_UICategory::getAllIds() +{ + // SAFE + osl::MutexGuard g(aMutex); + + if ( !m_bConfigAccessInitialized ) + { + initializeConfigAccess(); + m_bConfigAccessInitialized = true; + fillCache(); + } + + if ( m_xConfigAccess.is() ) + { + try + { + Sequence< OUString > aNameSeq = m_xConfigAccess->getElementNames(); + + if ( m_xGenericUICategories.is() ) + { + // Create concat list of supported user interface commands of the module + Sequence< OUString > aGenericNameSeq = m_xGenericUICategories->getElementNames(); + sal_uInt32 nCount1 = aNameSeq.getLength(); + sal_uInt32 nCount2 = aGenericNameSeq.getLength(); + + aNameSeq.realloc( nCount1 + nCount2 ); + OUString* pNameSeq = aNameSeq.getArray(); + const OUString* pGenericSeq = aGenericNameSeq.getConstArray(); + for ( sal_uInt32 i = 0; i < nCount2; i++ ) + pNameSeq[nCount1+i] = pGenericSeq[i]; + } + + return aNameSeq; + } + catch( const css::container::NoSuchElementException& ) + { + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + + return Sequence< OUString >(); +} + +void ConfigurationAccess_UICategory::initializeConfigAccess() +{ + try + { + Sequence<Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", Any(m_aConfigCategoryAccess)} + })); + + m_xConfigAccess.set( m_xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", aArgs ),UNO_QUERY ); + if ( m_xConfigAccess.is() ) + { + // Add as container listener + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + { + m_xConfigListener = new WeakContainerListener(this); + xContainer->addContainerListener(m_xConfigListener); + } + } + } + catch ( const WrappedTargetException& ) + { + } + catch ( const Exception& ) + { + } +} + +// container.XContainerListener +void SAL_CALL ConfigurationAccess_UICategory::elementInserted( const ContainerEvent& ) +{ +} + +void SAL_CALL ConfigurationAccess_UICategory::elementRemoved ( const ContainerEvent& ) +{ +} + +void SAL_CALL ConfigurationAccess_UICategory::elementReplaced( const ContainerEvent& ) +{ +} + +// lang.XEventListener +void SAL_CALL ConfigurationAccess_UICategory::disposing( const EventObject& aEvent ) +{ + // SAFE + // remove our reference to the config access + osl::MutexGuard g(aMutex); + + Reference< XInterface > xIfac1( aEvent.Source, UNO_QUERY ); + Reference< XInterface > xIfac2( m_xConfigAccess, UNO_QUERY ); + if ( xIfac1 == xIfac2 ) + m_xConfigAccess.clear(); +} + +class UICategoryDescription : public UICommandDescription +{ +public: + explicit UICategoryDescription( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.UICategoryDescription"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.UICategoryDescription"}; + } + +}; + +UICategoryDescription::UICategoryDescription( const Reference< XComponentContext >& rxContext ) : + UICommandDescription(rxContext,true) +{ + Reference< XNameAccess > xEmpty; + OUString aGenericCategories( "GenericCategories" ); + m_xGenericUICommands = new ConfigurationAccess_UICategory( aGenericCategories, xEmpty, rxContext ); + + // insert generic categories mappings + m_aModuleToCommandFileMap.emplace( OUString("generic"), aGenericCategories ); + + UICommandsHashMap::iterator pCatIter = m_aUICommandsHashMap.find( aGenericCategories ); + if ( pCatIter != m_aUICommandsHashMap.end() ) + pCatIter->second = m_xGenericUICommands; + + impl_fillElements("ooSetupFactoryCmdCategoryConfigRef"); +} + +struct Instance { + explicit Instance( + css::uno::Reference<css::uno::XComponentContext> const & context): + instance(static_cast<cppu::OWeakObject *>( + new UICategoryDescription(context))) + { + } + + css::uno::Reference<css::uno::XInterface> instance; +}; + +struct Singleton: + public rtl::StaticWithArg< + Instance, css::uno::Reference<css::uno::XComponentContext>, Singleton> +{}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_UICategoryDescription_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(static_cast<cppu::OWeakObject *>( + Singleton::get(context).instance.get())); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/uiconfigurationmanager.cxx b/framework/source/uiconfiguration/uiconfigurationmanager.cxx new file mode 100644 index 000000000..be337b580 --- /dev/null +++ b/framework/source/uiconfiguration/uiconfigurationmanager.cxx @@ -0,0 +1,1381 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uiconfiguration/imagemanager.hxx> +#include <uielement/rootitemcontainer.hxx> +#include <uielement/constitemcontainer.hxx> +#include <uielement/uielementtypenames.hxx> +#include <menuconfiguration.hxx> +#include <statusbarconfiguration.hxx> +#include <toolboxconfiguration.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/ElementExistException.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/InvalidStorageException.hpp> +#include <com/sun/star/embed/StorageWrappedTargetException.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/lang/IllegalAccessException.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/ui/UIElementType.hpp> +#include <com/sun/star/ui/ConfigurationEvent.hpp> +#include <com/sun/star/ui/DocumentAcceleratorConfiguration.hpp> +#include <com/sun/star/ui/XAcceleratorConfiguration.hpp> +#include <com/sun/star/ui/XUIConfigurationManager2.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <vcl/svapp.hxx> +#include <sal/log.hxx> + +#include <unordered_map> + +using namespace com::sun::star::uno; +using namespace com::sun::star::io; +using namespace com::sun::star::embed; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::beans; +using namespace com::sun::star::ui; +using namespace framework; + +namespace { + +class UIConfigurationManager : public ::cppu::WeakImplHelper< + css::lang::XServiceInfo , + css::ui::XUIConfigurationManager2 > +{ +public: + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.UIConfigurationManager"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.UIConfigurationManager"}; + } + + explicit UIConfigurationManager( const css::uno::Reference< css::uno::XComponentContext > & rxContext ); + + // XComponent + virtual void SAL_CALL dispose() override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + // XUIConfiguration + virtual void SAL_CALL addConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override; + virtual void SAL_CALL removeConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override; + + // XUIConfigurationManager + virtual void SAL_CALL reset() override; + virtual css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL getUIElementsInfo( sal_Int16 ElementType ) override; + virtual css::uno::Reference< css::container::XIndexContainer > SAL_CALL createSettings( ) override; + virtual sal_Bool SAL_CALL hasSettings( const OUString& ResourceURL ) override; + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getSettings( const OUString& ResourceURL, sal_Bool bWriteable ) override; + virtual void SAL_CALL replaceSettings( const OUString& ResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override; + virtual void SAL_CALL removeSettings( const OUString& ResourceURL ) override; + virtual void SAL_CALL insertSettings( const OUString& NewResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getImageManager() override; + virtual css::uno::Reference< css::ui::XAcceleratorConfiguration > SAL_CALL getShortCutManager() override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getEventsManager() override; + + // XUIConfigurationPersistence + virtual void SAL_CALL reload() override; + virtual void SAL_CALL store() override; + virtual void SAL_CALL storeToStorage( const css::uno::Reference< css::embed::XStorage >& Storage ) override; + virtual sal_Bool SAL_CALL isModified() override; + virtual sal_Bool SAL_CALL isReadOnly() override; + + // XUIConfigurationStorage + virtual void SAL_CALL setStorage( const css::uno::Reference< css::embed::XStorage >& Storage ) override; + virtual sal_Bool SAL_CALL hasStorage() override; + +private: + // private data types + enum NotifyOp + { + NotifyOp_Remove, + NotifyOp_Insert, + NotifyOp_Replace + }; + + struct UIElementInfo + { + UIElementInfo( const OUString& rResourceURL, const OUString& rUIName ) : + aResourceURL( rResourceURL), aUIName( rUIName ) {} + OUString aResourceURL; + OUString aUIName; + }; + + struct UIElementData + { + UIElementData() : bModified( false ), bDefault( true ) {}; + + OUString aResourceURL; + OUString aName; + bool bModified; // has been changed since last storing + bool bDefault; // default settings + css::uno::Reference< css::container::XIndexAccess > xSettings; + }; + + struct UIElementType; + friend struct UIElementType; + typedef std::unordered_map< OUString, UIElementData > UIElementDataHashMap; + + struct UIElementType + { + UIElementType() : bModified( false ), + bLoaded( false ), + nElementType( css::ui::UIElementType::UNKNOWN ) {} + + bool bModified; + bool bLoaded; + sal_Int16 nElementType; + UIElementDataHashMap aElementsHashMap; + css::uno::Reference< css::embed::XStorage > xStorage; + }; + + typedef std::vector< UIElementType > UIElementTypesVector; + typedef std::vector< css::ui::ConfigurationEvent > ConfigEventNotifyContainer; + typedef std::unordered_map< OUString, UIElementInfo > UIElementInfoHashMap; + + void impl_Initialize(); + void implts_notifyContainerListener( const css::ui::ConfigurationEvent& aEvent, NotifyOp eOp ); + void impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType ); + void impl_preloadUIElementTypeList( sal_Int16 nElementType ); + UIElementData* impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad = true ); + void impl_requestUIElementData( sal_Int16 nElementType, UIElementData& aUIElementData ); + void impl_storeElementTypeData( css::uno::Reference< css::embed::XStorage > const & xStorage, UIElementType& rElementType, bool bResetModifyState = true ); + void impl_resetElementTypeData( UIElementType& rDocElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer ); + void impl_reloadElementTypeData( UIElementType& rDocElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer, ConfigEventNotifyContainer& rReplaceNotifyContainer ); + + UIElementTypesVector m_aUIElements; + css::uno::Reference< css::embed::XStorage > m_xDocConfigStorage; + bool m_bReadOnly; + bool m_bModified; + bool m_bDisposed; + OUString m_aPropUIName; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + osl::Mutex m_mutex; + cppu::OMultiTypeInterfaceContainerHelper m_aListenerContainer; /// container for ALL Listener + css::uno::Reference< css::lang::XComponent > m_xImageManager; + css::uno::Reference< css::ui::XAcceleratorConfiguration > m_xAccConfig; +}; + +// important: The order and position of the elements must match the constant +// definition of "css::ui::UIElementType" +static OUStringLiteral UIELEMENTTYPENAMES[] = +{ + "", // Dummy value for unknown! + UIELEMENTTYPE_MENUBAR_NAME, + UIELEMENTTYPE_POPUPMENU_NAME, + UIELEMENTTYPE_TOOLBAR_NAME, + UIELEMENTTYPE_STATUSBAR_NAME, + UIELEMENTTYPE_FLOATINGWINDOW_NAME, + UIELEMENTTYPE_PROGRESSBAR_NAME, + UIELEMENTTYPE_TOOLPANEL_NAME +}; + +static const char RESOURCEURL_PREFIX[] = "private:resource/"; +static const sal_Int32 RESOURCEURL_PREFIX_SIZE = 17; + +sal_Int16 RetrieveTypeFromResourceURL( const OUString& aResourceURL ) +{ + + if (( aResourceURL.startsWith( RESOURCEURL_PREFIX ) ) && + ( aResourceURL.getLength() > RESOURCEURL_PREFIX_SIZE )) + { + OUString aTmpStr = aResourceURL.copy( RESOURCEURL_PREFIX_SIZE ); + sal_Int32 nIndex = aTmpStr.indexOf( '/' ); + if (( nIndex > 0 ) && ( aTmpStr.getLength() > nIndex )) + { + OUString aTypeStr( aTmpStr.copy( 0, nIndex )); + for ( int i = 0; i < UIElementType::COUNT; i++ ) + { + if ( aTypeStr == UIELEMENTTYPENAMES[i] ) + return sal_Int16( i ); + } + } + } + + return UIElementType::UNKNOWN; +} + +OUString RetrieveNameFromResourceURL( const OUString& aResourceURL ) +{ + if (( aResourceURL.startsWith( RESOURCEURL_PREFIX ) ) && + ( aResourceURL.getLength() > RESOURCEURL_PREFIX_SIZE )) + { + sal_Int32 nIndex = aResourceURL.lastIndexOf( '/' ); + if (( nIndex > 0 ) && (( nIndex+1 ) < aResourceURL.getLength())) + return aResourceURL.copy( nIndex+1 ); + } + + return OUString(); +} + +void UIConfigurationManager::impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType ) +{ + // preload list of element types on demand + impl_preloadUIElementTypeList( nElementType ); + + UIElementDataHashMap& rUserElements = m_aUIElements[nElementType].aElementsHashMap; + + for (auto const& elem : rUserElements) + { + UIElementData* pDataSettings = impl_findUIElementData( elem.second.aResourceURL, nElementType ); + if ( pDataSettings && !pDataSettings->bDefault ) + { + // Retrieve user interface name from XPropertySet interface + OUString aUIName; + Reference< XPropertySet > xPropSet( pDataSettings->xSettings, UNO_QUERY ); + if ( xPropSet.is() ) + { + Any a = xPropSet->getPropertyValue( m_aPropUIName ); + a >>= aUIName; + } + + UIElementInfo aInfo( elem.second.aResourceURL, aUIName ); + aUIElementInfoCollection.emplace( elem.second.aResourceURL, aInfo ); + } + } +} + +void UIConfigurationManager::impl_preloadUIElementTypeList( sal_Int16 nElementType ) +{ + UIElementType& rElementTypeData = m_aUIElements[nElementType]; + + if ( !rElementTypeData.bLoaded ) + { + Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage; + if ( xElementTypeStorage.is() ) + { + OUString aResURLPrefix = + RESOURCEURL_PREFIX + + UIELEMENTTYPENAMES[ nElementType ] + + "/"; + + UIElementDataHashMap& rHashMap = rElementTypeData.aElementsHashMap; + const Sequence< OUString > aUIElementNames = xElementTypeStorage->getElementNames(); + for ( OUString const & rElementName : aUIElementNames ) + { + UIElementData aUIElementData; + + // Resource name must be without ".xml" + sal_Int32 nIndex = rElementName.lastIndexOf( '.' ); + if (( nIndex > 0 ) && ( nIndex < rElementName.getLength() )) + { + OUString aExtension( rElementName.copy( nIndex+1 )); + OUString aUIElementName( rElementName.copy( 0, nIndex )); + + if (!aUIElementName.isEmpty() && + ( aExtension.equalsIgnoreAsciiCase("xml"))) + { + aUIElementData.aResourceURL = aResURLPrefix + aUIElementName; + aUIElementData.aName = rElementName; + aUIElementData.bModified = false; + aUIElementData.bDefault = false; + + // Create unordered_map entries for all user interface elements inside the storage. We don't load the + // settings to speed up the process. + rHashMap.emplace( aUIElementData.aResourceURL, aUIElementData ); + } + } + } + } + } + + rElementTypeData.bLoaded = true; +} + +void UIConfigurationManager::impl_requestUIElementData( sal_Int16 nElementType, UIElementData& aUIElementData ) +{ + UIElementType& rElementTypeData = m_aUIElements[nElementType]; + + Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage; + if ( xElementTypeStorage.is() && !aUIElementData.aName.isEmpty() ) + { + try + { + Reference< XStream > xStream = xElementTypeStorage->openStreamElement( aUIElementData.aName, ElementModes::READ ); + Reference< XInputStream > xInputStream = xStream->getInputStream(); + + if ( xInputStream.is() ) + { + switch ( nElementType ) + { + case css::ui::UIElementType::UNKNOWN: + break; + + case css::ui::UIElementType::MENUBAR: + case css::ui::UIElementType::POPUPMENU: + { + try + { + MenuConfiguration aMenuCfg( m_xContext ); + Reference< XIndexAccess > xContainer( aMenuCfg.CreateMenuBarConfigurationFromXML( xInputStream )); + auto pRootItemContainer = comphelper::getUnoTunnelImplementation<RootItemContainer>( xContainer ); + if ( pRootItemContainer ) + aUIElementData.xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer( pRootItemContainer, true ) ), UNO_QUERY ); + else + aUIElementData.xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer( xContainer, true ) ), UNO_QUERY ); + return; + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + case css::ui::UIElementType::TOOLBAR: + { + try + { + Reference< XIndexContainer > xIndexContainer( static_cast< OWeakObject * >( new RootItemContainer() ), UNO_QUERY ); + ToolBoxConfiguration::LoadToolBox( m_xContext, xInputStream, xIndexContainer ); + auto pRootItemContainer = comphelper::getUnoTunnelImplementation<RootItemContainer>( xIndexContainer ); + aUIElementData.xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer( pRootItemContainer, true ) ), UNO_QUERY ); + return; + } + catch ( const css::lang::WrappedTargetException& ) + { + } + + break; + } + + case css::ui::UIElementType::STATUSBAR: + { + try + { + Reference< XIndexContainer > xIndexContainer( static_cast< OWeakObject * >( new RootItemContainer() ), UNO_QUERY ); + StatusBarConfiguration::LoadStatusBar( m_xContext, xInputStream, xIndexContainer ); + auto pRootItemContainer = comphelper::getUnoTunnelImplementation<RootItemContainer>( xIndexContainer ); + aUIElementData.xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer( pRootItemContainer, true ) ), UNO_QUERY ); + return; + } + catch ( const css::lang::WrappedTargetException& ) + { + } + + break; + } + + case css::ui::UIElementType::FLOATINGWINDOW: + { + break; + } + } + } + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::io::IOException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } + } + + // At least we provide an empty settings container! + aUIElementData.xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer()), UNO_QUERY ); +} + +UIConfigurationManager::UIElementData* UIConfigurationManager::impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad ) +{ + // preload list of element types on demand + impl_preloadUIElementTypeList( nElementType ); + + // try to look into our document vector/unordered_map combination + UIElementDataHashMap& rUserHashMap = m_aUIElements[nElementType].aElementsHashMap; + UIElementDataHashMap::iterator pIter = rUserHashMap.find( aResourceURL ); + if ( pIter != rUserHashMap.end() ) + { + // Default data settings data means removed! + if ( pIter->second.bDefault ) + return &(pIter->second); + else + { + if ( !pIter->second.xSettings.is() && bLoad ) + impl_requestUIElementData( nElementType, pIter->second ); + return &(pIter->second); + } + } + + // Nothing has been found! + return nullptr; +} + +void UIConfigurationManager::impl_storeElementTypeData( Reference< XStorage > const & xStorage, UIElementType& rElementType, bool bResetModifyState ) +{ + UIElementDataHashMap& rHashMap = rElementType.aElementsHashMap; + + for (auto & elem : rHashMap) + { + UIElementData& rElement = elem.second; + if ( rElement.bModified ) + { + if ( rElement.bDefault ) + { + xStorage->removeElement( rElement.aName ); + rElement.bModified = false; // mark as not modified + } + else + { + Reference< XStream > xStream = xStorage->openStreamElement( rElement.aName, ElementModes::WRITE|ElementModes::TRUNCATE ); + Reference< XOutputStream > xOutputStream( xStream->getOutputStream() ); + + if ( xOutputStream.is() ) + { + switch( rElementType.nElementType ) + { + case css::ui::UIElementType::MENUBAR: + case css::ui::UIElementType::POPUPMENU: + { + try + { + MenuConfiguration aMenuCfg( m_xContext ); + aMenuCfg.StoreMenuBarConfigurationToXML( + rElement.xSettings, xOutputStream, rElementType.nElementType == css::ui::UIElementType::MENUBAR ); + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + case css::ui::UIElementType::TOOLBAR: + { + try + { + ToolBoxConfiguration::StoreToolBox( m_xContext, xOutputStream, rElement.xSettings ); + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + case css::ui::UIElementType::STATUSBAR: + { + try + { + StatusBarConfiguration::StoreStatusBar( m_xContext, xOutputStream, rElement.xSettings ); + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + default: + break; + } + } + + // mark as not modified if we store to our own storage + if ( bResetModifyState ) + rElement.bModified = false; + } + } + } + + // commit element type storage + Reference< XTransactedObject > xTransactedObject( xStorage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); + + // mark UIElementType as not modified if we store to our own storage + if ( bResetModifyState ) + rElementType.bModified = false; +} + +void UIConfigurationManager::impl_resetElementTypeData( + UIElementType& rDocElementType, + ConfigEventNotifyContainer& rRemoveNotifyContainer ) +{ + UIElementDataHashMap& rHashMap = rDocElementType.aElementsHashMap; + + Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY ); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + + // Make copies of the event structures to be thread-safe. We have to unlock our mutex before calling + // our listeners! + for (auto & elem : rHashMap) + { + UIElementData& rElement = elem.second; + if ( !rElement.bDefault ) + { + // Remove user-defined settings from document + ConfigurationEvent aEvent; + aEvent.ResourceURL = rElement.aResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.Element <<= rElement.xSettings; + + rRemoveNotifyContainer.push_back( aEvent ); + + // Mark element as default. + rElement.bModified = false; + rElement.bDefault = true; + } + else + rElement.bModified = false; + } + + // Remove all settings from our user interface elements + rHashMap.clear(); +} + +void UIConfigurationManager::impl_reloadElementTypeData( + UIElementType& rDocElementType, + ConfigEventNotifyContainer& rRemoveNotifyContainer, + ConfigEventNotifyContainer& rReplaceNotifyContainer ) +{ + UIElementDataHashMap& rHashMap = rDocElementType.aElementsHashMap; + Reference< XStorage > xElementStorage( rDocElementType.xStorage ); + + Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY ); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + sal_Int16 nType = rDocElementType.nElementType; + + for (auto & elem : rHashMap) + { + UIElementData& rElement = elem.second; + if ( rElement.bModified ) + { + if ( xElementStorage->hasByName( rElement.aName )) + { + // Replace settings with data from user layer + Reference< XIndexAccess > xOldSettings( rElement.xSettings ); + + impl_requestUIElementData( nType, rElement ); + + ConfigurationEvent aReplaceEvent; + + aReplaceEvent.ResourceURL = rElement.aResourceURL; + aReplaceEvent.Accessor <<= xThis; + aReplaceEvent.Source = xIfac; + aReplaceEvent.ReplacedElement <<= xOldSettings; + aReplaceEvent.Element <<= rElement.xSettings; + rReplaceNotifyContainer.push_back( aReplaceEvent ); + + rElement.bModified = false; + } + else + { + // Element settings are not in any storage => remove + ConfigurationEvent aRemoveEvent; + + aRemoveEvent.ResourceURL = rElement.aResourceURL; + aRemoveEvent.Accessor <<= xThis; + aRemoveEvent.Source = xIfac; + aRemoveEvent.Element <<= rElement.xSettings; + + rRemoveNotifyContainer.push_back( aRemoveEvent ); + + // Mark element as default and not modified. That means "not active" in the document anymore + rElement.bModified = false; + rElement.bDefault = true; + } + } + } + + rDocElementType.bModified = false; +} + +void UIConfigurationManager::impl_Initialize() +{ + // Initialize the top-level structures with the storage data + if ( m_xDocConfigStorage.is() ) + { + long nModes = m_bReadOnly ? ElementModes::READ : ElementModes::READWRITE; + + // Try to access our module sub folder + for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT; + i++ ) + { + Reference< XStorage > xElementTypeStorage; + try + { + xElementTypeStorage = m_xDocConfigStorage->openStorageElement( UIELEMENTTYPENAMES[i], nModes ); + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::io::IOException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } + + m_aUIElements[i].nElementType = i; + m_aUIElements[i].bModified = false; + m_aUIElements[i].xStorage = xElementTypeStorage; + } + } + else + { + // We have no storage, just initialize ui element types with empty storage! + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + m_aUIElements[i].xStorage = m_xDocConfigStorage; + } +} + +UIConfigurationManager::UIConfigurationManager( const css::uno::Reference< css::uno::XComponentContext > & rxContext ) : + m_bReadOnly( true ) + , m_bModified( false ) + , m_bDisposed( false ) + , m_aPropUIName( "UIName" ) + , m_xContext( rxContext ) + , m_aListenerContainer( m_mutex ) +{ + // Make sure we have a default initialized entry for every layer and user interface element type! + // The following code depends on this! + m_aUIElements.resize( css::ui::UIElementType::COUNT ); +} + +// XComponent +void SAL_CALL UIConfigurationManager::dispose() +{ + Reference< XComponent > xThis( static_cast< OWeakObject* >(this), UNO_QUERY ); + + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + { + SolarMutexGuard g; + try + { + if ( m_xImageManager.is() ) + m_xImageManager->dispose(); + } + catch ( const Exception& ) + { + } + + m_xImageManager.clear(); + m_aUIElements.clear(); + m_xDocConfigStorage.clear(); + m_bModified = false; + m_bDisposed = true; + } +} + +void SAL_CALL UIConfigurationManager::addEventListener( const Reference< XEventListener >& xListener ) +{ + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + } + + m_aListenerContainer.addInterface( cppu::UnoType<XEventListener>::get(), xListener ); +} + +void SAL_CALL UIConfigurationManager::removeEventListener( const Reference< XEventListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_aListenerContainer.removeInterface( cppu::UnoType<XEventListener>::get(), xListener ); +} + +// XUIConfigurationManager +void SAL_CALL UIConfigurationManager::addConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + } + + m_aListenerContainer.addInterface( cppu::UnoType<XUIConfigurationListener>::get(), xListener ); +} + +void SAL_CALL UIConfigurationManager::removeConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_aListenerContainer.removeInterface( cppu::UnoType<XUIConfigurationListener>::get(), xListener ); +} + +void SAL_CALL UIConfigurationManager::reset() +{ + SolarMutexClearableGuard aGuard; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + if ( isReadOnly() ) + return; + + if ( !m_xDocConfigStorage.is() ) + return; + + try + { + // Remove all elements from our user-defined storage! + bool bCommit( false ); + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + UIElementType& rElementType = m_aUIElements[i]; + + if ( rElementType.xStorage.is() ) + { + bool bCommitSubStorage( false ); + const Sequence< OUString > aUIElementStreamNames = rElementType.xStorage->getElementNames(); + for ( OUString const & rStreamName : aUIElementStreamNames ) + { + rElementType.xStorage->removeElement( rStreamName ); + bCommitSubStorage = true; + bCommit = true; + } + + if ( bCommitSubStorage ) + { + Reference< XTransactedObject > xTransactedObject( rElementType.xStorage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); + } + } + } + + // Commit changes + if ( bCommit ) + { + Reference< XTransactedObject > xTransactedObject( m_xDocConfigStorage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); + } + + // remove settings from user defined layer and notify listener about removed settings data! + // Try to access our module sub folder + ConfigEventNotifyContainer aRemoveEventNotifyContainer; + for ( sal_Int16 j = 1; j < css::ui::UIElementType::COUNT; j++ ) + { + UIElementType& rDocElementType = m_aUIElements[j]; + + impl_resetElementTypeData( rDocElementType, aRemoveEventNotifyContainer ); + rDocElementType.bModified = false; + } + + m_bModified = false; + + // Unlock mutex before notify our listeners + aGuard.clear(); + + // Notify our listeners + for (const ConfigurationEvent & k : aRemoveEventNotifyContainer) + implts_notifyContainerListener( k, NotifyOp_Remove ); + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } +} + +Sequence< Sequence< PropertyValue > > SAL_CALL UIConfigurationManager::getUIElementsInfo( sal_Int16 ElementType ) +{ + if (( ElementType < 0 ) || ( ElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + + SolarMutexGuard g; + if ( m_bDisposed ) + throw DisposedException(); + + std::vector< Sequence< PropertyValue > > aElementInfoSeq; + UIElementInfoHashMap aUIElementInfoCollection; + + if ( ElementType == css::ui::UIElementType::UNKNOWN ) + { + for ( sal_Int16 i = 0; i < css::ui::UIElementType::COUNT; i++ ) + impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, i ); + } + else + impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, ElementType ); + + Sequence< PropertyValue > aUIElementInfo( 2 ); + aUIElementInfo[0].Name = "ResourceURL"; + aUIElementInfo[1].Name = m_aPropUIName; + + aElementInfoSeq.resize( aUIElementInfoCollection.size() ); + sal_Int32 n = 0; + for (auto const& elem : aUIElementInfoCollection) + { + aUIElementInfo[0].Value <<= elem.second.aResourceURL; + aUIElementInfo[1].Value <<= elem.second.aUIName; + aElementInfoSeq[n++] = aUIElementInfo; + } + + return comphelper::containerToSequence(aElementInfoSeq); +} + +Reference< XIndexContainer > SAL_CALL UIConfigurationManager::createSettings() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + // Creates an empty item container which can be filled from outside + return Reference< XIndexContainer >( static_cast< OWeakObject * >( new RootItemContainer()), UNO_QUERY ); +} + +sal_Bool SAL_CALL UIConfigurationManager::hasSettings( const OUString& ResourceURL ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType, false ); + if ( pDataSettings && !pDataSettings->bDefault ) + return true; + + return false; +} + +Reference< XIndexAccess > SAL_CALL UIConfigurationManager::getSettings( const OUString& ResourceURL, sal_Bool bWriteable ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType ); + if ( pDataSettings && !pDataSettings->bDefault ) + { + // Create a copy of our data if someone wants to change the data. + if ( bWriteable ) + return Reference< XIndexAccess >( static_cast< OWeakObject * >( new RootItemContainer( pDataSettings->xSettings ) ), UNO_QUERY ); + else + return pDataSettings->xSettings; + } + + throw NoSuchElementException(); +} + +void SAL_CALL UIConfigurationManager::replaceSettings( const OUString& ResourceURL, const Reference< css::container::XIndexAccess >& aNewData ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + else if ( m_bReadOnly ) + throw IllegalAccessException(); + else + { + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType ); + if ( !pDataSettings || pDataSettings->bDefault ) + throw NoSuchElementException(); + // we have a settings entry in our user-defined layer - replace + Reference< XIndexAccess > xOldSettings = pDataSettings->xSettings; + + // Create a copy of the data if the container is not const + Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY ); + if ( xReplace.is() ) + pDataSettings->xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer( aNewData ) ), UNO_QUERY ); + else + pDataSettings->xSettings = aNewData; + + pDataSettings->bDefault = false; + pDataSettings->bModified = true; + m_bModified = true; + + // Modify type container + UIElementType& rElementType = m_aUIElements[nElementType]; + rElementType.bModified = true; + + Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY ); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + + // Create event to notify listener about replaced element settings + ConfigurationEvent aEvent; + + aEvent.ResourceURL = ResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.ReplacedElement <<= xOldSettings; + aEvent.Element <<= pDataSettings->xSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Replace ); + } +} + +void SAL_CALL UIConfigurationManager::removeSettings( const OUString& ResourceURL ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException( "The ResourceURL is not valid or " + "describes an unknown type. " + "ResourceURL: " + ResourceURL, nullptr, 0 ); + else if ( m_bReadOnly ) + throw IllegalAccessException( "The configuration manager is read-only. " + "ResourceURL: " + ResourceURL, nullptr ); + else + { + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException( "The configuration manager has been disposed, " + "and can't uphold its method specification anymore. " + "ResourceURL: " + ResourceURL, nullptr ); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType ); + if ( !pDataSettings ) + throw NoSuchElementException( "The settings data cannot be found. " + "ResourceURL: " + ResourceURL, nullptr); + // If element settings are default, we don't need to change anything! + if ( pDataSettings->bDefault ) + return; + else + { + Reference< XIndexAccess > xRemovedSettings = pDataSettings->xSettings; + pDataSettings->bDefault = true; + + // check if this is a default layer node + pDataSettings->bModified = true; // we have to remove this node from the user layer! + pDataSettings->xSettings.clear(); + m_bModified = true; // user layer must be written + + // Modify type container + UIElementType& rElementType = m_aUIElements[nElementType]; + rElementType.bModified = true; + + Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY ); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + + // Create event to notify listener about removed element settings + ConfigurationEvent aEvent; + + aEvent.ResourceURL = ResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.Element <<= xRemovedSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Remove ); + } + } +} + +void SAL_CALL UIConfigurationManager::insertSettings( const OUString& NewResourceURL, const Reference< XIndexAccess >& aNewData ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( NewResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + else if ( m_bReadOnly ) + throw IllegalAccessException(); + else + { + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + bool bInsertData( false ); + UIElementData aUIElementData; + UIElementData* pDataSettings = impl_findUIElementData( NewResourceURL, nElementType ); + + if ( pDataSettings && !pDataSettings->bDefault ) + throw ElementExistException(); + + if ( !pDataSettings ) + { + pDataSettings = &aUIElementData; + bInsertData = true; + } + + { + pDataSettings->bDefault = false; + pDataSettings->bModified = true; + + // Create a copy of the data if the container is not const + Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY ); + if ( xReplace.is() ) + pDataSettings->xSettings.set( static_cast< OWeakObject * >( new ConstItemContainer( aNewData ) ), UNO_QUERY ); + else + pDataSettings->xSettings = aNewData; + + m_bModified = true; + + UIElementType& rElementType = m_aUIElements[nElementType]; + rElementType.bModified = true; + + if ( bInsertData ) + { + pDataSettings->aName = RetrieveNameFromResourceURL( NewResourceURL ) + ".xml"; + pDataSettings->aResourceURL = NewResourceURL; + + UIElementDataHashMap& rElements = rElementType.aElementsHashMap; + rElements.emplace( NewResourceURL, *pDataSettings ); + } + + Reference< XIndexAccess > xInsertSettings( aUIElementData.xSettings ); + Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY ); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + + // Create event to notify listener about removed element settings + ConfigurationEvent aEvent; + + aEvent.ResourceURL = NewResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.Element <<= xInsertSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Insert ); + } + } +} + +Reference< XInterface > SAL_CALL UIConfigurationManager::getImageManager() +{ + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xImageManager.is() ) + { + m_xImageManager.set( static_cast< cppu::OWeakObject *>( new ImageManager( m_xContext )), + UNO_QUERY ); + Reference< XInitialization > xInit( m_xImageManager, UNO_QUERY ); + + Sequence<Any> aPropSeq(comphelper::InitAnyPropertySequence( + { + {"UserConfigStorage", Any(m_xDocConfigStorage)}, + {"ModuleIdentifier", Any(OUString())}, + })); + + xInit->initialize( aPropSeq ); + } + + return Reference< XInterface >( m_xImageManager, UNO_QUERY ); +} + +Reference< XAcceleratorConfiguration > SAL_CALL UIConfigurationManager::getShortCutManager() +{ + // SAFE -> + SolarMutexGuard g; + + if (!m_xAccConfig.is()) try + { + m_xAccConfig = DocumentAcceleratorConfiguration:: + createWithDocumentRoot(m_xContext, m_xDocConfigStorage); + } + catch ( const css::uno::DeploymentException& ) + { + SAL_WARN("fwk.uiconfiguration", "DocumentAcceleratorConfiguration" + " not available. This should happen only on mobile platforms."); + } + + return m_xAccConfig; +} + +Reference< XInterface > SAL_CALL UIConfigurationManager::getEventsManager() +{ + return Reference< XInterface >(); +} + +// XUIConfigurationStorage +void SAL_CALL UIConfigurationManager::setStorage( const Reference< XStorage >& Storage ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_xDocConfigStorage.is() ) + { + try + { + // Dispose old storage to be sure that it will be closed + m_xDocConfigStorage->dispose(); + } + catch ( const Exception& ) + { + } + } + + // We store the new storage. Be careful it could be an empty reference! + m_xDocConfigStorage = Storage; + m_bReadOnly = true; + + if ( m_xAccConfig.is() ) + m_xAccConfig->setStorage( m_xDocConfigStorage ); + + if ( m_xImageManager.is() ) + { + ImageManager* pImageManager = static_cast<ImageManager*>(m_xImageManager.get()); + if ( pImageManager ) + pImageManager->setStorage( m_xDocConfigStorage ); + } + + if ( m_xDocConfigStorage.is() ) + { + Reference< XPropertySet > xPropSet( m_xDocConfigStorage, UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + long nOpenMode = 0; + Any a = xPropSet->getPropertyValue("OpenMode"); + if ( a >>= nOpenMode ) + m_bReadOnly = !( nOpenMode & ElementModes::WRITE ); + } + catch ( const css::beans::UnknownPropertyException& ) + { + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + } + + impl_Initialize(); +} + +sal_Bool SAL_CALL UIConfigurationManager::hasStorage() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + return m_xDocConfigStorage.is(); +} + +// XUIConfigurationPersistence +void SAL_CALL UIConfigurationManager::reload() +{ + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !(m_xDocConfigStorage.is() && m_bModified && !m_bReadOnly) ) + return; + + // Try to access our module sub folder + ConfigEventNotifyContainer aRemoveNotifyContainer; + ConfigEventNotifyContainer aReplaceNotifyContainer; + for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + try + { + UIElementType& rDocElementType = m_aUIElements[i]; + if ( rDocElementType.bModified ) + impl_reloadElementTypeData( rDocElementType, aRemoveNotifyContainer, aReplaceNotifyContainer ); + } + catch ( const Exception& ) + { + throw IOException(); + } + } + + m_bModified = false; + + // Unlock mutex before notify our listeners + aGuard.clear(); + + // Notify our listeners + for (const ConfigurationEvent & j : aRemoveNotifyContainer) + implts_notifyContainerListener( j, NotifyOp_Remove ); + for (const ConfigurationEvent & k : aReplaceNotifyContainer) + implts_notifyContainerListener( k, NotifyOp_Replace ); +} + +void SAL_CALL UIConfigurationManager::store() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !(m_xDocConfigStorage.is() && m_bModified && !m_bReadOnly) ) + return; + + // Try to access our module sub folder + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + try + { + UIElementType& rElementType = m_aUIElements[i]; + + if ( rElementType.bModified && rElementType.xStorage.is() ) + impl_storeElementTypeData( rElementType.xStorage, rElementType ); + } + catch ( const Exception& ) + { + throw IOException(); + } + } + + m_bModified = false; + Reference< XTransactedObject > xTransactedObject( m_xDocConfigStorage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); +} + +void SAL_CALL UIConfigurationManager::storeToStorage( const Reference< XStorage >& Storage ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !(m_xDocConfigStorage.is() && m_bModified && !m_bReadOnly) ) + return; + + // Try to access our module sub folder + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + try + { + Reference< XStorage > xElementTypeStorage( Storage->openStorageElement( + UIELEMENTTYPENAMES[i], ElementModes::READWRITE )); + UIElementType& rElementType = m_aUIElements[i]; + + if ( rElementType.bModified && xElementTypeStorage.is() ) + impl_storeElementTypeData( xElementTypeStorage, rElementType, false ); // store data to storage, but don't reset modify flag! + } + catch ( const Exception& ) + { + throw IOException(); + } + } + + Reference< XTransactedObject > xTransactedObject( Storage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); +} + +sal_Bool SAL_CALL UIConfigurationManager::isModified() +{ + SolarMutexGuard g; + + return m_bModified; +} + +sal_Bool SAL_CALL UIConfigurationManager::isReadOnly() +{ + SolarMutexGuard g; + + return m_bReadOnly; +} + +void UIConfigurationManager::implts_notifyContainerListener( const ConfigurationEvent& aEvent, NotifyOp eOp ) +{ + ::cppu::OInterfaceContainerHelper* pContainer = m_aListenerContainer.getContainer( cppu::UnoType<css::ui::XUIConfigurationListener>::get()); + if ( pContainer == nullptr ) + return; + + ::cppu::OInterfaceIteratorHelper pIterator( *pContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + switch ( eOp ) + { + case NotifyOp_Replace: + static_cast< css::ui::XUIConfigurationListener*>(pIterator.next())->elementReplaced( aEvent ); + break; + case NotifyOp_Insert: + static_cast< css::ui::XUIConfigurationListener*>(pIterator.next())->elementInserted( aEvent ); + break; + case NotifyOp_Remove: + static_cast< css::ui::XUIConfigurationListener*>(pIterator.next())->elementRemoved( aEvent ); + break; + } + } + catch( const css::uno::RuntimeException& ) + { + pIterator.remove(); + } + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_UIConfigurationManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UIConfigurationManager(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/windowstateconfiguration.cxx b/framework/source/uiconfiguration/windowstateconfiguration.cxx new file mode 100644 index 000000000..f44b5cf13 --- /dev/null +++ b/framework/source/uiconfiguration/windowstateconfiguration.cxx @@ -0,0 +1,1413 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uiconfiguration/windowstateproperties.hxx> +#include <helper/mischelper.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/frame/XModuleManager2.hpp> +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/ui/DockingArea.hpp> +#include <com/sun/star/util/XChangesBatch.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/sequence.hxx> +#include <sal/log.hxx> + +#include <unordered_map> +#include <vector> + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace com::sun::star::configuration; +using namespace com::sun::star::container; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::ui; +using namespace framework; + +namespace { + +// Zero based indexes, order must be the same as WindowStateMask && CONFIGURATION_PROPERTIES! +static const sal_Int16 PROPERTY_LOCKED = 0; +static const sal_Int16 PROPERTY_DOCKED = 1; +static const sal_Int16 PROPERTY_VISIBLE = 2; +static const sal_Int16 PROPERTY_CONTEXT = 3; +static const sal_Int16 PROPERTY_HIDEFROMMENU = 4; +static const sal_Int16 PROPERTY_NOCLOSE = 5; +static const sal_Int16 PROPERTY_SOFTCLOSE = 6; +static const sal_Int16 PROPERTY_CONTEXTACTIVE = 7; +static const sal_Int16 PROPERTY_DOCKINGAREA = 8; +static const sal_Int16 PROPERTY_POS = 9; +static const sal_Int16 PROPERTY_SIZE = 10; +static const sal_Int16 PROPERTY_UINAME = 11; +static const sal_Int16 PROPERTY_INTERNALSTATE = 12; +static const sal_Int16 PROPERTY_STYLE = 13; +static const sal_Int16 PROPERTY_DOCKPOS = 14; +static const sal_Int16 PROPERTY_DOCKSIZE = 15; + +// Order must be the same as WindowStateMask!! +static const char* CONFIGURATION_PROPERTIES[] = +{ + WINDOWSTATE_PROPERTY_LOCKED, + WINDOWSTATE_PROPERTY_DOCKED, + WINDOWSTATE_PROPERTY_VISIBLE, + WINDOWSTATE_PROPERTY_CONTEXT, + WINDOWSTATE_PROPERTY_HIDEFROMENU, + WINDOWSTATE_PROPERTY_NOCLOSE, + WINDOWSTATE_PROPERTY_SOFTCLOSE, + WINDOWSTATE_PROPERTY_CONTEXTACTIVE, + WINDOWSTATE_PROPERTY_DOCKINGAREA, + WINDOWSTATE_PROPERTY_POS, + WINDOWSTATE_PROPERTY_SIZE, + WINDOWSTATE_PROPERTY_UINAME, + WINDOWSTATE_PROPERTY_INTERNALSTATE, + WINDOWSTATE_PROPERTY_STYLE, + WINDOWSTATE_PROPERTY_DOCKPOS, + WINDOWSTATE_PROPERTY_DOCKSIZE, + nullptr +}; + +// Configuration access class for WindowState supplier implementation + +class ConfigurationAccess_WindowState : public ::cppu::WeakImplHelper< XNameContainer, XContainerListener > +{ + public: + ConfigurationAccess_WindowState( const OUString& aWindowStateConfigFile, const Reference< XComponentContext >& rxContext ); + virtual ~ConfigurationAccess_WindowState() override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XNameContainer + virtual void SAL_CALL removeByName( const OUString& sName ) override; + + virtual void SAL_CALL insertByName( const OUString& sName, const css::uno::Any& aPropertySet ) override; + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString& sName, const css::uno::Any& aPropertySet ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + + virtual sal_Bool SAL_CALL hasElements() override; + + // container.XContainerListener + virtual void SAL_CALL elementInserted( const ContainerEvent& aEvent ) override; + virtual void SAL_CALL elementRemoved ( const ContainerEvent& aEvent ) override; + virtual void SAL_CALL elementReplaced( const ContainerEvent& aEvent ) override; + + // lang.XEventListener + virtual void SAL_CALL disposing( const EventObject& aEvent ) override; + + protected: + enum // WindowStateMask + { + WINDOWSTATE_MASK_DOCKINGAREA = 256, + WINDOWSTATE_MASK_POS = 512, + WINDOWSTATE_MASK_SIZE = 1024, + WINDOWSTATE_MASK_UINAME = 2048, + WINDOWSTATE_MASK_INTERNALSTATE = 4096, + WINDOWSTATE_MASK_STYLE = 8192, + WINDOWSTATE_MASK_DOCKPOS = 16384, + WINDOWSTATE_MASK_DOCKSIZE = 32768 + }; + + // Cache structure. Valid values are described by the eMask member. All other values should not be + // provided to outside code! + struct WindowStateInfo + { + WindowStateInfo() + : bLocked(false) + , bDocked(false) + , bVisible(false) + , bContext(false) + , bHideFromMenu(false) + , bNoClose(false) + , bSoftClose(false) + , bContextActive(false) + , aDockingArea(css::ui::DockingArea_DOCKINGAREA_TOP) + , aDockPos(0, 0) + , aPos(0, 0) + , aSize(0, 0) + , nInternalState(0) + , nStyle(0) + , nMask(0) + { + } + + bool bLocked : 1, + bDocked : 1, + bVisible : 1, + bContext : 1, + bHideFromMenu : 1, + bNoClose : 1, + bSoftClose : 1, + bContextActive : 1; + css::ui::DockingArea aDockingArea; + css::awt::Point aDockPos; + css::awt::Size aDockSize; + css::awt::Point aPos; + css::awt::Size aSize; + OUString aUIName; + sal_uInt32 nInternalState; + sal_uInt16 nStyle; + sal_uInt32 nMask; // see WindowStateMask + }; + + void impl_putPropertiesFromStruct( const WindowStateInfo& rWinStateInfo, Reference< XPropertySet > const & xPropSet ); + Any impl_insertCacheAndReturnSequence( const OUString& rResourceURL, Reference< XNameAccess > const & rNameAccess ); + WindowStateInfo& impl_insertCacheAndReturnWinState( const OUString& rResourceURL, Reference< XNameAccess > const & rNameAccess ); + Any impl_getSequenceFromStruct( const WindowStateInfo& rWinStateInfo ); + void impl_fillStructFromSequence( WindowStateInfo& rWinStateInfo, const Sequence< PropertyValue >& rSeq ); + Any impl_getWindowStateFromResourceURL( const OUString& rResourceURL ); + void impl_initializeConfigAccess(); + + private: + typedef std::unordered_map< OUString, + WindowStateInfo > ResourceURLToInfoCache; + + osl::Mutex m_aMutex; + OUString m_aConfigWindowAccess; + Reference< XMultiServiceFactory > m_xConfigProvider; + Reference< XNameAccess > m_xConfigAccess; + Reference< XContainerListener > m_xConfigListener; + ResourceURLToInfoCache m_aResourceURLToInfoCache; + bool m_bConfigAccessInitialized : 1, + m_bModified : 1; + std::vector< OUString > m_aPropArray; +}; + +ConfigurationAccess_WindowState::ConfigurationAccess_WindowState( const OUString& aModuleName, const Reference< XComponentContext >& rxContext ) : + // Create configuration hierarchical access name + m_aConfigWindowAccess( "/org.openoffice.Office.UI." + aModuleName + "/UIElements/States"), + m_xConfigProvider(theDefaultProvider::get( rxContext )), + m_bConfigAccessInitialized( false ), + m_bModified( false ) +{ + // Initialize access array with property names. + sal_Int32 n = 0; + while ( CONFIGURATION_PROPERTIES[n] ) + { + m_aPropArray.push_back( OUString::createFromAscii( CONFIGURATION_PROPERTIES[n] )); + ++n; + } +} + +ConfigurationAccess_WindowState::~ConfigurationAccess_WindowState() +{ + // SAFE + osl::MutexGuard g(m_aMutex); + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + xContainer->removeContainerListener(m_xConfigListener); +} + +// XNameAccess +Any SAL_CALL ConfigurationAccess_WindowState::getByName( const OUString& rResourceURL ) +{ + // SAFE + osl::MutexGuard g(m_aMutex); + + ResourceURLToInfoCache::const_iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL ); + if ( pIter != m_aResourceURLToInfoCache.end() ) + return impl_getSequenceFromStruct( pIter->second ); + else + { + Any a( impl_getWindowStateFromResourceURL( rResourceURL ) ); + if ( a == Any() ) + throw NoSuchElementException(); + return a; + } +} + +Sequence< OUString > SAL_CALL ConfigurationAccess_WindowState::getElementNames() +{ + // SAFE + osl::MutexGuard g(m_aMutex); + + if ( !m_bConfigAccessInitialized ) + { + impl_initializeConfigAccess(); + m_bConfigAccessInitialized = true; + } + + if ( m_xConfigAccess.is() ) + return m_xConfigAccess->getElementNames(); + else + return Sequence< OUString > (); +} + +sal_Bool SAL_CALL ConfigurationAccess_WindowState::hasByName( const OUString& rResourceURL ) +{ + // SAFE + osl::MutexGuard g(m_aMutex); + + ResourceURLToInfoCache::const_iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL ); + if ( pIter != m_aResourceURLToInfoCache.end() ) + return true; + else + { + Any a( impl_getWindowStateFromResourceURL( rResourceURL ) ); + if ( a == Any() ) + return false; + else + return true; + } +} + +// XElementAccess +Type SAL_CALL ConfigurationAccess_WindowState::getElementType() +{ + return cppu::UnoType<Sequence< PropertyValue >>::get(); +} + +sal_Bool SAL_CALL ConfigurationAccess_WindowState::hasElements() +{ + // SAFE + osl::MutexGuard g(m_aMutex); + + if ( !m_bConfigAccessInitialized ) + { + impl_initializeConfigAccess(); + m_bConfigAccessInitialized = true; + } + + if ( m_xConfigAccess.is() ) + return m_xConfigAccess->hasElements(); + else + return false; +} + +// XNameContainer +void SAL_CALL ConfigurationAccess_WindowState::removeByName( const OUString& rResourceURL ) +{ + // SAFE + osl::ClearableMutexGuard g(m_aMutex); + + ResourceURLToInfoCache::iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL ); + if ( pIter != m_aResourceURLToInfoCache.end() ) + m_aResourceURLToInfoCache.erase( pIter ); + + if ( !m_bConfigAccessInitialized ) + { + impl_initializeConfigAccess(); + m_bConfigAccessInitialized = true; + } + + try + { + // Remove must be write-through => remove element from configuration + Reference< XNameContainer > xNameContainer( m_xConfigAccess, UNO_QUERY ); + if ( xNameContainer.is() ) + { + g.clear(); + + xNameContainer->removeByName( rResourceURL ); + Reference< XChangesBatch > xFlush( m_xConfigAccess, UNO_QUERY ); + if ( xFlush.is() ) + xFlush->commitChanges(); + } + } + catch ( const css::lang::WrappedTargetException& ) + { + } +} + +void SAL_CALL ConfigurationAccess_WindowState::insertByName( const OUString& rResourceURL, const css::uno::Any& aPropertySet ) +{ + // SAFE + osl::ClearableMutexGuard g(m_aMutex); + + Sequence< PropertyValue > aPropSet; + if ( !(aPropertySet >>= aPropSet) ) + throw IllegalArgumentException(); + + ResourceURLToInfoCache::const_iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL ); + if ( pIter != m_aResourceURLToInfoCache.end() ) + throw ElementExistException(); + + if ( !m_bConfigAccessInitialized ) + { + impl_initializeConfigAccess(); + m_bConfigAccessInitialized = true; + } + + // Try to ask our configuration access + if ( !m_xConfigAccess.is() ) + return; + + if ( m_xConfigAccess->hasByName( rResourceURL ) ) + throw ElementExistException(); + + WindowStateInfo aWinStateInfo; + impl_fillStructFromSequence( aWinStateInfo, aPropSet ); + m_aResourceURLToInfoCache.emplace( rResourceURL, aWinStateInfo ); + + // insert must be write-through => insert element into configuration + Reference< XNameContainer > xNameContainer( m_xConfigAccess, UNO_QUERY ); + if ( !xNameContainer.is() ) + return; + + Reference< XSingleServiceFactory > xFactory( m_xConfigAccess, UNO_QUERY ); + g.clear(); + + try + { + Reference< XPropertySet > xPropSet( xFactory->createInstance(), UNO_QUERY ); + if ( xPropSet.is() ) + { + Any a; + impl_putPropertiesFromStruct( aWinStateInfo, xPropSet ); + a <<= xPropSet; + xNameContainer->insertByName( rResourceURL, a ); + Reference< XChangesBatch > xFlush( xFactory, UNO_QUERY ); + if ( xFlush.is() ) + xFlush->commitChanges(); + } + } + catch ( const Exception& ) + { + } +} + +// XNameReplace +void SAL_CALL ConfigurationAccess_WindowState::replaceByName( const OUString& rResourceURL, const css::uno::Any& aPropertySet ) +{ + // SAFE + osl::ClearableMutexGuard g(m_aMutex); + + Sequence< PropertyValue > aPropSet; + if ( !(aPropertySet >>= aPropSet) ) + throw IllegalArgumentException(); + + ResourceURLToInfoCache::iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL ); + if ( pIter != m_aResourceURLToInfoCache.end() ) + { + WindowStateInfo& rWinStateInfo = pIter->second; + impl_fillStructFromSequence( rWinStateInfo, aPropSet ); + m_bModified = true; + } + else + { + if ( !m_bConfigAccessInitialized ) + { + impl_initializeConfigAccess(); + m_bConfigAccessInitialized = true; + } + + // Try to ask our configuration access + Reference< XNameAccess > xNameAccess; + Any a( m_xConfigAccess->getByName( rResourceURL )); + + if ( !(a >>= xNameAccess) ) + throw NoSuchElementException(); + + WindowStateInfo& rWinStateInfo( impl_insertCacheAndReturnWinState( rResourceURL, xNameAccess )); + impl_fillStructFromSequence( rWinStateInfo, aPropSet ); + m_bModified = true; + pIter = m_aResourceURLToInfoCache.find( rResourceURL ); + + } + + if ( !(m_bModified && pIter != m_aResourceURLToInfoCache.end()) ) + return; + + Reference< XNameContainer > xNameContainer( m_xConfigAccess, UNO_QUERY ); + if ( !xNameContainer.is() ) + return; + + WindowStateInfo aWinStateInfo( pIter->second ); + OUString aResourceURL( pIter->first ); + m_bModified = false; + g.clear(); + + try + { + Reference< XPropertySet > xPropSet; + if ( xNameContainer->getByName( aResourceURL ) >>= xPropSet ) + { + impl_putPropertiesFromStruct( aWinStateInfo, xPropSet ); + + Reference< XChangesBatch > xFlush( m_xConfigAccess, UNO_QUERY ); + if ( xFlush.is() ) + xFlush->commitChanges(); + } + } + catch ( const Exception& ) + { + } + +} + +// container.XContainerListener +void SAL_CALL ConfigurationAccess_WindowState::elementInserted( const ContainerEvent& ) +{ + // do nothing - next time someone wants to retrieve this node we will find it in the configuration +} + +void SAL_CALL ConfigurationAccess_WindowState::elementRemoved ( const ContainerEvent& ) +{ +} + +void SAL_CALL ConfigurationAccess_WindowState::elementReplaced( const ContainerEvent& ) +{ +} + +// lang.XEventListener +void SAL_CALL ConfigurationAccess_WindowState::disposing( const EventObject& aEvent ) +{ + // SAFE + // remove our reference to the config access + osl::MutexGuard g(m_aMutex); + + Reference< XInterface > xIfac1( aEvent.Source, UNO_QUERY ); + Reference< XInterface > xIfac2( m_xConfigAccess, UNO_QUERY ); + if ( xIfac1 == xIfac2 ) + m_xConfigAccess.clear(); +} + +// private helper methods +Any ConfigurationAccess_WindowState::impl_getSequenceFromStruct( const WindowStateInfo& rWinStateInfo ) +{ + sal_Int32 i( 0 ); + sal_Int32 nCount( m_aPropArray.size() ); + std::vector< PropertyValue > aPropVec; + + for ( i = 0; i < nCount; i++ ) + { + if ( rWinStateInfo.nMask & ( 1 << i )) + { + // put value into the return sequence + PropertyValue pv; + pv.Name = m_aPropArray[i]; + + switch ( i ) + { + case PROPERTY_LOCKED: + pv.Value <<= rWinStateInfo.bLocked; break; + case PROPERTY_DOCKED: + pv.Value <<= rWinStateInfo.bDocked; break; + case PROPERTY_VISIBLE: + pv.Value <<= rWinStateInfo.bVisible; break; + case PROPERTY_CONTEXT: + pv.Value <<= rWinStateInfo.bContext; break; + case PROPERTY_HIDEFROMMENU: + pv.Value <<= rWinStateInfo.bHideFromMenu; break; + case PROPERTY_NOCLOSE: + pv.Value <<= rWinStateInfo.bNoClose; break; + case PROPERTY_SOFTCLOSE: + pv.Value <<= rWinStateInfo.bSoftClose; break; + case PROPERTY_CONTEXTACTIVE: + pv.Value <<= rWinStateInfo.bContextActive; break; + case PROPERTY_DOCKINGAREA: + pv.Value <<= rWinStateInfo.aDockingArea; break; + case PROPERTY_POS: + pv.Value <<= rWinStateInfo.aPos; break; + case PROPERTY_SIZE: + pv.Value <<= rWinStateInfo.aSize; break; + case PROPERTY_UINAME: + pv.Value <<= rWinStateInfo.aUIName; break; + case PROPERTY_INTERNALSTATE: + pv.Value <<= sal_Int32( rWinStateInfo.nInternalState ); break; + case PROPERTY_STYLE: + pv.Value <<= sal_Int16( rWinStateInfo.nStyle ); break; + case PROPERTY_DOCKPOS: + pv.Value <<= rWinStateInfo.aDockPos; break; + case PROPERTY_DOCKSIZE: + pv.Value <<= rWinStateInfo.aDockSize; break; + default: + assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" ); + } + aPropVec.push_back(pv); + } + } + + return makeAny( comphelper::containerToSequence(aPropVec) ); +} + +Any ConfigurationAccess_WindowState::impl_insertCacheAndReturnSequence( const OUString& rResourceURL, Reference< XNameAccess > const & xNameAccess ) +{ + sal_Int32 nMask( 0 ); + sal_Int32 nCount( m_aPropArray.size() ); + sal_Int32 i( 0 ); + std::vector< PropertyValue > aPropVec; + WindowStateInfo aWindowStateInfo; + + for ( i = 0; i < nCount; i++ ) + { + try + { + bool bAddToSeq( false ); + Any a( xNameAccess->getByName( m_aPropArray[i] ) ); + switch ( i ) + { + case PROPERTY_LOCKED: + case PROPERTY_DOCKED: + case PROPERTY_VISIBLE: + case PROPERTY_CONTEXT: + case PROPERTY_HIDEFROMMENU: + case PROPERTY_NOCLOSE: + case PROPERTY_SOFTCLOSE: + case PROPERTY_CONTEXTACTIVE: + { + bool bValue; + if ( a >>= bValue ) + { + sal_Int32 nValue( 1 << i ); + nMask |= nValue; + bAddToSeq = true; + switch ( i ) + { + case PROPERTY_LOCKED: + aWindowStateInfo.bLocked = bValue; break; + case PROPERTY_DOCKED: + aWindowStateInfo.bDocked = bValue; break; + case PROPERTY_VISIBLE: + aWindowStateInfo.bVisible = bValue; break; + case PROPERTY_CONTEXT: + aWindowStateInfo.bContext = bValue; break; + case PROPERTY_HIDEFROMMENU: + aWindowStateInfo.bHideFromMenu = bValue; break; + case PROPERTY_NOCLOSE: + aWindowStateInfo.bNoClose = bValue; break; + case PROPERTY_SOFTCLOSE: + aWindowStateInfo.bSoftClose = bValue; break; + case PROPERTY_CONTEXTACTIVE: + aWindowStateInfo.bContextActive = bValue; break; + } + } + } + break; + + case PROPERTY_DOCKINGAREA: + { + sal_Int32 nDockingArea = 0; + if ( a >>= nDockingArea ) + { + if (( nDockingArea >= 0 ) && + ( nDockingArea <= sal_Int32( DockingArea_DOCKINGAREA_RIGHT ))) + { + aWindowStateInfo.aDockingArea = static_cast<DockingArea>(nDockingArea); + nMask |= WINDOWSTATE_MASK_DOCKINGAREA; + a <<= aWindowStateInfo.aDockingArea; + bAddToSeq = true; + } + } + } + break; + + case PROPERTY_POS: + case PROPERTY_DOCKPOS: + { + OUString aString; + if ( a >>= aString ) + { + sal_Int32 nToken( 0 ); + OUString aXStr = aString.getToken( 0, ',', nToken ); + if ( nToken > 0 ) + { + css::awt::Point aPos; + aPos.X = aXStr.toInt32(); + aPos.Y = aString.getToken( 0, ',', nToken ).toInt32(); + + if ( i == PROPERTY_POS ) + { + aWindowStateInfo.aPos = aPos; + nMask |= WINDOWSTATE_MASK_POS; + } + else + { + aWindowStateInfo.aDockPos = aPos; + nMask |= WINDOWSTATE_MASK_DOCKPOS; + } + + a <<= aPos; + bAddToSeq = true; + } + } + } + break; + + case PROPERTY_SIZE: + case PROPERTY_DOCKSIZE: + { + OUString aString; + if ( a >>= aString ) + { + sal_Int32 nToken( 0 ); + OUString aStr = aString.getToken( 0, ',', nToken ); + if ( nToken > 0 ) + { + css::awt::Size aSize; + aSize.Width = aStr.toInt32(); + aSize.Height = aString.getToken( 0, ',', nToken ).toInt32(); + if ( i == PROPERTY_SIZE ) + { + aWindowStateInfo.aSize = aSize; + nMask |= WINDOWSTATE_MASK_SIZE; + } + else + { + aWindowStateInfo.aDockSize = aSize; + nMask |= WINDOWSTATE_MASK_DOCKSIZE; + } + + a <<= aSize; + bAddToSeq = true; + } + } + } + break; + + case PROPERTY_UINAME: + { + OUString aValue; + if ( a >>= aValue ) + { + nMask |= WINDOWSTATE_MASK_UINAME; + aWindowStateInfo.aUIName = aValue; + bAddToSeq = true; + } + } + break; + + case PROPERTY_INTERNALSTATE: + { + sal_uInt32 nValue = 0; + if ( a >>= nValue ) + { + nMask |= WINDOWSTATE_MASK_INTERNALSTATE; + aWindowStateInfo.nInternalState = nValue; + bAddToSeq = true; + } + } + break; + + case PROPERTY_STYLE: + { + sal_Int32 nValue = 0; + if ( a >>= nValue ) + { + nMask |= WINDOWSTATE_MASK_STYLE; + aWindowStateInfo.nStyle = sal_uInt16( nValue ); + bAddToSeq = true; + } + } + break; + + default: + assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" ); + } + + if ( bAddToSeq ) + { + // put value into the return sequence + PropertyValue pv; + pv.Name = m_aPropArray[i]; + pv.Value = a; + aPropVec.push_back(pv); + } + } + catch( const css::container::NoSuchElementException& ) + { + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + + aWindowStateInfo.nMask = nMask; + m_aResourceURLToInfoCache.emplace( rResourceURL, aWindowStateInfo ); + return makeAny( comphelper::containerToSequence(aPropVec) ); +} + +ConfigurationAccess_WindowState::WindowStateInfo& ConfigurationAccess_WindowState::impl_insertCacheAndReturnWinState( const OUString& rResourceURL, Reference< XNameAccess > const & rNameAccess ) +{ + sal_Int32 nMask( 0 ); + sal_Int32 nCount( m_aPropArray.size() ); + sal_Int32 i( 0 ); + WindowStateInfo aWindowStateInfo; + + for ( i = 0; i < nCount; i++ ) + { + try + { + Any a( rNameAccess->getByName( m_aPropArray[i] ) ); + switch ( i ) + { + case PROPERTY_LOCKED: + case PROPERTY_DOCKED: + case PROPERTY_VISIBLE: + case PROPERTY_CONTEXT: + case PROPERTY_HIDEFROMMENU: + case PROPERTY_NOCLOSE: + case PROPERTY_SOFTCLOSE: + case PROPERTY_CONTEXTACTIVE: + { + bool bValue; + if ( a >>= bValue ) + { + sal_Int32 nValue( 1 << i ); + nMask |= nValue; + switch ( i ) + { + case PROPERTY_LOCKED: + aWindowStateInfo.bLocked = bValue; break; + case PROPERTY_DOCKED: + aWindowStateInfo.bDocked = bValue; break; + case PROPERTY_VISIBLE: + aWindowStateInfo.bVisible = bValue; break; + case PROPERTY_CONTEXT: + aWindowStateInfo.bContext = bValue; break; + case PROPERTY_HIDEFROMMENU: + aWindowStateInfo.bHideFromMenu = bValue; break; + case PROPERTY_NOCLOSE: + aWindowStateInfo.bNoClose = bValue; break; + case PROPERTY_SOFTCLOSE: + aWindowStateInfo.bNoClose = bValue; break; + case PROPERTY_CONTEXTACTIVE: + aWindowStateInfo.bContextActive = bValue; break; + default: + SAL_WARN( "fwk.uiconfiguration", "Unknown boolean property in WindowState found!" ); + } + } + } + break; + + case PROPERTY_DOCKINGAREA: + { + sal_Int32 nDockingArea = 0; + if ( a >>= nDockingArea ) + { + if (( nDockingArea >= 0 ) && + ( nDockingArea <= sal_Int32( DockingArea_DOCKINGAREA_RIGHT ))) + { + aWindowStateInfo.aDockingArea = static_cast<DockingArea>(nDockingArea); + nMask |= WINDOWSTATE_MASK_DOCKINGAREA; + } + } + } + break; + + case PROPERTY_POS: + case PROPERTY_DOCKPOS: + { + OUString aString; + if ( a >>= aString ) + { + sal_Int32 nToken( 0 ); + OUString aXStr = aString.getToken( 0, ',', nToken ); + if ( nToken > 0 ) + { + css::awt::Point aPos; + aPos.X = aXStr.toInt32(); + aPos.Y = aString.getToken( 0, ',', nToken ).toInt32(); + + if ( i == PROPERTY_POS ) + { + aWindowStateInfo.aPos = aPos; + nMask |= WINDOWSTATE_MASK_POS; + } + else + { + aWindowStateInfo.aDockPos = aPos; + nMask |= WINDOWSTATE_MASK_DOCKPOS; + } + } + } + } + break; + + case PROPERTY_SIZE: + case PROPERTY_DOCKSIZE: + { + OUString aString; + if ( a >>= aString ) + { + sal_Int32 nToken( 0 ); + OUString aStr = aString.getToken( 0, ',', nToken ); + if ( nToken > 0 ) + { + css::awt::Size aSize; + aSize.Width = aStr.toInt32(); + aSize.Height = aString.getToken( 0, ',', nToken ).toInt32(); + if ( i == PROPERTY_SIZE ) + { + aWindowStateInfo.aSize = aSize; + nMask |= WINDOWSTATE_MASK_SIZE; + } + else + { + aWindowStateInfo.aDockSize = aSize; + nMask |= WINDOWSTATE_MASK_DOCKSIZE; + } + } + } + } + break; + + case PROPERTY_UINAME: + { + OUString aValue; + if ( a >>= aValue ) + { + nMask |= WINDOWSTATE_MASK_UINAME; + aWindowStateInfo.aUIName = aValue; + } + } + break; + + case PROPERTY_INTERNALSTATE: + { + sal_Int32 nValue = 0; + if ( a >>= nValue ) + { + nMask |= WINDOWSTATE_MASK_INTERNALSTATE; + aWindowStateInfo.nInternalState = sal_uInt32( nValue ); + } + } + break; + + case PROPERTY_STYLE: + { + sal_Int32 nValue = 0; + if ( a >>= nValue ) + { + nMask |= WINDOWSTATE_MASK_STYLE; + aWindowStateInfo.nStyle = sal_uInt16( nValue ); + } + } + break; + + default: + assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" ); + } + } + catch( const css::container::NoSuchElementException& ) + { + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + + aWindowStateInfo.nMask = nMask; + ResourceURLToInfoCache::iterator pIter = m_aResourceURLToInfoCache.emplace( rResourceURL, aWindowStateInfo ).first; + return pIter->second; +} + +Any ConfigurationAccess_WindowState::impl_getWindowStateFromResourceURL( const OUString& rResourceURL ) +{ + if ( !m_bConfigAccessInitialized ) + { + impl_initializeConfigAccess(); + m_bConfigAccessInitialized = true; + } + + try + { + // Try to ask our configuration access + if ( m_xConfigAccess.is() && m_xConfigAccess->hasByName( rResourceURL ) ) + { + + Reference< XNameAccess > xNameAccess( m_xConfigAccess->getByName( rResourceURL ), UNO_QUERY ); + if ( xNameAccess.is() ) + return impl_insertCacheAndReturnSequence( rResourceURL, xNameAccess ); + } + } + catch( const css::container::NoSuchElementException& ) + { + } + catch ( const css::lang::WrappedTargetException& ) + { + } + + return Any(); +} + +void ConfigurationAccess_WindowState::impl_fillStructFromSequence( WindowStateInfo& rWinStateInfo, const Sequence< PropertyValue >& rSeq ) +{ + sal_Int32 nCompareCount( m_aPropArray.size() ); + sal_Int32 nCount( rSeq.getLength() ); + sal_Int32 i( 0 ); + + for ( i = 0; i < nCount; i++ ) + { + for ( sal_Int32 j = 0; j < nCompareCount; j++ ) + { + if ( rSeq[i].Name == m_aPropArray[j] ) + { + switch ( j ) + { + case PROPERTY_LOCKED: + case PROPERTY_DOCKED: + case PROPERTY_VISIBLE: + case PROPERTY_CONTEXT: + case PROPERTY_HIDEFROMMENU: + case PROPERTY_NOCLOSE: + case PROPERTY_SOFTCLOSE: + case PROPERTY_CONTEXTACTIVE: + { + bool bValue; + if ( rSeq[i].Value >>= bValue ) + { + sal_Int32 nValue( 1 << j ); + rWinStateInfo.nMask |= nValue; + switch ( j ) + { + case PROPERTY_LOCKED: + rWinStateInfo.bLocked = bValue; + break; + case PROPERTY_DOCKED: + rWinStateInfo.bDocked = bValue; + break; + case PROPERTY_VISIBLE: + rWinStateInfo.bVisible = bValue; + break; + case PROPERTY_CONTEXT: + rWinStateInfo.bContext = bValue; + break; + case PROPERTY_HIDEFROMMENU: + rWinStateInfo.bHideFromMenu = bValue; + break; + case PROPERTY_NOCLOSE: + rWinStateInfo.bNoClose = bValue; + break; + case PROPERTY_SOFTCLOSE: + rWinStateInfo.bSoftClose = bValue; + break; + case PROPERTY_CONTEXTACTIVE: + rWinStateInfo.bContextActive = bValue; + break; + } + } + } + break; + + case PROPERTY_DOCKINGAREA: + { + css::ui::DockingArea eDockingArea; + if ( rSeq[i].Value >>= eDockingArea ) + { + rWinStateInfo.aDockingArea = eDockingArea; + rWinStateInfo.nMask |= WINDOWSTATE_MASK_DOCKINGAREA; + } + } + break; + + case PROPERTY_POS: + case PROPERTY_DOCKPOS: + { + css::awt::Point aPoint; + if ( rSeq[i].Value >>= aPoint ) + { + if ( j == PROPERTY_POS ) + { + rWinStateInfo.aPos = aPoint; + rWinStateInfo.nMask |= WINDOWSTATE_MASK_POS; + } + else + { + rWinStateInfo.aDockPos = aPoint; + rWinStateInfo.nMask |= WINDOWSTATE_MASK_DOCKPOS; + } + } + } + break; + + case PROPERTY_SIZE: + case PROPERTY_DOCKSIZE: + { + css::awt::Size aSize; + if ( rSeq[i].Value >>= aSize ) + { + if ( j == PROPERTY_SIZE ) + { + rWinStateInfo.aSize = aSize; + rWinStateInfo.nMask |= WINDOWSTATE_MASK_SIZE; + } + else + { + rWinStateInfo.aDockSize = aSize; + rWinStateInfo.nMask |= WINDOWSTATE_MASK_DOCKSIZE; + } + } + } + break; + + case PROPERTY_UINAME: + { + OUString aValue; + if ( rSeq[i].Value >>= aValue ) + { + rWinStateInfo.aUIName = aValue; + rWinStateInfo.nMask |= WINDOWSTATE_MASK_UINAME; + } + } + break; + + case PROPERTY_INTERNALSTATE: + { + sal_Int32 nValue = 0; + if ( rSeq[i].Value >>= nValue ) + { + rWinStateInfo.nInternalState = sal_uInt32( nValue ); + rWinStateInfo.nMask |= WINDOWSTATE_MASK_INTERNALSTATE; + } + } + break; + + case PROPERTY_STYLE: + { + sal_Int32 nValue = 0; + if ( rSeq[i].Value >>= nValue ) + { + rWinStateInfo.nStyle = sal_uInt16( nValue ); + rWinStateInfo.nMask |= WINDOWSTATE_MASK_STYLE; + } + } + break; + + default: + assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" ); + } + + break; + } + } + } +} + +void ConfigurationAccess_WindowState::impl_putPropertiesFromStruct( const WindowStateInfo& rWinStateInfo, Reference< XPropertySet > const & xPropSet ) +{ + sal_Int32 i( 0 ); + sal_Int32 nCount( m_aPropArray.size() ); + OUString aDelim( "," ); + + for ( i = 0; i < nCount; i++ ) + { + if ( rWinStateInfo.nMask & ( 1 << i )) + { + try + { + // put values into the property set + switch ( i ) + { + case PROPERTY_LOCKED: + xPropSet->setPropertyValue( m_aPropArray[i], makeAny( rWinStateInfo.bLocked ) ); break; + case PROPERTY_DOCKED: + xPropSet->setPropertyValue( m_aPropArray[i], makeAny( rWinStateInfo.bDocked ) ); break; + case PROPERTY_VISIBLE: + xPropSet->setPropertyValue( m_aPropArray[i], makeAny( rWinStateInfo.bVisible ) ); break; + case PROPERTY_CONTEXT: + xPropSet->setPropertyValue( m_aPropArray[i], makeAny( rWinStateInfo.bContext ) ); break; + case PROPERTY_HIDEFROMMENU: + xPropSet->setPropertyValue( m_aPropArray[i], makeAny( rWinStateInfo.bHideFromMenu ) ); break; + case PROPERTY_NOCLOSE: + xPropSet->setPropertyValue( m_aPropArray[i], makeAny( rWinStateInfo.bNoClose ) ); break; + case PROPERTY_SOFTCLOSE: + xPropSet->setPropertyValue( m_aPropArray[i], makeAny( rWinStateInfo.bSoftClose ) ); break; + case PROPERTY_CONTEXTACTIVE: + xPropSet->setPropertyValue( m_aPropArray[i], makeAny( rWinStateInfo.bContextActive ) ); break; + case PROPERTY_DOCKINGAREA: + xPropSet->setPropertyValue( m_aPropArray[i], makeAny( sal_Int16( rWinStateInfo.aDockingArea ) ) ); break; + case PROPERTY_POS: + case PROPERTY_DOCKPOS: + { + OUString aPosStr; + if ( i == PROPERTY_POS ) + aPosStr = OUString::number( rWinStateInfo.aPos.X ); + else + aPosStr = OUString::number( rWinStateInfo.aDockPos.X ); + aPosStr += aDelim; + if ( i == PROPERTY_POS ) + aPosStr += OUString::number( rWinStateInfo.aPos.Y ); + else + aPosStr += OUString::number( rWinStateInfo.aDockPos.Y ); + xPropSet->setPropertyValue( m_aPropArray[i], makeAny( aPosStr ) ); + break; + } + case PROPERTY_SIZE: + case PROPERTY_DOCKSIZE: + { + OUString aSizeStr; + if ( i == PROPERTY_SIZE ) + aSizeStr = OUString::number( rWinStateInfo.aSize.Width ); + else + aSizeStr = OUString::number( rWinStateInfo.aDockSize.Width ); + aSizeStr += aDelim; + if ( i == PROPERTY_SIZE ) + aSizeStr += OUString::number( rWinStateInfo.aSize.Height ); + else + aSizeStr += OUString::number( rWinStateInfo.aDockSize.Height ); + xPropSet->setPropertyValue( m_aPropArray[i], makeAny( aSizeStr ) ); + break; + } + case PROPERTY_UINAME: + xPropSet->setPropertyValue( m_aPropArray[i], makeAny( rWinStateInfo.aUIName ) ); break; + case PROPERTY_INTERNALSTATE: + xPropSet->setPropertyValue( m_aPropArray[i], makeAny( sal_Int32( rWinStateInfo.nInternalState )) ); break; + case PROPERTY_STYLE: + xPropSet->setPropertyValue( m_aPropArray[i], makeAny( sal_Int32( rWinStateInfo.nStyle )) ); break; + default: + assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" ); + } + } + catch( const Exception& ) + { + } + } + } +} + +void ConfigurationAccess_WindowState::impl_initializeConfigAccess() +{ + try + { + Sequence<Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", Any(m_aConfigWindowAccess)} + })); + m_xConfigAccess.set( m_xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationUpdateAccess", aArgs ), UNO_QUERY ); + if ( m_xConfigAccess.is() ) + { + // Add as container listener + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + { + m_xConfigListener = new WeakContainerListener(this); + xContainer->addContainerListener(m_xConfigListener); + } + } + } + catch ( const WrappedTargetException& ) + { + } + catch ( const Exception& ) + { + } +} + +typedef ::cppu::WeakComponentImplHelper< css::container::XNameAccess, + css::lang::XServiceInfo> WindowStateConfiguration_BASE; + +class WindowStateConfiguration : private cppu::BaseMutex, + public WindowStateConfiguration_BASE +{ +public: + explicit WindowStateConfiguration( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~WindowStateConfiguration() override; + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.WindowStateConfiguration"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.WindowStateConfiguration"}; + } + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + typedef std::unordered_map< OUString, + OUString > ModuleToWindowStateFileMap; + + typedef std::unordered_map< OUString, + css::uno::Reference< css::container::XNameAccess > > ModuleToWindowStateConfigHashMap; + +private: + css::uno::Reference< css::uno::XComponentContext> m_xContext; + ModuleToWindowStateFileMap m_aModuleToFileHashMap; + ModuleToWindowStateConfigHashMap m_aModuleToWindowStateHashMap; +}; + +WindowStateConfiguration::WindowStateConfiguration( const Reference< XComponentContext >& rxContext ) : + WindowStateConfiguration_BASE(m_aMutex), + m_xContext( rxContext ) +{ + css::uno::Reference< css::frame::XModuleManager2 > xModuleManager = + ModuleManager::create( m_xContext ); + Reference< XNameAccess > xEmptyNameAccess; + Sequence< OUString > aElementNames; + try + { + aElementNames = xModuleManager->getElementNames(); + } + catch (const css::uno::RuntimeException &) + { + } + Sequence< PropertyValue > aSeq; + + for ( OUString const & aModuleIdentifier : std::as_const(aElementNames) ) + { + if ( xModuleManager->getByName( aModuleIdentifier ) >>= aSeq ) + { + OUString aWindowStateFileStr; + for ( PropertyValue const & rProp : std::as_const(aSeq) ) + { + if ( rProp.Name == "ooSetupFactoryWindowStateConfigRef" ) + { + rProp.Value >>= aWindowStateFileStr; + break; + } + } + + if ( !aWindowStateFileStr.isEmpty() ) + { + // Create first mapping ModuleIdentifier ==> Window state configuration file + m_aModuleToFileHashMap.emplace( aModuleIdentifier, aWindowStateFileStr ); + + // Create second mapping Command File ==> Window state configuration instance + ModuleToWindowStateConfigHashMap::iterator pIter = m_aModuleToWindowStateHashMap.find( aWindowStateFileStr ); + if ( pIter == m_aModuleToWindowStateHashMap.end() ) + m_aModuleToWindowStateHashMap.emplace( aWindowStateFileStr, xEmptyNameAccess ); + } + } + } +} + +WindowStateConfiguration::~WindowStateConfiguration() +{ + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + m_aModuleToFileHashMap.clear(); + m_aModuleToWindowStateHashMap.clear(); +} + +Any SAL_CALL WindowStateConfiguration::getByName( const OUString& aModuleIdentifier ) +{ + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + ModuleToWindowStateFileMap::const_iterator pIter = m_aModuleToFileHashMap.find( aModuleIdentifier ); + if ( pIter != m_aModuleToFileHashMap.end() ) + { + Any a; + OUString aWindowStateConfigFile( pIter->second ); + + ModuleToWindowStateConfigHashMap::iterator pModuleIter = m_aModuleToWindowStateHashMap.find( aWindowStateConfigFile ); + if ( pModuleIter != m_aModuleToWindowStateHashMap.end() ) + { + if ( pModuleIter->second.is() ) + a <<= pModuleIter->second; + else + { + Reference< XNameAccess > xResourceURLWindowState; + ConfigurationAccess_WindowState* pModuleWindowState = new ConfigurationAccess_WindowState( aWindowStateConfigFile, m_xContext ); + xResourceURLWindowState.set( static_cast< cppu::OWeakObject* >( pModuleWindowState ),UNO_QUERY ); + pModuleIter->second = xResourceURLWindowState; + a <<= xResourceURLWindowState; + } + + return a; + } + } + + throw NoSuchElementException(); +} + +Sequence< OUString > SAL_CALL WindowStateConfiguration::getElementNames() +{ + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + return comphelper::mapKeysToSequence( m_aModuleToFileHashMap ); +} + +sal_Bool SAL_CALL WindowStateConfiguration::hasByName( const OUString& aName ) +{ + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + ModuleToWindowStateFileMap::const_iterator pIter = m_aModuleToFileHashMap.find( aName ); + return ( pIter != m_aModuleToFileHashMap.end() ); +} + +// XElementAccess +Type SAL_CALL WindowStateConfiguration::getElementType() +{ + return cppu::UnoType<XNameAccess>::get(); +} + +sal_Bool SAL_CALL WindowStateConfiguration::hasElements() +{ + // We always have at least one module. So it is valid to return true! + return true; +} + +struct Instance { + explicit Instance( + css::uno::Reference<css::uno::XComponentContext> const & context): + instance(static_cast<cppu::OWeakObject *>( + new WindowStateConfiguration(context))) + { + } + + css::uno::Reference<css::uno::XInterface> instance; +}; + +struct Singleton: + public rtl::StaticWithArg< + Instance, css::uno::Reference<css::uno::XComponentContext>, Singleton> +{}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_WindowStateConfiguration_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(static_cast<cppu::OWeakObject *>( + Singleton::get(context).instance.get())); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |