/* -*- 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 <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/lang/Locale.hpp>
#include <osl/diagnose.h>
#include <sal/log.hxx>
#include <unotools/localedatawrapper.hxx>
#include <formulaopt.hxx>
#include <global.hxx>
#include <formulagroup.hxx>
#include <sc.hrc>
#include <utility>
using namespace utl;
using namespace com::sun::star::uno;
namespace lang = ::com::sun::star::lang;
ScFormulaOptions::ScFormulaOptions()
{
SetDefaults();
}
void ScFormulaOptions::SetDefaults()
{
bUseEnglishFuncName = false;
eFormulaGrammar = ::formula::FormulaGrammar::GRAM_NATIVE;
mbWriteCalcConfig = true;
meOOXMLRecalc = RECALC_ASK;
meODFRecalc = RECALC_ASK;
meReCalcOptiRowHeights = RECALC_ASK;
// unspecified means use the current formula syntax.
aCalcConfig.reset();
ResetFormulaSeparators();
}
void ScFormulaOptions::ResetFormulaSeparators()
{
GetDefaultFormulaSeparators(aFormulaSepArg, aFormulaSepArrayCol, aFormulaSepArrayRow);
}
void ScFormulaOptions::GetDefaultFormulaSeparators(
OUString& rSepArg, OUString& rSepArrayCol, OUString& rSepArrayRow)
{
// Defaults to the old separator values.
rSepArg = ";";
rSepArrayCol = ";";
rSepArrayRow = "|";
const lang::Locale& rLocale = ScGlobal::GetLocale();
const OUString& rLang = rLocale.Language;
if (rLang == "ru")
// Don't do automatic guess for these languages, and fall back to
// the old separator set.
return;
const LocaleDataWrapper& rLocaleData = ScGlobal::getLocaleData();
const OUString& rDecSep = rLocaleData.getNumDecimalSep();
const OUString& rListSep = rLocaleData.getListSep();
if (rDecSep.isEmpty() || rListSep.isEmpty())
// Something is wrong. Stick with the default separators.
return;
sal_Unicode cDecSep = rDecSep[0];
sal_Unicode cListSep = rListSep[0];
sal_Unicode cDecSepAlt = rLocaleData.getNumDecimalSepAlt().toChar(); // usually 0 (empty)
// Excel by default uses system's list separator as the parameter
// separator, which in English locales is a comma. However, OOo's list
// separator value is set to ';' for all English locales. Because of this
// discrepancy, we will hardcode the separator value here, for now.
// Similar for decimal separator alternative.
// However, if the decimal separator alternative is '.' and the decimal
// separator is ',' this makes no sense, fall back to ';' in that case.
if (cDecSep == '.' || (cDecSepAlt == '.' && cDecSep != ','))
cListSep = ',';
else if (cDecSep == ',' && cDecSepAlt == '.')
cListSep = ';';
// Special case for de_CH locale.
if (rLocale.Language == "de" && rLocale.Country == "CH")
cListSep = ';';
// by default, the parameter separator equals the locale-specific
// list separator.
rSepArg = OUString(cListSep);
if (cDecSep == cListSep && cDecSep != ';')
// if the decimal and list separators are equal, set the
// parameter separator to be ';', unless they are both
// semicolon in which case don't change the decimal separator.
rSepArg = ";";
rSepArrayCol = ",";
if (cDecSep == ',')
rSepArrayCol = ".";
rSepArrayRow = ";";
}
bool ScFormulaOptions::operator==( const ScFormulaOptions& rOpt ) const
{
return bUseEnglishFuncName == rOpt.bUseEnglishFuncName
&& eFormulaGrammar == rOpt.eFormulaGrammar
&& aCalcConfig == rOpt.aCalcConfig
&& mbWriteCalcConfig == rOpt.mbWriteCalcConfig
&& aFormulaSepArg == rOpt.aFormulaSepArg
&& aFormulaSepArrayRow == rOpt.aFormulaSepArrayRow
&& aFormulaSepArrayCol == rOpt.aFormulaSepArrayCol
&& meOOXMLRecalc == rOpt.meOOXMLRecalc
&& meODFRecalc == rOpt.meODFRecalc
&& meReCalcOptiRowHeights == rOpt.meReCalcOptiRowHeights;
}
bool ScFormulaOptions::operator!=( const ScFormulaOptions& rOpt ) const
{
return !(operator==(rOpt));
}
ScTpFormulaItem::ScTpFormulaItem( ScFormulaOptions aOpt ) :
SfxPoolItem ( SID_SCFORMULAOPTIONS, SfxItemType::ScTpFormulaItemType ),
theOptions (std::move( aOpt ))
{
}
ScTpFormulaItem::~ScTpFormulaItem()
{
}
bool ScTpFormulaItem::operator==( const SfxPoolItem& rItem ) const
{
assert(SfxPoolItem::operator==(rItem));
const ScTpFormulaItem& rPItem = static_cast<const ScTpFormulaItem&>(rItem);
return ( theOptions == rPItem.theOptions );
}
ScTpFormulaItem* ScTpFormulaItem::Clone( SfxItemPool * ) const
{
return new ScTpFormulaItem( *this );
}
constexpr OUStringLiteral CFGPATH_FORMULA = u"Office.Calc/Formula";
#define SCFORMULAOPT_GRAMMAR 0
#define SCFORMULAOPT_ENGLISH_FUNCNAME 1
#define SCFORMULAOPT_SEP_ARG 2
#define SCFORMULAOPT_SEP_ARRAY_ROW 3
#define SCFORMULAOPT_SEP_ARRAY_COL 4
#define SCFORMULAOPT_STRING_REF_SYNTAX 5
#define SCFORMULAOPT_STRING_CONVERSION 6
#define SCFORMULAOPT_EMPTY_OUSTRING_AS_ZERO 7
#define SCFORMULAOPT_OOXML_RECALC 8
#define SCFORMULAOPT_ODF_RECALC 9
#define SCFORMULAOPT_ROW_HEIGHT_RECALC 10
#define SCFORMULAOPT_OPENCL_AUTOSELECT 11
#define SCFORMULAOPT_OPENCL_DEVICE 12
#define SCFORMULAOPT_OPENCL_SUBSET_ONLY 13
#define SCFORMULAOPT_OPENCL_MIN_SIZE 14
#define SCFORMULAOPT_OPENCL_SUBSET_OPS 15
Sequence<OUString> ScFormulaCfg::GetPropertyNames()
{
return {u"Syntax/Grammar"_ustr, // SCFORMULAOPT_GRAMMAR
u"Syntax/EnglishFunctionName"_ustr, // SCFORMULAOPT_ENGLISH_FUNCNAME
u"Syntax/SeparatorArg"_ustr, // SCFORMULAOPT_SEP_ARG
u"Syntax/SeparatorArrayRow"_ustr, // SCFORMULAOPT_SEP_ARRAY_ROW
u"Syntax/SeparatorArrayCol"_ustr, // SCFORMULAOPT_SEP_ARRAY_COL
u"Syntax/StringRefAddressSyntax"_ustr, // SCFORMULAOPT_STRING_REF_SYNTAX
u"Syntax/StringConversion"_ustr, // SCFORMULAOPT_STRING_CONVERSION
u"Syntax/EmptyStringAsZero"_ustr, // SCFORMULAOPT_EMPTY_OUSTRING_AS_ZERO
u"Load/OOXMLRecalcMode"_ustr, // SCFORMULAOPT_OOXML_RECALC
u"Load/ODFRecalcMode"_ustr, // SCFORMULAOPT_ODF_RECALC
u"Load/RecalcOptimalRowHeightMode"_ustr, // SCFORMULAOPT_ROW_HEIGHT_RECALC
u"Calculation/OpenCLAutoSelect"_ustr, // SCFORMULAOPT_OPENCL_AUTOSELECT
u"Calculation/OpenCLDevice"_ustr, // SCFORMULAOPT_OPENCL_DEVICE
u"Calculation/OpenCLSubsetOnly"_ustr, // SCFORMULAOPT_OPENCL_SUBSET_ONLY
u"Calculation/OpenCLMinimumDataSize"_ustr, // SCFORMULAOPT_OPENCL_MIN_SIZE
u"Calculation/OpenCLSubsetOpCodes"_ustr}; // SCFORMULAOPT_OPENCL_SUBSET_OPS
}
ScFormulaCfg::PropsToIds ScFormulaCfg::GetPropNamesToId()
{
Sequence<OUString> aPropNames = GetPropertyNames();
static sal_uInt16 aVals[] = {
SCFORMULAOPT_GRAMMAR,
SCFORMULAOPT_ENGLISH_FUNCNAME,
SCFORMULAOPT_SEP_ARG,
SCFORMULAOPT_SEP_ARRAY_ROW,
SCFORMULAOPT_SEP_ARRAY_COL,
SCFORMULAOPT_STRING_REF_SYNTAX,
SCFORMULAOPT_STRING_CONVERSION,
SCFORMULAOPT_EMPTY_OUSTRING_AS_ZERO,
SCFORMULAOPT_OOXML_RECALC,
SCFORMULAOPT_ODF_RECALC,
SCFORMULAOPT_ROW_HEIGHT_RECALC,
SCFORMULAOPT_OPENCL_AUTOSELECT,
SCFORMULAOPT_OPENCL_DEVICE,
SCFORMULAOPT_OPENCL_SUBSET_ONLY,
SCFORMULAOPT_OPENCL_MIN_SIZE,
SCFORMULAOPT_OPENCL_SUBSET_OPS,
};
OSL_ENSURE( SAL_N_ELEMENTS(aVals) == aPropNames.getLength(), "Properties and ids are out of Sync");
PropsToIds aPropIdMap;
for ( sal_Int32 i=0; i<aPropNames.getLength(); ++i )
aPropIdMap[aPropNames[i]] = aVals[ i ];
return aPropIdMap;
}
ScFormulaCfg::ScFormulaCfg() :
ConfigItem( CFGPATH_FORMULA )
{
Sequence<OUString> aNames = GetPropertyNames();
UpdateFromProperties( aNames );
EnableNotification( aNames );
}
void ScFormulaCfg::UpdateFromProperties( const Sequence<OUString>& aNames )
{
Sequence<Any> aValues = GetProperties(aNames);
const Any* pValues = aValues.getConstArray();
OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
PropsToIds aPropMap = GetPropNamesToId();
if(aValues.getLength() != aNames.getLength())
return;
sal_Int32 nIntVal = 0;
for(int nProp = 0; nProp < aNames.getLength(); nProp++)
{
PropsToIds::iterator it_end = aPropMap.end();
PropsToIds::iterator it = aPropMap.find( aNames[nProp] );
if(pValues[nProp].hasValue() && it != it_end )
{
switch(it->second)
{
case SCFORMULAOPT_GRAMMAR:
{
// Get default value in case this option is not set.
::formula::FormulaGrammar::Grammar eGram = GetFormulaSyntax();
do
{
if (!(pValues[nProp] >>= nIntVal))
// extracting failed.
break;
switch (nIntVal)
{
case 0: // Calc A1
eGram = ::formula::FormulaGrammar::GRAM_NATIVE;
break;
case 1: // Excel A1
eGram = ::formula::FormulaGrammar::GRAM_NATIVE_XL_A1;
break;
case 2: // Excel R1C1
eGram = ::formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1;
break;
default:
;
}
}
while (false);
SetFormulaSyntax(eGram);
}
break;
case SCFORMULAOPT_ENGLISH_FUNCNAME:
{
bool bEnglish = false;
if (pValues[nProp] >>= bEnglish)
SetUseEnglishFuncName(bEnglish);
}
break;
case SCFORMULAOPT_SEP_ARG:
{
OUString aSep;
if ((pValues[nProp] >>= aSep) && !aSep.isEmpty())
SetFormulaSepArg(aSep);
}
break;
case SCFORMULAOPT_SEP_ARRAY_ROW:
{
OUString aSep;
if ((pValues[nProp] >>= aSep) && !aSep.isEmpty())
SetFormulaSepArrayRow(aSep);
}
break;
case SCFORMULAOPT_SEP_ARRAY_COL:
{
OUString aSep;
if ((pValues[nProp] >>= aSep) && !aSep.isEmpty())
SetFormulaSepArrayCol(aSep);
}
break;
case SCFORMULAOPT_STRING_REF_SYNTAX:
{
// Get default value in case this option is not set.
::formula::FormulaGrammar::AddressConvention eConv = GetCalcConfig().meStringRefAddressSyntax;
do
{
if (!(pValues[nProp] >>= nIntVal))
// extraction failed.
break;
switch (nIntVal)
{
case -1: // Same as the formula grammar.
eConv = formula::FormulaGrammar::CONV_UNSPECIFIED;
break;
case 0: // Calc A1
eConv = formula::FormulaGrammar::CONV_OOO;
break;
case 1: // Excel A1
eConv = formula::FormulaGrammar::CONV_XL_A1;
break;
case 2: // Excel R1C1
eConv = formula::FormulaGrammar::CONV_XL_R1C1;
break;
case 3: // Calc A1 | Excel A1
eConv = formula::FormulaGrammar::CONV_A1_XL_A1;
break;
default:
;
}
}
while (false);
GetCalcConfig().meStringRefAddressSyntax = eConv;
}
break;
case SCFORMULAOPT_STRING_CONVERSION:
{
// Get default value in case this option is not set.
ScCalcConfig::StringConversion eConv = GetCalcConfig().meStringConversion;
do
{
if (!(pValues[nProp] >>= nIntVal))
// extraction failed.
break;
switch (nIntVal)
{
case 0:
eConv = ScCalcConfig::StringConversion::ILLEGAL;
break;
case 1:
eConv = ScCalcConfig::StringConversion::ZERO;
break;
case 2:
eConv = ScCalcConfig::StringConversion::UNAMBIGUOUS;
break;
case 3:
eConv = ScCalcConfig::StringConversion::LOCALE;
break;
default:
SAL_WARN("sc", "unknown string conversion option!");
}
}
while (false);
GetCalcConfig().meStringConversion = eConv;
}
break;
case SCFORMULAOPT_EMPTY_OUSTRING_AS_ZERO:
{
bool bVal = GetCalcConfig().mbEmptyStringAsZero;
pValues[nProp] >>= bVal;
GetCalcConfig().mbEmptyStringAsZero = bVal;
}
break;
case SCFORMULAOPT_OOXML_RECALC:
{
ScRecalcOptions eOpt = RECALC_ASK;
if (pValues[nProp] >>= nIntVal)
{
switch (nIntVal)
{
case 0:
eOpt = RECALC_ALWAYS;
break;
case 1:
eOpt = RECALC_NEVER;
break;
case 2:
eOpt = RECALC_ASK;
break;
default:
SAL_WARN("sc", "unknown ooxml recalc option!");
}
}
SetOOXMLRecalcOptions(eOpt);
}
break;
case SCFORMULAOPT_ODF_RECALC:
{
ScRecalcOptions eOpt = RECALC_ASK;
if (pValues[nProp] >>= nIntVal)
{
switch (nIntVal)
{
case 0:
eOpt = RECALC_ALWAYS;
break;
case 1:
eOpt = RECALC_NEVER;
break;
case 2:
eOpt = RECALC_ASK;
break;
default:
SAL_WARN("sc", "unknown odf recalc option!");
}
}
SetODFRecalcOptions(eOpt);
}
break;
case SCFORMULAOPT_ROW_HEIGHT_RECALC:
{
ScRecalcOptions eOpt = RECALC_ASK;
if (pValues[nProp] >>= nIntVal)
{
switch (nIntVal)
{
case 0:
eOpt = RECALC_ALWAYS;
break;
case 1:
eOpt = RECALC_NEVER;
break;
case 2:
eOpt = RECALC_ASK;
break;
default:
SAL_WARN("sc", "unknown optimal row height recalc option!");
}
}
SetReCalcOptiRowHeights(eOpt);
}
break;
case SCFORMULAOPT_OPENCL_AUTOSELECT:
{
bool bVal = GetCalcConfig().mbOpenCLAutoSelect;
pValues[nProp] >>= bVal;
GetCalcConfig().mbOpenCLAutoSelect = bVal;
}
break;
case SCFORMULAOPT_OPENCL_DEVICE:
{
OUString aOpenCLDevice = GetCalcConfig().maOpenCLDevice;
pValues[nProp] >>= aOpenCLDevice;
GetCalcConfig().maOpenCLDevice = aOpenCLDevice;
}
break;
case SCFORMULAOPT_OPENCL_SUBSET_ONLY:
{
bool bVal = GetCalcConfig().mbOpenCLSubsetOnly;
pValues[nProp] >>= bVal;
GetCalcConfig().mbOpenCLSubsetOnly = bVal;
}
break;
case SCFORMULAOPT_OPENCL_MIN_SIZE:
{
sal_Int32 nVal = GetCalcConfig().mnOpenCLMinimumFormulaGroupSize;
pValues[nProp] >>= nVal;
GetCalcConfig().mnOpenCLMinimumFormulaGroupSize = nVal;
}
break;
case SCFORMULAOPT_OPENCL_SUBSET_OPS:
{
OUString sVal = ScOpCodeSetToSymbolicString(GetCalcConfig().mpOpenCLSubsetOpCodes);
pValues[nProp] >>= sVal;
GetCalcConfig().mpOpenCLSubsetOpCodes = ScStringToOpCodeSet(sVal);
}
break;
}
}
}
}
void ScFormulaCfg::ImplCommit()
{
Sequence<OUString> aNames = GetPropertyNames();
Sequence<Any> aValues(aNames.getLength());
Any* pValues = aValues.getArray();
Sequence<Any> aOldValues = GetProperties(aNames);
Any* pOldValues = aOldValues.getArray();
bool bSetOpenCL = false;
for (int nProp = 0; nProp < aNames.getLength(); ++nProp)
{
switch (nProp)
{
case SCFORMULAOPT_GRAMMAR :
{
sal_Int32 nVal = 0;
switch (GetFormulaSyntax())
{
case ::formula::FormulaGrammar::GRAM_NATIVE_XL_A1: nVal = 1; break;
case ::formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1: nVal = 2; break;
default: break;
}
pValues[nProp] <<= nVal;
}
break;
case SCFORMULAOPT_ENGLISH_FUNCNAME:
{
bool b = GetUseEnglishFuncName();
pValues[nProp] <<= b;
}
break;
case SCFORMULAOPT_SEP_ARG:
pValues[nProp] <<= GetFormulaSepArg();
break;
case SCFORMULAOPT_SEP_ARRAY_ROW:
pValues[nProp] <<= GetFormulaSepArrayRow();
break;
case SCFORMULAOPT_SEP_ARRAY_COL:
pValues[nProp] <<= GetFormulaSepArrayCol();
break;
case SCFORMULAOPT_STRING_REF_SYNTAX:
{
sal_Int32 nVal = -1;
if (GetWriteCalcConfig())
{
switch (GetCalcConfig().meStringRefAddressSyntax)
{
case ::formula::FormulaGrammar::CONV_OOO: nVal = 0; break;
case ::formula::FormulaGrammar::CONV_XL_A1: nVal = 1; break;
case ::formula::FormulaGrammar::CONV_XL_R1C1: nVal = 2; break;
case ::formula::FormulaGrammar::CONV_A1_XL_A1: nVal = 3; break;
default: break;
}
pValues[nProp] <<= nVal;
}
else
{
pValues[nProp] = pOldValues[nProp];
}
}
break;
case SCFORMULAOPT_STRING_CONVERSION:
{
if (GetWriteCalcConfig())
{
sal_Int32 nVal = 3;
switch (GetCalcConfig().meStringConversion)
{
case ScCalcConfig::StringConversion::ILLEGAL: nVal = 0; break;
case ScCalcConfig::StringConversion::ZERO: nVal = 1; break;
case ScCalcConfig::StringConversion::UNAMBIGUOUS: nVal = 2; break;
case ScCalcConfig::StringConversion::LOCALE: nVal = 3; break;
}
pValues[nProp] <<= nVal;
}
else
{
pValues[nProp] = pOldValues[nProp];
}
}
break;
case SCFORMULAOPT_EMPTY_OUSTRING_AS_ZERO:
{
if (GetWriteCalcConfig())
{
bool bVal = GetCalcConfig().mbEmptyStringAsZero;
pValues[nProp] <<= bVal;
}
else
{
pValues[nProp] = pOldValues[nProp];
}
}
break;
case SCFORMULAOPT_OOXML_RECALC:
{
sal_Int32 nVal = 2;
switch (GetOOXMLRecalcOptions())
{
case RECALC_ALWAYS:
nVal = 0;
break;
case RECALC_NEVER:
nVal = 1;
break;
case RECALC_ASK:
nVal = 2;
break;
}
pValues[nProp] <<= nVal;
}
break;
case SCFORMULAOPT_ODF_RECALC:
{
sal_Int32 nVal = 2;
switch (GetODFRecalcOptions())
{
case RECALC_ALWAYS:
nVal = 0;
break;
case RECALC_NEVER:
nVal = 1;
break;
case RECALC_ASK:
nVal = 2;
break;
}
pValues[nProp] <<= nVal;
}
break;
case SCFORMULAOPT_ROW_HEIGHT_RECALC:
{
sal_Int32 nVal = 2;
switch (GetReCalcOptiRowHeights())
{
case RECALC_ALWAYS:
nVal = 0;
break;
case RECALC_NEVER:
nVal = 1;
break;
case RECALC_ASK:
nVal = 2;
break;
default:
SAL_WARN("sc", "unknown optimal row height recalc option!");
}
pValues[nProp] <<= nVal;
}
break;
case SCFORMULAOPT_OPENCL_AUTOSELECT:
{
bool bVal = GetCalcConfig().mbOpenCLAutoSelect;
pValues[nProp] <<= bVal;
bSetOpenCL = true;
}
break;
case SCFORMULAOPT_OPENCL_DEVICE:
{
OUString aOpenCLDevice = GetCalcConfig().maOpenCLDevice;
pValues[nProp] <<= aOpenCLDevice;
bSetOpenCL = true;
}
break;
case SCFORMULAOPT_OPENCL_SUBSET_ONLY:
{
bool bVal = GetCalcConfig().mbOpenCLSubsetOnly;
pValues[nProp] <<= bVal;
}
break;
case SCFORMULAOPT_OPENCL_MIN_SIZE:
{
sal_Int32 nVal = GetCalcConfig().mnOpenCLMinimumFormulaGroupSize;
pValues[nProp] <<= nVal;
}
break;
case SCFORMULAOPT_OPENCL_SUBSET_OPS:
{
OUString sVal = ScOpCodeSetToSymbolicString(GetCalcConfig().mpOpenCLSubsetOpCodes);
pValues[nProp] <<= sVal;
}
break;
}
}
#if !HAVE_FEATURE_OPENCL
(void) bSetOpenCL;
#else
if(bSetOpenCL)
sc::FormulaGroupInterpreter::switchOpenCLDevice(
GetCalcConfig().maOpenCLDevice, GetCalcConfig().mbOpenCLAutoSelect);
#endif
PutProperties(aNames, aValues);
}
void ScFormulaCfg::SetOptions( const ScFormulaOptions& rNew )
{
*static_cast<ScFormulaOptions*>(this) = rNew;
SetModified();
}
void ScFormulaCfg::Notify( const css::uno::Sequence< OUString >& rNames)
{
UpdateFromProperties( rNames );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V1048 The 'eOpt' variable was assigned the same value.
↑ V1048 The 'eOpt' variable was assigned the same value.
↑ V1048 The 'eOpt' variable was assigned the same value.
↑ V1048 The 'nVal' variable was assigned the same value.
↑ V1048 The 'nVal' variable was assigned the same value.
↑ V1048 The 'nVal' variable was assigned the same value.
↑ V1048 The 'nVal' variable was assigned the same value.