xref: /core/sc/source/core/tool/consoli.cxx (revision 1a588c71)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
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>
29 
30 #include <string.h>
31 #include <memory>
32 
33 #define SC_CONS_NOTFOUND    -1
34 
35 const OpCode eOpCodeTable[] = {      //  order as for enum ScSubTotalFunc
36         ocBad,                              //  none
37         ocAverage,
38         ocCount,
39         ocCount2,
40         ocMax,
41         ocMin,
42         ocProduct,
43         ocStDev,
44         ocStDevP,
45         ocSum,
46         ocVar,
47         ocVarP };
48 
49 template< typename T >
lcl_AddString(::std::vector<OUString> & rData,T & nCount,const OUString & rInsert)50 static void lcl_AddString( ::std::vector<OUString>& rData, T& nCount, const OUString& rInsert )
51 {
52     rData.push_back( rInsert);
53     ++nCount;
54 }
55 
ScConsData()56 ScConsData::ScConsData() :
57     eFunction(SUBTOTAL_FUNC_SUM),
58     bReference(false),
59     bColByName(false),
60     bRowByName(false),
61     nColCount(0),
62     nRowCount(0),
63     nDataCount(0),
64     bCornerUsed(false)
65 {
66 }
67 
~ScConsData()68 ScConsData::~ScConsData()
69 {
70 }
71 
DeleteData()72 void ScConsData::DeleteData()
73 {
74     ppRefs.reset();
75     ppFunctionData.reset();
76     ppUsed.reset();
77     ppTitlePos.reset();
78     ::std::vector<OUString>().swap( maColHeaders);
79     ::std::vector<OUString>().swap( maRowHeaders);
80     ::std::vector<OUString>().swap( maTitles);
81     nDataCount = 0;
82 
83     if (bColByName) nColCount = 0;                  // otherwise maColHeaders is wrong
84     if (bRowByName) nRowCount = 0;
85 
86     bCornerUsed = false;
87     aCornerText.clear();
88 }
89 
InitData()90 void ScConsData::InitData()
91 {
92     if (bReference && nColCount && !ppRefs)
93     {
94         ppRefs.reset(new std::unique_ptr<ScReferenceList[]>[nColCount]);
95         for (SCSIZE i=0; i<nColCount; i++)
96             ppRefs[i].reset(new ScReferenceList[nRowCount]);
97     }
98     else if (nColCount && !ppFunctionData)
99     {
100         ppFunctionData.reset( new std::unique_ptr<ScFunctionData[]>[nColCount] );
101         for (SCSIZE i=0; i<nColCount; i++)
102         {
103             ppFunctionData[i].reset( new ScFunctionData[nRowCount] );
104         }
105     }
106 
107     if (nColCount && !ppUsed)
108     {
109         ppUsed.reset( new std::unique_ptr<bool[]>[nColCount] );
110         for (SCSIZE i=0; i<nColCount; i++)
111         {
112             ppUsed[i].reset( new bool[nRowCount] );
113             memset( ppUsed[i].get(), 0, nRowCount * sizeof(bool) );
114         }
115     }
116 
117     if (nRowCount && nDataCount && !ppTitlePos)
118     {
119         ppTitlePos.reset( new std::unique_ptr<SCSIZE[]>[nRowCount] );
120         for (SCSIZE i=0; i<nRowCount; i++)
121         {
122             ppTitlePos[i].reset( new SCSIZE[nDataCount] );
123             memset( ppTitlePos[i].get(), 0, nDataCount * sizeof(SCSIZE) );    //TODO: not necessary ?
124         }
125     }
126 
127     //  CornerText: single String
128 }
129 
DoneFields()130 void ScConsData::DoneFields()
131 {
132     InitData();
133 }
134 
SetSize(SCCOL nCols,SCROW nRows)135 void ScConsData::SetSize( SCCOL nCols, SCROW nRows )
136 {
137     DeleteData();
138     nColCount = static_cast<SCSIZE>(nCols);
139     nRowCount = static_cast<SCSIZE>(nRows);
140 }
141 
GetSize(SCCOL & rCols,SCROW & rRows) const142 void ScConsData::GetSize( SCCOL& rCols, SCROW& rRows ) const
143 {
144     rCols = static_cast<SCCOL>(nColCount);
145     rRows = static_cast<SCROW>(nRowCount);
146 }
147 
SetFlags(ScSubTotalFunc eFunc,bool bColName,bool bRowName,bool bRef)148 void ScConsData::SetFlags( ScSubTotalFunc eFunc, bool bColName, bool bRowName, bool bRef )
149 {
150     DeleteData();
151     bReference = bRef;
152     bColByName = bColName;
153     if (bColName) nColCount = 0;
154     bRowByName = bRowName;
155     if (bRowName) nRowCount = 0;
156     eFunction = eFunc;
157 }
158 
AddFields(const ScDocument * pSrcDoc,SCTAB nTab,SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2)159 void ScConsData::AddFields( const ScDocument* pSrcDoc, SCTAB nTab,
160                             SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
161 {
162     ++nDataCount;
163 
164     OUString aTitle;
165 
166     SCCOL nStartCol = nCol1;
167     SCROW nStartRow = nRow1;
168     if (bColByName) ++nStartRow;
169     if (bRowByName) ++nStartCol;
170 
171     if (bColByName)
172     {
173         for (SCCOL nCol=nStartCol; nCol<=nCol2; nCol++)
174         {
175             aTitle = pSrcDoc->GetString(nCol, nRow1, nTab);
176             if (!aTitle.isEmpty())
177             {
178                 bool bFound = false;
179                 for (SCSIZE i=0; i<nColCount && !bFound; i++)
180                     if ( maColHeaders[i] == aTitle )
181                         bFound = true;
182                 if (!bFound)
183                     lcl_AddString( maColHeaders, nColCount, aTitle );
184             }
185         }
186     }
187 
188     if (!bRowByName)
189         return;
190 
191     for (SCROW nRow=nStartRow; nRow<=nRow2; nRow++)
192     {
193         aTitle = pSrcDoc->GetString(nCol1, nRow, nTab);
194         if (!aTitle.isEmpty())
195         {
196             bool bFound = false;
197             for (SCSIZE i=0; i<nRowCount && !bFound; i++)
198                 if ( maRowHeaders[i] == aTitle )
199                     bFound = true;
200             if (!bFound)
201                 lcl_AddString( maRowHeaders, nRowCount, aTitle );
202         }
203     }
204 }
205 
AddName(const OUString & rName)206 void ScConsData::AddName( const OUString& rName )
207 {
208     SCSIZE nArrX;
209     SCSIZE nArrY;
210 
211     if (!bReference)
212         return;
213 
214     maTitles.push_back( rName);
215     size_t nTitleCount = maTitles.size();
216 
217     for (nArrY=0; nArrY<nRowCount; nArrY++)
218     {
219         //  set all data to same length
220 
221         SCSIZE nMax = 0;
222         for (nArrX=0; nArrX<nColCount; nArrX++)
223             nMax = std::max( nMax, ppRefs[nArrX][nArrY].size() );
224 
225         for (nArrX=0; nArrX<nColCount; nArrX++)
226         {
227             ppUsed[nArrX][nArrY] = true;
228             ppRefs[nArrX][nArrY].resize( nMax, { SC_CONS_NOTFOUND, SC_CONS_NOTFOUND, SC_CONS_NOTFOUND });
229         }
230 
231         //  store positions
232 
233         if (ppTitlePos)
234             if (nTitleCount < nDataCount)
235                 ppTitlePos[nArrY][nTitleCount] = nMax;
236     }
237 }
238 
AddData(ScDocument * pSrcDoc,SCTAB nTab,SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2)239 void ScConsData::AddData( ScDocument* pSrcDoc, SCTAB nTab,
240                             SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
241 {
242     PutInOrder(nCol1,nCol2);
243     PutInOrder(nRow1,nRow2);
244     if ( nCol2 >= sal::static_int_cast<SCCOL>(nCol1 + nColCount) && !bColByName )
245     {
246         OSL_FAIL("range too big");
247         nCol2 = sal::static_int_cast<SCCOL>( nCol1 + nColCount - 1 );
248     }
249     if ( nRow2 >= sal::static_int_cast<SCROW>(nRow1 + nRowCount) && !bRowByName )
250     {
251         OSL_FAIL("range too big");
252         nRow2 = sal::static_int_cast<SCROW>( nRow1 + nRowCount - 1 );
253     }
254 
255     SCCOL nCol;
256     SCROW nRow;
257 
258     // left top corner
259 
260     if ( bColByName && bRowByName )
261     {
262         OUString aThisCorner = pSrcDoc->GetString(nCol1, nRow1, nTab);
263         if (bCornerUsed)
264         {
265             if (aCornerText != aThisCorner)
266                 aCornerText.clear();
267         }
268         else
269         {
270             aCornerText = aThisCorner;
271             bCornerUsed = true;
272         }
273     }
274 
275     // search title
276 
277     SCCOL nStartCol = nCol1;
278     SCROW nStartRow = nRow1;
279     if (bColByName) ++nStartRow;
280     if (bRowByName) ++nStartCol;
281     OUString aTitle;
282     std::unique_ptr<SCCOL[]> pDestCols;
283     std::unique_ptr<SCROW[]> pDestRows;
284     if (bColByName)
285     {
286         pDestCols.reset(new SCCOL[nCol2-nStartCol+1]);
287         for (nCol=nStartCol; nCol<=nCol2; nCol++)
288         {
289             aTitle = pSrcDoc->GetString(nCol, nRow1, nTab);
290             SCCOL nPos = SC_CONS_NOTFOUND;
291             if (!aTitle.isEmpty())
292             {
293                 bool bFound = false;
294                 for (SCSIZE i=0; i<nColCount && !bFound; i++)
295                     if ( maColHeaders[i] == aTitle )
296                     {
297                         nPos = static_cast<SCCOL>(i);
298                         bFound = true;
299                     }
300                 OSL_ENSURE(bFound, "column not found");
301             }
302             pDestCols[nCol-nStartCol] = nPos;
303         }
304     }
305     if (bRowByName)
306     {
307         pDestRows.reset(new SCROW[nRow2-nStartRow+1]);
308         for (nRow=nStartRow; nRow<=nRow2; nRow++)
309         {
310             aTitle = pSrcDoc->GetString(nCol1, nRow, nTab);
311             SCROW nPos = SC_CONS_NOTFOUND;
312             if (!aTitle.isEmpty())
313             {
314                 bool bFound = false;
315                 for (SCSIZE i=0; i<nRowCount && !bFound; i++)
316                     if ( maRowHeaders[i] == aTitle )
317                     {
318                         nPos = static_cast<SCROW>(i);
319                         bFound = true;
320                     }
321                 OSL_ENSURE(bFound, "row not found");
322             }
323             pDestRows[nRow-nStartRow] = nPos;
324         }
325     }
326     nCol1 = nStartCol;
327     nRow1 = nStartRow;
328 
329     // data
330 
331     bool bAnyCell = ( eFunction == SUBTOTAL_FUNC_CNT2 );
332     for (nCol=nCol1; nCol<=nCol2; nCol++)
333     {
334         SCCOL nArrX = nCol-nCol1;
335         if (bColByName) nArrX = pDestCols[nArrX];
336         if (nArrX != SC_CONS_NOTFOUND)
337         {
338             for (nRow=nRow1; nRow<=nRow2; nRow++)
339             {
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 ) ) )
345                 {
346                     if (bReference)
347                     {
348                         ppUsed[nArrX][nArrY] = true;
349                         ppRefs[nArrX][nArrY].push_back( { nCol, nRow, nTab } );
350                     }
351                     else
352                     {
353                         double nVal = pSrcDoc->GetValue( nCol, nRow, nTab );
354                         if (!ppUsed[nArrX][nArrY])
355                         {
356                             ppUsed[nArrX][nArrY] = true;
357                             ppFunctionData[nArrX][nArrY] = ScFunctionData( eFunction);
358                         }
359                         ppFunctionData[nArrX][nArrY].update( nVal);
360                     }
361                 }
362             }
363         }
364     }
365 }
366 
367 // check before, how many rows to insert (for Undo)
368 
GetInsertCount() const369 SCROW ScConsData::GetInsertCount() const
370 {
371     SCROW nInsert = 0;
372     SCSIZE nArrX;
373     SCSIZE nArrY;
374     if ( ppRefs && ppUsed )
375     {
376         for (nArrY=0; nArrY<nRowCount; nArrY++)
377         {
378             SCSIZE nNeeded = 0;
379             for (nArrX=0; nArrX<nColCount; nArrX++)
380                 nNeeded = std::max( nNeeded, ppRefs[nArrX][nArrY].size() );
381 
382             nInsert += nNeeded;
383         }
384     }
385     return nInsert;
386 }
387 
388 // store completed data to document
389 //TODO: optimize on columns?
390 
OutputToDocument(ScDocument & rDestDoc,SCCOL nCol,SCROW nRow,SCTAB nTab)391 void ScConsData::OutputToDocument( ScDocument& rDestDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
392 {
393     OpCode eOpCode = eOpCodeTable[eFunction];
394 
395     SCSIZE nArrX;
396     SCSIZE nArrY;
397 
398     // left top corner
399 
400     if ( bColByName && bRowByName && !aCornerText.isEmpty() )
401         rDestDoc.SetString( nCol, nRow, nTab, aCornerText );
402 
403     // title
404 
405     SCCOL nStartCol = nCol;
406     SCROW nStartRow = nRow;
407     if (bColByName) ++nStartRow;
408     if (bRowByName) ++nStartCol;
409 
410     if (bColByName)
411         for (SCSIZE i=0; i<nColCount; i++)
412             rDestDoc.SetString( sal::static_int_cast<SCCOL>(nStartCol+i), nRow, nTab, maColHeaders[i] );
413     if (bRowByName)
414         for (SCSIZE j=0; j<nRowCount; j++)
415             rDestDoc.SetString( nCol, sal::static_int_cast<SCROW>(nStartRow+j), nTab, maRowHeaders[j] );
416 
417     nCol = nStartCol;
418     nRow = nStartRow;
419 
420     // data
421 
422     if ( ppFunctionData && ppUsed )    // insert values directly
423     {
424         for (nArrX=0; nArrX<nColCount; nArrX++)
425             for (nArrY=0; nArrY<nRowCount; nArrY++)
426                 if (ppUsed[nArrX][nArrY])
427                 {
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 );
432                     else
433                         rDestDoc.SetValue( sal::static_int_cast<SCCOL>(nCol+nArrX),
434                                            sal::static_int_cast<SCROW>(nRow+nArrY), nTab, fVal );
435                 }
436     }
437 
438     if ( !(ppRefs && ppUsed) )     // insert Reference
439                                 return;
440 
441                             //TODO: differentiate, if split into categories
442     OUString aString;
443 
444     ScSingleRefData aSRef;  // data for Reference formula cells
445     aSRef.InitFlags();      // this reference is absolute at all times
446     aSRef.SetFlag3D(true);
447 
448     ScComplexRefData aCRef; // data for Sum cells
449     aCRef.InitFlags();
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);
452 
453     for (nArrY=0; nArrY<nRowCount; nArrY++)
454     {
455         SCSIZE nNeeded = 0;
456         for (nArrX=0; nArrX<nColCount; nArrX++)
457             nNeeded = std::max( nNeeded, ppRefs[nArrX][nArrY].size() );
458 
459         if (nNeeded)
460         {
461             rDestDoc.InsertRow( 0,nTab, rDestDoc.MaxCol(),nTab, nRow+nArrY, nNeeded );
462 
463             for (nArrX=0; nArrX<nColCount; nArrX++)
464                 if (ppUsed[nArrX][nArrY])
465                 {
466                     SCSIZE nCount = ppRefs[nArrX][nArrY].size();
467                     if (nCount)
468                     {
469                         for (SCSIZE nPos=0; nPos<nCount; nPos++)
470                         {
471                             ScReferenceEntry aRef = ppRefs[nArrX][nArrY][nPos];
472                             if (aRef.nTab != SC_CONS_NOTFOUND)
473                             {
474                                 // insert reference (absolute, 3d)
475 
476                                 aSRef.SetAddress(rDestDoc.GetSheetLimits(), ScAddress(aRef.nCol,aRef.nRow,aRef.nTab), ScAddress());
477 
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);
485                             }
486                         }
487 
488                         // insert sum (relative, not 3d)
489 
490                         ScAddress aDest( sal::static_int_cast<SCCOL>(nCol+nArrX),
491                                          sal::static_int_cast<SCROW>(nRow+nArrY+nNeeded), nTab );
492 
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);
496 
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);
505                     }
506                 }
507 
508             // insert outline
509 
510             ScOutlineArray& rOutArr = rDestDoc.GetOutlineTable( nTab, true )->GetRowArray();
511             SCROW nOutStart = nRow+nArrY;
512             SCROW nOutEnd = nRow+nArrY+nNeeded-1;
513             bool bSize = false;
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 );
519 
520             // sub title
521 
522             if (ppTitlePos && !maTitles.empty() && !maRowHeaders.empty())
523             {
524                 for (SCSIZE nPos=0; nPos<nDataCount; nPos++)
525                 {
526                     SCSIZE nTPos = ppTitlePos[nArrY][nPos];
527                     bool bDo = true;
528                     if (nPos+1<nDataCount)
529                         if (ppTitlePos[nArrY][nPos+1] == nTPos)
530                             bDo = false;                                    // empty
531                     if ( bDo && nTPos < nNeeded )
532                     {
533                         aString = maRowHeaders[nArrY] + " / " + maTitles[nPos];
534                         rDestDoc.SetString( nCol-1, nRow+nArrY+nTPos, nTab, aString );
535                     }
536                 }
537             }
538 
539             nRow += nNeeded;
540         }
541     }
542 }
543 
544 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
545