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 "globstr.hrc"
24 #include "subtotal.hxx"
25 #include <formula/errorcodes.hxx>
26 #include "formulacell.hxx"
27 #include "tokenarray.hxx"
28 #include <osl/diagnose.h>
32 #include <boost/scoped_array.hpp>
34 #define SC_CONS_NOTFOUND -1
37 static const OpCode eOpCodeTable
[] = { // Reihenfolge wie bei enum ScSubTotalFunc
51 void ScReferenceList::AddEntry( SCCOL nCol
, SCROW nRow
, SCTAB nTab
)
53 ScReferenceEntry
* pOldData
= pData
;
54 pData
= new ScReferenceEntry
[ nFullSize
+1 ];
57 memcpy( pData
, pOldData
, nCount
* sizeof(ScReferenceEntry
) );
60 while (nCount
< nFullSize
)
62 pData
[nCount
].nCol
= SC_CONS_NOTFOUND
;
63 pData
[nCount
].nRow
= SC_CONS_NOTFOUND
;
64 pData
[nCount
].nTab
= SC_CONS_NOTFOUND
;
67 pData
[nCount
].nCol
= nCol
;
68 pData
[nCount
].nRow
= nRow
;
69 pData
[nCount
].nTab
= nTab
;
74 template< typename T
>
75 static void lcl_AddString( ::std::vector
<OUString
>& rData
, T
& nCount
, const OUString
& rInsert
)
77 rData
.push_back( rInsert
);
81 ScConsData::ScConsData() :
82 eFunction(SUBTOTAL_FUNC_SUM
),
99 ScConsData::~ScConsData()
104 #define DELETEARR(ppArray,nCount) \
108 for(i=0; i<nCount; i++) \
109 delete[] ppArray[i]; \
114 void ScConsData::DeleteData()
118 for (SCSIZE i
=0; i
<nColCount
; i
++)
120 for (SCSIZE j
=0; j
<nRowCount
; j
++)
122 ppRefs
[i
][j
].Clear();
129 DELETEARR( ppCount
, nColCount
);
130 DELETEARR( ppSum
, nColCount
);
131 DELETEARR( ppSumSqr
,nColCount
);
132 DELETEARR( ppUsed
, nColCount
); // erst nach ppRefs !!!
133 DELETEARR( ppTitlePos
, nRowCount
);
134 ::std::vector
<OUString
>().swap( maColHeaders
);
135 ::std::vector
<OUString
>().swap( maRowHeaders
);
136 ::std::vector
<OUString
>().swap( maTitles
);
139 if (bColByName
) nColCount
= 0; // sonst stimmt maColHeaders nicht
140 if (bRowByName
) nRowCount
= 0;
149 void ScConsData::InitData()
151 if (bReference
&& nColCount
&& !ppRefs
)
153 ppRefs
= new ScReferenceList
*[nColCount
];
154 for (SCSIZE i
=0; i
<nColCount
; i
++)
155 ppRefs
[i
] = new ScReferenceList
[nRowCount
];
157 else if (nColCount
&& !ppCount
)
159 ppCount
= new double*[nColCount
];
160 ppSum
= new double*[nColCount
];
161 ppSumSqr
= new double*[nColCount
];
162 for (SCSIZE i
=0; i
<nColCount
; i
++)
164 ppCount
[i
] = new double[nRowCount
];
165 ppSum
[i
] = new double[nRowCount
];
166 ppSumSqr
[i
] = new double[nRowCount
];
170 if (nColCount
&& !ppUsed
)
172 ppUsed
= new bool*[nColCount
];
173 for (SCSIZE i
=0; i
<nColCount
; i
++)
175 ppUsed
[i
] = new bool[nRowCount
];
176 memset( ppUsed
[i
], 0, nRowCount
* sizeof(bool) );
180 if (nRowCount
&& nDataCount
&& !ppTitlePos
)
182 ppTitlePos
= new SCSIZE
*[nRowCount
];
183 for (SCSIZE i
=0; i
<nRowCount
; i
++)
185 ppTitlePos
[i
] = new SCSIZE
[nDataCount
];
186 memset( ppTitlePos
[i
], 0, nDataCount
* sizeof(SCSIZE
) ); //TODO: not necessary ?
190 // CornerText: einzelner String
193 void ScConsData::DoneFields()
198 void ScConsData::SetSize( SCCOL nCols
, SCROW nRows
)
201 nColCount
= static_cast<SCSIZE
>(nCols
);
202 nRowCount
= static_cast<SCSIZE
>(nRows
);
205 void ScConsData::GetSize( SCCOL
& rCols
, SCROW
& rRows
) const
207 rCols
= static_cast<SCCOL
>(nColCount
);
208 rRows
= static_cast<SCROW
>(nRowCount
);
211 void ScConsData::SetFlags( ScSubTotalFunc eFunc
, bool bColName
, bool bRowName
, bool bRef
)
215 bColByName
= bColName
;
216 if (bColName
) nColCount
= 0;
217 bRowByName
= bRowName
;
218 if (bRowName
) nRowCount
= 0;
222 void ScConsData::AddFields( ScDocument
* pSrcDoc
, SCTAB nTab
,
223 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
229 SCCOL nStartCol
= nCol1
;
230 SCROW nStartRow
= nRow1
;
231 if (bColByName
) ++nStartRow
;
232 if (bRowByName
) ++nStartCol
;
236 for (SCCOL nCol
=nStartCol
; nCol
<=nCol2
; nCol
++)
238 aTitle
= pSrcDoc
->GetString(nCol
, nRow1
, nTab
);
239 if (!aTitle
.isEmpty())
242 for (SCSIZE i
=0; i
<nColCount
&& !bFound
; i
++)
243 if ( maColHeaders
[i
] == aTitle
)
246 lcl_AddString( maColHeaders
, nColCount
, aTitle
);
253 for (SCROW nRow
=nStartRow
; nRow
<=nRow2
; nRow
++)
255 aTitle
= pSrcDoc
->GetString(nCol1
, nRow
, nTab
);
256 if (!aTitle
.isEmpty())
259 for (SCSIZE i
=0; i
<nRowCount
&& !bFound
; i
++)
260 if ( maRowHeaders
[i
] == aTitle
)
263 lcl_AddString( maRowHeaders
, nRowCount
, aTitle
);
269 void ScConsData::AddName( const OUString
& rName
)
276 maTitles
.push_back( rName
);
277 size_t nTitleCount
= maTitles
.size();
279 for (nArrY
=0; nArrY
<nRowCount
; nArrY
++)
281 // Daten auf gleiche Laenge bringen
284 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
285 if (ppUsed
[nArrX
][nArrY
])
286 nMax
= std::max( nMax
, ppRefs
[nArrX
][nArrY
].GetCount() );
288 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
290 if (!ppUsed
[nArrX
][nArrY
])
292 ppUsed
[nArrX
][nArrY
] = true;
293 ppRefs
[nArrX
][nArrY
].Init();
295 ppRefs
[nArrX
][nArrY
].SetFullSize(nMax
);
298 // Positionen eintragen
301 if (nTitleCount
< nDataCount
)
302 ppTitlePos
[nArrY
][nTitleCount
] = nMax
;
307 // rCount < 0 <=> Fehler aufgetreten
309 static void lcl_UpdateArray( ScSubTotalFunc eFunc
,
310 double& rCount
, double& rSum
, double& rSumSqr
, double nVal
)
316 case SUBTOTAL_FUNC_SUM
:
317 if (!SubTotal::SafePlus(rSum
, nVal
))
320 case SUBTOTAL_FUNC_PROD
:
321 if (!SubTotal::SafeMult(rSum
, nVal
))
324 case SUBTOTAL_FUNC_CNT
:
325 case SUBTOTAL_FUNC_CNT2
:
328 case SUBTOTAL_FUNC_AVE
:
329 if (!SubTotal::SafePlus(rSum
, nVal
))
334 case SUBTOTAL_FUNC_MAX
:
338 case SUBTOTAL_FUNC_MIN
:
342 case SUBTOTAL_FUNC_STD
:
343 case SUBTOTAL_FUNC_STDP
:
344 case SUBTOTAL_FUNC_VAR
:
345 case SUBTOTAL_FUNC_VARP
:
347 bool bOk
= SubTotal::SafePlus(rSum
, nVal
);
348 bOk
= bOk
&& SubTotal::SafeMult(nVal
, nVal
);
349 bOk
= bOk
&& SubTotal::SafePlus(rSumSqr
, nVal
);
358 // added to avoid warnings
363 static void lcl_InitArray( ScSubTotalFunc eFunc
,
364 double& rCount
, double& rSum
, double& rSumSqr
, double nVal
)
369 case SUBTOTAL_FUNC_SUM
:
370 case SUBTOTAL_FUNC_MAX
:
371 case SUBTOTAL_FUNC_MIN
:
372 case SUBTOTAL_FUNC_PROD
:
373 case SUBTOTAL_FUNC_AVE
:
376 case SUBTOTAL_FUNC_STD
:
377 case SUBTOTAL_FUNC_STDP
:
378 case SUBTOTAL_FUNC_VAR
:
379 case SUBTOTAL_FUNC_VARP
:
382 bool bOk
= SubTotal::SafeMult(nVal
, nVal
);
394 static double lcl_CalcData( ScSubTotalFunc eFunc
,
395 double& fCount
, double fSum
, double fSumSqr
)
402 case SUBTOTAL_FUNC_CNT
:
403 case SUBTOTAL_FUNC_CNT2
:
406 case SUBTOTAL_FUNC_SUM
:
407 case SUBTOTAL_FUNC_MAX
:
408 case SUBTOTAL_FUNC_MIN
:
409 case SUBTOTAL_FUNC_PROD
:
412 case SUBTOTAL_FUNC_AVE
:
414 fVal
= fSum
/ fCount
;
418 case SUBTOTAL_FUNC_STD
:
420 if (fCount
> 1 && SubTotal::SafeMult(fSum
, fSum
))
421 fVal
= sqrt((fSumSqr
- fSum
/fCount
)/(fCount
-1.0));
426 case SUBTOTAL_FUNC_STDP
:
428 if (fCount
> 0 && SubTotal::SafeMult(fSum
, fSum
))
429 fVal
= sqrt((fSumSqr
- fSum
/fCount
)/fCount
);
434 case SUBTOTAL_FUNC_VAR
:
436 if (fCount
> 1 && SubTotal::SafeMult(fSum
, fSum
))
437 fVal
= (fSumSqr
- fSum
/fCount
)/(fCount
-1.0);
442 case SUBTOTAL_FUNC_VARP
:
444 if (fCount
> 0 && SubTotal::SafeMult(fSum
, fSum
))
445 fVal
= (fSumSqr
- fSum
/fCount
)/fCount
;
452 OSL_FAIL("Consoli::CalcData: unknown function");
460 void ScConsData::AddData( ScDocument
* pSrcDoc
, SCTAB nTab
,
461 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
463 PutInOrder(nCol1
,nCol2
);
464 PutInOrder(nRow1
,nRow2
);
465 if ( nCol2
>= sal::static_int_cast
<SCCOL
>(nCol1
+ nColCount
) && !bColByName
)
467 OSL_FAIL("range too big");
468 nCol2
= sal::static_int_cast
<SCCOL
>( nCol1
+ nColCount
- 1 );
470 if ( nRow2
>= sal::static_int_cast
<SCROW
>(nRow1
+ nRowCount
) && !bRowByName
)
472 OSL_FAIL("range too big");
473 nRow2
= sal::static_int_cast
<SCROW
>( nRow1
+ nRowCount
- 1 );
481 if ( bColByName
&& bRowByName
)
483 OUString aThisCorner
= pSrcDoc
->GetString(nCol1
, nRow1
, nTab
);
486 if (aCornerText
!= aThisCorner
)
491 aCornerText
= aThisCorner
;
498 SCCOL nStartCol
= nCol1
;
499 SCROW nStartRow
= nRow1
;
500 if (bColByName
) ++nStartRow
;
501 if (bRowByName
) ++nStartCol
;
503 boost::scoped_array
<SCCOL
> pDestCols
;
504 boost::scoped_array
<SCROW
> pDestRows
;
507 pDestCols
.reset(new SCCOL
[nCol2
-nStartCol
+1]);
508 for (nCol
=nStartCol
; nCol
<=nCol2
; nCol
++)
510 aTitle
= pSrcDoc
->GetString(nCol
, nRow1
, nTab
);
511 SCCOL nPos
= SC_CONS_NOTFOUND
;
512 if (!aTitle
.isEmpty())
515 for (SCSIZE i
=0; i
<nColCount
&& !bFound
; i
++)
516 if ( maColHeaders
[i
] == aTitle
)
518 nPos
= static_cast<SCCOL
>(i
);
521 OSL_ENSURE(bFound
, "column not found");
523 pDestCols
[nCol
-nStartCol
] = nPos
;
528 pDestRows
.reset(new SCROW
[nRow2
-nStartRow
+1]);
529 for (nRow
=nStartRow
; nRow
<=nRow2
; nRow
++)
531 aTitle
= pSrcDoc
->GetString(nCol1
, nRow
, nTab
);
532 SCROW nPos
= SC_CONS_NOTFOUND
;
533 if (!aTitle
.isEmpty())
536 for (SCSIZE i
=0; i
<nRowCount
&& !bFound
; i
++)
537 if ( maRowHeaders
[i
] == aTitle
)
539 nPos
= static_cast<SCROW
>(i
);
542 OSL_ENSURE(bFound
, "row not found");
544 pDestRows
[nRow
-nStartRow
] = nPos
;
552 bool bAnyCell
= ( eFunction
== SUBTOTAL_FUNC_CNT2
);
553 for (nCol
=nCol1
; nCol
<=nCol2
; nCol
++)
555 SCCOL nArrX
= nCol
-nCol1
;
556 if (bColByName
) nArrX
= pDestCols
[nArrX
];
557 if (nArrX
!= SC_CONS_NOTFOUND
)
559 for (nRow
=nRow1
; nRow
<=nRow2
; nRow
++)
561 SCROW nArrY
= nRow
-nRow1
;
562 if (bRowByName
) nArrY
= pDestRows
[nArrY
];
563 if ( nArrY
!= SC_CONS_NOTFOUND
&& (
564 bAnyCell
? pSrcDoc
->HasData( nCol
, nRow
, nTab
)
565 : pSrcDoc
->HasValueData( nCol
, nRow
, nTab
) ) )
569 if (ppUsed
[nArrX
][nArrY
])
570 ppRefs
[nArrX
][nArrY
].AddEntry( nCol
, nRow
, nTab
);
573 ppUsed
[nArrX
][nArrY
] = true;
574 ppRefs
[nArrX
][nArrY
].Init();
575 ppRefs
[nArrX
][nArrY
].AddEntry( nCol
, nRow
, nTab
);
581 pSrcDoc
->GetValue( nCol
, nRow
, nTab
, nVal
);
582 if (ppUsed
[nArrX
][nArrY
])
583 lcl_UpdateArray( eFunction
, ppCount
[nArrX
][nArrY
],
584 ppSum
[nArrX
][nArrY
], ppSumSqr
[nArrX
][nArrY
],
588 ppUsed
[nArrX
][nArrY
] = true;
589 lcl_InitArray( eFunction
, ppCount
[nArrX
][nArrY
],
591 ppSumSqr
[nArrX
][nArrY
], nVal
);
600 // vorher testen, wieviele Zeilen eingefuegt werden (fuer Undo)
602 SCROW
ScConsData::GetInsertCount() const
607 if ( ppRefs
&& ppUsed
)
609 for (nArrY
=0; nArrY
<nRowCount
; nArrY
++)
612 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
613 if (ppUsed
[nArrX
][nArrY
])
614 nNeeded
= std::max( nNeeded
, ppRefs
[nArrX
][nArrY
].GetCount() );
622 // store completed data to document
623 //TODO: optimize on columns?
625 void ScConsData::OutputToDocument( ScDocument
* pDestDoc
, SCCOL nCol
, SCROW nRow
, SCTAB nTab
)
627 OpCode eOpCode
= eOpCodeTable
[eFunction
];
634 if ( bColByName
&& bRowByName
&& !aCornerText
.isEmpty() )
635 pDestDoc
->SetString( nCol
, nRow
, nTab
, aCornerText
);
639 SCCOL nStartCol
= nCol
;
640 SCROW nStartRow
= nRow
;
641 if (bColByName
) ++nStartRow
;
642 if (bRowByName
) ++nStartCol
;
645 for (SCSIZE i
=0; i
<nColCount
; i
++)
646 pDestDoc
->SetString( sal::static_int_cast
<SCCOL
>(nStartCol
+i
), nRow
, nTab
, maColHeaders
[i
] );
648 for (SCSIZE j
=0; j
<nRowCount
; j
++)
649 pDestDoc
->SetString( nCol
, sal::static_int_cast
<SCROW
>(nStartRow
+j
), nTab
, maRowHeaders
[j
] );
656 if ( ppCount
&& ppUsed
) // Werte direkt einfuegen
658 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
659 for (nArrY
=0; nArrY
<nRowCount
; nArrY
++)
660 if (ppUsed
[nArrX
][nArrY
])
662 double fVal
= lcl_CalcData( eFunction
, ppCount
[nArrX
][nArrY
],
664 ppSumSqr
[nArrX
][nArrY
]);
665 if (ppCount
[nArrX
][nArrY
] < 0.0)
666 pDestDoc
->SetError( sal::static_int_cast
<SCCOL
>(nCol
+nArrX
),
667 sal::static_int_cast
<SCROW
>(nRow
+nArrY
), nTab
, errNoValue
);
669 pDestDoc
->SetValue( sal::static_int_cast
<SCCOL
>(nCol
+nArrX
),
670 sal::static_int_cast
<SCROW
>(nRow
+nArrY
), nTab
, fVal
);
674 if ( ppRefs
&& ppUsed
) // insert Reference
676 //TODO: differentiate, if split into categories
679 ScSingleRefData aSRef
; // data for Referece formula cells
680 aSRef
.InitFlags(); // this reference is absolute at all times
681 aSRef
.SetFlag3D(true);
683 ScComplexRefData aCRef
; // data for Sum cells
685 aCRef
.Ref1
.SetColRel(true); aCRef
.Ref1
.SetRowRel(true); aCRef
.Ref1
.SetTabRel(true);
686 aCRef
.Ref2
.SetColRel(true); aCRef
.Ref2
.SetRowRel(true); aCRef
.Ref2
.SetTabRel(true);
688 for (nArrY
=0; nArrY
<nRowCount
; nArrY
++)
691 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
692 if (ppUsed
[nArrX
][nArrY
])
693 nNeeded
= std::max( nNeeded
, ppRefs
[nArrX
][nArrY
].GetCount() );
697 pDestDoc
->InsertRow( 0,nTab
, MAXCOL
,nTab
, nRow
+nArrY
, nNeeded
);
699 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
700 if (ppUsed
[nArrX
][nArrY
])
702 ScReferenceList
& rList
= ppRefs
[nArrX
][nArrY
];
703 SCSIZE nCount
= rList
.GetCount();
706 for (SCSIZE nPos
=0; nPos
<nCount
; nPos
++)
708 ScReferenceEntry aRef
= rList
.GetEntry(nPos
);
709 if (aRef
.nTab
!= SC_CONS_NOTFOUND
)
711 // Referenz einfuegen (absolut, 3d)
713 aSRef
.SetAddress(ScAddress(aRef
.nCol
,aRef
.nRow
,aRef
.nTab
), ScAddress());
715 ScTokenArray aRefArr
;
716 aRefArr
.AddSingleReference(aSRef
);
717 aRefArr
.AddOpCode(ocStop
);
718 ScAddress
aDest( sal::static_int_cast
<SCCOL
>(nCol
+nArrX
),
719 sal::static_int_cast
<SCROW
>(nRow
+nArrY
+nPos
), nTab
);
720 ScFormulaCell
* pCell
= new ScFormulaCell(pDestDoc
, aDest
, aRefArr
);
721 pDestDoc
->SetFormulaCell(aDest
, pCell
);
725 // Summe einfuegen (relativ, nicht 3d)
727 ScAddress
aDest( sal::static_int_cast
<SCCOL
>(nCol
+nArrX
),
728 sal::static_int_cast
<SCROW
>(nRow
+nArrY
+nNeeded
), nTab
);
730 ScRange
aRange(sal::static_int_cast
<SCCOL
>(nCol
+nArrX
), nRow
+nArrY
, nTab
);
731 aRange
.aEnd
.SetRow(nRow
+nArrY
+nNeeded
-1);
732 aCRef
.SetRange(aRange
, aDest
);
735 aArr
.AddOpCode(eOpCode
); // ausgewaehlte Funktion
736 aArr
.AddOpCode(ocOpen
);
737 aArr
.AddDoubleReference(aCRef
);
738 aArr
.AddOpCode(ocClose
);
739 aArr
.AddOpCode(ocStop
);
740 ScFormulaCell
* pCell
= new ScFormulaCell(pDestDoc
, aDest
, aArr
);
741 pDestDoc
->SetFormulaCell(aDest
, pCell
);
745 // Gliederung einfuegen
747 ScOutlineArray
& rOutArr
= pDestDoc
->GetOutlineTable( nTab
, true )->GetRowArray();
748 SCROW nOutStart
= nRow
+nArrY
;
749 SCROW nOutEnd
= nRow
+nArrY
+nNeeded
-1;
751 rOutArr
.Insert( nOutStart
, nOutEnd
, bSize
);
752 for (SCROW nOutRow
=nOutStart
; nOutRow
<=nOutEnd
; nOutRow
++)
753 pDestDoc
->ShowRow( nOutRow
, nTab
, false );
754 pDestDoc
->SetDrawPageSize(nTab
);
755 pDestDoc
->UpdateOutlineRow( nOutStart
, nOutEnd
, nTab
, false );
759 if (ppTitlePos
&& !maTitles
.empty() && !maRowHeaders
.empty())
761 OUString
aDelim( " / " );
762 for (SCSIZE nPos
=0; nPos
<nDataCount
; nPos
++)
764 SCSIZE nTPos
= ppTitlePos
[nArrY
][nPos
];
766 if (nPos
+1<nDataCount
)
767 if (ppTitlePos
[nArrY
][nPos
+1] == nTPos
)
769 if ( bDo
&& nTPos
< nNeeded
)
771 aString
= maRowHeaders
[nArrY
];
773 aString
+= maTitles
[nPos
];
774 pDestDoc
->SetString( nCol
-1, nRow
+nArrY
+nTPos
, nTab
, aString
);
785 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */