diff options
Diffstat (limited to 'sc/source/core/tool/consoli.cxx')
-rw-r--r-- | sc/source/core/tool/consoli.cxx | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/sc/source/core/tool/consoli.cxx b/sc/source/core/tool/consoli.cxx new file mode 100644 index 000000000..aa8dbcc3e --- /dev/null +++ b/sc/source/core/tool/consoli.cxx @@ -0,0 +1,544 @@ +/* -*- 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 . + */ + +#include <consoli.hxx> +#include <document.hxx> +#include <olinetab.hxx> +#include <subtotal.hxx> +#include <formula/errorcodes.hxx> +#include <formulacell.hxx> +#include <tokenarray.hxx> +#include <osl/diagnose.h> +#include <refdata.hxx> + +#include <string.h> +#include <memory> + +#define SC_CONS_NOTFOUND -1 + +const OpCode eOpCodeTable[] = { // order as for enum ScSubTotalFunc + ocBad, // none + ocAverage, + ocCount, + ocCount2, + ocMax, + ocMin, + ocProduct, + ocStDev, + ocStDevP, + ocSum, + ocVar, + ocVarP }; + +template< typename T > +static void lcl_AddString( ::std::vector<OUString>& rData, T& nCount, const OUString& rInsert ) +{ + rData.push_back( rInsert); + ++nCount; +} + +ScConsData::ScConsData() : + eFunction(SUBTOTAL_FUNC_SUM), + bReference(false), + bColByName(false), + bRowByName(false), + nColCount(0), + nRowCount(0), + nDataCount(0), + bCornerUsed(false) +{ +} + +ScConsData::~ScConsData() +{ +} + +void ScConsData::DeleteData() +{ + ppRefs.reset(); + ppFunctionData.reset(); + ppUsed.reset(); + ppTitlePos.reset(); + ::std::vector<OUString>().swap( maColHeaders); + ::std::vector<OUString>().swap( maRowHeaders); + ::std::vector<OUString>().swap( maTitles); + nDataCount = 0; + + if (bColByName) nColCount = 0; // otherwise maColHeaders is wrong + if (bRowByName) nRowCount = 0; + + bCornerUsed = false; + aCornerText.clear(); +} + +void ScConsData::InitData() +{ + if (bReference && nColCount && !ppRefs) + { + ppRefs.reset(new std::unique_ptr<ScReferenceList[]>[nColCount]); + for (SCSIZE i=0; i<nColCount; i++) + ppRefs[i].reset(new ScReferenceList[nRowCount]); + } + else if (nColCount && !ppFunctionData) + { + ppFunctionData.reset( new std::unique_ptr<ScFunctionData[]>[nColCount] ); + for (SCSIZE i=0; i<nColCount; i++) + { + ppFunctionData[i].reset( new ScFunctionData[nRowCount] ); + } + } + + if (nColCount && !ppUsed) + { + ppUsed.reset( new std::unique_ptr<bool[]>[nColCount] ); + for (SCSIZE i=0; i<nColCount; i++) + { + ppUsed[i].reset( new bool[nRowCount] ); + memset( ppUsed[i].get(), 0, nRowCount * sizeof(bool) ); + } + } + + if (nRowCount && nDataCount && !ppTitlePos) + { + ppTitlePos.reset( new std::unique_ptr<SCSIZE[]>[nRowCount] ); + for (SCSIZE i=0; i<nRowCount; i++) + { + ppTitlePos[i].reset( new SCSIZE[nDataCount] ); + memset( ppTitlePos[i].get(), 0, nDataCount * sizeof(SCSIZE) ); //TODO: not necessary ? + } + } + + // CornerText: single String +} + +void ScConsData::DoneFields() +{ + InitData(); +} + +void ScConsData::SetSize( SCCOL nCols, SCROW nRows ) +{ + DeleteData(); + nColCount = static_cast<SCSIZE>(nCols); + nRowCount = static_cast<SCSIZE>(nRows); +} + +void ScConsData::GetSize( SCCOL& rCols, SCROW& rRows ) const +{ + rCols = static_cast<SCCOL>(nColCount); + rRows = static_cast<SCROW>(nRowCount); +} + +void ScConsData::SetFlags( ScSubTotalFunc eFunc, bool bColName, bool bRowName, bool bRef ) +{ + DeleteData(); + bReference = bRef; + bColByName = bColName; + if (bColName) nColCount = 0; + bRowByName = bRowName; + if (bRowName) nRowCount = 0; + eFunction = eFunc; +} + +void ScConsData::AddFields( const ScDocument* pSrcDoc, SCTAB nTab, + SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) +{ + ++nDataCount; + + OUString aTitle; + + SCCOL nStartCol = nCol1; + SCROW nStartRow = nRow1; + if (bColByName) ++nStartRow; + if (bRowByName) ++nStartCol; + + if (bColByName) + { + for (SCCOL nCol=nStartCol; nCol<=nCol2; nCol++) + { + aTitle = pSrcDoc->GetString(nCol, nRow1, nTab); + if (!aTitle.isEmpty()) + { + bool bFound = false; + for (SCSIZE i=0; i<nColCount && !bFound; i++) + if ( maColHeaders[i] == aTitle ) + bFound = true; + if (!bFound) + lcl_AddString( maColHeaders, nColCount, aTitle ); + } + } + } + + if (!bRowByName) + return; + + for (SCROW nRow=nStartRow; nRow<=nRow2; nRow++) + { + aTitle = pSrcDoc->GetString(nCol1, nRow, nTab); + if (!aTitle.isEmpty()) + { + bool bFound = false; + for (SCSIZE i=0; i<nRowCount && !bFound; i++) + if ( maRowHeaders[i] == aTitle ) + bFound = true; + if (!bFound) + lcl_AddString( maRowHeaders, nRowCount, aTitle ); + } + } +} + +void ScConsData::AddName( const OUString& rName ) +{ + SCSIZE nArrX; + SCSIZE nArrY; + + if (!bReference) + return; + + maTitles.push_back( rName); + size_t nTitleCount = maTitles.size(); + + for (nArrY=0; nArrY<nRowCount; nArrY++) + { + // set all data to same length + + SCSIZE nMax = 0; + for (nArrX=0; nArrX<nColCount; nArrX++) + nMax = std::max( nMax, ppRefs[nArrX][nArrY].size() ); + + for (nArrX=0; nArrX<nColCount; nArrX++) + { + ppUsed[nArrX][nArrY] = true; + ppRefs[nArrX][nArrY].resize( nMax, { SC_CONS_NOTFOUND, SC_CONS_NOTFOUND, SC_CONS_NOTFOUND }); + } + + // store positions + + if (ppTitlePos) + if (nTitleCount < nDataCount) + ppTitlePos[nArrY][nTitleCount] = nMax; + } +} + +void ScConsData::AddData( ScDocument* pSrcDoc, SCTAB nTab, + SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) +{ + PutInOrder(nCol1,nCol2); + PutInOrder(nRow1,nRow2); + if ( nCol2 >= sal::static_int_cast<SCCOL>(nCol1 + nColCount) && !bColByName ) + { + OSL_FAIL("range too big"); + nCol2 = sal::static_int_cast<SCCOL>( nCol1 + nColCount - 1 ); + } + if ( nRow2 >= sal::static_int_cast<SCROW>(nRow1 + nRowCount) && !bRowByName ) + { + OSL_FAIL("range too big"); + nRow2 = sal::static_int_cast<SCROW>( nRow1 + nRowCount - 1 ); + } + + SCCOL nCol; + SCROW nRow; + + // left top corner + + if ( bColByName && bRowByName ) + { + OUString aThisCorner = pSrcDoc->GetString(nCol1, nRow1, nTab); + if (bCornerUsed) + { + if (aCornerText != aThisCorner) + aCornerText.clear(); + } + else + { + aCornerText = aThisCorner; + bCornerUsed = true; + } + } + + // search title + + SCCOL nStartCol = nCol1; + SCROW nStartRow = nRow1; + if (bColByName) ++nStartRow; + if (bRowByName) ++nStartCol; + OUString aTitle; + std::unique_ptr<SCCOL[]> pDestCols; + std::unique_ptr<SCROW[]> pDestRows; + if (bColByName) + { + pDestCols.reset(new SCCOL[nCol2-nStartCol+1]); + for (nCol=nStartCol; nCol<=nCol2; nCol++) + { + aTitle = pSrcDoc->GetString(nCol, nRow1, nTab); + SCCOL nPos = SC_CONS_NOTFOUND; + if (!aTitle.isEmpty()) + { + bool bFound = false; + for (SCSIZE i=0; i<nColCount && !bFound; i++) + if ( maColHeaders[i] == aTitle ) + { + nPos = static_cast<SCCOL>(i); + bFound = true; + } + OSL_ENSURE(bFound, "column not found"); + } + pDestCols[nCol-nStartCol] = nPos; + } + } + if (bRowByName) + { + pDestRows.reset(new SCROW[nRow2-nStartRow+1]); + for (nRow=nStartRow; nRow<=nRow2; nRow++) + { + aTitle = pSrcDoc->GetString(nCol1, nRow, nTab); + SCROW nPos = SC_CONS_NOTFOUND; + if (!aTitle.isEmpty()) + { + bool bFound = false; + for (SCSIZE i=0; i<nRowCount && !bFound; i++) + if ( maRowHeaders[i] == aTitle ) + { + nPos = static_cast<SCROW>(i); + bFound = true; + } + OSL_ENSURE(bFound, "row not found"); + } + pDestRows[nRow-nStartRow] = nPos; + } + } + nCol1 = nStartCol; + nRow1 = nStartRow; + + // data + + bool bAnyCell = ( eFunction == SUBTOTAL_FUNC_CNT2 ); + for (nCol=nCol1; nCol<=nCol2; nCol++) + { + SCCOL nArrX = nCol-nCol1; + if (bColByName) nArrX = pDestCols[nArrX]; + if (nArrX != SC_CONS_NOTFOUND) + { + for (nRow=nRow1; nRow<=nRow2; nRow++) + { + SCROW nArrY = nRow-nRow1; + if (bRowByName) nArrY = pDestRows[nArrY]; + if ( nArrY != SC_CONS_NOTFOUND && ( + bAnyCell ? pSrcDoc->HasData( nCol, nRow, nTab ) + : pSrcDoc->HasValueData( nCol, nRow, nTab ) ) ) + { + if (bReference) + { + ppUsed[nArrX][nArrY] = true; + ppRefs[nArrX][nArrY].push_back( { nCol, nRow, nTab } ); + } + else + { + double nVal = pSrcDoc->GetValue( nCol, nRow, nTab ); + if (!ppUsed[nArrX][nArrY]) + { + ppUsed[nArrX][nArrY] = true; + ppFunctionData[nArrX][nArrY] = ScFunctionData( eFunction); + } + ppFunctionData[nArrX][nArrY].update( nVal); + } + } + } + } + } +} + +// check before, how many rows to insert (for Undo) + +SCROW ScConsData::GetInsertCount() const +{ + SCROW nInsert = 0; + SCSIZE nArrX; + SCSIZE nArrY; + if ( ppRefs && ppUsed ) + { + for (nArrY=0; nArrY<nRowCount; nArrY++) + { + SCSIZE nNeeded = 0; + for (nArrX=0; nArrX<nColCount; nArrX++) + nNeeded = std::max( nNeeded, ppRefs[nArrX][nArrY].size() ); + + nInsert += nNeeded; + } + } + return nInsert; +} + +// store completed data to document +//TODO: optimize on columns? + +void ScConsData::OutputToDocument( ScDocument& rDestDoc, SCCOL nCol, SCROW nRow, SCTAB nTab ) +{ + OpCode eOpCode = eOpCodeTable[eFunction]; + + SCSIZE nArrX; + SCSIZE nArrY; + + // left top corner + + if ( bColByName && bRowByName && !aCornerText.isEmpty() ) + rDestDoc.SetString( nCol, nRow, nTab, aCornerText ); + + // title + + SCCOL nStartCol = nCol; + SCROW nStartRow = nRow; + if (bColByName) ++nStartRow; + if (bRowByName) ++nStartCol; + + if (bColByName) + for (SCSIZE i=0; i<nColCount; i++) + rDestDoc.SetString( sal::static_int_cast<SCCOL>(nStartCol+i), nRow, nTab, maColHeaders[i] ); + if (bRowByName) + for (SCSIZE j=0; j<nRowCount; j++) + rDestDoc.SetString( nCol, sal::static_int_cast<SCROW>(nStartRow+j), nTab, maRowHeaders[j] ); + + nCol = nStartCol; + nRow = nStartRow; + + // data + + if ( ppFunctionData && ppUsed ) // insert values directly + { + for (nArrX=0; nArrX<nColCount; nArrX++) + for (nArrY=0; nArrY<nRowCount; nArrY++) + if (ppUsed[nArrX][nArrY]) + { + double fVal = ppFunctionData[nArrX][nArrY].getResult(); + if (ppFunctionData[nArrX][nArrY].getError()) + rDestDoc.SetError( sal::static_int_cast<SCCOL>(nCol+nArrX), + sal::static_int_cast<SCROW>(nRow+nArrY), nTab, FormulaError::NoValue ); + else + rDestDoc.SetValue( sal::static_int_cast<SCCOL>(nCol+nArrX), + sal::static_int_cast<SCROW>(nRow+nArrY), nTab, fVal ); + } + } + + if ( !(ppRefs && ppUsed) ) // insert Reference + return; + + //TODO: differentiate, if split into categories + OUString aString; + + ScSingleRefData aSRef; // data for Reference formula cells + aSRef.InitFlags(); // this reference is absolute at all times + aSRef.SetFlag3D(true); + + ScComplexRefData aCRef; // data for Sum cells + aCRef.InitFlags(); + aCRef.Ref1.SetColRel(true); aCRef.Ref1.SetRowRel(true); aCRef.Ref1.SetTabRel(true); + aCRef.Ref2.SetColRel(true); aCRef.Ref2.SetRowRel(true); aCRef.Ref2.SetTabRel(true); + + for (nArrY=0; nArrY<nRowCount; nArrY++) + { + SCSIZE nNeeded = 0; + for (nArrX=0; nArrX<nColCount; nArrX++) + nNeeded = std::max( nNeeded, ppRefs[nArrX][nArrY].size() ); + + if (nNeeded) + { + rDestDoc.InsertRow( 0,nTab, rDestDoc.MaxCol(),nTab, nRow+nArrY, nNeeded ); + + for (nArrX=0; nArrX<nColCount; nArrX++) + if (ppUsed[nArrX][nArrY]) + { + SCSIZE nCount = ppRefs[nArrX][nArrY].size(); + if (nCount) + { + for (SCSIZE nPos=0; nPos<nCount; nPos++) + { + ScReferenceEntry aRef = ppRefs[nArrX][nArrY][nPos]; + if (aRef.nTab != SC_CONS_NOTFOUND) + { + // insert reference (absolute, 3d) + + aSRef.SetAddress(rDestDoc.GetSheetLimits(), ScAddress(aRef.nCol,aRef.nRow,aRef.nTab), ScAddress()); + + ScTokenArray aRefArr(rDestDoc); + aRefArr.AddSingleReference(aSRef); + aRefArr.AddOpCode(ocStop); + ScAddress aDest( sal::static_int_cast<SCCOL>(nCol+nArrX), + sal::static_int_cast<SCROW>(nRow+nArrY+nPos), nTab ); + ScFormulaCell* pCell = new ScFormulaCell(rDestDoc, aDest, aRefArr); + rDestDoc.SetFormulaCell(aDest, pCell); + } + } + + // insert sum (relative, not 3d) + + ScAddress aDest( sal::static_int_cast<SCCOL>(nCol+nArrX), + sal::static_int_cast<SCROW>(nRow+nArrY+nNeeded), nTab ); + + ScRange aRange(sal::static_int_cast<SCCOL>(nCol+nArrX), nRow+nArrY, nTab); + aRange.aEnd.SetRow(nRow+nArrY+nNeeded-1); + aCRef.SetRange(rDestDoc.GetSheetLimits(), aRange, aDest); + + ScTokenArray aArr(rDestDoc); + aArr.AddOpCode(eOpCode); // selected function + aArr.AddOpCode(ocOpen); + aArr.AddDoubleReference(aCRef); + aArr.AddOpCode(ocClose); + aArr.AddOpCode(ocStop); + ScFormulaCell* pCell = new ScFormulaCell(rDestDoc, aDest, aArr); + rDestDoc.SetFormulaCell(aDest, pCell); + } + } + + // insert outline + + ScOutlineArray& rOutArr = rDestDoc.GetOutlineTable( nTab, true )->GetRowArray(); + SCROW nOutStart = nRow+nArrY; + SCROW nOutEnd = nRow+nArrY+nNeeded-1; + bool bSize = false; + rOutArr.Insert( nOutStart, nOutEnd, bSize ); + for (SCROW nOutRow=nOutStart; nOutRow<=nOutEnd; nOutRow++) + rDestDoc.ShowRow( nOutRow, nTab, false ); + rDestDoc.SetDrawPageSize(nTab); + rDestDoc.UpdateOutlineRow( nOutStart, nOutEnd, nTab, false ); + + // sub title + + if (ppTitlePos && !maTitles.empty() && !maRowHeaders.empty()) + { + for (SCSIZE nPos=0; nPos<nDataCount; nPos++) + { + SCSIZE nTPos = ppTitlePos[nArrY][nPos]; + bool bDo = true; + if (nPos+1<nDataCount) + if (ppTitlePos[nArrY][nPos+1] == nTPos) + bDo = false; // empty + if ( bDo && nTPos < nNeeded ) + { + aString = maRowHeaders[nArrY] + " / " + maTitles[nPos]; + rDestDoc.SetString( nCol-1, nRow+nArrY+nTPos, nTab, aString ); + } + } + } + + nRow += nNeeded; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |