/* -*- 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 <memory>
#include <colorscale.hxx>
#include <document.hxx>
#include <formulacell.hxx>
#include <fillinfo.hxx>
#include <bitmaps.hlst>
#include <tokenarray.hxx>
#include <refupdatecontext.hxx>
#include <refdata.hxx>
#include <editeng/fhgtitem.hxx>
#include <scitems.hxx>
#include <formula/token.hxx>
#include <vcl/bitmapex.hxx>
#include <algorithm>
#include <cassert>
ScFormulaListener::ScFormulaListener(ScFormulaCell* pCell):
mbDirty(false),
mrDoc(pCell->GetDocument())
{
startListening( pCell->GetCode(), ScRange(pCell->aPos) );
}
ScFormulaListener::ScFormulaListener(ScDocument& rDoc):
mbDirty(false),
mrDoc(rDoc)
{
}
ScFormulaListener::ScFormulaListener(ScDocument& rDoc, const ScRangeList& rRange):
mbDirty(false),
mrDoc(rDoc)
{
startListening(rRange);
}
void ScFormulaListener::startListening(const ScTokenArray* pArr, const ScRange& rRange)
{
if (!pArr || mrDoc.IsClipOrUndo())
return;
for ( auto t: pArr->References() )
{
switch (t->GetType())
{
case formula::svSingleRef:
{
ScAddress aCell = t->GetSingleRef()->toAbs(mrDoc, rRange.aStart);
ScAddress aCell2 = t->GetSingleRef()->toAbs(mrDoc, rRange.aEnd);
ScRange aRange(aCell, aCell2);
if (aRange.IsValid())
mrDoc.StartListeningArea(aRange, false, this);
}
break;
case formula::svDoubleRef:
{
const ScSingleRefData& rRef1 = *t->GetSingleRef();
const ScSingleRefData& rRef2 = *t->GetSingleRef2();
ScAddress aCell1 = rRef1.toAbs(mrDoc, rRange.aStart);
ScAddress aCell2 = rRef2.toAbs(mrDoc, rRange.aStart);
ScAddress aCell3 = rRef1.toAbs(mrDoc, rRange.aEnd);
ScAddress aCell4 = rRef2.toAbs(mrDoc, rRange.aEnd);
ScRange aRange1(aCell1, aCell3);
ScRange aRange2(aCell2, aCell4);
aRange1.ExtendTo(aRange2);
if (aRange1.IsValid())
{
if (t->GetOpCode() == ocColRowNameAuto)
{ // automagically
if ( rRef1.IsColRel() )
{ // ColName
aRange1.aEnd.SetRow(mrDoc.MaxRow());
}
else
{ // RowName
aRange1.aEnd.SetCol(mrDoc.MaxCol());
}
}
mrDoc.StartListeningArea(aRange1, false, this);
}
}
break;
default:
; // nothing
}
}
}
void ScFormulaListener::startListening(const ScRangeList& rRange)
{
if (mrDoc.IsClipOrUndo())
return;
size_t nLength = rRange.size();
for (size_t i = 0; i < nLength; ++i)
{
const ScRange& aRange = rRange[i];
mrDoc.StartListeningArea(aRange, false, this);
}
}
void ScFormulaListener::addTokenArray(const ScTokenArray* pArray, const ScRange& rRange)
{
startListening(pArray, rRange);
}
void ScFormulaListener::setCallback(const std::function<void()>& aCallback)
{
maCallbackFunction = aCallback;
}
void ScFormulaListener::stopListening()
{
if (mrDoc.IsClipOrUndo())
return;
EndListeningAll();
}
ScFormulaListener::~ScFormulaListener()
{
stopListening();
}
void ScFormulaListener::Notify(const SfxHint& rHint)
{
mbDirty = true;
if (rHint.GetId() == SfxHintId::Dying)
return;
if (maCallbackFunction)
maCallbackFunction();
}
bool ScFormulaListener::NeedsRepaint() const
{
bool bRet = mbDirty;
mbDirty = false;
return bRet;
}
ScColorScaleEntry::ScColorScaleEntry():
mnVal(0),
mpFormat(nullptr),
meType(COLORSCALE_VALUE),
mbGreaterThanOrEqual(true)
{
}
ScColorScaleEntry::ScColorScaleEntry(double nVal, const Color& rCol, ScColorScaleEntryType eType):
mnVal(nVal),
mpFormat(nullptr),
maColor(rCol),
meType(eType),
mbGreaterThanOrEqual(true)
{
}
ScColorScaleEntry::ScColorScaleEntry(const ScColorScaleEntry& rEntry):
mnVal(rEntry.mnVal),
mpFormat(rEntry.mpFormat),
maColor(rEntry.maColor),
meType(rEntry.meType),
mbGreaterThanOrEqual(true)
{
setListener();
if(rEntry.mpCell)
{
mpCell.reset(new ScFormulaCell(*rEntry.mpCell, rEntry.mpCell->GetDocument(), rEntry.mpCell->aPos, ScCloneFlags::NoMakeAbsExternal));
mpCell->StartListeningTo(mpCell->GetDocument());
mpListener.reset(new ScFormulaListener(mpCell.get()));
}
}
ScColorScaleEntry::ScColorScaleEntry(ScDocument* pDoc, const ScColorScaleEntry& rEntry):
mnVal(rEntry.mnVal),
mpFormat(rEntry.mpFormat),
maColor(rEntry.maColor),
meType(rEntry.meType),
mbGreaterThanOrEqual(true)
{
setListener();
if(rEntry.mpCell)
{
mpCell.reset(new ScFormulaCell(*rEntry.mpCell, *pDoc, rEntry.mpCell->aPos, ScCloneFlags::NoMakeAbsExternal));
mpCell->StartListeningTo( *pDoc );
mpListener.reset(new ScFormulaListener(mpCell.get()));
if (mpFormat)
mpListener->setCallback([&]() { mpFormat->DoRepaint();});
}
}
ScColorScaleEntry::~ScColorScaleEntry() COVERITY_NOEXCEPT_FALSE
{
if(mpCell)
mpCell->EndListeningTo(mpCell->GetDocument());
}
void ScColorScaleEntry::SetFormula( const OUString& rFormula, ScDocument& rDoc, const ScAddress& rAddr, formula::FormulaGrammar::Grammar eGrammar )
{
mpCell.reset(new ScFormulaCell( rDoc, rAddr, rFormula, eGrammar ));
mpCell->StartListeningTo( rDoc );
mpListener.reset(new ScFormulaListener(mpCell.get()));
if (mpFormat)
mpListener->setCallback([&]() { mpFormat->DoRepaint();});
}
const ScTokenArray* ScColorScaleEntry::GetFormula() const
{
if(mpCell)
{
return mpCell->GetCode();
}
return nullptr;
}
OUString ScColorScaleEntry::GetFormula( formula::FormulaGrammar::Grammar eGrammar ) const
{
if(mpCell)
{
return mpCell->GetFormula(eGrammar);
}
return OUString();
}
double ScColorScaleEntry::GetValue() const
{
if(mpCell)
{
mpCell->Interpret();
if(mpCell->IsValue())
return mpCell->GetValue();
return std::numeric_limits<double>::max();
}
return mnVal;
}
bool ScColorScaleEntry::GetGreaterThanOrEqual() const
{
return mbGreaterThanOrEqual;
}
void ScColorScaleEntry::SetGreaterThanOrEqual(bool bGreaterThanOrEqual)
{
mbGreaterThanOrEqual = bGreaterThanOrEqual;
}
void ScColorScaleEntry::SetValue(double nValue)
{
mnVal = nValue;
mpCell.reset();
setListener();
}
void ScColorScaleEntry::UpdateReference( const sc::RefUpdateContext& rCxt )
{
if (!mpCell)
{
setListener();
return;
}
mpCell->UpdateReference(rCxt);
mpListener.reset(new ScFormulaListener(mpCell.get()));
SetRepaintCallback(mpFormat);
}
void ScColorScaleEntry::UpdateInsertTab( const sc::RefUpdateInsertTabContext& rCxt )
{
if (!mpCell)
{
setListener();
return;
}
mpCell->UpdateInsertTab(rCxt);
mpListener.reset(new ScFormulaListener(mpCell.get()));
SetRepaintCallback(mpFormat);
}
void ScColorScaleEntry::UpdateDeleteTab( const sc::RefUpdateDeleteTabContext& rCxt )
{
if (!mpCell)
{
setListener();
return;
}
mpCell->UpdateDeleteTab(rCxt);
mpListener.reset(new ScFormulaListener(mpCell.get()));
SetRepaintCallback(mpFormat);
}
void ScColorScaleEntry::UpdateMoveTab( const sc::RefUpdateMoveTabContext& rCxt )
{
if (!mpCell)
{
setListener();
return;
}
SCTAB nTabNo = rCxt.getNewTab(mpCell->aPos.Tab());
mpCell->UpdateMoveTab(rCxt, nTabNo);
mpListener.reset(new ScFormulaListener(mpCell.get()));
SetRepaintCallback(mpFormat);
}
void ScColorScaleEntry::SetColor(const Color& rColor)
{
maColor = rColor;
}
void ScColorScaleEntry::SetRepaintCallback(ScConditionalFormat* pFormat)
{
mpFormat = pFormat;
setListener();
if (mpFormat && mpListener)
mpListener->setCallback([&]() { mpFormat->DoRepaint();});
}
void ScColorScaleEntry::SetType( ScColorScaleEntryType eType )
{
meType = eType;
if(eType != COLORSCALE_FORMULA)
{
mpCell.reset();
mpListener.reset();
}
setListener();
}
void ScColorScaleEntry::setListener()
{
if (!mpFormat)
return;
if (meType == COLORSCALE_PERCENT || meType == COLORSCALE_PERCENTILE
|| meType == COLORSCALE_MIN || meType == COLORSCALE_MAX
|| meType == COLORSCALE_AUTO)
{
mpListener.reset(new ScFormulaListener(*mpFormat->GetDocument(), mpFormat->GetRange()));
mpListener->setCallback([&]() { mpFormat->DoRepaint();});
}
}
void ScColorScaleEntry::SetRepaintCallback(const std::function<void()>& func)
{
mpListener->setCallback(func);
}
ScColorFormat::ScColorFormat(ScDocument* pDoc)
: ScFormatEntry(pDoc)
, mpParent(nullptr)
{
}
ScColorFormat::~ScColorFormat()
{
}
void ScColorFormat::SetParent( ScConditionalFormat* pParent )
{
mpParent = pParent;
}
ScColorScaleFormat::ScColorScaleFormat(ScDocument* pDoc):
ScColorFormat(pDoc)
{
}
ScColorScaleFormat::ScColorScaleFormat(ScDocument* pDoc, const ScColorScaleFormat& rFormat):
ScColorFormat(pDoc)
{
for(const auto& rxEntry : rFormat)
{
maColorScales.emplace_back(new ScColorScaleEntry(pDoc, *rxEntry));
}
auto aCache = rFormat.GetCache();
SetCache(aCache);
}
ScColorFormat* ScColorScaleFormat::Clone(ScDocument* pDoc) const
{
return new ScColorScaleFormat(pDoc, *this);
}
ScColorScaleFormat::~ScColorScaleFormat()
{
}
void ScColorScaleFormat::SetParent(ScConditionalFormat* pFormat)
{
for (auto itr = begin(), itrEnd = end(); itr != itrEnd; ++itr)
{
(*itr)->SetRepaintCallback(pFormat);
}
ScColorFormat::SetParent(pFormat);
}
void ScColorScaleFormat::AddEntry( ScColorScaleEntry* pEntry )
{
maColorScales.push_back(std::unique_ptr<ScColorScaleEntry, o3tl::default_delete<ScColorScaleEntry>>(pEntry));
maColorScales.back()->SetRepaintCallback(mpParent);
}
bool ScColorScaleFormat::IsEqual(const ScFormatEntry& rOther, bool /*bIgnoreSrcPos*/) const
{
if (GetType() != rOther.GetType())
return false;
const ScColorScaleFormat& r = static_cast<const ScColorScaleFormat&>(rOther);
for (size_t i = 0; i < maColorScales.size(); ++i)
{
if (!maColorScales[i]->GetColor().IsRGBEqual(r.maColorScales[i]->GetColor().GetRGBColor())
|| maColorScales[i]->GetType() != r.maColorScales[i]->GetType()
|| maColorScales[i]->GetValue() != r.maColorScales[i]->GetValue())
return false;
}
return true;
}
double ScColorScaleFormat::GetMinValue() const
{
ScColorScaleEntries::const_iterator itr = maColorScales.begin();
if((*itr)->GetType() == COLORSCALE_VALUE || (*itr)->GetType() == COLORSCALE_FORMULA)
return (*itr)->GetValue();
else
{
return getMinValue();
}
}
double ScColorScaleFormat::GetMaxValue() const
{
ScColorScaleEntries::const_reverse_iterator itr = maColorScales.rbegin();
if((*itr)->GetType() == COLORSCALE_VALUE || (*itr)->GetType() == COLORSCALE_FORMULA)
return (*itr)->GetValue();
else
{
return getMaxValue();
}
}
void ScColorScaleFormat::calcMinMax(double& rMin, double& rMax) const
{
rMin = GetMinValue();
rMax = GetMaxValue();
}
const ScRangeList& ScColorFormat::GetRange() const
{
return mpParent->GetRange();
}
std::vector<double> ScColorFormat::GetCache() const
{
if (!mpParent)
return {};
std::vector<double>* pRes = mpParent->GetCache();
if (pRes)
return *pRes;
return {};
}
void ScColorFormat::SetCache(const std::vector<double>& aValues) const
{
if (!mpParent)
return;
mpParent->SetCache(aValues);
}
std::vector<double>& ScColorFormat::getValues() const
{
assert(mpParent);
std::vector<double>* pCache = mpParent->GetCache();
if (!pCache || pCache->empty())
{
if (!pCache)
{
SetCache({});
pCache = mpParent->GetCache();
assert(pCache);
}
std::vector<double>& rValues = *pCache;
size_t n = GetRange().size();
const ScRangeList& aRanges = GetRange();
for(size_t i = 0; i < n; ++i)
{
const ScRange & rRange = aRanges[i];
SCTAB nTab = rRange.aStart.Tab();
SCCOL nColStart = rRange.aStart.Col();
SCROW nRowStart = rRange.aStart.Row();
SCCOL nColEnd = rRange.aEnd.Col();
SCROW nRowEnd = rRange.aEnd.Row();
if(nRowEnd == mpDoc->MaxRow())
{
bool bShrunk = false;
mpDoc->ShrinkToUsedDataArea(bShrunk, nTab, nColStart, nRowStart,
nColEnd, nRowEnd, false);
}
for(SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol)
{
for(SCROW nRow = nRowStart; nRow <= nRowEnd; ++nRow)
{
ScAddress aAddr(nCol, nRow, nTab);
ScRefCellValue rCell(*mpDoc, aAddr);
if(rCell.hasNumeric())
{
double aVal = rCell.getValue();
rValues.push_back(aVal);
}
}
}
}
std::sort(rValues.begin(), rValues.end());
}
return *pCache;
}
double ScColorFormat::getMinValue() const
{
std::vector<double>& rValues = getValues();
if(rValues.empty())
return 0;
return rValues[0];
}
double ScColorFormat::getMaxValue() const
{
std::vector<double>& rValues = getValues();
if(rValues.empty())
return 0;
return rValues[rValues.size()-1];
}
void ScColorFormat::startRendering()
{
}
void ScColorFormat::endRendering()
{
}
void ScColorFormat::updateValues()
{
getMinValue();
getMaxValue();
}
namespace {
sal_uInt8 GetColorValue( double nVal, double nVal1, sal_uInt8 nColVal1, double nVal2, sal_uInt8 nColVal2 )
{
if (nVal <= nVal1)
return nColVal1;
if (nVal >= nVal2)
return nColVal2;
sal_uInt8 nColVal = static_cast<int>((nVal - nVal1)/(nVal2-nVal1)*(nColVal2-nColVal1))+nColVal1;
return nColVal;
}
Color CalcColor( double nVal, double nVal1, const Color& rCol1, double nVal2, const Color& rCol2)
{
sal_uInt8 nColRed = GetColorValue(nVal, nVal1, rCol1.GetRed(), nVal2, rCol2.GetRed());
sal_uInt8 nColBlue = GetColorValue(nVal, nVal1, rCol1.GetBlue(), nVal2, rCol2.GetBlue());
sal_uInt8 nColGreen = GetColorValue(nVal, nVal1, rCol1.GetGreen(), nVal2, rCol2.GetGreen());
return Color(nColRed, nColGreen, nColBlue);
}
/**
* @param rVector sorted vector of the array
* @param fPercentile percentile
*/
double GetPercentile( const std::vector<double>& rArray, double fPercentile )
{
assert(!rArray.empty());
SAL_WARN_IF(fPercentile < 0, "sc", "negative percentile");
if (fPercentile < 0)
return rArray.front();
assert(fPercentile <= 1);
size_t nSize = rArray.size();
double fFloor = ::rtl::math::approxFloor(fPercentile * (nSize-1));
size_t nIndex = static_cast<size_t>(fFloor);
double fDiff = fPercentile * (nSize-1) - fFloor;
std::vector<double>::const_iterator iter = rArray.begin() + nIndex;
if (fDiff == 0.0)
return *iter;
else
{
double fVal = *iter;
iter = rArray.begin() + nIndex+1;
return fVal + fDiff * (*iter - fVal);
}
}
}
double ScColorScaleFormat::CalcValue(double nMin, double nMax, const ScColorScaleEntries::const_iterator& itr) const
{
switch((*itr)->GetType())
{
case COLORSCALE_PERCENT:
return nMin + (nMax-nMin)*((*itr)->GetValue()/100);
case COLORSCALE_MIN:
return nMin;
case COLORSCALE_MAX:
return nMax;
case COLORSCALE_PERCENTILE:
{
std::vector<double>& rValues = getValues();
if(rValues.size() == 1)
return rValues[0];
else
{
double fPercentile = (*itr)->GetValue()/100.0;
return GetPercentile(rValues, fPercentile);
}
}
default:
break;
}
return (*itr)->GetValue();
}
std::optional<Color> ScColorScaleFormat::GetColor( const ScAddress& rAddr ) const
{
ScRefCellValue rCell(*mpDoc, rAddr);
if(!rCell.hasNumeric())
return std::optional<Color>();
// now we have for sure a value
double nVal = rCell.getValue();
if (maColorScales.size() < 2)
return std::optional<Color>();
double nMin = std::numeric_limits<double>::max();
double nMax = std::numeric_limits<double>::min();
calcMinMax(nMin, nMax);
// this check is for safety
if(nMin > nMax)
return std::optional<Color>();
ScColorScaleEntries::const_iterator itr = begin();
double nValMin = CalcValue(nMin, nMax, itr);
Color rColMin = (*itr)->GetColor();
++itr;
double nValMax = CalcValue(nMin, nMax, itr);
Color rColMax = (*itr)->GetColor();
// tdf#155321 for the last percentile value, use always the end of the color scale,
// i.e. not the first possible color in the case of repeating values
bool bEqual = COLORSCALE_PERCENTILE == (*itr)->GetType() && nVal == nMax && nVal == nValMax;
++itr;
while(itr != end() && (nVal > nValMax || bEqual))
{
rColMin = rColMax;
nValMin = !bEqual ? nValMax : nValMax - 1;
rColMax = (*itr)->GetColor();
nValMax = CalcValue(nMin, nMax, itr);
++itr;
}
Color aColor = CalcColor(nVal, nValMin, rColMin, nValMax, rColMax);
return aColor;
}
void ScColorScaleFormat::UpdateReference( sc::RefUpdateContext& rCxt )
{
for(ScColorScaleEntries::iterator itr = begin(); itr != end(); ++itr)
(*itr)->UpdateReference(rCxt);
}
void ScColorScaleFormat::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
{
for (ScColorScaleEntries::iterator it = begin(); it != end(); ++it)
(*it)->UpdateInsertTab(rCxt);
}
void ScColorScaleFormat::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
{
for (ScColorScaleEntries::iterator it = begin(); it != end(); ++it)
(*it)->UpdateDeleteTab(rCxt);
}
void ScColorScaleFormat::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
{
for (ScColorScaleEntries::iterator it = begin(); it != end(); ++it)
(*it)->UpdateMoveTab(rCxt);
}
ScFormatEntry::Type ScColorScaleFormat::GetType() const
{
return Type::Colorscale;
}
ScColorScaleEntries::iterator ScColorScaleFormat::begin()
{
return maColorScales.begin();
}
ScColorScaleEntries::const_iterator ScColorScaleFormat::begin() const
{
return maColorScales.begin();
}
ScColorScaleEntries::iterator ScColorScaleFormat::end()
{
return maColorScales.end();
}
ScColorScaleEntries::const_iterator ScColorScaleFormat::end() const
{
return maColorScales.end();
}
ScColorScaleEntry* ScColorScaleFormat::GetEntry(size_t nPos)
{
if (maColorScales.size() <= nPos)
return nullptr;
return maColorScales[nPos].get();
}
const ScColorScaleEntry* ScColorScaleFormat::GetEntry(size_t nPos) const
{
if (maColorScales.size() <= nPos)
return nullptr;
return maColorScales[nPos].get();
}
size_t ScColorScaleFormat::size() const
{
return maColorScales.size();
}
void ScColorScaleFormat::EnsureSize()
{
if (maColorScales.size() < 2)
{
// TODO: create 2 valid entries
}
}
ScDataBarFormat::ScDataBarFormat(ScDocument* pDoc):
ScColorFormat(pDoc),
mpFormatData(new ScDataBarFormatData())
{
}
ScDataBarFormat::ScDataBarFormat(ScDocument* pDoc, const ScDataBarFormat& rFormat):
ScColorFormat(pDoc),
mpFormatData(new ScDataBarFormatData(*rFormat.mpFormatData))
{
}
void ScDataBarFormat::SetDataBarData( ScDataBarFormatData* pData )
{
mpFormatData.reset(pData);
if (mpParent)
{
mpFormatData->mpUpperLimit->SetRepaintCallback(mpParent);
mpFormatData->mpLowerLimit->SetRepaintCallback(mpParent);
}
}
ScDataBarFormatData* ScDataBarFormat::GetDataBarData()
{
return mpFormatData.get();
}
const ScDataBarFormatData* ScDataBarFormat::GetDataBarData() const
{
return mpFormatData.get();
}
ScColorFormat* ScDataBarFormat::Clone(ScDocument* pDoc) const
{
return new ScDataBarFormat(pDoc, *this);
}
void ScDataBarFormat::SetParent(ScConditionalFormat* pFormat)
{
if (mpFormatData)
{
mpFormatData->mpUpperLimit->SetRepaintCallback(pFormat);
mpFormatData->mpLowerLimit->SetRepaintCallback(pFormat);
}
ScColorFormat::SetParent(pFormat);
}
ScFormatEntry::Type ScDataBarFormat::GetType() const
{
return Type::Databar;
}
bool ScDataBarFormat::IsEqual(const ScFormatEntry& rOther, bool /*bIgnoreSrcPos*/) const
{
if (GetType() != rOther.GetType())
return false;
const ScDataBarFormat& r = static_cast<const ScDataBarFormat&>(rOther);
bool bEq = (mpFormatData->maAxisColor.IsRGBEqual(r.mpFormatData->maAxisColor)
&& mpFormatData->maPositiveColor.IsRGBEqual(r.mpFormatData->maPositiveColor)
&& mpFormatData->mxNegativeColor == r.mpFormatData->mxNegativeColor
&& mpFormatData->meAxisPosition == r.mpFormatData->meAxisPosition
&& mpFormatData->mbGradient == r.mpFormatData->mbGradient
&& mpFormatData->mbOnlyBar == r.mpFormatData->mbOnlyBar);
if (mpFormatData->mpUpperLimit->GetType() == r.mpFormatData->mpUpperLimit->GetType()
&& bEq)
{
bEq = (mpFormatData->mpUpperLimit->GetColor().IsRGBEqual(
r.mpFormatData->mpUpperLimit->GetColor())
&& mpFormatData->mpUpperLimit->GetValue()
== r.mpFormatData->mpUpperLimit->GetValue());
}
if (mpFormatData->mpLowerLimit->GetType() == r.mpFormatData->mpLowerLimit->GetType()
&& bEq)
{
bEq = (mpFormatData->mpLowerLimit->GetColor().IsRGBEqual(
r.mpFormatData->mpLowerLimit->GetColor())
&& mpFormatData->mpLowerLimit->GetValue()
== r.mpFormatData->mpLowerLimit->GetValue());
}
return bEq;
}
void ScDataBarFormat::UpdateReference( sc::RefUpdateContext& rCxt )
{
mpFormatData->mpUpperLimit->UpdateReference(rCxt);
mpFormatData->mpLowerLimit->UpdateReference(rCxt);
}
void ScDataBarFormat::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
{
mpFormatData->mpUpperLimit->UpdateInsertTab(rCxt);
mpFormatData->mpLowerLimit->UpdateInsertTab(rCxt);
}
void ScDataBarFormat::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
{
mpFormatData->mpUpperLimit->UpdateDeleteTab(rCxt);
mpFormatData->mpLowerLimit->UpdateDeleteTab(rCxt);
}
void ScDataBarFormat::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
{
mpFormatData->mpUpperLimit->UpdateMoveTab(rCxt);
mpFormatData->mpLowerLimit->UpdateMoveTab(rCxt);
}
double ScDataBarFormat::getMin(double nMin, double nMax) const
{
switch(mpFormatData->mpLowerLimit->GetType())
{
case COLORSCALE_MIN:
return nMin;
case COLORSCALE_AUTO:
return std::min<double>(0, nMin);
case COLORSCALE_PERCENT:
return nMin + (nMax-nMin)/100*mpFormatData->mpLowerLimit->GetValue();
case COLORSCALE_PERCENTILE:
{
double fPercentile = mpFormatData->mpLowerLimit->GetValue()/100.0;
std::vector<double>& rValues = getValues();
return GetPercentile(rValues, fPercentile);
}
default:
break;
}
return mpFormatData->mpLowerLimit->GetValue();
}
double ScDataBarFormat::getMax(double nMin, double nMax) const
{
switch(mpFormatData->mpUpperLimit->GetType())
{
case COLORSCALE_MAX:
return nMax;
case COLORSCALE_AUTO:
return std::max<double>(0, nMax);
case COLORSCALE_PERCENT:
return nMin + (nMax-nMin)/100*mpFormatData->mpUpperLimit->GetValue();
case COLORSCALE_PERCENTILE:
{
double fPercentile = mpFormatData->mpUpperLimit->GetValue()/100.0;
std::vector<double>& rValues = getValues();
return GetPercentile(rValues, fPercentile);
}
default:
break;
}
return mpFormatData->mpUpperLimit->GetValue();
}
std::unique_ptr<ScDataBarInfo> ScDataBarFormat::GetDataBarInfo(const ScAddress& rAddr) const
{
ScRefCellValue rCell(*mpDoc, rAddr);
if(!rCell.hasNumeric())
return nullptr;
// now we have for sure a value
double nValMin = getMinValue();
double nValMax = getMaxValue();
double nMin = getMin(nValMin, nValMax);
double nMax = getMax(nValMin, nValMax);
double nMinLength = mpFormatData->mnMinLength;
double nMaxLength = mpFormatData->mnMaxLength;
double nValue = rCell.getValue();
std::unique_ptr<ScDataBarInfo> pInfo(new ScDataBarInfo);
if(mpFormatData->meAxisPosition == databar::NONE)
{
if(nValue <= nMin)
{
pInfo->mnLength = nMinLength;
}
else if(nValue >= nMax)
{
pInfo->mnLength = nMaxLength;
}
else
{
double nDiff = nMax - nMin;
pInfo->mnLength = nMinLength + (nValue - nMin)/nDiff * (nMaxLength-nMinLength);
}
pInfo->mnZero = 0;
}
else if (mpFormatData->meAxisPosition == databar::AUTOMATIC)
{
// if auto is used we may need to adjust it
// for the length calculation
if (mpFormatData->mpLowerLimit->GetType() == COLORSCALE_AUTO && nMin > 0)
nMin = 0;
if (mpFormatData->mpUpperLimit->GetType() == COLORSCALE_MAX && nMax < 0)
nMax = 0;
//calculate the zero position first
if(nMin < 0)
{
if(nMax < 0)
pInfo->mnZero = 100;
else
{
pInfo->mnZero = -100*nMin/(nMax-nMin);
}
}
else
pInfo->mnZero = 0;
double nMinNonNegative = std::max(0.0, nMin);
double nMaxNonPositive = std::min(0.0, nMax);
//calculate the length
if(nValue < 0 && nMin < 0)
{
if (nValue < nMin)
pInfo->mnLength = -100;
else
pInfo->mnLength = -100 * (nValue-nMaxNonPositive)/(nMin-nMaxNonPositive);
}
else
{
if ( nValue > nMax )
pInfo->mnLength = 100;
else if (nValue <= nMin)
pInfo->mnLength = 0;
else
pInfo->mnLength = 100 * (nValue-nMinNonNegative)/(nMax-nMinNonNegative);
}
}
else if( mpFormatData->meAxisPosition == databar::MIDDLE)
{
pInfo->mnZero = 50;
double nAbsMax = std::max(std::abs(nMin), std::abs(nMax));
if (nValue < 0 && nMin < 0)
{
if (nValue < nMin)
pInfo->mnLength = nMaxLength * (nMin/nAbsMax);
else
pInfo->mnLength = nMaxLength * (nValue/nAbsMax);
}
else
{
if (nValue > nMax)
pInfo->mnLength = nMaxLength * (nMax/nAbsMax);
else
pInfo->mnLength = nMaxLength * (std::max(nValue, nMin)/nAbsMax);
}
}
else
assert(false);
// set color
if(mpFormatData->mbNeg && nValue < 0)
{
if(mpFormatData->mxNegativeColor)
{
pInfo->maColor = *mpFormatData->mxNegativeColor;
}
else
{
// default negative color is red
pInfo->maColor = COL_LIGHTRED;
}
}
else
pInfo->maColor = mpFormatData->maPositiveColor;
pInfo->mbGradient = mpFormatData->mbGradient;
pInfo->mbShowValue = !mpFormatData->mbOnlyBar;
pInfo->maAxisColor = mpFormatData->maAxisColor;
return pInfo;
}
void ScDataBarFormat::EnsureSize()
{
if (!mpFormatData->mpLowerLimit)
{
// TODO: implement
}
if (!mpFormatData->mpUpperLimit)
{
// TODO: implement
}
}
ScIconSetFormatData::ScIconSetFormatData(ScIconSetFormatData const& rOther)
: eIconSetType(rOther.eIconSetType)
, mbShowValue(rOther.mbShowValue)
, mbReverse(rOther.mbReverse)
, mbCustom(rOther.mbCustom)
, maCustomVector(rOther.maCustomVector)
{
m_Entries.reserve(rOther.m_Entries.size());
for (auto const& it : rOther.m_Entries)
{
m_Entries.emplace_back(new ScColorScaleEntry(*it));
}
}
ScIconSetFormat::ScIconSetFormat(ScDocument* pDoc):
ScColorFormat(pDoc),
mpFormatData(new ScIconSetFormatData)
{
}
ScIconSetFormat::ScIconSetFormat(ScDocument* pDoc, const ScIconSetFormat& rFormat):
ScColorFormat(pDoc),
mpFormatData(new ScIconSetFormatData(*rFormat.mpFormatData))
{
}
ScColorFormat* ScIconSetFormat::Clone( ScDocument* pDoc ) const
{
return new ScIconSetFormat(pDoc, *this);
}
void ScIconSetFormat::SetParent(ScConditionalFormat* pFormat)
{
for(iterator itr = begin(); itr != end(); ++itr)
{
(*itr)->SetRepaintCallback(pFormat);
}
ScColorFormat::SetParent(pFormat);
}
void ScIconSetFormat::SetIconSetData( ScIconSetFormatData* pFormatData )
{
mpFormatData.reset( pFormatData );
SetParent(mpParent);
}
ScIconSetFormatData* ScIconSetFormat::GetIconSetData()
{
return mpFormatData.get();
}
const ScIconSetFormatData* ScIconSetFormat::GetIconSetData() const
{
return mpFormatData.get();
}
std::unique_ptr<ScIconSetInfo> ScIconSetFormat::GetIconSetInfo(const ScAddress& rAddr) const
{
ScRefCellValue rCell(*mpDoc, rAddr);
if(!rCell.hasNumeric())
return nullptr;
// now we have for sure a value
double nVal = rCell.getValue();
if (mpFormatData->m_Entries.size() < 2)
return nullptr;
double nMin = GetMinValue();
double nMax = GetMaxValue();
sal_Int32 nIndex = 0;
const_iterator itr = begin();
++itr;
double nValMax = CalcValue(nMin, nMax, itr);
++itr;
bool bGreaterThanOrEqual = true;
while(itr != end() && nVal >= nValMax)
{
bGreaterThanOrEqual = (*itr)->GetGreaterThanOrEqual();
++nIndex;
nValMax = CalcValue(nMin, nMax, itr);
++itr;
}
if (bGreaterThanOrEqual)
{
if(nVal >= nValMax)
++nIndex;
}
else
{
if(nVal > nValMax)
++nIndex;
}
std::unique_ptr<ScIconSetInfo> pInfo(new ScIconSetInfo);
const SfxPoolItem& rPoolItem = mpDoc->GetPattern(rAddr)->GetItem(ATTR_FONT_HEIGHT);
tools::Long aFontHeight = static_cast<const SvxFontHeightItem&>(rPoolItem).GetHeight();
pInfo->mnHeight = aFontHeight;
if(mpFormatData->mbReverse)
{
sal_Int32 nMaxIndex = mpFormatData->m_Entries.size() - 1;
nIndex = nMaxIndex - nIndex;
}
if (mpFormatData->mbCustom && sal_Int32(mpFormatData->maCustomVector.size()) > nIndex)
{
ScIconSetType eCustomType = mpFormatData->maCustomVector[nIndex].first;
sal_Int32 nCustomIndex = mpFormatData->maCustomVector[nIndex].second;
if (nCustomIndex == -1)
{
return nullptr;
}
pInfo->eIconSetType = eCustomType;
pInfo->nIconIndex = nCustomIndex;
}
else
{
pInfo->nIconIndex = nIndex;
pInfo->eIconSetType = mpFormatData->eIconSetType;
}
pInfo->mbShowValue = mpFormatData->mbShowValue;
return pInfo;
}
ScFormatEntry::Type ScIconSetFormat::GetType() const
{
return Type::Iconset;
}
void ScIconSetFormat::UpdateReference( sc::RefUpdateContext& rCxt )
{
for(iterator itr = begin(); itr != end(); ++itr)
{
(*itr)->UpdateReference(rCxt);
}
}
void ScIconSetFormat::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
{
for(iterator itr = begin(); itr != end(); ++itr)
{
(*itr)->UpdateInsertTab(rCxt);
}
}
void ScIconSetFormat::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
{
for(iterator itr = begin(); itr != end(); ++itr)
{
(*itr)->UpdateDeleteTab(rCxt);
}
}
void ScIconSetFormat::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
{
for(iterator itr = begin(); itr != end(); ++itr)
{
(*itr)->UpdateMoveTab(rCxt);
}
}
ScIconSetFormat::iterator ScIconSetFormat::begin()
{
return mpFormatData->m_Entries.begin();
}
ScIconSetFormat::const_iterator ScIconSetFormat::begin() const
{
return mpFormatData->m_Entries.begin();
}
ScIconSetFormat::iterator ScIconSetFormat::end()
{
return mpFormatData->m_Entries.end();
}
ScIconSetFormat::const_iterator ScIconSetFormat::end() const
{
return mpFormatData->m_Entries.end();
}
double ScIconSetFormat::GetMinValue() const
{
const_iterator itr = begin();
if ((*itr)->GetType() == COLORSCALE_VALUE || (*itr)->GetType() == COLORSCALE_FORMULA)
return (*itr)->GetValue();
else
{
return getMinValue();
}
}
double ScIconSetFormat::GetMaxValue() const
{
auto const itr = mpFormatData->m_Entries.rbegin();
if ((*itr)->GetType() == COLORSCALE_VALUE || (*itr)->GetType() == COLORSCALE_FORMULA)
return (*itr)->GetValue();
else
{
return getMaxValue();
}
}
double ScIconSetFormat::CalcValue(double nMin, double nMax, const ScIconSetFormat::const_iterator& itr) const
{
switch ((*itr)->GetType())
{
case COLORSCALE_PERCENT:
return nMin + (nMax-nMin)*((*itr)->GetValue()/100);
case COLORSCALE_MIN:
return nMin;
case COLORSCALE_MAX:
return nMax;
case COLORSCALE_PERCENTILE:
{
std::vector<double>& rValues = getValues();
if(rValues.size() == 1)
return rValues[0];
else
{
double fPercentile = (*itr)->GetValue()/100.0;
return GetPercentile(rValues, fPercentile);
}
}
default:
break;
}
return (*itr)->GetValue();
}
constexpr ScIconSetMap ScIconSetFormat::g_IconSetMap[] = {
{ u"3Arrows"_ustr, IconSet_3Arrows, 3 },
{ u"3ArrowsGray"_ustr, IconSet_3ArrowsGray, 3 },
{ u"3Flags"_ustr, IconSet_3Flags, 3 },
{ u"3TrafficLights1"_ustr, IconSet_3TrafficLights1, 3 },
{ u"3TrafficLights2"_ustr, IconSet_3TrafficLights2, 3 },
{ u"3Signs"_ustr, IconSet_3Signs, 3 },
{ u"3Symbols"_ustr, IconSet_3Symbols, 3 },
{ u"3Symbols2"_ustr, IconSet_3Symbols2, 3 },
{ u"3Smilies"_ustr, IconSet_3Smilies, 3 },
{ u"3ColorSmilies"_ustr, IconSet_3ColorSmilies, 3 },
{ u"3Stars"_ustr, IconSet_3Stars, 3 },
{ u"3Triangles"_ustr, IconSet_3Triangles, 3 },
{ u"4Arrows"_ustr, IconSet_4Arrows, 4 },
{ u"4ArrowsGray"_ustr, IconSet_4ArrowsGray, 4 },
{ u"4RedToBlack"_ustr, IconSet_4RedToBlack, 4 },
{ u"4Rating"_ustr, IconSet_4Rating, 4 },
{ u"4TrafficLights"_ustr, IconSet_4TrafficLights, 4 },
{ u"5Arrows"_ustr, IconSet_5Arrows, 5 },
{ u"5ArrowsGray"_ustr, IconSet_5ArrowsGray, 5 },
{ u"5Rating"_ustr, IconSet_5Ratings, 5 },
{ u"5Quarters"_ustr, IconSet_5Quarters, 5 },
{ u"5Boxes"_ustr, IconSet_5Boxes, 5 },
{ u""_ustr, IconSet_3Arrows, 0 }
};
size_t ScIconSetFormat::size() const
{
return mpFormatData->m_Entries.size();
}
namespace {
constexpr OUString a3TrafficLights1[] = {
BMP_ICON_SET_CIRCLES1_RED, BMP_ICON_SET_CIRCLES1_YELLOW, BMP_ICON_SET_CIRCLES1_GREEN
};
constexpr OUString a3TrafficLights2[] = {
BMP_ICON_SET_TRAFFICLIGHTS_RED, BMP_ICON_SET_TRAFFICLIGHTS_YELLOW, BMP_ICON_SET_TRAFFICLIGHTS_GREEN
};
constexpr OUString a3Arrows[] = {
BMP_ICON_SET_COLORARROWS_DOWN, BMP_ICON_SET_COLORARROWS_SAME, BMP_ICON_SET_COLORARROWS_UP
};
constexpr OUString a3ArrowsGray[] = {
BMP_ICON_SET_GRAYARROWS_DOWN, BMP_ICON_SET_GRAYARROWS_SAME, BMP_ICON_SET_GRAYARROWS_UP
};
constexpr OUString a3Flags[] = {
BMP_ICON_SET_FLAGS_RED, BMP_ICON_SET_FLAGS_YELLOW, BMP_ICON_SET_FLAGS_GREEN
};
constexpr OUString a3Smilies[] = {
BMP_ICON_SET_NEGATIVE_YELLOW_SMILIE, BMP_ICON_SET_NEUTRAL_YELLOW_SMILIE, BMP_ICON_SET_POSITIVE_YELLOW_SMILIE
};
constexpr OUString a3ColorSmilies[] = {
BMP_ICON_SET_NEGATIVE_RED_SMILIE, BMP_ICON_SET_NEUTRAL_YELLOW_SMILIE, BMP_ICON_SET_POSITIVE_GREEN_SMILIE
};
constexpr OUString a3Stars[] = {
BMP_ICON_SET_STARS_EMPTY, BMP_ICON_SET_STARS_HALF, BMP_ICON_SET_STARS_FULL
};
constexpr OUString a3Triangles[] = {
BMP_ICON_SET_TRIANGLES_DOWN, BMP_ICON_SET_TRIANGLES_SAME, BMP_ICON_SET_TRIANGLES_UP
};
constexpr OUString a4Arrows[] = {
BMP_ICON_SET_COLORARROWS_DOWN, BMP_ICON_SET_COLORARROWS_SLIGHTLY_DOWN, BMP_ICON_SET_COLORARROWS_SLIGHTLY_UP, BMP_ICON_SET_COLORARROWS_UP
};
constexpr OUString a4ArrowsGray[] = {
BMP_ICON_SET_GRAYARROWS_DOWN, BMP_ICON_SET_GRAYARROWS_SLIGHTLY_DOWN, BMP_ICON_SET_GRAYARROWS_SLIGHTLY_UP, BMP_ICON_SET_GRAYARROWS_UP
};
constexpr OUString a5Arrows[] = {
BMP_ICON_SET_COLORARROWS_DOWN, BMP_ICON_SET_COLORARROWS_SLIGHTLY_DOWN,
BMP_ICON_SET_COLORARROWS_SAME, BMP_ICON_SET_COLORARROWS_SLIGHTLY_UP, BMP_ICON_SET_COLORARROWS_UP
};
constexpr OUString a5ArrowsGray[] = {
BMP_ICON_SET_GRAYARROWS_DOWN, BMP_ICON_SET_GRAYARROWS_SLIGHTLY_DOWN,
BMP_ICON_SET_GRAYARROWS_SAME, BMP_ICON_SET_GRAYARROWS_SLIGHTLY_UP, BMP_ICON_SET_GRAYARROWS_UP
};
constexpr OUString a4TrafficLights[] = {
BMP_ICON_SET_CIRCLES1_GRAY, BMP_ICON_SET_CIRCLES1_RED,
BMP_ICON_SET_CIRCLES1_YELLOW, BMP_ICON_SET_CIRCLES1_GREEN
};
constexpr OUString a5Quarters[] = {
BMP_ICON_SET_PIES_EMPTY, BMP_ICON_SET_PIES_ONE_QUARTER, BMP_ICON_SET_PIES_HALF,
BMP_ICON_SET_PIES_THREE_QUARTER, BMP_ICON_SET_PIES_FULL,
};
constexpr OUString a5Boxes[] = {
BMP_ICON_SET_SQUARES_EMPTY, BMP_ICON_SET_SQUARES_ONE_QUARTER,
BMP_ICON_SET_SQUARES_HALF, BMP_ICON_SET_SQUARES_THREE_QUARTER,
BMP_ICON_SET_SQUARES_FULL
};
constexpr OUString a3Symbols1[] = {
BMP_ICON_SET_SYMBOLS1_CROSS, BMP_ICON_SET_SYMBOLS1_EXCLAMATION_MARK, BMP_ICON_SET_SYMBOLS1_CHECK
};
constexpr OUString a3Signs[] = {
BMP_ICON_SET_SHAPES_DIAMOND, BMP_ICON_SET_SHAPES_TRIANGLE, BMP_ICON_SET_SHAPES_CIRCLE
};
constexpr OUString a4RedToBlack[] = {
BMP_ICON_SET_CIRCLES2_DARK_GRAY, BMP_ICON_SET_CIRCLES2_LIGHT_GRAY,
BMP_ICON_SET_CIRCLES2_LIGHT_RED, BMP_ICON_SET_CIRCLES2_DARK_RED
};
constexpr OUString a4Ratings[] = {
BMP_ICON_SET_BARS_ONE_QUARTER, BMP_ICON_SET_BARS_HALF,
BMP_ICON_SET_BARS_THREE_QUARTER, BMP_ICON_SET_BARS_FULL
};
constexpr OUString a5Ratings[] = {
BMP_ICON_SET_BARS_EMPTY, BMP_ICON_SET_BARS_ONE_QUARTER, BMP_ICON_SET_BARS_HALF,
BMP_ICON_SET_BARS_THREE_QUARTER, BMP_ICON_SET_BARS_FULL
};
struct ScIconSetBitmapMap {
ScIconSetType eType;
const OUString* pBitmaps;
};
const ScIconSetBitmapMap aBitmapMap[] = {
{ IconSet_3Arrows, a3Arrows },
{ IconSet_3ArrowsGray, a3ArrowsGray },
{ IconSet_3Flags, a3Flags },
{ IconSet_3Signs, a3Signs },
{ IconSet_3Symbols, a3Symbols1 },
{ IconSet_3Symbols2, a3Symbols1 },
{ IconSet_3TrafficLights1, a3TrafficLights1 },
{ IconSet_3TrafficLights2, a3TrafficLights2 },
{ IconSet_3Smilies, a3Smilies },
{ IconSet_3ColorSmilies, a3ColorSmilies },
{ IconSet_3Triangles, a3Triangles },
{ IconSet_3Stars, a3Stars },
{ IconSet_4Arrows, a4Arrows },
{ IconSet_4ArrowsGray, a4ArrowsGray },
{ IconSet_4Rating, a4Ratings },
{ IconSet_4RedToBlack, a4RedToBlack },
{ IconSet_4TrafficLights, a4TrafficLights },
{ IconSet_5Arrows, a5Arrows },
{ IconSet_5ArrowsGray, a5ArrowsGray },
{ IconSet_5Quarters, a5Quarters },
{ IconSet_5Ratings, a5Ratings },
{ IconSet_5Boxes, a5Boxes }
};
const ScIconSetMap* findIconSetType(ScIconSetType eType)
{
const ScIconSetMap* pMap = ScIconSetFormat::g_IconSetMap;
for (; !pMap->aName.isEmpty(); ++pMap)
{
if (pMap->eType == eType)
return pMap;
}
return nullptr;
}
}
OUString ScIconSetFormat::getIconSetName( ScIconSetType eType )
{
const ScIconSetMap* pMap = findIconSetType(eType);
if (pMap)
return pMap->aName;
return u""_ustr;
}
sal_Int32 ScIconSetFormat::getIconSetElements( ScIconSetType eType )
{
const ScIconSetMap* pMap = findIconSetType(eType);
if (pMap)
return pMap->nElements;
return 0;
}
OUString ScIconSetFormat::getIconName(ScIconSetType const eType, sal_Int32 const nIndex)
{
OUString sBitmap;
for(const ScIconSetBitmapMap & i : aBitmapMap)
{
if(i.eType == eType)
{
sBitmap = *(i.pBitmaps + nIndex);
break;
}
}
assert(!sBitmap.isEmpty());
return sBitmap;
}
BitmapEx& ScIconSetFormat::getBitmap(sc::IconSetBitmapMap & rIconSetBitmapMap,
ScIconSetType const eType, sal_Int32 const nIndex)
{
OUString sBitmap(ScIconSetFormat::getIconName(eType, nIndex));
std::map<OUString, BitmapEx>::iterator itr = rIconSetBitmapMap.find(sBitmap);
if (itr != rIconSetBitmapMap.end())
return itr->second;
BitmapEx aBitmap(sBitmap);
std::pair<OUString, BitmapEx> aPair(sBitmap, aBitmap);
std::pair<std::map<OUString, BitmapEx>::iterator, bool> itrNew = rIconSetBitmapMap.insert(aPair);
assert(itrNew.second);
return itrNew.first->second;
}
void ScIconSetFormat::EnsureSize()
{
ScIconSetType eType = mpFormatData->eIconSetType;
for (const ScIconSetMap & i : g_IconSetMap)
{
if (i.eType == eType)
{
// size_t nElements = aIconSetMap[i].nElements;
// TODO: implement
break;
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V1023 A pointer without owner is added to the 'maColorScales' container by the 'emplace_back' method. A memory leak will occur in case of an exception.
↑ V1023 A pointer without owner is added to the 'm_Entries' container by the 'emplace_back' method. A memory leak will occur in case of an exception.