/* -*- 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 .
*/
bool bScriptReturnedFalse = false; // default: do not abort
// 1) entered or calculated value
css::uno::Any aParam0(rInput); if ( pCell ) // if cell exists, call interpret
{ if ( pCell->IsValue() )
aParam0 <<= pCell->GetValue(); else
aParam0 <<= pCell->GetString().getString();
}
// 2) Position of the cell
OUString aPosStr(rPos.Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, &rDocument, rDocument.GetAddressConvention()));
// Set up parameters
css::uno::Sequence< css::uno::Any > aParams{ aParam0, css::uno::Any(aPosStr) };
// use link-update flag to prevent closing the document // while the macro is running bool bWasInLinkUpdate = rDocument.IsInLinkUpdate(); if ( !bWasInLinkUpdate )
rDocument.SetInLinkUpdate( true );
if ( !bWasInLinkUpdate )
rDocument.SetInLinkUpdate( false );
// Check the return value from the script // The contents of the cell get reset if the script returns false bool bTmp = false; if ( eRet == ERRCODE_NONE &&
aRet.getValueType() == cppu::UnoType<bool>::get() &&
( aRet >>= bTmp ) &&
!bTmp )
{
bScriptReturnedFalse = true;
}
if ( eRet == ERRCODE_BASIC_METHOD_NOT_FOUND && !pCell ) // Macro not found (only with input)
{ //TODO: different error message, if found, but not bAllowed ??
std::shared_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
VclMessageType::Warning, VclButtonsType::Ok,
ScResId(STR_VALID_MACRONOTFOUND)));
xBox->runAsync(xBox, [] (sal_uInt32){ });
}
// the distinction between document- and app-basic has to be done // by checking the parent (as in ScInterpreter::ScMacro), not by looping // over all open documents, because this may be called from within loading, // when SfxObjectShell::GetFirst/GetNext won't find the document.
if ( pObject->GetParent() )
aBasicStr = pObject->GetParent()->GetName(); // Basic of document else
aBasicStr = SfxGetpApp()->GetName(); // Basic of application
// Parameter for Macro
SbxArrayRef refPar = new SbxArray;
// 1) entered or calculated value
OUString aValStr = rInput; double nValue = 0.0; bool bIsValue = false; if ( pCell ) // if cell set, called from interpret
{
bIsValue = pCell->IsValue(); if ( bIsValue )
nValue = pCell->GetValue(); else
aValStr = pCell->GetString().getString();
} if ( bIsValue )
refPar->Get(1)->PutDouble(nValue); else
refPar->Get(1)->PutString(aValStr);
// 2) Position of the cell
OUString aPosStr(rPos.Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, &rDocument, rDocument.GetAddressConvention()));
refPar->Get(2)->PutString(aPosStr);
// use link-update flag to prevent closing the document // while the macro is running bool bWasInLinkUpdate = rDocument.IsInLinkUpdate(); if ( !bWasInLinkUpdate )
rDocument.SetInLinkUpdate( true );
if ( pCell )
rDocument.LockTable( rPos.Tab() );
SbxVariableRef refRes = new SbxVariable;
ErrCode eRet = pDocSh->CallBasic( aMacroStr, aBasicStr, refPar.get(), refRes.get() ); if ( pCell )
rDocument.UnlockTable( rPos.Tab() );
if ( !bWasInLinkUpdate )
rDocument.SetInLinkUpdate( false );
// Interrupt input if Basic macro returns false if ( eRet == ERRCODE_NONE && refRes->GetType() == SbxBOOL && !refRes->GetBool() )
bRet = true;
bDone = true;
} #endif if ( !bDone && !pCell ) // Macro not found (only with input)
{ //TODO: different error message, if found, but not bAllowed ??
std::shared_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
VclMessageType::Warning, VclButtonsType::Ok,
ScResId(STR_VALID_MACRONOTFOUND)));
xBox->runAsync(xBox, [](sal_uInt32) {});
}
if (rTest[0] == '=')
{ if (!isFormulaResultsValidatable(rTest, rPos, pFormatter, rStrResult, nVal, nFormat, bIsVal)) returnfalse;
// check whether empty cells are allowed if (rStrResult.isEmpty()) return IsIgnoreBlank();
} else
{
pFormatter = GetDocument().GetFormatTable();
// get the value if any
nFormat = rPattern.GetNumberFormat(pFormatter);
bIsVal = pFormatter->IsNumberFormat(rTest, nFormat, nVal);
rStrResult = rTest;
}
ScRefCellValue aTmpCell;
svl::SharedString aSS; if (bIsVal)
{
aTmpCell = ScRefCellValue(nVal);
} else
{
aSS = mrDoc.GetSharedStringPool().intern(rStrResult);
aTmpCell = ScRefCellValue(&aSS);
}
/** To test numeric data text length in IsDataValidTextLen().
If mpFormatter is not set, it is obtained from the document and the format key is determined from the cell position's attribute pattern.
*/ struct ScValidationDataIsNumeric
{
SvNumberFormatter* mpFormatter; double mfVal;
sal_uInt32 mnFormat;
// For numeric values use the resulting input line string to // determine length, otherwise an once accepted value maybe could // not be edited again, for example abbreviated dates or leading // zeros or trailing zeros after decimal separator change length.
OUString aStr = pDataNumeric->mpFormatter->GetInputLineString( pDataNumeric->mfVal, pDataNumeric->mnFormat);
nLen = aStr.getLength();
}
ScRefCellValue aTmpCell( static_cast<double>(nLen)); return IsCellValid( aTmpCell, rPos);
}
bool ScValidationData::IsDataValid( const OUString& rTest, const ScPatternAttr& rPattern, const ScAddress& rPos ) const
{ if ( eDataMode == SC_VALID_ANY ) // check if any cell content is allowed returntrue;
if (rTest.isEmpty()) // check whether empty cells are allowed return IsIgnoreBlank();
if (rTest[0] == '=')
{ if (!isFormulaResultsValidatable(rTest, rPos, pFormatter, rStrResult, nVal, nFormat, bIsVal)) returnfalse;
// check whether empty cells are allowed if (rStrResult.isEmpty()) return IsIgnoreBlank();
} else
{
pFormatter = GetDocument().GetFormatTable();
// get the value if any
nFormat = rPattern.GetNumberFormat(pFormatter);
bIsVal = pFormatter->IsNumberFormat(rTest, nFormat, nVal);
rStrResult = rTest;
}
case SC_VALID_WHOLE: case SC_VALID_DECIMAL: case SC_VALID_DATE: // Date/Time is only formatting case SC_VALID_TIME:
bOk = bIsVal; if ( bOk && eDataMode == SC_VALID_WHOLE )
bOk = ::rtl::math::approxEqual( nVal, floor(nVal+0.5) ); // integers if ( bOk )
bOk = IsCellValid(rCell, rPos); break;
case SC_VALID_TEXTLEN: if (!bIsVal)
bOk = IsDataValidTextLen( aString, rPos, nullptr); else
{
ScValidationDataIsNumeric aDataNumeric( nVal);
bOk = IsDataValidTextLen( aString, rPos, &aDataNumeric);
} break;
bool bColRowName = pFCell->HasColRowName(); if (bColRowName)
{ // ColRowName from RPN-Code? if (pFCell->GetCode()->GetCodeLen() <= 1)
{ // ==1: area // ==0: would be an area if...
OUString aBraced = "(" + rTest + ")";
pFCell.emplace(mrDoc, rPos, aBraced, true);
pFCell->SetLimitString(true);
} else
bColRowName = false;
}
FormulaError nErrCode = pFCell->GetErrCode(); if (nErrCode == FormulaError::NONE || pFCell->IsMatrix())
{
pFormatter = mrDoc.GetFormatTable(); const Color* pColor; if (pFCell->IsMatrix())
{
rStrResult = pFCell->GetString().getString();
} elseif (pFCell->IsValue())
{
nVal = pFCell->GetValue();
nFormat = pFormatter->GetStandardFormat(nVal, 0,
pFCell->GetFormatType(), ScGlobal::eLnge);
pFormatter->GetOutputString(nVal, nFormat, rStrResult, &pColor);
bIsVal = true;
} else
{
nFormat = pFormatter->GetStandardFormat(
pFCell->GetFormatType(), ScGlobal::eLnge);
pFormatter->GetOutputString(pFCell->GetString().getString(), nFormat,
rStrResult, &pColor); // Indicate it's a string, so a number string doesn't look numeric. // Escape embedded quotation marks first by doubling them, as // usual. Actually the result can be copy-pasted from the result // box as literal into a formula expression.
rStrResult = "\"" + rStrResult.replaceAll("\"", "\"\"") + "\"";
}
ScRange aTestRange; if (bColRowName || (aTestRange.Parse(rTest, mrDoc) & ScRefFlags::VALID))
rStrResult += " ..."; // area
returntrue;
} else
{ returnfalse;
}
}
namespace {
/** Token array helper. Iterates over all string tokens. @descr The token array must contain separated string tokens only.
@param bSkipEmpty true = Ignores string tokens with empty strings. */ class ScStringTokenIterator
{ public: explicit ScStringTokenIterator( const ScTokenArray& rTokArr ) :
maIter( rTokArr ), mbOk( true ) {}
/** Returns the string of the first string token or NULL on error or empty token array. */
rtl_uString* First(); /** Returns the string of the next string token or NULL on error or end of token array. */
rtl_uString* Next();
/** Returns false, if a wrong token has been found. Does NOT return false on end of token array. */ bool Ok() const { return mbOk; }
private:
svl::SharedString maCurString; /// Current string.
FormulaTokenArrayPlainIterator maIter; bool mbOk; /// true = correct token or end of token array.
};
maCurString = svl::SharedString(); // start with invalid string. if (mbOk && pToken)
maCurString = pToken->GetString();
// string found but empty -> get next token; otherwise return it return (maCurString.isValid() && maCurString.isEmpty()) ? Next() : maCurString.getData();
}
/** Returns the number format of the passed cell, or the standard format. */
sal_uInt32 lclGetCellFormat( const ScDocument& rDoc, const ScAddress& rPos )
{ const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() ); if( !pPattern )
pPattern = &rDoc.getCellAttributeHelper().getDefaultCellAttribute(); return pPattern->GetNumberFormat( rDoc.GetFormatTable() );
}
// Make sure the formula gets interpreted and a result is delivered, // regardless of the AutoCalc setting.
aValidationSrc.Interpret();
ScMatrixRef xMatRef; const ScMatrix *pValues = aValidationSrc.GetMatrix(); if (!pValues)
{ // The somewhat nasty case of either an error occurred, or the // dereferenced value of a single cell reference or an immediate result // is stored as a single value.
// Use an interim matrix to create the TypedStrData below.
xMatRef = new ScMatrix(1, 1, 0.0);
FormulaError nErrCode = aValidationSrc.GetErrCode(); if (nErrCode != FormulaError::NONE)
{ /* TODO : to use later in an alert box? * OUString rStrResult = "..."; * rStrResult += ScGlobal::GetLongErrorString(nErrCode);
*/
/* XL artificially limits things to a single col or row in the UI but does * not list the constraint in MOOXml. If a defined name or INDIRECT * resulting in 1D is entered in the UI and the definition later modified * to 2D, it is evaluated fine and also stored and loaded. Let's get ahead * of the curve and support 2d. In XL, values are listed row-wise, do the
* same. */ for( nRow = 0; nRow < nRows ; nRow++ )
{ for( nCol = 0; nCol < nCols ; nCol++ )
{
ScTokenArray aCondTokArr(rDocument);
std::unique_ptr<ScTypedStrData> pEntry;
OUString aValStr;
ScMatrixValue nMatVal = pValues->Get( nCol, nRow);
// Do not add multiple empty strings to the validation list, // especially not if they'd bloat the tail with a million empty // entries for a column range, fdo#61520 if (aValStr.isEmpty())
{ if (bHaveEmpty) continue;
bHaveEmpty = true;
}
if( FormulaError::NONE != nErr )
{
aValStr = ScGlobal::GetErrorString( nErr );
} else
{ // FIXME FIXME FIXME // Feature regression. Date formats are lost passing through the matrix //pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr ); //For external reference and a formula that results in an area or array, date formats are still lost. if ( bRef )
{
aValStr = rDocument.GetInputString(static_cast<SCCOL>(nCol+aRange.aStart.Col()), static_cast<SCROW>(nRow+aRange.aStart.Row()), aRange.aStart.Tab());
} else
{
aValStr = pFormatter->GetInputLineString( nMatVal.fVal, nDestFormat );
}
}
if (!rCell.isEmpty() && rMatch < 0)
{ // I am not sure errors will work here, but a user can no // manually enter an error yet so the point is somewhat moot.
aCondTokArr.AddDouble( nMatVal.fVal );
} if( nullptr != pStrings )
pEntry.reset(new ScTypedStrData(aValStr, nMatVal.fVal, nMatVal.fVal, ScTypedStrData::Value));
}
if (rMatch < 0 && !rCell.isEmpty() && IsEqualToTokenArray(rCell, rPos, aCondTokArr))
{
rMatch = n; // short circuit on the first match if not filling the list if( nullptr == pStrings ) returntrue;
}
/* Compare input cell with all supported tokens from the formula. Currently a formula may contain: 1) A list of strings (at least one string). 2) A single cell or range reference. 3) A single defined name (must contain a cell/range reference, another name, or DB range, or a formula resulting in a cell/range reference or matrix/array). 4) A single database range. 5) A formula resulting in a cell/range reference or matrix/array.
*/
svl::SharedStringPool& rSPool = GetDocument().GetSharedStringPool();
sal_uInt32 nFormat = lclGetCellFormat( GetDocument(), rPos );
ScStringTokenIterator aIt( *pTokArr ); for (rtl_uString* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next())
{ /* Do not break the loop, if a valid string has been found.
This is to find invalid tokens following in the formula. */ if( !bIsValid )
{ // create a formula containing a single string or number
ScTokenArray aCondTokArr(GetDocument()); double fValue;
OUString aStr(pString); if (GetDocument().GetFormatTable()->IsNumberFormat(aStr, nFormat, fValue))
aCondTokArr.AddDouble( fValue ); else
aCondTokArr.AddString(rSPool.intern(aStr));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.