/* -*- 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/config.h>
#include <map>
#include <svtools/extcolorcfg.hxx>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/lang/Locale.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <tools/color.hxx>
#include <unotools/configitem.hxx>
#include <unotools/configpaths.hxx>
#include <com/sun/star/uno/Sequence.h>
#include <svl/poolitem.hxx>
#include <svl/hint.hxx>
#include <osl/mutex.hxx>
#include <sal/log.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/event.hxx>
#include <rtl/instance.hxx>
#include <rtl/strbuf.hxx>
using namespace utl;
using namespace com::sun::star;
namespace svtools
{
static sal_Int32 nExtendedColorRefCount_Impl = 0;
namespace
{
struct ColorMutex_Impl
: public rtl::Static< ::osl::Mutex, ColorMutex_Impl > {};
}
ExtendedColorConfig_Impl* ExtendedColorConfig::m_pImpl = nullptr;
class ExtendedColorConfig_Impl : public utl::ConfigItem, public SfxBroadcaster
{
typedef std::map<OUString, OUString> TDisplayNames;
typedef std::map<OUString, ExtendedColorConfigValue> TConfigValues;
typedef ::std::vector<TConfigValues::iterator> TMapPos;
typedef ::std::pair< TConfigValues, TMapPos > TComponentMapping;
typedef std::map<OUString, TComponentMapping> TComponents;
TComponents m_aConfigValues;
TDisplayNames m_aComponentDisplayNames;
::std::vector<TComponents::iterator> m_aConfigValuesPos;
OUString m_sLoadedScheme;
bool m_bIsBroadcastEnabled;
static bool m_bLockBroadcast;
static bool m_bBroadcastWhenUnlocked;
uno::Sequence< OUString> GetPropertyNames(const OUString& rScheme);
void FillComponentColors(uno::Sequence < OUString >& _rComponents,const TDisplayNames& _rDisplayNames);
virtual void ImplCommit() override;
public:
explicit ExtendedColorConfig_Impl();
virtual ~ExtendedColorConfig_Impl() override;
void Load(const OUString& rScheme);
void CommitCurrentSchemeName();
//changes the name of the current scheme but doesn't load it!
void SetCurrentSchemeName(const OUString& rSchemeName) {m_sLoadedScheme = rSchemeName;}
bool ExistsScheme(const OUString& _sSchemeName);
virtual void Notify( const uno::Sequence<OUString>& aPropertyNames) override;
sal_Int32 GetComponentCount() const;
OUString GetComponentName(sal_uInt32 _nPos) const;
OUString GetComponentDisplayName(const OUString& _sComponentName) const;
sal_Int32 GetComponentColorCount(const OUString& _sName) const;
ExtendedColorConfigValue GetComponentColorConfigValue(const OUString& _sName,sal_uInt32 _nPos) const;
ExtendedColorConfigValue GetColorConfigValue(const OUString& _sComponentName,const OUString& _sName)
{
TComponents::iterator aFind = m_aConfigValues.find(_sComponentName);
if ( aFind != m_aConfigValues.end() )
{
TConfigValues::iterator aFind2 = aFind->second.first.find(_sName);
if ( aFind2 != aFind->second.first.end() )
return aFind2->second;
}
#if OSL_DEBUG_LEVEL > 0
SAL_WARN( "svtools", "Could find the required config:\n"
"component: " << _sComponentName
<< "\nname: " << _sName );
#endif
return ExtendedColorConfigValue();
}
void SetColorConfigValue(const OUString& _sName,
const ExtendedColorConfigValue& rValue );
void AddScheme(const OUString& rNode);
void RemoveScheme(const OUString& rNode);
using ConfigItem::SetModified;
using ConfigItem::ClearModified;
void SettingsChanged();
static void DisableBroadcast();
static void EnableBroadcast();
static void LockBroadcast();
static void UnlockBroadcast();
DECL_LINK( DataChangedEventListener, VclSimpleEvent&, void );
};
uno::Sequence< OUString> ExtendedColorConfig_Impl::GetPropertyNames(const OUString& rScheme)
{
uno::Sequence< OUString> aNames(GetNodeNames(rScheme));
for(OUString & i : aNames)
{
i = rScheme + "/" + i;
}
return aNames;
}
sal_Int32 ExtendedColorConfig_Impl::GetComponentCount() const
{
return m_aConfigValues.size();
}
sal_Int32 ExtendedColorConfig_Impl::GetComponentColorCount(const OUString& _sName) const
{
sal_Int32 nSize = 0;
TComponents::const_iterator aFind = m_aConfigValues.find(_sName);
if ( aFind != m_aConfigValues.end() )
{
nSize = aFind->second.first.size();
}
return nSize;
}
ExtendedColorConfigValue ExtendedColorConfig_Impl::GetComponentColorConfigValue(const OUString& _sName,sal_uInt32 _nPos) const
{
TComponents::const_iterator aFind = m_aConfigValues.find(_sName);
if ( aFind != m_aConfigValues.end() )
{
if ( _nPos < aFind->second.second.size() )
{
return aFind->second.second[_nPos]->second;
}
}
return ExtendedColorConfigValue();
}
OUString ExtendedColorConfig_Impl::GetComponentDisplayName(const OUString& _sComponentName) const
{
OUString sRet;
TDisplayNames::const_iterator aFind = m_aComponentDisplayNames.find(_sComponentName);
if ( aFind != m_aComponentDisplayNames.end() )
sRet = aFind->second;
return sRet;
}
OUString ExtendedColorConfig_Impl::GetComponentName(sal_uInt32 _nPos) const
{
OUString sRet;
if ( _nPos < m_aConfigValuesPos.size() )
sRet = m_aConfigValuesPos[_nPos]->first;
return sRet;
}
bool ExtendedColorConfig_Impl::m_bLockBroadcast = false;
bool ExtendedColorConfig_Impl::m_bBroadcastWhenUnlocked = false;
ExtendedColorConfig_Impl::ExtendedColorConfig_Impl() :
ConfigItem("Office.ExtendedColorScheme"),
m_bIsBroadcastEnabled(true)
{
//try to register on the root node - if possible
uno::Sequence < OUString > aNames(1);
EnableNotification( aNames );
Load(OUString());
::Application::AddEventListener( LINK(this, ExtendedColorConfig_Impl, DataChangedEventListener) );
}
ExtendedColorConfig_Impl::~ExtendedColorConfig_Impl()
{
::Application::RemoveEventListener( LINK(this, ExtendedColorConfig_Impl, DataChangedEventListener) );
}
void ExtendedColorConfig_Impl::DisableBroadcast()
{
if ( ExtendedColorConfig::m_pImpl )
ExtendedColorConfig::m_pImpl->m_bIsBroadcastEnabled = false;
}
void ExtendedColorConfig_Impl::EnableBroadcast()
{
if ( ExtendedColorConfig::m_pImpl )
ExtendedColorConfig::m_pImpl->m_bIsBroadcastEnabled = true;
}
void lcl_addString(uno::Sequence < OUString >& _rSeq,const OUString& _sAdd)
{
for(OUString & i : _rSeq)
i += _sAdd;
}
void ExtendedColorConfig_Impl::Load(const OUString& rScheme)
{
m_aComponentDisplayNames.clear();
m_aConfigValuesPos.clear();
m_aConfigValues.clear();
// fill display names
TDisplayNames aDisplayNameMap;
uno::Sequence < OUString > aComponentNames = GetPropertyNames("EntryNames");
OUString sDisplayName("/DisplayName");
for(OUString & componentName : aComponentNames)
{
uno::Sequence < OUString > aComponentDisplayNames(1);
aComponentDisplayNames[0] = componentName + sDisplayName;
uno::Sequence< uno::Any > aComponentDisplayNamesValue = GetProperties( aComponentDisplayNames );
OUString sComponentDisplayName;
if ( aComponentDisplayNamesValue.getLength() && (aComponentDisplayNamesValue[0] >>= sComponentDisplayName) )
{
sal_Int32 nIndex = 0;
m_aComponentDisplayNames.emplace(componentName.getToken(1,'/',nIndex),sComponentDisplayName);
}
componentName += "/Entries";
uno::Sequence < OUString > aDisplayNames = GetPropertyNames(componentName);
lcl_addString(aDisplayNames,sDisplayName);
uno::Sequence< uno::Any > aDisplayNamesValue = GetProperties( aDisplayNames );
const OUString* pDispIter = aDisplayNames.getConstArray();
const OUString* pDispEnd = pDispIter + aDisplayNames.getLength();
for(sal_Int32 j = 0;pDispIter != pDispEnd;++pDispIter,++j)
{
sal_Int32 nIndex = 0;
pDispIter->getToken(0,'/',nIndex);
OUString sName = pDispIter->copy(nIndex);
sName = sName.copy(0,sName.lastIndexOf(sDisplayName));
OUString sCurrentDisplayName;
aDisplayNamesValue[j] >>= sCurrentDisplayName;
aDisplayNameMap.emplace(sName,sCurrentDisplayName);
}
}
// load color settings
OUString sScheme(rScheme);
if(sScheme.isEmpty())
{
//detect current scheme name
uno::Sequence < OUString > aCurrent { "ExtendedColorScheme/CurrentColorScheme" };
uno::Sequence< uno::Any > aCurrentVal = GetProperties( aCurrent );
aCurrentVal.getConstArray()[0] >>= sScheme;
} // if(!sScheme.getLength())
m_sLoadedScheme = sScheme;
OUString sBase = "ExtendedColorScheme/ColorSchemes/"
+ sScheme;
bool bFound = ExistsScheme(sScheme);
if ( bFound )
{
aComponentNames = GetPropertyNames(sBase);
FillComponentColors(aComponentNames,aDisplayNameMap);
}
if ( m_sLoadedScheme.isEmpty() )
m_sLoadedScheme = "default";
if ( sScheme != "default" )
{
if ( ExistsScheme("default") )
{
aComponentNames = GetPropertyNames("ExtendedColorScheme/ColorSchemes/default");
FillComponentColors(aComponentNames,aDisplayNameMap);
}
}
if ( !bFound && !sScheme.isEmpty() )
{
AddScheme(sScheme);
CommitCurrentSchemeName();
}
}
void ExtendedColorConfig_Impl::FillComponentColors(uno::Sequence < OUString >& _rComponents,const TDisplayNames& _rDisplayNames)
{
const OUString sColorEntries("/Entries");
for(OUString const & component : _rComponents)
{
OUString sComponentName = component.copy(component.lastIndexOf('/')+1);
if ( m_aConfigValues.find(sComponentName) == m_aConfigValues.end() )
{
OUString sEntry = component + sColorEntries;
uno::Sequence < OUString > aColorNames = GetPropertyNames(sEntry);
uno::Sequence < OUString > aDefaultColorNames = aColorNames;
const OUString sColor("/Color");
const OUString sDefaultColor("/DefaultColor");
lcl_addString(aColorNames,sColor);
lcl_addString(aDefaultColorNames,sDefaultColor);
uno::Sequence< uno::Any > aColors = GetProperties( aColorNames );
const uno::Any* pColors = aColors.getConstArray();
uno::Sequence< uno::Any > aDefaultColors = GetProperties( aDefaultColorNames );
bool bDefaultColorFound = aDefaultColors.getLength() != 0;
const uno::Any* pDefaultColors = aDefaultColors.getConstArray();
OUString* pColorIter = aColorNames.getArray();
OUString* pColorEnd = pColorIter + aColorNames.getLength();
m_aConfigValuesPos.push_back(m_aConfigValues.emplace(sComponentName,TComponentMapping(TConfigValues(),TMapPos())).first);
TConfigValues& aConfigValues = (*m_aConfigValuesPos.rbegin())->second.first;
TMapPos& aConfigValuesPos = (*m_aConfigValuesPos.rbegin())->second.second;
for(int i = 0; pColorIter != pColorEnd; ++pColorIter ,++i)
{
if ( aConfigValues.find(*pColorIter) == aConfigValues.end() )
{
sal_Int32 nIndex = 0;
pColorIter->getToken(2,'/',nIndex);
OUString sName(pColorIter->copy(nIndex)),sDisplayName;
OUString sTemp = sName.copy(0,sName.lastIndexOf(sColor));
TDisplayNames::const_iterator aFind = _rDisplayNames.find(sTemp);
nIndex = 0;
sName = sName.getToken(2,'/',nIndex);
OSL_ENSURE(aFind != _rDisplayNames.end(),"DisplayName is not in EntryNames config list!");
if ( aFind != _rDisplayNames.end() )
sDisplayName = aFind->second;
OSL_ENSURE(pColors[i].hasValue(),"Color config entry has NIL as color value set!");
OSL_ENSURE(pDefaultColors[i].hasValue(),"Color config entry has NIL as color value set!");
Color nColor, nDefaultColor;
pColors[i] >>= nColor;
if ( bDefaultColorFound )
pDefaultColors[i] >>= nDefaultColor;
else
nDefaultColor = nColor;
ExtendedColorConfigValue aValue(sName,sDisplayName,nColor,nDefaultColor);
aConfigValuesPos.push_back(aConfigValues.emplace(sName,aValue).first);
}
} // for(int i = 0; pColorIter != pColorEnd; ++pColorIter ,++i)
}
}
}
void ExtendedColorConfig_Impl::Notify( const uno::Sequence<OUString>& /*rPropertyNames*/)
{
//loading via notification always uses the default setting
Load(OUString());
SolarMutexGuard aVclGuard;
if(m_bLockBroadcast)
{
m_bBroadcastWhenUnlocked = true;
}
else
Broadcast(SfxHint(SfxHintId::ColorsChanged));
}
void ExtendedColorConfig_Impl::ImplCommit()
{
if ( m_sLoadedScheme.isEmpty() )
return;
const OUString sColorEntries("Entries");
const OUString sColor("/Color");
OUString sBase = "ExtendedColorScheme/ColorSchemes/"
+ m_sLoadedScheme;
const OUString s_sSep("/");
for (auto const& configValue : m_aConfigValues)
{
if ( ConfigItem::AddNode(sBase, configValue.first) )
{
OUString sNode = sBase
+ s_sSep
+ configValue.first
//ConfigItem::AddNode(sNode, sColorEntries);
+ s_sSep
+ sColorEntries;
uno::Sequence < beans::PropertyValue > aPropValues(configValue.second.first.size());
beans::PropertyValue* pPropValues = aPropValues.getArray();
for (auto const& elem : configValue.second.first)
{
pPropValues->Name = sNode + s_sSep + elem.first;
ConfigItem::AddNode(sNode, elem.first);
pPropValues->Name += sColor;
pPropValues->Value <<= elem.second.getColor();
// the default color will never be changed
++pPropValues;
}
SetSetProperties("ExtendedColorScheme/ColorSchemes", aPropValues);
}
}
CommitCurrentSchemeName();
}
void ExtendedColorConfig_Impl::CommitCurrentSchemeName()
{
//save current scheme name
uno::Sequence < OUString > aCurrent { "ExtendedColorScheme/CurrentColorScheme" };
uno::Sequence< uno::Any > aCurrentVal(1);
aCurrentVal.getArray()[0] <<= m_sLoadedScheme;
PutProperties(aCurrent, aCurrentVal);
}
bool ExtendedColorConfig_Impl::ExistsScheme(const OUString& _sSchemeName)
{
OUString sBase("ExtendedColorScheme/ColorSchemes");
uno::Sequence < OUString > aComponentNames = GetPropertyNames(sBase);
sBase += "/" + _sSchemeName;
const OUString* pCompIter = aComponentNames.getConstArray();
const OUString* pCompEnd = pCompIter + aComponentNames.getLength();
for(;pCompIter != pCompEnd && *pCompIter != sBase;++pCompIter)
;
return pCompIter != pCompEnd;
}
void ExtendedColorConfig_Impl::SetColorConfigValue(const OUString& _sName, const ExtendedColorConfigValue& rValue )
{
TComponents::iterator aFind = m_aConfigValues.find(_sName);
if ( aFind != m_aConfigValues.end() )
{
TConfigValues::iterator aFind2 = aFind->second.first.find(rValue.getName());
if ( aFind2 != aFind->second.first.end() )
aFind2->second = rValue;
SetModified();
}
}
void ExtendedColorConfig_Impl::AddScheme(const OUString& rScheme)
{
if(ConfigItem::AddNode("ExtendedColorScheme/ColorSchemes", rScheme))
{
m_sLoadedScheme = rScheme;
Commit();
}
}
void ExtendedColorConfig_Impl::RemoveScheme(const OUString& rScheme)
{
uno::Sequence< OUString > aElements { rScheme };
ClearNodeElements("ExtendedColorScheme/ColorSchemes", aElements);
}
void ExtendedColorConfig_Impl::SettingsChanged()
{
SolarMutexGuard aVclGuard;
Broadcast( SfxHint( SfxHintId::ColorsChanged ) );
}
void ExtendedColorConfig_Impl::LockBroadcast()
{
m_bLockBroadcast = true;
}
void ExtendedColorConfig_Impl::UnlockBroadcast()
{
if ( m_bBroadcastWhenUnlocked )
{
m_bBroadcastWhenUnlocked = ExtendedColorConfig::m_pImpl != nullptr;
if ( m_bBroadcastWhenUnlocked )
{
if ( ExtendedColorConfig::m_pImpl && ExtendedColorConfig::m_pImpl->m_bIsBroadcastEnabled )
{
m_bBroadcastWhenUnlocked = false;
ExtendedColorConfig::m_pImpl->Broadcast(SfxHint(SfxHintId::ColorsChanged));
}
}
}
m_bLockBroadcast = false;
}
IMPL_LINK( ExtendedColorConfig_Impl, DataChangedEventListener, VclSimpleEvent&, rEvent, void )
{
if ( rEvent.GetId() == VclEventId::ApplicationDataChanged )
{
DataChangedEvent* pData = static_cast<DataChangedEvent*>(static_cast<VclWindowEvent&>(rEvent).GetData());
if ( (pData->GetType() == DataChangedEventType::SETTINGS) &&
(pData->GetFlags() & AllSettingsFlags::STYLE) )
{
SettingsChanged();
}
}
}
ExtendedColorConfig::ExtendedColorConfig()
{
::osl::MutexGuard aGuard( ColorMutex_Impl::get() );
if ( !m_pImpl )
m_pImpl = new ExtendedColorConfig_Impl;
++nExtendedColorRefCount_Impl;
StartListening( *m_pImpl);
}
ExtendedColorConfig::~ExtendedColorConfig()
{
::osl::MutexGuard aGuard( ColorMutex_Impl::get() );
EndListening( *m_pImpl);
if(!--nExtendedColorRefCount_Impl)
{
delete m_pImpl;
m_pImpl = nullptr;
}
}
ExtendedColorConfigValue ExtendedColorConfig::GetColorValue(const OUString& _sComponentName,const OUString& _sName)const
{
return m_pImpl->GetColorConfigValue(_sComponentName,_sName);
}
sal_Int32 ExtendedColorConfig::GetComponentCount() const
{
return m_pImpl->GetComponentCount();
}
sal_Int32 ExtendedColorConfig::GetComponentColorCount(const OUString& _sName) const
{
return m_pImpl->GetComponentColorCount(_sName);
}
ExtendedColorConfigValue ExtendedColorConfig::GetComponentColorConfigValue(const OUString& _sName,sal_uInt32 _nPos) const
{
return m_pImpl->GetComponentColorConfigValue(_sName,_nPos);
}
OUString ExtendedColorConfig::GetComponentName(sal_uInt32 _nPos) const
{
return m_pImpl->GetComponentName(_nPos);
}
OUString ExtendedColorConfig::GetComponentDisplayName(const OUString& _sComponentName) const
{
return m_pImpl->GetComponentDisplayName(_sComponentName);
}
void ExtendedColorConfig::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
{
SolarMutexGuard aVclGuard;
Broadcast( rHint );
}
EditableExtendedColorConfig::EditableExtendedColorConfig() :
m_pImpl(new ExtendedColorConfig_Impl),
m_bModified(false)
{
ExtendedColorConfig_Impl::LockBroadcast();
}
EditableExtendedColorConfig::~EditableExtendedColorConfig()
{
ExtendedColorConfig_Impl::UnlockBroadcast();
if(m_bModified)
m_pImpl->SetModified();
if(m_pImpl->IsModified())
m_pImpl->Commit();
}
void EditableExtendedColorConfig::DeleteScheme(const OUString& rScheme )
{
m_pImpl->RemoveScheme(rScheme);
}
void EditableExtendedColorConfig::AddScheme(const OUString& rScheme )
{
m_pImpl->AddScheme(rScheme);
}
void EditableExtendedColorConfig::LoadScheme(const OUString& rScheme )
{
if(m_bModified)
m_pImpl->SetModified();
if(m_pImpl->IsModified())
m_pImpl->Commit();
m_bModified = false;
m_pImpl->Load(rScheme);
//the name of the loaded scheme has to be committed separately
m_pImpl->CommitCurrentSchemeName();
}
// Changes the name of the current scheme but doesn't load it!
void EditableExtendedColorConfig::SetCurrentSchemeName(const OUString& rScheme)
{
m_pImpl->SetCurrentSchemeName(rScheme);
m_pImpl->CommitCurrentSchemeName();
}
void EditableExtendedColorConfig::SetColorValue(
const OUString& _sName, const ExtendedColorConfigValue& rValue)
{
m_pImpl->SetColorConfigValue(_sName, rValue);
m_pImpl->ClearModified();
m_bModified = true;
}
void EditableExtendedColorConfig::SetModified()
{
m_bModified = true;
}
void EditableExtendedColorConfig::Commit()
{
if(m_bModified)
m_pImpl->SetModified();
if(m_pImpl->IsModified())
m_pImpl->Commit();
m_bModified = false;
}
void EditableExtendedColorConfig::DisableBroadcast()
{
ExtendedColorConfig_Impl::DisableBroadcast();
}
void EditableExtendedColorConfig::EnableBroadcast()
{
ExtendedColorConfig_Impl::EnableBroadcast();
}
sal_Int32 EditableExtendedColorConfig::GetComponentCount() const
{
return m_pImpl->GetComponentCount();
}
sal_Int32 EditableExtendedColorConfig::GetComponentColorCount(const OUString& _sName) const
{
return m_pImpl->GetComponentColorCount(_sName);
}
ExtendedColorConfigValue EditableExtendedColorConfig::GetComponentColorConfigValue(const OUString& _sName,sal_uInt32 _nPos) const
{
return m_pImpl->GetComponentColorConfigValue(_sName,_nPos);
}
OUString EditableExtendedColorConfig::GetComponentName(sal_uInt32 _nPos) const
{
return m_pImpl->GetComponentName(_nPos);
}
}//namespace svtools
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V560 A part of conditional expression is always true: ExtendedColorConfig::m_pImpl.