999 lines
35 KiB
C++
999 lines
35 KiB
C++
/* -*- 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 "qahelper.hxx"
|
|
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
|
|
#include <comphelper/propertysequence.hxx>
|
|
#include "csv_handler.hxx"
|
|
#include "debughelper.hxx"
|
|
#include <drwlayer.hxx>
|
|
#include <comphelper/sequence.hxx>
|
|
#include <comphelper/servicehelper.hxx>
|
|
#include <compiler.hxx>
|
|
#include <conditio.hxx>
|
|
#include <stlsheet.hxx>
|
|
#include <formulacell.hxx>
|
|
#include <formulagroup.hxx>
|
|
#include <svx/svdpage.hxx>
|
|
#include <svx/svdoole2.hxx>
|
|
#include <tools/UnitConversion.hxx>
|
|
#include <editeng/brushitem.hxx>
|
|
#include <editeng/justifyitem.hxx>
|
|
#include <clipcontext.hxx>
|
|
#include <clipparam.hxx>
|
|
#include <refundo.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <svl/gridprinter.hxx>
|
|
#include <sfx2/docfile.hxx>
|
|
#include <undoblk.hxx>
|
|
#include <scdll.hxx>
|
|
#include <scitems.hxx>
|
|
#include <stringutil.hxx>
|
|
#include <tokenarray.hxx>
|
|
#include <vcl/keycodes.hxx>
|
|
#include <vcl/scheduler.hxx>
|
|
#include <o3tl/safeint.hxx>
|
|
|
|
#include <orcus/csv_parser.hpp>
|
|
|
|
#include <cstdlib>
|
|
#include <fstream>
|
|
|
|
#include <com/sun/star/chart2/XChartDocument.hpp>
|
|
#include <com/sun/star/chart2/data/XDataReceiver.hpp>
|
|
#include <com/sun/star/document/MacroExecMode.hpp>
|
|
|
|
using namespace com::sun::star;
|
|
using namespace ::com::sun::star::uno;
|
|
using ::std::cout;
|
|
using ::std::cerr;
|
|
using ::std::endl;
|
|
|
|
FormulaGrammarSwitch::FormulaGrammarSwitch(ScDocument* pDoc, formula::FormulaGrammar::Grammar eGrammar) :
|
|
mpDoc(pDoc), meOldGrammar(pDoc->GetGrammar())
|
|
{
|
|
mpDoc->SetGrammar(eGrammar);
|
|
}
|
|
|
|
FormulaGrammarSwitch::~FormulaGrammarSwitch()
|
|
{
|
|
mpDoc->SetGrammar(meOldGrammar);
|
|
}
|
|
|
|
// calc data structure pretty printer
|
|
std::ostream& operator<<(std::ostream& rStrm, const ScAddress& rAddr)
|
|
{
|
|
rStrm << "Col: " << rAddr.Col() << " Row: " << rAddr.Row() << " Tab: " << rAddr.Tab() << "\n";
|
|
return rStrm;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& rStrm, const ScRange& rRange)
|
|
{
|
|
rStrm << "ScRange: " << rRange.aStart << rRange.aEnd << "\n";
|
|
return rStrm;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& rStrm, const ScRangeList& rList)
|
|
{
|
|
rStrm << "ScRangeList: \n";
|
|
for(size_t i = 0; i < rList.size(); ++i)
|
|
rStrm << rList[i];
|
|
return rStrm;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& rStrm, const OpCode& rCode)
|
|
{
|
|
rStrm << static_cast<sal_uInt16>(rCode);
|
|
return rStrm;
|
|
}
|
|
|
|
void ScModelTestBase::loadFile(const OUString& aFileName, std::string& aContent)
|
|
{
|
|
OString aOFileName = OUStringToOString(aFileName, RTL_TEXTENCODING_UTF8);
|
|
|
|
#ifdef ANDROID
|
|
size_t size;
|
|
if (strncmp(aOFileName.getStr(), "/assets/", sizeof("/assets/")-1) == 0) {
|
|
const char *contents = (const char *) lo_apkentry(aOFileName.getStr(), &size);
|
|
if (contents != 0) {
|
|
aContent = std::string(contents, size);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
std::ifstream aFile(aOFileName.getStr());
|
|
|
|
OString aErrorMsg = "Could not open csv file: " + aOFileName;
|
|
CPPUNIT_ASSERT_MESSAGE(aErrorMsg.getStr(), aFile);
|
|
std::ostringstream aOStream;
|
|
aOStream << aFile.rdbuf();
|
|
aFile.close();
|
|
aContent = aOStream.str();
|
|
}
|
|
|
|
void ScModelTestBase::testFile(const OUString& aFileName, ScDocument& rDoc, SCTAB nTab, StringType aStringFormat)
|
|
{
|
|
csv_handler aHandler(&rDoc, nTab, aStringFormat);
|
|
orcus::csv::parser_config aConfig;
|
|
aConfig.delimiters.push_back(',');
|
|
aConfig.delimiters.push_back(';');
|
|
aConfig.text_qualifier = '"';
|
|
aConfig.trim_cell_value = false;
|
|
|
|
std::string aContent;
|
|
loadFile(aFileName, aContent);
|
|
orcus::csv_parser<csv_handler> parser(aContent, aHandler, aConfig);
|
|
try
|
|
{
|
|
parser.parse();
|
|
}
|
|
catch (const orcus::parse_error& e)
|
|
{
|
|
std::cout << "reading csv content file failed: " << e.what() << std::endl;
|
|
OString aErrorMsg = OString::Concat("csv parser error: ") + e.what();
|
|
CPPUNIT_ASSERT_MESSAGE(aErrorMsg.getStr(), false);
|
|
}
|
|
}
|
|
|
|
void ScModelTestBase::testCondFile( const OUString& aFileName, ScDocument* pDoc, SCTAB nTab, bool bCommaAsDelimiter )
|
|
{
|
|
conditional_format_handler aHandler(pDoc, nTab);
|
|
orcus::csv::parser_config aConfig;
|
|
if ( bCommaAsDelimiter )
|
|
aConfig.delimiters.push_back(',');
|
|
aConfig.delimiters.push_back(';');
|
|
aConfig.text_qualifier = '"';
|
|
std::string aContent;
|
|
loadFile(aFileName, aContent);
|
|
orcus::csv_parser<conditional_format_handler> parser(aContent, aHandler, aConfig);
|
|
try
|
|
{
|
|
parser.parse();
|
|
}
|
|
catch (const orcus::parse_error& e)
|
|
{
|
|
std::cout << "reading csv content file failed: " << e.what() << std::endl;
|
|
OString aErrorMsg = OString::Concat("csv parser error: ") + e.what();
|
|
CPPUNIT_ASSERT_MESSAGE(aErrorMsg.getStr(), false);
|
|
}
|
|
}
|
|
|
|
void ScModelTestBase::testFormats(ScDocument* pDoc,std::u16string_view sFormat)
|
|
{
|
|
//test Sheet1 with csv file
|
|
OUString aCSVFileName = createFilePath(u"contentCSV/numberFormat.csv");
|
|
testFile(aCSVFileName, *pDoc, 0, StringType::PureString);
|
|
//need to test the color of B3
|
|
//it's not a font color!
|
|
//formatting for B5: # ??/100 gets lost during import
|
|
|
|
//test Sheet2
|
|
const ScPatternAttr* pPattern = pDoc->GetPattern(0, 0, 1);
|
|
vcl::Font aFont;
|
|
model::ComplexColor aComplexColor;
|
|
|
|
pPattern->fillFontOnly(aFont);
|
|
pPattern->fillColor(aComplexColor, ScAutoFontColorMode::Raw);
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("font size should be 10", tools::Long(200), aFont.GetFontSize().getHeight());
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("font color should be black", COL_AUTO, aComplexColor.getFinalColor());
|
|
pPattern = pDoc->GetPattern(0,1,1);
|
|
pPattern->fillFontOnly(aFont);
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("font size should be 12", tools::Long(240), aFont.GetFontSize().getHeight());
|
|
pPattern = pDoc->GetPattern(0,2,1);
|
|
pPattern->fillFontOnly(aFont);
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be italic", ITALIC_NORMAL, aFont.GetItalic());
|
|
pPattern = pDoc->GetPattern(0,4,1);
|
|
pPattern->fillFontOnly(aFont);
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be bold", WEIGHT_BOLD, aFont.GetWeight());
|
|
pPattern = pDoc->GetPattern(1,0,1);
|
|
pPattern->fillFontOnly(aFont);
|
|
pPattern->fillColor(aComplexColor, ScAutoFontColorMode::Raw);
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be blue", COL_BLUE, aComplexColor.getFinalColor());
|
|
pPattern = pDoc->GetPattern(1,1,1);
|
|
pPattern->fillFontOnly(aFont);
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be striked out with a single line", STRIKEOUT_SINGLE, aFont.GetStrikeout());
|
|
//some tests on sheet2 only for ods
|
|
if (sFormat == u"calc8")
|
|
{
|
|
pPattern = pDoc->GetPattern(1,2,1);
|
|
pPattern->fillFontOnly(aFont);
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be striked out with a double line", STRIKEOUT_DOUBLE, aFont.GetStrikeout());
|
|
pPattern = pDoc->GetPattern(1,3,1);
|
|
pPattern->fillFontOnly(aFont);
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be underlined with a dotted line", LINESTYLE_DOTTED, aFont.GetUnderline());
|
|
//check row height import
|
|
//disable for now until we figure out cause of win tinderboxes test failures
|
|
//CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(256), pDoc->GetRowHeight(0,1) ); //0.178in
|
|
//CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(304), pDoc->GetRowHeight(1,1) ); //0.211in
|
|
//CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(477), pDoc->GetRowHeight(5,1) ); //0.3311in
|
|
//check column width import
|
|
CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(555), pDoc->GetColWidth(4,1) ); //0.3854in
|
|
CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(1280), pDoc->GetColWidth(5,1) ); //0.889in
|
|
CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(4153), pDoc->GetColWidth(6,1) ); //2.8839in
|
|
//test case for i53253 where a cell has text with different styles and space between the text.
|
|
OUString aTestStr = pDoc->GetString(3,0,1);
|
|
OUString aKnownGoodStr(u"text14 space"_ustr);
|
|
CPPUNIT_ASSERT_EQUAL( aKnownGoodStr, aTestStr );
|
|
//test case for cell text with line breaks.
|
|
aTestStr = pDoc->GetString(3,5,1);
|
|
aKnownGoodStr = "Hello,\nCalc!";
|
|
CPPUNIT_ASSERT_EQUAL( aKnownGoodStr, aTestStr );
|
|
}
|
|
pPattern = pDoc->GetPattern(1,4,1);
|
|
Color aColor = pPattern->GetItem(ATTR_BACKGROUND).GetColor();
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("background color should be green", COL_LIGHTGREEN, aColor);
|
|
pPattern = pDoc->GetPattern(2,0,1);
|
|
SvxCellHorJustify eHorJustify = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue();
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("cell content should be aligned centre horizontally", SvxCellHorJustify::Center, eHorJustify);
|
|
//test alignment
|
|
pPattern = pDoc->GetPattern(2,1,1);
|
|
eHorJustify = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue();
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("cell content should be aligned right horizontally", SvxCellHorJustify::Right, eHorJustify);
|
|
pPattern = pDoc->GetPattern(2,2,1);
|
|
eHorJustify = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue();
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("cell content should be aligned block horizontally", SvxCellHorJustify::Block, eHorJustify);
|
|
|
|
//test Sheet3 only for ods and xlsx
|
|
if ( sFormat == u"calc8" || sFormat == u"Calc Office Open XML" )
|
|
{
|
|
aCSVFileName = createFilePath(u"contentCSV/conditionalFormatting.csv");
|
|
testCondFile(aCSVFileName, pDoc, 2);
|
|
// test parent cell style import ( fdo#55198 )
|
|
if ( sFormat == u"Calc Office Open XML" )
|
|
{
|
|
pPattern = pDoc->GetPattern(1,1,3);
|
|
ScStyleSheet* pStyleSheet = const_cast<ScStyleSheet*>(pPattern->GetStyleSheet());
|
|
// check parent style name
|
|
OUString sExpected(u"Excel Built-in Date"_ustr);
|
|
OUString sResult = pStyleSheet->GetName();
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("parent style for Sheet4.B2 is 'Excel Built-in Date'", sExpected, sResult);
|
|
// check align of style
|
|
SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
|
|
eHorJustify = rItemSet.Get( ATTR_HOR_JUSTIFY ).GetValue();
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("'Excel Built-in Date' style should be aligned centre horizontally", SvxCellHorJustify::Center, eHorJustify);
|
|
// check date format ( should be just month e.g. 29 )
|
|
sResult =pDoc->GetString( 1,1,3 );
|
|
sExpected = "29";
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("'Excel Built-in Date' style should just display month", sExpected, sResult );
|
|
|
|
// check actual align applied to cell, should be the same as
|
|
// the style
|
|
eHorJustify = pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue();
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("cell with 'Excel Built-in Date' style should be aligned centre horizontally", SvxCellHorJustify::Center, eHorJustify);
|
|
}
|
|
}
|
|
|
|
ScConditionalFormat* pCondFormat = pDoc->GetCondFormat(0,0,2);
|
|
const ScRangeList& rRange = pCondFormat->GetRange();
|
|
CPPUNIT_ASSERT_EQUAL(ScRangeList(ScRange(0,0,2,3,0,2)), rRange);
|
|
|
|
pCondFormat = pDoc->GetCondFormat(0,1,2);
|
|
const ScRangeList& rRange2 = pCondFormat->GetRange();
|
|
CPPUNIT_ASSERT_EQUAL(ScRangeList(ScRange(0,1,2,0,1,2)), rRange2);
|
|
|
|
pCondFormat = pDoc->GetCondFormat(1,1,2);
|
|
const ScRangeList& rRange3 = pCondFormat->GetRange();
|
|
CPPUNIT_ASSERT_EQUAL(ScRangeList(ScRange(1,1,2,3,1,2)), rRange3);
|
|
}
|
|
|
|
void ScModelTestBase::goToCell(const OUString& rCell)
|
|
{
|
|
uno::Sequence<beans::PropertyValue> aArgs
|
|
= comphelper::InitPropertySequence({ { "ToPoint", uno::Any(rCell) } });
|
|
dispatchCommand(mxComponent, u".uno:GoToCell"_ustr, aArgs);
|
|
}
|
|
|
|
void ScModelTestBase::typeString(std::u16string_view rStr)
|
|
{
|
|
ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(mxComponent);
|
|
for (const char16_t c : rStr)
|
|
{
|
|
pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, c, 0);
|
|
pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, c, 0);
|
|
Scheduler::ProcessEventsToIdle();
|
|
}
|
|
}
|
|
|
|
void ScModelTestBase::insertStringToCell(const OUString& rCell, std::u16string_view rStr)
|
|
{
|
|
goToCell(rCell);
|
|
|
|
typeString(rStr);
|
|
|
|
ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(mxComponent);
|
|
pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
|
|
pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
|
|
Scheduler::ProcessEventsToIdle();
|
|
}
|
|
|
|
void ScModelTestBase::insertArrayToCell(const OUString& rCell, std::u16string_view rStr)
|
|
{
|
|
goToCell(rCell);
|
|
|
|
typeString(rStr);
|
|
|
|
ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(mxComponent);
|
|
pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_MOD1 | KEY_SHIFT | awt::Key::RETURN);
|
|
pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_MOD1 | KEY_SHIFT | awt::Key::RETURN);
|
|
Scheduler::ProcessEventsToIdle();
|
|
}
|
|
|
|
void ScModelTestBase::insertNewSheet(ScDocument& rDoc)
|
|
{
|
|
sal_Int32 nTabs = static_cast<sal_Int32>(rDoc.GetTableCount());
|
|
|
|
uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
|
|
{ { "Name", uno::Any(u"NewTab"_ustr) }, { "Index", uno::Any(nTabs + 1) } }));
|
|
dispatchCommand(mxComponent, u".uno:Insert"_ustr, aArgs);
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(nTabs + 1), rDoc.GetTableCount());
|
|
}
|
|
|
|
void ScModelTestBase::executeAutoSum()
|
|
{
|
|
dispatchCommand(mxComponent, u".uno:AutoSum"_ustr, {});
|
|
|
|
// Use RETURN key to exit autosum edit view
|
|
ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(mxComponent);
|
|
pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
|
|
pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
|
|
Scheduler::ProcessEventsToIdle();
|
|
}
|
|
|
|
const SdrOle2Obj* ScModelTestBase::getSingleOleObject(ScDocument& rDoc, sal_uInt16 nPage)
|
|
{
|
|
// Retrieve the chart object instance from the 2nd page (for the 2nd sheet).
|
|
ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
|
|
if (!pDrawLayer)
|
|
{
|
|
cout << "Failed to retrieve the drawing layer object." << endl;
|
|
return nullptr;
|
|
}
|
|
|
|
const SdrPage* pPage = pDrawLayer->GetPage(nPage);
|
|
if (!pPage)
|
|
{
|
|
cout << "Failed to retrieve the page object." << endl;
|
|
return nullptr;
|
|
}
|
|
|
|
if (pPage->GetObjCount() != 1)
|
|
{
|
|
cout << "This page should contain one drawing object." << endl;
|
|
return nullptr;
|
|
}
|
|
|
|
const SdrObject* pObj = pPage->GetObj(0);
|
|
if (!pObj)
|
|
{
|
|
cout << "Failed to retrieve the drawing object." << endl;
|
|
return nullptr;
|
|
}
|
|
|
|
if (pObj->GetObjIdentifier() != SdrObjKind::OLE2)
|
|
{
|
|
cout << "This is not an OLE2 object." << endl;
|
|
return nullptr;
|
|
}
|
|
|
|
return static_cast<const SdrOle2Obj*>(pObj);
|
|
}
|
|
|
|
const SdrOle2Obj* ScModelTestBase::getSingleChartObject(ScDocument& rDoc, sal_uInt16 nPage)
|
|
{
|
|
const SdrOle2Obj* pObj = getSingleOleObject(rDoc, nPage);
|
|
|
|
if (!pObj)
|
|
return pObj;
|
|
|
|
if (!pObj->IsChart())
|
|
{
|
|
cout << "This should be a chart object." << endl;
|
|
return nullptr;
|
|
}
|
|
|
|
return pObj;
|
|
}
|
|
|
|
static std::vector<OUString> getChartRangeRepresentations(const SdrOle2Obj& rChartObj)
|
|
{
|
|
std::vector<OUString> aRangeReps;
|
|
|
|
// Make sure the chart object has correct range references.
|
|
Reference<frame::XModel> xModel = rChartObj.getXModel();
|
|
if (!xModel.is())
|
|
{
|
|
cout << "Failed to get the embedded object interface." << endl;
|
|
return aRangeReps;
|
|
}
|
|
|
|
Reference<chart2::XChartDocument> xChartDoc(xModel, UNO_QUERY);
|
|
if (!xChartDoc.is())
|
|
{
|
|
cout << "Failed to get the chart document interface." << endl;
|
|
return aRangeReps;
|
|
}
|
|
|
|
Reference<chart2::data::XDataSource> xDataSource(xChartDoc, UNO_QUERY);
|
|
if (!xDataSource.is())
|
|
{
|
|
cout << "Failed to get the data source interface." << endl;
|
|
return aRangeReps;
|
|
}
|
|
|
|
Sequence<Reference<chart2::data::XLabeledDataSequence> > xDataSeqs = xDataSource->getDataSequences();
|
|
if (!xDataSeqs.hasElements())
|
|
{
|
|
cout << "There should be at least one data sequences." << endl;
|
|
return aRangeReps;
|
|
}
|
|
|
|
Reference<chart2::data::XDataReceiver> xDataRec(xChartDoc, UNO_QUERY);
|
|
if (!xDataRec.is())
|
|
{
|
|
cout << "Failed to get the data receiver interface." << endl;
|
|
return aRangeReps;
|
|
}
|
|
|
|
Sequence<OUString> aRangeRepSeqs = xDataRec->getUsedRangeRepresentations();
|
|
comphelper::sequenceToContainer(aRangeReps, aRangeRepSeqs);
|
|
|
|
return aRangeReps;
|
|
}
|
|
|
|
ScRangeList ScModelTestBase::getChartRanges(ScDocument& rDoc, const SdrOle2Obj& rChartObj)
|
|
{
|
|
std::vector<OUString> aRangeReps = getChartRangeRepresentations(rChartObj);
|
|
ScRangeList aRanges;
|
|
for (size_t i = 0, n = aRangeReps.size(); i < n; ++i)
|
|
{
|
|
ScRange aRange;
|
|
ScRefFlags nRes = aRange.Parse(aRangeReps[i], rDoc, rDoc.GetAddressConvention());
|
|
if (nRes & ScRefFlags::VALID)
|
|
// This is a range address.
|
|
aRanges.push_back(aRange);
|
|
else
|
|
{
|
|
// Parse it as a single cell address.
|
|
ScAddress aAddr;
|
|
nRes = aAddr.Parse(aRangeReps[i], rDoc, rDoc.GetAddressConvention());
|
|
CPPUNIT_ASSERT_MESSAGE("Failed to parse a range representation.", (nRes & ScRefFlags::VALID));
|
|
aRanges.push_back(ScRange(aAddr));
|
|
}
|
|
}
|
|
|
|
return aRanges;
|
|
}
|
|
|
|
bool checkOutput(
|
|
const ScDocument* pDoc, const ScRange& aOutRange,
|
|
const std::vector<std::vector<const char*>>& aCheck, const char* pCaption )
|
|
{
|
|
bool bResult = true;
|
|
const ScAddress& s = aOutRange.aStart;
|
|
const ScAddress& e = aOutRange.aEnd;
|
|
svl::GridPrinter printer(e.Row() - s.Row() + 1, e.Col() - s.Col() + 1, CALC_DEBUG_OUTPUT != 0);
|
|
SCROW nOutRowSize = e.Row() - s.Row() + 1;
|
|
SCCOL nOutColSize = e.Col() - s.Col() + 1;
|
|
|
|
// Check if expected size iz smaller than actual size (and prevent a crash)
|
|
if (aCheck.size() < o3tl::make_unsigned(nOutRowSize) || aCheck[0].size() < o3tl::make_unsigned(nOutColSize))
|
|
{
|
|
// Dump the arrays to console, so we can compare
|
|
std::cout << "Expected data:" << std::endl;
|
|
for (size_t nRow = 0; nRow < aCheck.size(); ++nRow)
|
|
{
|
|
for (size_t nCol = 0; nCol < aCheck[nRow].size(); ++nCol)
|
|
{
|
|
const char* p = aCheck[nRow][nCol];
|
|
if (p)
|
|
{
|
|
OUString aCheckVal = OUString::createFromAscii(p);
|
|
std::cout << "'" << aCheckVal << "', ";
|
|
}
|
|
else
|
|
std::cout << "null, ";
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
std::cout << "Actual data:" << std::endl;
|
|
for (SCROW nRow = 0; nRow < nOutRowSize; ++nRow)
|
|
{
|
|
for (SCCOL nCol = 0; nCol < nOutColSize; ++nCol)
|
|
{
|
|
OUString aVal = pDoc->GetString(nCol + s.Col(), nRow + s.Row(), s.Tab());
|
|
std::cout << "'" << aVal << "', ";
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
std::cout << std::endl;
|
|
|
|
return false;
|
|
}
|
|
|
|
for (SCROW nRow = 0; nRow < nOutRowSize; ++nRow)
|
|
{
|
|
for (SCCOL nCol = 0; nCol < nOutColSize; ++nCol)
|
|
{
|
|
OUString aVal = pDoc->GetString(nCol + s.Col(), nRow + s.Row(), s.Tab());
|
|
printer.set(nRow, nCol, aVal);
|
|
const char* p = aCheck[nRow][nCol];
|
|
if (p)
|
|
{
|
|
OUString aCheckVal = OUString::createFromAscii(p);
|
|
bool bEqual = aCheckVal == aVal;
|
|
if (!bEqual)
|
|
{
|
|
std::cout << "Expected: " << aCheckVal << " Actual: " << aVal << std::endl;
|
|
bResult = false;
|
|
}
|
|
}
|
|
else if (!aVal.isEmpty())
|
|
{
|
|
std::cout << "Empty cell expected" << std::endl;
|
|
bResult = false;
|
|
}
|
|
}
|
|
}
|
|
printer.print(pCaption);
|
|
return bResult;
|
|
}
|
|
|
|
void ScUcalcTestBase::setUp()
|
|
{
|
|
BootstrapFixture::setUp();
|
|
|
|
ScDLL::Init();
|
|
|
|
m_xDocShell
|
|
= new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT | SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS
|
|
| SfxModelFlags::DISABLE_DOCUMENT_RECOVERY);
|
|
m_xDocShell->DoInitUnitTest();
|
|
|
|
m_pDoc = &m_xDocShell->GetDocument();
|
|
}
|
|
|
|
void ScUcalcTestBase::tearDown()
|
|
{
|
|
m_xDocShell->DoClose();
|
|
m_xDocShell.clear();
|
|
|
|
test::BootstrapFixture::tearDown();
|
|
}
|
|
|
|
void ScModelTestBase::createScDoc(const char* pName, const char* pPassword, bool bCheckWarningError)
|
|
{
|
|
if (!pName)
|
|
loadFromURL(u"private:factory/scalc"_ustr);
|
|
else
|
|
loadFromFile(OUString::createFromAscii(pName), pPassword);
|
|
|
|
uno::Reference<lang::XServiceInfo> xServiceInfo(mxComponent, uno::UNO_QUERY_THROW);
|
|
CPPUNIT_ASSERT(xServiceInfo->supportsService(u"com.sun.star.sheet.SpreadsheetDocument"_ustr));
|
|
|
|
if (bCheckWarningError)
|
|
CPPUNIT_ASSERT(!getScDocShell()->GetMedium()->GetWarningError());
|
|
}
|
|
|
|
ScDocument* ScModelTestBase::getScDoc()
|
|
{
|
|
ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(mxComponent);
|
|
CPPUNIT_ASSERT(pModelObj);
|
|
return pModelObj->GetDocument();
|
|
}
|
|
|
|
ScDocument* ScModelTestBase::getScDoc2()
|
|
{
|
|
ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(mxComponent2);
|
|
CPPUNIT_ASSERT(pModelObj);
|
|
return pModelObj->GetDocument();
|
|
}
|
|
|
|
ScDocShell* ScModelTestBase::getScDocShell()
|
|
{
|
|
SfxObjectShell* pFoundShell = SfxObjectShell::GetShellFromComponent(mxComponent);
|
|
CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell);
|
|
ScDocShell* pDocSh = dynamic_cast<ScDocShell*>(pFoundShell);
|
|
CPPUNIT_ASSERT(pDocSh);
|
|
return pDocSh;
|
|
}
|
|
|
|
ScTabViewShell* ScModelTestBase::getViewShell()
|
|
{
|
|
ScDocShell* pDocSh = getScDocShell();
|
|
ScTabViewShell* pTabViewShell = pDocSh->GetBestViewShell(false);
|
|
CPPUNIT_ASSERT_MESSAGE("No ScTabViewShell", pTabViewShell);
|
|
return pTabViewShell;
|
|
}
|
|
|
|
void ScModelTestBase::miscRowHeightsTest( TestParam const * aTestValues, unsigned int numElems)
|
|
{
|
|
for ( unsigned int index=0; index<numElems; ++index )
|
|
{
|
|
const std::u16string_view sFileName = aTestValues[ index ].sTestDoc;
|
|
const OUString sExportType = aTestValues[ index ].sExportType;
|
|
loadFromFile(sFileName);
|
|
|
|
if ( !sExportType.isEmpty() )
|
|
saveAndReload(sExportType);
|
|
|
|
ScDocument* pDoc = getScDoc();
|
|
|
|
for (int i=0; i<aTestValues[ index ].nRowData; ++i)
|
|
{
|
|
SCROW nRow = aTestValues[ index ].pData[ i].nStartRow;
|
|
SCROW nEndRow = aTestValues[ index ].pData[ i ].nEndRow;
|
|
SCTAB nTab = aTestValues[ index ].pData[ i ].nTab;
|
|
int nExpectedHeight = aTestValues[ index ].pData[ i ].nExpectedHeight;
|
|
if ( nExpectedHeight == -1 )
|
|
nExpectedHeight = convertTwipToMm100(ScGlobal::GetStandardRowHeight());
|
|
bool bCheckOpt = ( ( aTestValues[ index ].pData[ i ].nCheck & CHECK_OPTIMAL ) == CHECK_OPTIMAL );
|
|
for ( ; nRow <= nEndRow; ++nRow )
|
|
{
|
|
SAL_INFO( "sc.qa", " checking row " << nRow << " for height " << nExpectedHeight );
|
|
int nHeight = convertTwipToMm100(pDoc->GetRowHeight(nRow, nTab, false));
|
|
if ( bCheckOpt )
|
|
{
|
|
bool bOpt = !(pDoc->GetRowFlags( nRow, nTab ) & CRFlags::ManualSize);
|
|
CPPUNIT_ASSERT_EQUAL(aTestValues[ index ].pData[ i ].bOptimal, bOpt);
|
|
}
|
|
CPPUNIT_ASSERT_EQUAL(nExpectedHeight, nHeight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScModelTestBase::enableOpenCL()
|
|
{
|
|
/**
|
|
* Turn on OpenCL group interpreter. Call this after the document is
|
|
* loaded and before performing formula calculation.
|
|
*/
|
|
sc::FormulaGroupInterpreter::enableOpenCL_UnitTestsOnly();
|
|
}
|
|
|
|
void ScModelTestBase::disableOpenCL()
|
|
{
|
|
sc::FormulaGroupInterpreter::disableOpenCL_UnitTestsOnly();
|
|
}
|
|
|
|
void ScModelTestBase::initTestEnv(std::u16string_view fileName)
|
|
{
|
|
// Some documents contain macros, disable them, otherwise
|
|
// the "Error, BASIC runtime error." dialog is prompted
|
|
// and it crashes in tearDown
|
|
std::vector<beans::PropertyValue> args;
|
|
beans::PropertyValue aMacroValue;
|
|
aMacroValue.Name = "MacroExecutionMode";
|
|
aMacroValue.Handle = -1;
|
|
aMacroValue.Value <<= document::MacroExecMode::NEVER_EXECUTE;
|
|
aMacroValue.State = beans::PropertyState_DIRECT_VALUE;
|
|
args.push_back(aMacroValue);
|
|
|
|
disableOpenCL();
|
|
CPPUNIT_ASSERT(!ScCalcConfig::isOpenCLEnabled());
|
|
|
|
// Open the document with OpenCL disabled
|
|
mxComponent = mxDesktop->loadComponentFromURL(
|
|
createFileURL(fileName), u"_default"_ustr, 0, comphelper::containerToSequence(args));
|
|
|
|
enableOpenCL();
|
|
CPPUNIT_ASSERT(ScCalcConfig::isOpenCLEnabled());
|
|
|
|
// it's not possible to open the same document twice, thus, create a temp file
|
|
createTempCopy(fileName);
|
|
|
|
// Open the document with OpenCL enabled
|
|
mxComponent2 = mxDesktop->loadComponentFromURL(
|
|
maTempFile.GetURL(), u"_default"_ustr, 0, comphelper::containerToSequence(args));
|
|
|
|
// Check there are 2 documents
|
|
uno::Reference<frame::XFrames> xFrames = mxDesktop->getFrames();
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), xFrames->getCount());
|
|
}
|
|
|
|
ScRange ScUcalcTestBase::insertRangeData(
|
|
ScDocument* pDoc, const ScAddress& rPos, const std::vector<std::vector<const char*>>& rData )
|
|
{
|
|
if (rData.empty())
|
|
return ScRange(ScAddress::INITIALIZE_INVALID);
|
|
|
|
ScAddress aPos = rPos;
|
|
|
|
SCCOL nColWidth = 1;
|
|
for (const std::vector<const char*>& rRow : rData)
|
|
nColWidth = std::max<SCCOL>(nColWidth, rRow.size());
|
|
|
|
ScRange aRange(aPos);
|
|
aRange.aEnd.IncCol(nColWidth-1);
|
|
aRange.aEnd.IncRow(rData.size()-1);
|
|
|
|
clearRange(pDoc, aRange);
|
|
|
|
for (const std::vector<const char*>& rRow : rData)
|
|
{
|
|
aPos.SetCol(rPos.Col());
|
|
|
|
for (const char* pStr : rRow)
|
|
{
|
|
if (!pStr)
|
|
{
|
|
aPos.IncCol();
|
|
continue;
|
|
}
|
|
|
|
OUString aStr(pStr, strlen(pStr), RTL_TEXTENCODING_UTF8);
|
|
|
|
ScSetStringParam aParam; // Leave default.
|
|
aParam.meStartListening = sc::NoListening;
|
|
pDoc->SetString(aPos, aStr, &aParam);
|
|
|
|
aPos.IncCol();
|
|
}
|
|
|
|
aPos.IncRow();
|
|
}
|
|
|
|
pDoc->StartAllListeners(aRange);
|
|
printRange(pDoc, aRange, "Range data content");
|
|
return aRange;
|
|
}
|
|
|
|
ScUndoCut* ScUcalcTestBase::cutToClip(ScDocShell& rDocSh, const ScRange& rRange, ScDocument* pClipDoc, bool bCreateUndo)
|
|
{
|
|
ScDocument* pSrcDoc = &rDocSh.GetDocument();
|
|
|
|
ScClipParam aClipParam(rRange, true);
|
|
ScMarkData aMark(pSrcDoc->GetSheetLimits());
|
|
aMark.SetMarkArea(rRange);
|
|
pSrcDoc->CopyToClip(aClipParam, pClipDoc, &aMark, false, false);
|
|
|
|
// Taken from ScViewFunc::CutToClip()
|
|
ScDocumentUniquePtr pUndoDoc;
|
|
if (bCreateUndo)
|
|
{
|
|
pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
|
|
pUndoDoc->InitUndoSelected( *pSrcDoc, aMark );
|
|
// all sheets - CopyToDocument skips those that don't exist in pUndoDoc
|
|
ScRange aCopyRange = rRange;
|
|
aCopyRange.aStart.SetTab(0);
|
|
aCopyRange.aEnd.SetTab(pSrcDoc->GetTableCount()-1);
|
|
pSrcDoc->CopyToDocument( aCopyRange,
|
|
(InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS,
|
|
false, *pUndoDoc );
|
|
}
|
|
|
|
aMark.MarkToMulti();
|
|
pSrcDoc->DeleteSelection( InsertDeleteFlags::ALL, aMark );
|
|
aMark.MarkToSimple();
|
|
|
|
if (pUndoDoc)
|
|
return new ScUndoCut( &rDocSh, rRange, rRange.aEnd, aMark, std::move(pUndoDoc) );
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void ScUcalcTestBase::copyToClip(ScDocument* pSrcDoc, const ScRange& rRange, ScDocument* pClipDoc)
|
|
{
|
|
ScClipParam aClipParam(rRange, false);
|
|
ScMarkData aMark(pSrcDoc->GetSheetLimits());
|
|
aMark.SetMarkArea(rRange);
|
|
pSrcDoc->CopyToClip(aClipParam, pClipDoc, &aMark, false, false);
|
|
}
|
|
|
|
void ScUcalcTestBase::pasteFromClip(ScDocument* pDestDoc, const ScRange& rDestRange, ScDocument* pClipDoc)
|
|
{
|
|
ScMarkData aMark(pDestDoc->GetSheetLimits());
|
|
aMark.SetMarkArea(rDestRange);
|
|
pDestDoc->CopyFromClip(rDestRange, aMark, InsertDeleteFlags::ALL, nullptr, pClipDoc);
|
|
}
|
|
|
|
ScUndoPaste* ScUcalcTestBase::createUndoPaste(ScDocShell& rDocSh, const ScRange& rRange, ScDocumentUniquePtr pUndoDoc)
|
|
{
|
|
ScDocument& rDoc = rDocSh.GetDocument();
|
|
ScMarkData aMarkData(rDoc.GetSheetLimits());
|
|
aMarkData.SetMarkArea(rRange);
|
|
std::unique_ptr<ScRefUndoData> pRefUndoData(new ScRefUndoData(&rDoc));
|
|
|
|
return new ScUndoPaste(
|
|
&rDocSh, rRange, aMarkData, std::move(pUndoDoc), nullptr, InsertDeleteFlags::ALL, std::move(pRefUndoData), false);
|
|
}
|
|
|
|
void ScUcalcTestBase::pasteOneCellFromClip(ScDocument* pDestDoc, const ScRange& rDestRange, ScDocument* pClipDoc, InsertDeleteFlags eFlags)
|
|
{
|
|
ScMarkData aMark(pDestDoc->GetSheetLimits());
|
|
aMark.SetMarkArea(rDestRange);
|
|
sc::CopyFromClipContext aCxt(*pDestDoc, nullptr, pClipDoc, eFlags, false, false);
|
|
aCxt.setDestRange(rDestRange.aStart.Col(), rDestRange.aStart.Row(),
|
|
rDestRange.aEnd.Col(), rDestRange.aEnd.Row());
|
|
aCxt.setTabRange(rDestRange.aStart.Tab(), rDestRange.aEnd.Tab());
|
|
pDestDoc->CopyOneCellFromClip(aCxt, rDestRange.aStart.Col(), rDestRange.aStart.Row(),
|
|
rDestRange.aEnd.Col(), rDestRange.aEnd.Row());
|
|
}
|
|
|
|
void ScUcalcTestBase::setCalcAsShown(ScDocument* pDoc, bool bCalcAsShown)
|
|
{
|
|
ScDocOptions aOpt = pDoc->GetDocOptions();
|
|
aOpt.SetCalcAsShown(bCalcAsShown);
|
|
pDoc->SetDocOptions(aOpt);
|
|
}
|
|
|
|
ScDocShell* ScUcalcTestBase::findLoadedDocShellByName(std::u16string_view rName)
|
|
{
|
|
ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>, false));
|
|
while (pShell)
|
|
{
|
|
SfxMedium* pMedium = pShell->GetMedium();
|
|
if (pMedium)
|
|
{
|
|
OUString aName = pMedium->GetName();
|
|
if (aName == rName)
|
|
return pShell;
|
|
}
|
|
pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, checkSfxObjectShell<ScDocShell>, false));
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool ScUcalcTestBase::insertRangeNames(
|
|
ScDocument* pDoc, ScRangeName* pNames, const RangeNameDef* p, const RangeNameDef* pEnd)
|
|
{
|
|
ScAddress aA1(0, 0, 0);
|
|
for (; p != pEnd; ++p)
|
|
{
|
|
ScRangeData* pNew = new ScRangeData(
|
|
*pDoc,
|
|
OUString::createFromAscii(p->mpName),
|
|
OUString::createFromAscii(p->mpExpr),
|
|
aA1, ScRangeData::Type::Name,
|
|
formula::FormulaGrammar::GRAM_ENGLISH);
|
|
pNew->SetIndex(p->mnIndex);
|
|
bool bSuccess = pNames->insert(pNew);
|
|
if (!bSuccess)
|
|
{
|
|
cerr << "Insertion failed." << endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
OUString ScUcalcTestBase::getRangeByName(ScDocument* pDoc, const OUString& aRangeName)
|
|
{
|
|
ScRangeData* pName = pDoc->GetRangeName()->findByUpperName(aRangeName.toAsciiUpperCase());
|
|
CPPUNIT_ASSERT(pName);
|
|
return pName->GetSymbol(pDoc->GetGrammar());
|
|
}
|
|
|
|
#if CALC_DEBUG_OUTPUT != 0
|
|
void ScUcalcTestBase::printFormula(ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, const char* pCaption)
|
|
{
|
|
if (pCaption != nullptr)
|
|
cout << pCaption << ", ";
|
|
cout << nCol << "/" << nRow << ": " << pDoc->GetFormula(nCol, nRow, nTab);
|
|
cout << endl;
|
|
}
|
|
#else
|
|
// Avoid unused parameter warning
|
|
void ScUcalcTestBase::printFormula(ScDocument*, SCCOL, SCROW, SCTAB, const char*) {}
|
|
#endif
|
|
|
|
void ScUcalcTestBase::printRange(ScDocument* pDoc, const ScRange& rRange, const char* pCaption,
|
|
const bool printFormula)
|
|
{
|
|
SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
|
|
SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
|
|
svl::GridPrinter printer(nRow2 - nRow1 + 1, nCol2 - nCol1 + 1, CALC_DEBUG_OUTPUT != 0);
|
|
for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
|
|
{
|
|
for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
|
|
{
|
|
ScAddress aPos(nCol, nRow, rRange.aStart.Tab());
|
|
ScRefCellValue aCell(*pDoc, aPos);
|
|
OUString aVal = printFormula ? pDoc->GetFormula(nCol, nRow, rRange.aStart.Tab())
|
|
: ScCellFormat::GetOutputString(*pDoc, aPos, aCell);
|
|
printer.set(nRow - nRow1, nCol - nCol1, aVal);
|
|
}
|
|
}
|
|
printer.print(pCaption);
|
|
}
|
|
|
|
void ScUcalcTestBase::printRange(ScDocument* pDoc, const ScRange& rRange, const OString& rCaption,
|
|
const bool printFormula)
|
|
{
|
|
printRange(pDoc, rRange, rCaption.getStr(), printFormula);
|
|
}
|
|
|
|
void ScUcalcTestBase::clearRange(ScDocument* pDoc, const ScRange& rRange)
|
|
{
|
|
ScMarkData aMarkData(pDoc->GetSheetLimits());
|
|
aMarkData.SetMarkArea(rRange);
|
|
pDoc->DeleteArea(
|
|
rRange.aStart.Col(), rRange.aStart.Row(),
|
|
rRange.aEnd.Col(), rRange.aEnd.Row(), aMarkData, InsertDeleteFlags::CONTENTS);
|
|
}
|
|
|
|
void ScUcalcTestBase::clearSheet(ScDocument* pDoc, SCTAB nTab)
|
|
{
|
|
ScRange aRange(0,0,nTab,pDoc->MaxCol(),pDoc->MaxRow(),nTab);
|
|
clearRange(pDoc, aRange);
|
|
}
|
|
|
|
bool ScUcalcTestBase::checkFormulaPosition(ScDocument& rDoc, const ScAddress& rPos)
|
|
{
|
|
OUString aStr(rPos.Format(ScRefFlags::VALID));
|
|
const ScFormulaCell* pFC = rDoc.GetFormulaCell(rPos);
|
|
if (!pFC)
|
|
{
|
|
cerr << "Formula cell expected at " << aStr << " but not found." << endl;
|
|
return false;
|
|
}
|
|
|
|
if (pFC->aPos != rPos)
|
|
{
|
|
OUString aStr2(pFC->aPos.Format(ScRefFlags::VALID));
|
|
cerr << "Formula cell at " << aStr << " has incorrect position of " << aStr2 << endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ScUcalcTestBase::checkFormulaPositions(
|
|
ScDocument& rDoc, SCTAB nTab, SCCOL nCol, const SCROW* pRows, size_t nRowCount)
|
|
{
|
|
ScAddress aPos(nCol, 0, nTab);
|
|
for (size_t i = 0; i < nRowCount; ++i)
|
|
{
|
|
SCROW nRow = pRows[i];
|
|
aPos.SetRow(nRow);
|
|
|
|
if (!checkFormulaPosition(rDoc, aPos))
|
|
{
|
|
OUString aStr(aPos.Format(ScRefFlags::VALID));
|
|
cerr << "Formula cell position failed at " << aStr << "." << endl;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::unique_ptr<ScTokenArray> ScUcalcTestBase::compileFormula(
|
|
ScDocument* pDoc, const OUString& rFormula,
|
|
formula::FormulaGrammar::Grammar eGram )
|
|
{
|
|
ScAddress aPos(0,0,0);
|
|
ScCompiler aComp(*pDoc, aPos, eGram);
|
|
return aComp.CompileString(rFormula);
|
|
}
|
|
|
|
void ScUcalcTestBase::clearFormulaCellChangedFlag( ScDocument& rDoc, const ScRange& rRange )
|
|
{
|
|
const ScAddress& s = rRange.aStart;
|
|
const ScAddress& e = rRange.aEnd;
|
|
for (SCTAB nTab = s.Tab(); nTab <= e.Tab(); ++nTab)
|
|
{
|
|
for (SCCOL nCol = s.Col(); nCol <= e.Col(); ++nCol)
|
|
{
|
|
for (SCROW nRow = s.Row(); nRow <= e.Row(); ++nRow)
|
|
{
|
|
ScAddress aPos(nCol, nRow, nTab);
|
|
ScFormulaCell* pFC = rDoc.GetFormulaCell(aPos);
|
|
if (pFC)
|
|
pFC->SetChanged(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|