diff options
Diffstat (limited to 'sc/source/filter/xml/xmlcondformat.cxx')
-rw-r--r-- | sc/source/filter/xml/xmlcondformat.cxx | 1009 |
1 files changed, 1009 insertions, 0 deletions
diff --git a/sc/source/filter/xml/xmlcondformat.cxx b/sc/source/filter/xml/xmlcondformat.cxx new file mode 100644 index 000000000..77280be47 --- /dev/null +++ b/sc/source/filter/xml/xmlcondformat.cxx @@ -0,0 +1,1009 @@ +/* -*- 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 "xmlcondformat.hxx" +#include "xmlimprt.hxx" +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlictxt.hxx> +#include <xmloff/xmltoken.hxx> +#include <sal/log.hxx> + +#include <compiler.hxx> +#include <colorscale.hxx> +#include <conditio.hxx> +#include <document.hxx> +#include <sax/tools/converter.hxx> +#include <rangelst.hxx> +#include <rangeutl.hxx> +#include "XMLConverter.hxx" +#include <stylehelper.hxx> +#include <tokenarray.hxx> + +using namespace xmloff::token; + +ScXMLConditionalFormatsContext::ScXMLConditionalFormatsContext( ScXMLImport& rImport ): + ScXMLImportContext( rImport ) +{ + GetScImport().SetNewCondFormatData(); + GetScImport().GetDocument()->SetCondFormList(new ScConditionalFormatList(), GetScImport().GetTables().GetCurrentSheet()); +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLConditionalFormatsContext::createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + SvXMLImportContext* pContext = nullptr; + sax_fastparser::FastAttributeList *pAttribList = + &sax_fastparser::castToFastAttributeList( xAttrList ); + + switch (nElement) + { + case XML_ELEMENT( CALC_EXT, XML_CONDITIONAL_FORMAT ): + pContext = new ScXMLConditionalFormatContext( GetScImport(), pAttribList, *this ); + break; + } + + return pContext; +} + +IMPL_LINK(ScXMLConditionalFormatsContext, FormatDeletedHdl, ScConditionalFormat*, pFormat, void) +{ + mvCondFormatData.erase(std::remove_if(mvCondFormatData.begin(), mvCondFormatData.end(), + [pFormat](CondFormatData& r){ return r.mpFormat == pFormat; }), + mvCondFormatData.end()); +} + +void SAL_CALL ScXMLConditionalFormatsContext::endFastElement( sal_Int32 /*nElement*/ ) +{ + ScDocument* pDoc = GetScImport().GetDocument(); + + SCTAB nTab = GetScImport().GetTables().GetCurrentSheet(); + ScConditionalFormatList* pCondFormatList = pDoc->GetCondFormList(nTab); + bool bDeleted = !pCondFormatList->CheckAllEntries(LINK(this, ScXMLConditionalFormatsContext, FormatDeletedHdl)); + + SAL_WARN_IF(bDeleted, "sc", "conditional formats have been deleted because they contained empty range info"); + + for (const auto& i : mvCondFormatData) + { + pDoc->AddCondFormatData( i.mpFormat->GetRange(), i.mnTab, i.mpFormat->GetKey() ); + } +} + +ScXMLConditionalFormatContext::ScXMLConditionalFormatContext( ScXMLImport& rImport, + const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, + ScXMLConditionalFormatsContext& rParent ): + ScXMLImportContext( rImport ), + mrParent( rParent ) +{ + OUString sRange; + + if ( rAttrList.is() ) + { + for (auto &aIter : *rAttrList) + { + switch (aIter.getToken()) + { + case XML_ELEMENT( CALC_EXT, XML_TARGET_RANGE_ADDRESS ): + sRange = aIter.toString(); + break; + default: + break; + } + } + } + + ScRangeList aRangeList; + ScDocument* pDoc = GetScImport().GetDocument(); + assert(pDoc); + ScRangeStringConverter::GetRangeListFromString(aRangeList, sRange, *pDoc, + formula::FormulaGrammar::CONV_ODF); + + mxFormat.reset(new ScConditionalFormat(0, pDoc)); + mxFormat->SetRange(aRangeList); +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLConditionalFormatContext::createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + SvXMLImportContext* pContext = nullptr; + sax_fastparser::FastAttributeList *pAttribList = + &sax_fastparser::castToFastAttributeList( xAttrList ); + + switch (nElement) + { + case XML_ELEMENT( CALC_EXT, XML_CONDITION ): + pContext = new ScXMLCondContext( GetScImport(), pAttribList, mxFormat.get() ); + break; + case XML_ELEMENT( CALC_EXT, XML_COLOR_SCALE ): + pContext = new ScXMLColorScaleFormatContext( GetScImport(), mxFormat.get() ); + break; + case XML_ELEMENT( CALC_EXT, XML_DATA_BAR ): + pContext = new ScXMLDataBarFormatContext( GetScImport(), pAttribList, mxFormat.get() ); + break; + case XML_ELEMENT( CALC_EXT, XML_ICON_SET ): + pContext = new ScXMLIconSetFormatContext( GetScImport(), pAttribList, mxFormat.get() ); + break; + case XML_ELEMENT( CALC_EXT, XML_DATE_IS ): + pContext = new ScXMLDateContext( GetScImport(), pAttribList, mxFormat.get() ); + break; + default: + break; + } + + return pContext; +} + +static bool HasRelRefIgnoringSheet0Relative( ScDocument* pDoc, const ScTokenArray* pTokens, sal_uInt16 nRecursion = 0 ) +{ + if (pTokens) + { + formula::FormulaTokenArrayPlainIterator aIter( *pTokens ); + formula::FormulaToken* t; + for( t = aIter.Next(); t; t = aIter.Next() ) + { + switch( t->GetType() ) + { + case formula::svDoubleRef: + { + ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2; + if ( rRef2.IsColRel() || rRef2.IsRowRel() || (rRef2.IsFlag3D() && rRef2.IsTabRel()) ) + return true; + [[fallthrough]]; + } + + case formula::svSingleRef: + { + ScSingleRefData& rRef1 = *t->GetSingleRef(); + if ( rRef1.IsColRel() || rRef1.IsRowRel() || (rRef1.IsFlag3D() && rRef1.IsTabRel()) ) + return true; + } + break; + + case formula::svIndex: + { + if( t->GetOpCode() == ocName ) // DB areas always absolute + if( ScRangeData* pRangeData = pDoc->FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex()) ) + if( (nRecursion < 42) && HasRelRefIgnoringSheet0Relative( pDoc, pRangeData->GetCode(), nRecursion + 1 ) ) + return true; + } + break; + + // #i34474# function result dependent on cell position + case formula::svByte: + { + switch( t->GetOpCode() ) + { + case ocRow: // ROW() returns own row index + case ocColumn: // COLUMN() returns own column index + case ocSheet: // SHEET() returns own sheet index + case ocCell: // CELL() may return own cell address + return true; + default: + break; + } + } + break; + + default: + break; + } + } + } + return false; +} + +static bool HasOneSingleFullyRelativeReference( const ScTokenArray* pTokens, ScSingleRefData& rOffset ) +{ + int nCount = 0; + if (pTokens) + { + formula::FormulaTokenArrayPlainIterator aIter( *pTokens ); + formula::FormulaToken* t; + for( t = aIter.Next(); t; t = aIter.Next() ) + { + switch( t->GetType() ) + { + case formula::svSingleRef: + { + ScSingleRefData& rRef1 = *t->GetSingleRef(); + if ( rRef1.IsColRel() && rRef1.IsRowRel() && !rRef1.IsFlag3D() && rRef1.IsTabRel() ) + { + nCount++; + if (nCount == 1) + { + rOffset = rRef1; + } + } + } + break; + + default: + break; + } + } + } + return nCount == 1; +} + +void SAL_CALL ScXMLConditionalFormatContext::endFastElement( sal_Int32 /*nElement*/ ) +{ + ScDocument* pDoc = GetScImport().GetDocument(); + assert(pDoc); + + SCTAB nTab = GetScImport().GetTables().GetCurrentSheet(); + std::unique_ptr<ScConditionalFormat> pFormat(std::move(mxFormat)); + + bool bEligibleForCache = true; + bool bSingleRelativeReference = false; + ScSingleRefData aOffsetForSingleRelRef; + std::unique_ptr<ScTokenArray> pTokens; + for (size_t nFormatEntryIx = 0; nFormatEntryIx < pFormat->size(); ++nFormatEntryIx) + { + auto pFormatEntry = pFormat->GetEntry(nFormatEntryIx); + auto pCondFormatEntry = dynamic_cast<const ScCondFormatEntry*>(pFormatEntry); + + if (pCondFormatEntry == nullptr || + (pCondFormatEntry->GetOperation() != ScConditionMode::Equal && + pCondFormatEntry->GetOperation() != ScConditionMode::Direct)) + { + bEligibleForCache = false; + break; + } + + ScAddress aSrcPos; + OUString aSrcString = pCondFormatEntry->GetSrcString(); + if ( !aSrcString.isEmpty() ) + aSrcPos.Parse( aSrcString, *pDoc ); + ScCompiler aComp( *pDoc, aSrcPos ); + aComp.SetGrammar( formula::FormulaGrammar::GRAM_ODFF ); + pTokens = aComp.CompileString( pCondFormatEntry->GetExpression(aSrcPos, 0), "" ); + if (HasRelRefIgnoringSheet0Relative( pDoc, pTokens.get() )) + { + // In general not eligible, but some might be. We handle one very special case: When the + // conditional format has one entry, the reference position is the first cell of the + // range, and with a single fully relative reference in its expression. (Possibly these + // conditions could be loosened, but I am too tired to think on that right now.) + if (pFormat->size() == 1 && + pFormat->GetRange().size() == 1 && + pFormat->GetRange()[0].aStart == aSrcPos && + HasOneSingleFullyRelativeReference( pTokens.get(), aOffsetForSingleRelRef )) + { + bSingleRelativeReference = true; + } + else + { + bEligibleForCache = false; + break; + } + } + } + + if (bEligibleForCache) + { + for (auto& aCacheEntry : mrParent.maCache) + if (aCacheEntry.mnAge < SAL_MAX_INT64) + aCacheEntry.mnAge++; + + for (auto& aCacheEntry : mrParent.maCache) + { + if (!aCacheEntry.mpFormat) + continue; + + if (aCacheEntry.mpFormat->size() != pFormat->size()) + continue; + + // Check if the conditional format is identical to an existing one (but with different range) and can be shared + for (size_t nFormatEntryIx = 0; nFormatEntryIx < pFormat->size(); ++nFormatEntryIx) + { + auto pCacheFormatEntry = aCacheEntry.mpFormat->GetEntry(nFormatEntryIx); + auto pFormatEntry = pFormat->GetEntry(nFormatEntryIx); + if (pCacheFormatEntry->GetType() != pFormatEntry->GetType() || + pFormatEntry->GetType() != ScFormatEntry::Type::Condition) + break; + + auto pCacheCondFormatEntry = static_cast<const ScCondFormatEntry*>(pCacheFormatEntry); + auto pCondFormatEntry = static_cast<const ScCondFormatEntry*>(pFormatEntry); + + if (pCacheCondFormatEntry->GetStyle() != pCondFormatEntry->GetStyle()) + break; + + // Note That comparing the formulas of the ScConditionEntry at this stage is + // comparing just the *strings* of the formulas. For the bSingleRelativeReference + // case we compare the tokenized ("compiled") formulas. + if (bSingleRelativeReference) + { + if (aCacheEntry.mbSingleRelativeReference && + pTokens->EqualTokens(aCacheEntry.mpTokens.get())) + ; + else + break; + } + else if (!pCacheCondFormatEntry->IsEqual(*pCondFormatEntry, /*bIgnoreSrcPos*/true)) + { + break; + } + // If we get here on the last round through the for loop, we have a cache hit + if (nFormatEntryIx == pFormat->size() - 1) + { + // Mark cache entry as fresh, do necessary mangling of it and just return + aCacheEntry.mnAge = 0; + for (size_t k = 0; k < pFormat->GetRange().size(); ++k) + aCacheEntry.mpFormat->GetRangeList().Join(pFormat->GetRange()[k]); + return; + } + } + } + } + + sal_uLong nIndex = pDoc->AddCondFormat(std::move(pFormat), nTab); + ScConditionalFormat* pInsertedFormat = pDoc->GetCondFormList(nTab)->GetFormat(nIndex); + assert(pInsertedFormat && pInsertedFormat->GetKey() == nIndex); + + mrParent.mvCondFormatData.push_back( { pInsertedFormat, nTab } ); + + if (!bEligibleForCache) + return; + + // Not found in cache, replace oldest cache entry + sal_Int64 nOldestAge = -1; + size_t nIndexOfOldest = 0; + for (auto& aCacheEntry : mrParent.maCache) + { + if (aCacheEntry.mnAge > nOldestAge) + { + nOldestAge = aCacheEntry.mnAge; + nIndexOfOldest = (&aCacheEntry - &mrParent.maCache.front()); + } + } + mrParent.maCache[nIndexOfOldest].mpFormat = pInsertedFormat; + mrParent.maCache[nIndexOfOldest].mbSingleRelativeReference = bSingleRelativeReference; + mrParent.maCache[nIndexOfOldest].mpTokens = std::move(pTokens); + mrParent.maCache[nIndexOfOldest].mnAge = 0; +} + +ScXMLConditionalFormatContext::~ScXMLConditionalFormatContext() +{ +} + +ScXMLColorScaleFormatContext::ScXMLColorScaleFormatContext( ScXMLImport& rImport, + ScConditionalFormat* pFormat): + ScXMLImportContext( rImport ), + pColorScaleFormat(nullptr) +{ + pColorScaleFormat = new ScColorScaleFormat(GetScImport().GetDocument()); + pFormat->AddEntry(pColorScaleFormat); +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLColorScaleFormatContext::createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + SvXMLImportContext* pContext = nullptr; + sax_fastparser::FastAttributeList *pAttribList = + &sax_fastparser::castToFastAttributeList( xAttrList ); + + switch (nElement) + { + case XML_ELEMENT( CALC_EXT, XML_COLOR_SCALE_ENTRY ): + pContext = new ScXMLColorScaleFormatEntryContext( GetScImport(), pAttribList, pColorScaleFormat ); + break; + default: + break; + } + + return pContext; +} + +ScXMLDataBarFormatContext::ScXMLDataBarFormatContext( ScXMLImport& rImport, + const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, + ScConditionalFormat* pFormat): + ScXMLImportContext( rImport ), + mpFormatData(nullptr), + mpParent(pFormat), + mnIndex(0) +{ + OUString sPositiveColor; + OUString sNegativeColor; + OUString sGradient; + OUString sAxisPosition; + OUString sShowValue; + OUString sAxisColor; + OUString sMinLength; + OUString sMaxLength; + + if ( rAttrList.is() ) + { + for (auto &aIter : *rAttrList) + { + switch (aIter.getToken()) + { + case XML_ELEMENT( CALC_EXT, XML_POSITIVE_COLOR ): + sPositiveColor = aIter.toString(); + break; + case XML_ELEMENT( CALC_EXT, XML_GRADIENT ): + sGradient = aIter.toString(); + break; + case XML_ELEMENT( CALC_EXT, XML_NEGATIVE_COLOR ): + sNegativeColor = aIter.toString(); + break; + case XML_ELEMENT( CALC_EXT, XML_AXIS_POSITION ): + sAxisPosition = aIter.toString(); + break; + case XML_ELEMENT( CALC_EXT, XML_SHOW_VALUE ): + sShowValue = aIter.toString(); + break; + case XML_ELEMENT( CALC_EXT, XML_AXIS_COLOR ): + sAxisColor = aIter.toString(); + break; + case XML_ELEMENT( CALC_EXT, XML_MIN_LENGTH ): + sMinLength = aIter.toString(); + break; + case XML_ELEMENT( CALC_EXT, XML_MAX_LENGTH ): + sMaxLength = aIter.toString(); + break; + default: + break; + } + } + } + + ScDataBarFormat* pDataBarFormat = new ScDataBarFormat(rImport.GetDocument()); + mpFormatData = new ScDataBarFormatData(); + pDataBarFormat->SetDataBarData(mpFormatData); + if(!sGradient.isEmpty()) + { + bool bGradient = true; + (void)sax::Converter::convertBool( bGradient, sGradient); + mpFormatData->mbGradient = bGradient; + } + + if(!sPositiveColor.isEmpty()) + { + sax::Converter::convertColor( mpFormatData->maPositiveColor, sPositiveColor ); + } + + if(!sNegativeColor.isEmpty()) + { + // we might check here for 0xff0000 and don't write it + Color nColor; + sax::Converter::convertColor( nColor, sNegativeColor ); + mpFormatData->mxNegativeColor = nColor; + } + else + mpFormatData->mbNeg = false; + + if(!sAxisPosition.isEmpty()) + { + if(sAxisPosition == "middle") + mpFormatData->meAxisPosition = databar::MIDDLE; + else if (sAxisPosition == "none") + mpFormatData->meAxisPosition = databar::NONE; + } + + if(!sAxisColor.isEmpty()) + { + sax::Converter::convertColor( mpFormatData->maAxisColor, sAxisColor ); + } + + if(!sShowValue.isEmpty()) + { + bool bShowValue = true; + (void)sax::Converter::convertBool( bShowValue, sShowValue ); + mpFormatData->mbOnlyBar = !bShowValue; + } + + if (!sMinLength.isEmpty()) + { + double nVal = sMinLength.toDouble(); + mpFormatData->mnMinLength = nVal; + } + + if (!sMaxLength.isEmpty()) + { + double nVal = sMaxLength.toDouble(); + if (nVal == 0.0) + nVal = 100.0; + mpFormatData->mnMaxLength = nVal; + } + + pFormat->AddEntry(pDataBarFormat); +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLDataBarFormatContext::createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + SvXMLImportContext* pContext = nullptr; + sax_fastparser::FastAttributeList *pAttribList = + &sax_fastparser::castToFastAttributeList( xAttrList ); + + switch (nElement) + { + case XML_ELEMENT( CALC_EXT, XML_FORMATTING_ENTRY ): + case XML_ELEMENT( CALC_EXT, XML_DATA_BAR_ENTRY ): + { + ScColorScaleEntry* pEntry(nullptr); + pContext = new ScXMLFormattingEntryContext( GetScImport(), pAttribList, pEntry ); + pEntry->SetRepaintCallback(mpParent); + if(mnIndex == 0) + { + mpFormatData->mpLowerLimit.reset(pEntry); + } + else if (mnIndex == 1) + { + mpFormatData->mpUpperLimit.reset(pEntry); + } + else + { + // data bars only support 2 entries + assert(false); + } + ++mnIndex; + } + break; + default: + break; + } + + return pContext; +} + +ScXMLIconSetFormatContext::ScXMLIconSetFormatContext(ScXMLImport& rImport, + const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, + ScConditionalFormat* pFormat): + ScXMLImportContext( rImport ), + mpParent(pFormat) +{ + OUString aIconSetType, sShowValue; + if ( rAttrList.is() ) + { + for (auto &aIter : *rAttrList) + { + switch (aIter.getToken()) + { + case XML_ELEMENT( CALC_EXT, XML_ICON_SET_TYPE ): + aIconSetType = aIter.toString(); + break; + case XML_ELEMENT( CALC_EXT, XML_SHOW_VALUE ): + sShowValue = aIter.toString(); + break; + default: + break; + } + } + } + + const ScIconSetMap* pMap = ScIconSetFormat::g_IconSetMap; + ScIconSetType eType = IconSet_3Arrows; + for(; pMap->pName; ++pMap) + { + OUString aName = OUString::createFromAscii(pMap->pName); + if(aName ==aIconSetType) + { + eType = pMap->eType; + break; + } + } + + ScIconSetFormat* pIconSetFormat = new ScIconSetFormat(GetScImport().GetDocument()); + ScIconSetFormatData* pIconSetFormatData = new ScIconSetFormatData; + + if(!sShowValue.isEmpty()) + { + bool bShowValue = true; + (void)sax::Converter::convertBool( bShowValue, sShowValue ); + pIconSetFormatData->mbShowValue = !bShowValue; + } + + pIconSetFormatData->eIconSetType = eType; + pIconSetFormat->SetIconSetData(pIconSetFormatData); + pFormat->AddEntry(pIconSetFormat); + + mpFormatData = pIconSetFormatData; +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLIconSetFormatContext::createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + SvXMLImportContext* pContext = nullptr; + sax_fastparser::FastAttributeList *pAttribList = + &sax_fastparser::castToFastAttributeList( xAttrList ); + + switch (nElement) + { + case XML_ELEMENT( CALC_EXT, XML_FORMATTING_ENTRY ): + { + ScColorScaleEntry* pEntry(nullptr); + pContext = new ScXMLFormattingEntryContext( GetScImport(), pAttribList, pEntry ); + mpFormatData->m_Entries.emplace_back(pEntry); + pEntry->SetRepaintCallback(mpParent); + } + break; + default: + break; + } + + return pContext; +} + +namespace { + +void GetConditionData(const OUString& rValue, ScConditionMode& eMode, OUString& rExpr1, OUString& rExpr2) +{ + if(rValue.startsWith("unique")) + { + eMode = ScConditionMode::NotDuplicate; + } + else if(rValue.startsWith("duplicate")) + { + eMode = ScConditionMode::Duplicate; + } + else if(rValue.startsWith("between")) + { + const sal_Unicode* pStr = rValue.getStr(); + const sal_Unicode* pStart = pStr + 8; + const sal_Unicode* pEnd = pStr + rValue.getLength(); + rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ','); + rExpr2 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')'); + eMode = ScConditionMode::Between; + } + else if(rValue.startsWith("not-between")) + { + const sal_Unicode* pStr = rValue.getStr(); + const sal_Unicode* pStart = pStr + 12; + const sal_Unicode* pEnd = pStr + rValue.getLength(); + rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ','); + rExpr2 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')'); + eMode = ScConditionMode::NotBetween; + } + else if(rValue.startsWith("<=")) + { + rExpr1 = rValue.copy(2); + eMode = ScConditionMode::EqLess; + } + else if(rValue.startsWith(">=")) + { + rExpr1 = rValue.copy(2); + eMode = ScConditionMode::EqGreater; + } + else if(rValue.startsWith("!=")) + { + rExpr1 = rValue.copy(2); + eMode = ScConditionMode::NotEqual; + } + else if(rValue.startsWith("<")) + { + rExpr1 = rValue.copy(1); + eMode = ScConditionMode::Less; + } + else if(rValue.startsWith("=")) + { + rExpr1 = rValue.copy(1); + eMode = ScConditionMode::Equal; + } + else if(rValue.startsWith(">")) + { + rExpr1 = rValue.copy(1); + eMode = ScConditionMode::Greater; + } + else if(rValue.startsWith("formula-is")) + { + const sal_Unicode* pStr = rValue.getStr(); + const sal_Unicode* pStart = pStr + 11; + const sal_Unicode* pEnd = pStr + rValue.getLength(); + rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')'); + eMode = ScConditionMode::Direct; + } + else if(rValue.startsWith("top-elements")) + { + const sal_Unicode* pStr = rValue.getStr(); + const sal_Unicode* pStart = pStr + 13; + const sal_Unicode* pEnd = pStr + rValue.getLength(); + rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')'); + eMode = ScConditionMode::Top10; + } + else if(rValue.startsWith("bottom-elements")) + { + const sal_Unicode* pStr = rValue.getStr(); + const sal_Unicode* pStart = pStr + 16; + const sal_Unicode* pEnd = pStr + rValue.getLength(); + rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')'); + eMode = ScConditionMode::Bottom10; + } + else if(rValue.startsWith("top-percent")) + { + const sal_Unicode* pStr = rValue.getStr(); + const sal_Unicode* pStart = pStr + 12; + const sal_Unicode* pEnd = pStr + rValue.getLength(); + rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')'); + eMode = ScConditionMode::TopPercent; + } + else if(rValue.startsWith("bottom-percent")) + { + const sal_Unicode* pStr = rValue.getStr(); + const sal_Unicode* pStart = pStr + 15; + const sal_Unicode* pEnd = pStr + rValue.getLength(); + rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')'); + eMode = ScConditionMode::BottomPercent; + } + else if(rValue.startsWith("above-average")) + { + eMode = ScConditionMode::AboveAverage; + } + else if(rValue.startsWith("below-average")) + { + eMode = ScConditionMode::BelowAverage; + } + else if(rValue.startsWith("above-equal-average")) + { + eMode = ScConditionMode::AboveEqualAverage; + } + else if(rValue.startsWith("below-equal-average")) + { + eMode = ScConditionMode::BelowEqualAverage; + } + else if(rValue.startsWith("is-error")) + { + eMode = ScConditionMode::Error; + } + else if(rValue.startsWith("is-no-error")) + { + eMode = ScConditionMode::NoError; + } + else if(rValue.startsWith("begins-with")) + { + eMode = ScConditionMode::BeginsWith; + const sal_Unicode* pStr = rValue.getStr(); + const sal_Unicode* pStart = pStr + 12; + const sal_Unicode* pEnd = pStr + rValue.getLength(); + rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')'); + } + else if(rValue.startsWith("ends-with")) + { + eMode = ScConditionMode::EndsWith; + const sal_Unicode* pStr = rValue.getStr(); + const sal_Unicode* pStart = pStr + 10; + const sal_Unicode* pEnd = pStr + rValue.getLength(); + rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')'); + } + else if(rValue.startsWith("contains-text")) + { + eMode = ScConditionMode::ContainsText; + const sal_Unicode* pStr = rValue.getStr(); + const sal_Unicode* pStart = pStr + 14; + const sal_Unicode* pEnd = pStr + rValue.getLength(); + rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')'); + } + else if(rValue.startsWith("not-contains-text")) + { + eMode = ScConditionMode::NotContainsText; + const sal_Unicode* pStr = rValue.getStr(); + const sal_Unicode* pStart = pStr + 18; + const sal_Unicode* pEnd = pStr + rValue.getLength(); + rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')'); + } + else + eMode = ScConditionMode::NONE; +} + +} + +ScXMLCondContext::ScXMLCondContext( ScXMLImport& rImport, + const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, + ScConditionalFormat* pFormat ): + ScXMLImportContext( rImport ) +{ + OUString sExpression; + OUString sStyle; + OUString sAddress; + + if ( rAttrList.is() ) + { + for (auto &aIter : *rAttrList) + { + switch (aIter.getToken()) + { + case XML_ELEMENT( CALC_EXT, XML_VALUE ): + sExpression = aIter.toString(); + break; + case XML_ELEMENT( CALC_EXT, XML_APPLY_STYLE_NAME ): + sStyle = ScStyleNameConversion::ProgrammaticToDisplayName(aIter.toString(), SfxStyleFamily::Para ); + break; + case XML_ELEMENT( CALC_EXT, XML_BASE_CELL_ADDRESS ): + sAddress = aIter.toString(); + break; + default: + break; + } + } + } + + OUString aExpr1; + OUString aExpr2; + ScConditionMode eMode; + GetConditionData(sExpression, eMode, aExpr1, aExpr2); + + ScCondFormatEntry* pFormatEntry = new ScCondFormatEntry(eMode, aExpr1, aExpr2, *GetScImport().GetDocument(), ScAddress(), sStyle, + OUString(), OUString(), formula::FormulaGrammar::GRAM_ODFF, formula::FormulaGrammar::GRAM_ODFF); + pFormatEntry->SetSrcString(sAddress); + + pFormat->AddEntry(pFormatEntry); +} + +namespace { + +void setColorEntryType(std::u16string_view rType, ScColorScaleEntry* pEntry, const OUString& rFormula, + ScXMLImport& rImport) +{ + if(rType == u"minimum") + pEntry->SetType(COLORSCALE_MIN); + else if(rType == u"maximum") + pEntry->SetType(COLORSCALE_MAX); + else if(rType == u"percentile") + pEntry->SetType(COLORSCALE_PERCENTILE); + else if(rType == u"percent") + pEntry->SetType(COLORSCALE_PERCENT); + else if(rType == u"formula") + { + pEntry->SetType(COLORSCALE_FORMULA); + //position does not matter, only table is important + pEntry->SetFormula(rFormula, *rImport.GetDocument(), ScAddress(0,0,rImport.GetTables().GetCurrentSheet()), formula::FormulaGrammar::GRAM_ODFF); + } + else if(rType == u"auto-minimum") + pEntry->SetType(COLORSCALE_AUTO); + else if(rType == u"auto-maximum") + pEntry->SetType(COLORSCALE_AUTO); +} + +} + +ScXMLColorScaleFormatEntryContext::ScXMLColorScaleFormatEntryContext( ScXMLImport& rImport, + const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, + ScColorScaleFormat* pFormat): + ScXMLImportContext( rImport ) +{ + double nVal = 0; + Color aColor; + + OUString sType; + OUString sVal; + OUString sColor; + + if ( rAttrList.is() ) + { + for (auto &aIter : *rAttrList) + { + switch (aIter.getToken()) + { + case XML_ELEMENT( CALC_EXT, XML_TYPE ): + sType = aIter.toString(); + break; + case XML_ELEMENT( CALC_EXT, XML_VALUE ): + sVal = aIter.toString(); + break; + case XML_ELEMENT( CALC_EXT, XML_COLOR ): + sColor = aIter.toString(); + break; + default: + break; + } + } + } + + sax::Converter::convertColor(aColor, sColor); + + if(!sVal.isEmpty()) + sax::Converter::convertDouble(nVal, sVal); + + auto pFormatEntry = new ScColorScaleEntry(nVal, aColor); + setColorEntryType(sType, pFormatEntry, sVal, GetScImport()); + pFormat->AddEntry(pFormatEntry); +} + +ScXMLFormattingEntryContext::ScXMLFormattingEntryContext( ScXMLImport& rImport, + const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, + ScColorScaleEntry*& pColorScaleEntry): + ScXMLImportContext( rImport ) +{ + OUString sVal; + OUString sType; + + if ( rAttrList.is() ) + { + for (auto &aIter : *rAttrList) + { + switch (aIter.getToken()) + { + case XML_ELEMENT( CALC_EXT, XML_TYPE ): + sType = aIter.toString(); + break; + case XML_ELEMENT( CALC_EXT, XML_VALUE ): + sVal = aIter.toString(); + break; + default: + break; + } + } + } + + double nVal = 0; + if(!sVal.isEmpty()) + sax::Converter::convertDouble(nVal, sVal); + + pColorScaleEntry = new ScColorScaleEntry(nVal, Color()); + setColorEntryType(sType, pColorScaleEntry, sVal, GetScImport()); +} + +namespace { + +condformat::ScCondFormatDateType getDateFromString(const OUString& rString) +{ + if(rString == "today") + return condformat::TODAY; + else if(rString == "yesterday") + return condformat::YESTERDAY; + else if(rString == "tomorrow") + return condformat::TOMORROW; + else if(rString == "last-7-days") + return condformat::LAST7DAYS; + else if(rString == "this-week") + return condformat::THISWEEK; + else if(rString == "last-week") + return condformat::LASTWEEK; + else if(rString == "next-week") + return condformat::NEXTWEEK; + else if(rString == "this-month") + return condformat::THISMONTH; + else if(rString == "last-month") + return condformat::LASTMONTH; + else if(rString == "next-month") + return condformat::NEXTMONTH; + else if(rString == "this-year") + return condformat::THISYEAR; + else if(rString == "last-year") + return condformat::LASTYEAR; + else if(rString == "next-year") + return condformat::NEXTYEAR; + + SAL_WARN("sc", "unknown date type: " << rString); + return condformat::TODAY; +} + +} + +ScXMLDateContext::ScXMLDateContext( ScXMLImport& rImport, + const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, + ScConditionalFormat* pFormat ): + ScXMLImportContext( rImport ) +{ + OUString sDateType, sStyle; + if ( rAttrList.is() ) + { + for (auto &aIter : *rAttrList) + { + switch (aIter.getToken()) + { + case XML_ELEMENT( CALC_EXT, XML_DATE ): + sDateType = aIter.toString(); + break; + case XML_ELEMENT( CALC_EXT, XML_STYLE ): + sStyle = ScStyleNameConversion::ProgrammaticToDisplayName(aIter.toString(), SfxStyleFamily::Para ); + break; + default: + break; + } + } + } + + ScCondDateFormatEntry* pFormatEntry = new ScCondDateFormatEntry(GetScImport().GetDocument()); + pFormatEntry->SetStyleName(sStyle); + pFormatEntry->SetDateType(getDateFromString(sDateType)); + pFormat->AddEntry(pFormatEntry); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |