1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <consoli.hxx>
21 #include <document.hxx>
22 #include <olinetab.hxx>
23 #include <subtotal.hxx>
24 #include <formula/errorcodes.hxx>
25 #include <formulacell.hxx>
26 #include <tokenarray.hxx>
27 #include <osl/diagnose.h>
28 #include <refdata.hxx>
33 #define SC_CONS_NOTFOUND -1
35 const OpCode eOpCodeTable
[] = { // order as for enum ScSubTotalFunc
49 template< typename T
>
50 static void lcl_AddString( ::std::vector
<OUString
>& rData
, T
& nCount
, const OUString
& rInsert
)
52 rData
.push_back( rInsert
);
56 ScConsData::ScConsData() :
57 eFunction(SUBTOTAL_FUNC_SUM
),
68 ScConsData::~ScConsData()
72 void ScConsData::DeleteData()
75 ppFunctionData
.reset();
78 ::std::vector
<OUString
>().swap( maColHeaders
);
79 ::std::vector
<OUString
>().swap( maRowHeaders
);
80 ::std::vector
<OUString
>().swap( maTitles
);
83 if (bColByName
) nColCount
= 0; // otherwise maColHeaders is wrong
84 if (bRowByName
) nRowCount
= 0;
90 void ScConsData::InitData()
92 if (bReference
&& nColCount
&& !ppRefs
)
94 ppRefs
.reset(new std::unique_ptr
<ScReferenceList
[]>[nColCount
]);
95 for (SCSIZE i
=0; i
<nColCount
; i
++)
96 ppRefs
[i
].reset(new ScReferenceList
[nRowCount
]);
98 else if (nColCount
&& !ppFunctionData
)
100 ppFunctionData
.reset( new std::unique_ptr
<ScFunctionData
[]>[nColCount
] );
101 for (SCSIZE i
=0; i
<nColCount
; i
++)
103 ppFunctionData
[i
].reset( new ScFunctionData
[nRowCount
] );
107 if (nColCount
&& !ppUsed
)
109 ppUsed
.reset( new std::unique_ptr
<bool[]>[nColCount
] );
110 for (SCSIZE i
=0; i
<nColCount
; i
++)
112 ppUsed
[i
].reset( new bool[nRowCount
] );
113 memset( ppUsed
[i
].get(), 0, nRowCount
* sizeof(bool) );
117 if (nRowCount
&& nDataCount
&& !ppTitlePos
)
119 ppTitlePos
.reset( new std::unique_ptr
<SCSIZE
[]>[nRowCount
] );
120 for (SCSIZE i
=0; i
<nRowCount
; i
++)
122 ppTitlePos
[i
].reset( new SCSIZE
[nDataCount
] );
123 memset( ppTitlePos
[i
].get(), 0, nDataCount
* sizeof(SCSIZE
) ); //TODO: not necessary ?
127 // CornerText: single String
130 void ScConsData::DoneFields()
135 void ScConsData::SetSize( SCCOL nCols
, SCROW nRows
)
138 nColCount
= static_cast<SCSIZE
>(nCols
);
139 nRowCount
= static_cast<SCSIZE
>(nRows
);
142 void ScConsData::GetSize( SCCOL
& rCols
, SCROW
& rRows
) const
144 rCols
= static_cast<SCCOL
>(nColCount
);
145 rRows
= static_cast<SCROW
>(nRowCount
);
148 void ScConsData::SetFlags( ScSubTotalFunc eFunc
, bool bColName
, bool bRowName
, bool bRef
)
152 bColByName
= bColName
;
153 if (bColName
) nColCount
= 0;
154 bRowByName
= bRowName
;
155 if (bRowName
) nRowCount
= 0;
159 void ScConsData::AddFields( const ScDocument
* pSrcDoc
, SCTAB nTab
,
160 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
166 SCCOL nStartCol
= nCol1
;
167 SCROW nStartRow
= nRow1
;
168 if (bColByName
) ++nStartRow
;
169 if (bRowByName
) ++nStartCol
;
173 for (SCCOL nCol
=nStartCol
; nCol
<=nCol2
; nCol
++)
175 aTitle
= pSrcDoc
->GetString(nCol
, nRow1
, nTab
);
176 if (!aTitle
.isEmpty())
179 for (SCSIZE i
=0; i
<nColCount
&& !bFound
; i
++)
180 if ( maColHeaders
[i
] == aTitle
)
183 lcl_AddString( maColHeaders
, nColCount
, aTitle
);
191 for (SCROW nRow
=nStartRow
; nRow
<=nRow2
; nRow
++)
193 aTitle
= pSrcDoc
->GetString(nCol1
, nRow
, nTab
);
194 if (!aTitle
.isEmpty())
197 for (SCSIZE i
=0; i
<nRowCount
&& !bFound
; i
++)
198 if ( maRowHeaders
[i
] == aTitle
)
201 lcl_AddString( maRowHeaders
, nRowCount
, aTitle
);
206 void ScConsData::AddName( const OUString
& rName
)
214 maTitles
.push_back( rName
);
215 size_t nTitleCount
= maTitles
.size();
217 for (nArrY
=0; nArrY
<nRowCount
; nArrY
++)
219 // set all data to same length
222 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
223 nMax
= std::max( nMax
, ppRefs
[nArrX
][nArrY
].size() );
225 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
227 ppUsed
[nArrX
][nArrY
] = true;
228 ppRefs
[nArrX
][nArrY
].resize( nMax
, { SC_CONS_NOTFOUND
, SC_CONS_NOTFOUND
, SC_CONS_NOTFOUND
});
234 if (nTitleCount
< nDataCount
)
235 ppTitlePos
[nArrY
][nTitleCount
] = nMax
;
239 void ScConsData::AddData( ScDocument
* pSrcDoc
, SCTAB nTab
,
240 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
242 PutInOrder(nCol1
,nCol2
);
243 PutInOrder(nRow1
,nRow2
);
244 if ( nCol2
>= sal::static_int_cast
<SCCOL
>(nCol1
+ nColCount
) && !bColByName
)
246 OSL_FAIL("range too big");
247 nCol2
= sal::static_int_cast
<SCCOL
>( nCol1
+ nColCount
- 1 );
249 if ( nRow2
>= sal::static_int_cast
<SCROW
>(nRow1
+ nRowCount
) && !bRowByName
)
251 OSL_FAIL("range too big");
252 nRow2
= sal::static_int_cast
<SCROW
>( nRow1
+ nRowCount
- 1 );
260 if ( bColByName
&& bRowByName
)
262 OUString aThisCorner
= pSrcDoc
->GetString(nCol1
, nRow1
, nTab
);
265 if (aCornerText
!= aThisCorner
)
270 aCornerText
= aThisCorner
;
277 SCCOL nStartCol
= nCol1
;
278 SCROW nStartRow
= nRow1
;
279 if (bColByName
) ++nStartRow
;
280 if (bRowByName
) ++nStartCol
;
282 std::unique_ptr
<SCCOL
[]> pDestCols
;
283 std::unique_ptr
<SCROW
[]> pDestRows
;
286 pDestCols
.reset(new SCCOL
[nCol2
-nStartCol
+1]);
287 for (nCol
=nStartCol
; nCol
<=nCol2
; nCol
++)
289 aTitle
= pSrcDoc
->GetString(nCol
, nRow1
, nTab
);
290 SCCOL nPos
= SC_CONS_NOTFOUND
;
291 if (!aTitle
.isEmpty())
294 for (SCSIZE i
=0; i
<nColCount
&& !bFound
; i
++)
295 if ( maColHeaders
[i
] == aTitle
)
297 nPos
= static_cast<SCCOL
>(i
);
300 OSL_ENSURE(bFound
, "column not found");
302 pDestCols
[nCol
-nStartCol
] = nPos
;
307 pDestRows
.reset(new SCROW
[nRow2
-nStartRow
+1]);
308 for (nRow
=nStartRow
; nRow
<=nRow2
; nRow
++)
310 aTitle
= pSrcDoc
->GetString(nCol1
, nRow
, nTab
);
311 SCROW nPos
= SC_CONS_NOTFOUND
;
312 if (!aTitle
.isEmpty())
315 for (SCSIZE i
=0; i
<nRowCount
&& !bFound
; i
++)
316 if ( maRowHeaders
[i
] == aTitle
)
318 nPos
= static_cast<SCROW
>(i
);
321 OSL_ENSURE(bFound
, "row not found");
323 pDestRows
[nRow
-nStartRow
] = nPos
;
331 bool bAnyCell
= ( eFunction
== SUBTOTAL_FUNC_CNT2
);
332 for (nCol
=nCol1
; nCol
<=nCol2
; nCol
++)
334 SCCOL nArrX
= nCol
-nCol1
;
335 if (bColByName
) nArrX
= pDestCols
[nArrX
];
336 if (nArrX
!= SC_CONS_NOTFOUND
)
338 for (nRow
=nRow1
; nRow
<=nRow2
; nRow
++)
340 SCROW nArrY
= nRow
-nRow1
;
341 if (bRowByName
) nArrY
= pDestRows
[nArrY
];
342 if ( nArrY
!= SC_CONS_NOTFOUND
&& (
343 bAnyCell
? pSrcDoc
->HasData( nCol
, nRow
, nTab
)
344 : pSrcDoc
->HasValueData( nCol
, nRow
, nTab
) ) )
348 ppUsed
[nArrX
][nArrY
] = true;
349 ppRefs
[nArrX
][nArrY
].push_back( { nCol
, nRow
, nTab
} );
353 double nVal
= pSrcDoc
->GetValue( nCol
, nRow
, nTab
);
354 if (!ppUsed
[nArrX
][nArrY
])
356 ppUsed
[nArrX
][nArrY
] = true;
357 ppFunctionData
[nArrX
][nArrY
] = ScFunctionData( eFunction
);
359 ppFunctionData
[nArrX
][nArrY
].update( nVal
);
367 // check before, how many rows to insert (for Undo)
369 SCROW
ScConsData::GetInsertCount() const
374 if ( ppRefs
&& ppUsed
)
376 for (nArrY
=0; nArrY
<nRowCount
; nArrY
++)
379 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
380 nNeeded
= std::max( nNeeded
, ppRefs
[nArrX
][nArrY
].size() );
388 // store completed data to document
389 //TODO: optimize on columns?
391 void ScConsData::OutputToDocument( ScDocument
& rDestDoc
, SCCOL nCol
, SCROW nRow
, SCTAB nTab
)
393 OpCode eOpCode
= eOpCodeTable
[eFunction
];
400 if ( bColByName
&& bRowByName
&& !aCornerText
.isEmpty() )
401 rDestDoc
.SetString( nCol
, nRow
, nTab
, aCornerText
);
405 SCCOL nStartCol
= nCol
;
406 SCROW nStartRow
= nRow
;
407 if (bColByName
) ++nStartRow
;
408 if (bRowByName
) ++nStartCol
;
411 for (SCSIZE i
=0; i
<nColCount
; i
++)
412 rDestDoc
.SetString( sal::static_int_cast
<SCCOL
>(nStartCol
+i
), nRow
, nTab
, maColHeaders
[i
] );
414 for (SCSIZE j
=0; j
<nRowCount
; j
++)
415 rDestDoc
.SetString( nCol
, sal::static_int_cast
<SCROW
>(nStartRow
+j
), nTab
, maRowHeaders
[j
] );
422 if ( ppFunctionData
&& ppUsed
) // insert values directly
424 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
425 for (nArrY
=0; nArrY
<nRowCount
; nArrY
++)
426 if (ppUsed
[nArrX
][nArrY
])
428 double fVal
= ppFunctionData
[nArrX
][nArrY
].getResult();
429 if (ppFunctionData
[nArrX
][nArrY
].getError())
430 rDestDoc
.SetError( sal::static_int_cast
<SCCOL
>(nCol
+nArrX
),
431 sal::static_int_cast
<SCROW
>(nRow
+nArrY
), nTab
, FormulaError::NoValue
);
433 rDestDoc
.SetValue( sal::static_int_cast
<SCCOL
>(nCol
+nArrX
),
434 sal::static_int_cast
<SCROW
>(nRow
+nArrY
), nTab
, fVal
);
438 if ( !(ppRefs
&& ppUsed
) ) // insert Reference
441 //TODO: differentiate, if split into categories
444 ScSingleRefData aSRef
; // data for Reference formula cells
445 aSRef
.InitFlags(); // this reference is absolute at all times
446 aSRef
.SetFlag3D(true);
448 ScComplexRefData aCRef
; // data for Sum cells
450 aCRef
.Ref1
.SetColRel(true); aCRef
.Ref1
.SetRowRel(true); aCRef
.Ref1
.SetTabRel(true);
451 aCRef
.Ref2
.SetColRel(true); aCRef
.Ref2
.SetRowRel(true); aCRef
.Ref2
.SetTabRel(true);
453 for (nArrY
=0; nArrY
<nRowCount
; nArrY
++)
456 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
457 nNeeded
= std::max( nNeeded
, ppRefs
[nArrX
][nArrY
].size() );
461 rDestDoc
.InsertRow( 0,nTab
, rDestDoc
.MaxCol(),nTab
, nRow
+nArrY
, nNeeded
);
463 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
464 if (ppUsed
[nArrX
][nArrY
])
466 SCSIZE nCount
= ppRefs
[nArrX
][nArrY
].size();
469 for (SCSIZE nPos
=0; nPos
<nCount
; nPos
++)
471 ScReferenceEntry aRef
= ppRefs
[nArrX
][nArrY
][nPos
];
472 if (aRef
.nTab
!= SC_CONS_NOTFOUND
)
474 // insert reference (absolute, 3d)
476 aSRef
.SetAddress(rDestDoc
.GetSheetLimits(), ScAddress(aRef
.nCol
,aRef
.nRow
,aRef
.nTab
), ScAddress());
478 ScTokenArray
aRefArr(rDestDoc
);
479 aRefArr
.AddSingleReference(aSRef
);
480 aRefArr
.AddOpCode(ocStop
);
481 ScAddress
aDest( sal::static_int_cast
<SCCOL
>(nCol
+nArrX
),
482 sal::static_int_cast
<SCROW
>(nRow
+nArrY
+nPos
), nTab
);
483 ScFormulaCell
* pCell
= new ScFormulaCell(rDestDoc
, aDest
, aRefArr
);
484 rDestDoc
.SetFormulaCell(aDest
, pCell
);
488 // insert sum (relative, not 3d)
490 ScAddress
aDest( sal::static_int_cast
<SCCOL
>(nCol
+nArrX
),
491 sal::static_int_cast
<SCROW
>(nRow
+nArrY
+nNeeded
), nTab
);
493 ScRange
aRange(sal::static_int_cast
<SCCOL
>(nCol
+nArrX
), nRow
+nArrY
, nTab
);
494 aRange
.aEnd
.SetRow(nRow
+nArrY
+nNeeded
-1);
495 aCRef
.SetRange(rDestDoc
.GetSheetLimits(), aRange
, aDest
);
497 ScTokenArray
aArr(rDestDoc
);
498 aArr
.AddOpCode(eOpCode
); // selected function
499 aArr
.AddOpCode(ocOpen
);
500 aArr
.AddDoubleReference(aCRef
);
501 aArr
.AddOpCode(ocClose
);
502 aArr
.AddOpCode(ocStop
);
503 ScFormulaCell
* pCell
= new ScFormulaCell(rDestDoc
, aDest
, aArr
);
504 rDestDoc
.SetFormulaCell(aDest
, pCell
);
510 ScOutlineArray
& rOutArr
= rDestDoc
.GetOutlineTable( nTab
, true )->GetRowArray();
511 SCROW nOutStart
= nRow
+nArrY
;
512 SCROW nOutEnd
= nRow
+nArrY
+nNeeded
-1;
514 rOutArr
.Insert( nOutStart
, nOutEnd
, bSize
);
515 for (SCROW nOutRow
=nOutStart
; nOutRow
<=nOutEnd
; nOutRow
++)
516 rDestDoc
.ShowRow( nOutRow
, nTab
, false );
517 rDestDoc
.SetDrawPageSize(nTab
);
518 rDestDoc
.UpdateOutlineRow( nOutStart
, nOutEnd
, nTab
, false );
522 if (ppTitlePos
&& !maTitles
.empty() && !maRowHeaders
.empty())
524 for (SCSIZE nPos
=0; nPos
<nDataCount
; nPos
++)
526 SCSIZE nTPos
= ppTitlePos
[nArrY
][nPos
];
528 if (nPos
+1<nDataCount
)
529 if (ppTitlePos
[nArrY
][nPos
+1] == nTPos
)
530 bDo
= false; // empty
531 if ( bDo
&& nTPos
< nNeeded
)
533 aString
= maRowHeaders
[nArrY
] + " / " + maTitles
[nPos
];
534 rDestDoc
.SetString( nCol
-1, nRow
+nArrY
+nTPos
, nTab
, aString
);
544 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */