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"
32 #define SC_CONS_NOTFOUND -1
35 static const OpCode eOpCodeTable
[] = { // Reihenfolge wie bei enum ScSubTotalFunc
49 void ScReferenceList::AddEntry( SCCOL nCol
, SCROW nRow
, SCTAB nTab
)
51 ScReferenceEntry
* pOldData
= pData
;
52 pData
= new ScReferenceEntry
[ nFullSize
+1 ];
55 memcpy( pData
, pOldData
, nCount
* sizeof(ScReferenceEntry
) );
58 while (nCount
< nFullSize
)
60 pData
[nCount
].nCol
= SC_CONS_NOTFOUND
;
61 pData
[nCount
].nRow
= SC_CONS_NOTFOUND
;
62 pData
[nCount
].nTab
= SC_CONS_NOTFOUND
;
65 pData
[nCount
].nCol
= nCol
;
66 pData
[nCount
].nRow
= nRow
;
67 pData
[nCount
].nTab
= nTab
;
72 template< typename T
>
73 static void lcl_AddString( OUString
*& pData
, T
& nCount
, const OUString
& rInsert
)
75 OUString
* pOldData
= pData
;
76 pData
= new OUString
[ nCount
+1 ];
79 memcpy( pData
, pOldData
, nCount
* sizeof(OUString
) );
82 pData
[nCount
] = rInsert
;
86 ScConsData::ScConsData() :
87 eFunction(SUBTOTAL_FUNC_SUM
),
108 ScConsData::~ScConsData()
113 #define DELETEARR(ppArray,nCount) \
117 for(i=0; i<nCount; i++) \
118 delete[] ppArray[i]; \
123 void ScConsData::DeleteData()
127 for (SCSIZE i
=0; i
<nColCount
; i
++)
129 for (SCSIZE j
=0; j
<nRowCount
; j
++)
131 ppRefs
[i
][j
].Clear();
138 DELETEARR( ppCount
, nColCount
);
139 DELETEARR( ppSum
, nColCount
);
140 DELETEARR( ppSumSqr
,nColCount
);
141 DELETEARR( ppUsed
, nColCount
); // erst nach ppRefs !!!
142 DELETEARR( ppTitlePos
, nRowCount
);
143 delete[] mpColHeaders
;
145 delete[] mpRowHeaders
;
152 if (bColByName
) nColCount
= 0; // sonst stimmt mpColHeaders nicht
153 if (bRowByName
) nRowCount
= 0;
162 void ScConsData::InitData( sal_Bool bDelete
)
167 if (bReference
&& nColCount
&& !ppRefs
)
169 ppRefs
= new ScReferenceList
*[nColCount
];
170 for (SCSIZE i
=0; i
<nColCount
; i
++)
171 ppRefs
[i
] = new ScReferenceList
[nRowCount
];
173 else if (nColCount
&& !ppCount
)
175 ppCount
= new double*[nColCount
];
176 ppSum
= new double*[nColCount
];
177 ppSumSqr
= new double*[nColCount
];
178 for (SCSIZE i
=0; i
<nColCount
; i
++)
180 ppCount
[i
] = new double[nRowCount
];
181 ppSum
[i
] = new double[nRowCount
];
182 ppSumSqr
[i
] = new double[nRowCount
];
186 if (nColCount
&& !ppUsed
)
188 ppUsed
= new sal_Bool
*[nColCount
];
189 for (SCSIZE i
=0; i
<nColCount
; i
++)
191 ppUsed
[i
] = new sal_Bool
[nRowCount
];
192 memset( ppUsed
[i
], 0, nRowCount
* sizeof(sal_Bool
) );
196 if (nRowCount
&& nDataCount
&& !ppTitlePos
)
198 ppTitlePos
= new SCSIZE
*[nRowCount
];
199 for (SCSIZE i
=0; i
<nRowCount
; i
++)
201 ppTitlePos
[i
] = new SCSIZE
[nDataCount
];
202 memset( ppTitlePos
[i
], 0, nDataCount
* sizeof(SCSIZE
) ); //! unnoetig ?
206 // CornerText: einzelner String
209 void ScConsData::DoneFields()
214 void ScConsData::SetSize( SCCOL nCols
, SCROW nRows
)
217 nColCount
= static_cast<SCSIZE
>(nCols
);
218 nRowCount
= static_cast<SCSIZE
>(nRows
);
221 void ScConsData::GetSize( SCCOL
& rCols
, SCROW
& rRows
) const
223 rCols
= static_cast<SCCOL
>(nColCount
);
224 rRows
= static_cast<SCROW
>(nRowCount
);
227 void ScConsData::SetFlags( ScSubTotalFunc eFunc
, sal_Bool bColName
, sal_Bool bRowName
, sal_Bool bRef
)
231 bColByName
= bColName
;
232 if (bColName
) nColCount
= 0;
233 bRowByName
= bRowName
;
234 if (bRowName
) nRowCount
= 0;
238 void ScConsData::AddFields( ScDocument
* pSrcDoc
, SCTAB nTab
,
239 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
245 SCCOL nStartCol
= nCol1
;
246 SCROW nStartRow
= nRow1
;
247 if (bColByName
) ++nStartRow
;
248 if (bRowByName
) ++nStartCol
;
252 for (SCCOL nCol
=nStartCol
; nCol
<=nCol2
; nCol
++)
254 aTitle
= pSrcDoc
->GetString(nCol
, nRow1
, nTab
);
255 if (!aTitle
.isEmpty())
258 for (SCSIZE i
=0; i
<nColCount
&& !bFound
; i
++)
259 if ( mpColHeaders
[i
] == aTitle
)
262 lcl_AddString( mpColHeaders
, nColCount
, aTitle
);
269 for (SCROW nRow
=nStartRow
; nRow
<=nRow2
; nRow
++)
271 aTitle
= pSrcDoc
->GetString(nCol1
, nRow
, nTab
);
272 if (!aTitle
.isEmpty())
275 for (SCSIZE i
=0; i
<nRowCount
&& !bFound
; i
++)
276 if ( mpRowHeaders
[i
] == aTitle
)
279 lcl_AddString( mpRowHeaders
, nRowCount
, aTitle
);
285 void ScConsData::AddName( const OUString
& rName
)
292 lcl_AddString( mpTitles
, nTitleCount
, rName
);
294 for (nArrY
=0; nArrY
<nRowCount
; nArrY
++)
296 // Daten auf gleiche Laenge bringen
299 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
300 if (ppUsed
[nArrX
][nArrY
])
301 nMax
= std::max( nMax
, ppRefs
[nArrX
][nArrY
].GetCount() );
303 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
305 if (!ppUsed
[nArrX
][nArrY
])
307 ppUsed
[nArrX
][nArrY
] = sal_True
;
308 ppRefs
[nArrX
][nArrY
].Init();
310 ppRefs
[nArrX
][nArrY
].SetFullSize(nMax
);
313 // Positionen eintragen
316 if (nTitleCount
< nDataCount
)
317 ppTitlePos
[nArrY
][nTitleCount
] = nMax
;
322 // rCount < 0 <=> Fehler aufgetreten
324 static void lcl_UpdateArray( ScSubTotalFunc eFunc
,
325 double& rCount
, double& rSum
, double& rSumSqr
, double nVal
)
331 case SUBTOTAL_FUNC_SUM
:
332 if (!SubTotal::SafePlus(rSum
, nVal
))
335 case SUBTOTAL_FUNC_PROD
:
336 if (!SubTotal::SafeMult(rSum
, nVal
))
339 case SUBTOTAL_FUNC_CNT
:
340 case SUBTOTAL_FUNC_CNT2
:
343 case SUBTOTAL_FUNC_AVE
:
344 if (!SubTotal::SafePlus(rSum
, nVal
))
349 case SUBTOTAL_FUNC_MAX
:
353 case SUBTOTAL_FUNC_MIN
:
357 case SUBTOTAL_FUNC_STD
:
358 case SUBTOTAL_FUNC_STDP
:
359 case SUBTOTAL_FUNC_VAR
:
360 case SUBTOTAL_FUNC_VARP
:
362 sal_Bool bOk
= SubTotal::SafePlus(rSum
, nVal
);
363 bOk
= bOk
&& SubTotal::SafeMult(nVal
, nVal
);
364 bOk
= bOk
&& SubTotal::SafePlus(rSumSqr
, nVal
);
373 // added to avoid warnings
378 static void lcl_InitArray( ScSubTotalFunc eFunc
,
379 double& rCount
, double& rSum
, double& rSumSqr
, double nVal
)
384 case SUBTOTAL_FUNC_SUM
:
385 case SUBTOTAL_FUNC_MAX
:
386 case SUBTOTAL_FUNC_MIN
:
387 case SUBTOTAL_FUNC_PROD
:
388 case SUBTOTAL_FUNC_AVE
:
391 case SUBTOTAL_FUNC_STD
:
392 case SUBTOTAL_FUNC_STDP
:
393 case SUBTOTAL_FUNC_VAR
:
394 case SUBTOTAL_FUNC_VARP
:
397 sal_Bool bOk
= SubTotal::SafeMult(nVal
, nVal
);
409 static double lcl_CalcData( ScSubTotalFunc eFunc
,
410 double& fCount
, double fSum
, double fSumSqr
)
417 case SUBTOTAL_FUNC_CNT
:
418 case SUBTOTAL_FUNC_CNT2
:
421 case SUBTOTAL_FUNC_SUM
:
422 case SUBTOTAL_FUNC_MAX
:
423 case SUBTOTAL_FUNC_MIN
:
424 case SUBTOTAL_FUNC_PROD
:
427 case SUBTOTAL_FUNC_AVE
:
429 fVal
= fSum
/ fCount
;
433 case SUBTOTAL_FUNC_STD
:
435 if (fCount
> 1 && SubTotal::SafeMult(fSum
, fSum
))
436 fVal
= sqrt((fSumSqr
- fSum
/fCount
)/(fCount
-1.0));
441 case SUBTOTAL_FUNC_STDP
:
443 if (fCount
> 0 && SubTotal::SafeMult(fSum
, fSum
))
444 fVal
= sqrt((fSumSqr
- fSum
/fCount
)/fCount
);
449 case SUBTOTAL_FUNC_VAR
:
451 if (fCount
> 1 && SubTotal::SafeMult(fSum
, fSum
))
452 fVal
= (fSumSqr
- fSum
/fCount
)/(fCount
-1.0);
457 case SUBTOTAL_FUNC_VARP
:
459 if (fCount
> 0 && SubTotal::SafeMult(fSum
, fSum
))
460 fVal
= (fSumSqr
- fSum
/fCount
)/fCount
;
467 OSL_FAIL("Consoli::CalcData: unknown function");
475 void ScConsData::AddData( ScDocument
* pSrcDoc
, SCTAB nTab
,
476 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
478 PutInOrder(nCol1
,nCol2
);
479 PutInOrder(nRow1
,nRow2
);
480 if ( nCol2
>= sal::static_int_cast
<SCCOL
>(nCol1
+ nColCount
) && !bColByName
)
482 OSL_FAIL("range too big");
483 nCol2
= sal::static_int_cast
<SCCOL
>( nCol1
+ nColCount
- 1 );
485 if ( nRow2
>= sal::static_int_cast
<SCROW
>(nRow1
+ nRowCount
) && !bRowByName
)
487 OSL_FAIL("range too big");
488 nRow2
= sal::static_int_cast
<SCROW
>( nRow1
+ nRowCount
- 1 );
496 if ( bColByName
&& bRowByName
)
498 OUString aThisCorner
= pSrcDoc
->GetString(nCol1
, nRow1
, nTab
);
501 if (aCornerText
!= aThisCorner
)
506 aCornerText
= aThisCorner
;
507 bCornerUsed
= sal_True
;
513 SCCOL nStartCol
= nCol1
;
514 SCROW nStartRow
= nRow1
;
515 if (bColByName
) ++nStartRow
;
516 if (bRowByName
) ++nStartCol
;
518 SCCOL
* pDestCols
= NULL
;
519 SCROW
* pDestRows
= NULL
;
522 pDestCols
= new SCCOL
[nCol2
-nStartCol
+1];
523 for (nCol
=nStartCol
; nCol
<=nCol2
; nCol
++)
525 aTitle
= pSrcDoc
->GetString(nCol
, nRow1
, nTab
);
526 SCCOL nPos
= SC_CONS_NOTFOUND
;
527 if (!aTitle
.isEmpty())
530 for (SCSIZE i
=0; i
<nColCount
&& !bFound
; i
++)
531 if ( mpColHeaders
[i
] == aTitle
)
533 nPos
= static_cast<SCCOL
>(i
);
536 OSL_ENSURE(bFound
, "column not found");
538 pDestCols
[nCol
-nStartCol
] = nPos
;
543 pDestRows
= new SCROW
[nRow2
-nStartRow
+1];
544 for (nRow
=nStartRow
; nRow
<=nRow2
; nRow
++)
546 aTitle
= pSrcDoc
->GetString(nCol1
, nRow
, nTab
);
547 SCROW nPos
= SC_CONS_NOTFOUND
;
548 if (!aTitle
.isEmpty())
551 for (SCSIZE i
=0; i
<nRowCount
&& !bFound
; i
++)
552 if ( mpRowHeaders
[i
] == aTitle
)
554 nPos
= static_cast<SCROW
>(i
);
557 OSL_ENSURE(bFound
, "row not found");
559 pDestRows
[nRow
-nStartRow
] = nPos
;
567 bool bAnyCell
= ( eFunction
== SUBTOTAL_FUNC_CNT2
);
568 for (nCol
=nCol1
; nCol
<=nCol2
; nCol
++)
570 SCCOL nArrX
= nCol
-nCol1
;
571 if (bColByName
) nArrX
= pDestCols
[nArrX
];
572 if (nArrX
!= SC_CONS_NOTFOUND
)
574 for (nRow
=nRow1
; nRow
<=nRow2
; nRow
++)
576 SCROW nArrY
= nRow
-nRow1
;
577 if (bRowByName
) nArrY
= pDestRows
[nArrY
];
578 if ( nArrY
!= SC_CONS_NOTFOUND
&& (
579 bAnyCell
? pSrcDoc
->HasData( nCol
, nRow
, nTab
)
580 : pSrcDoc
->HasValueData( nCol
, nRow
, nTab
) ) )
584 if (ppUsed
[nArrX
][nArrY
])
585 ppRefs
[nArrX
][nArrY
].AddEntry( nCol
, nRow
, nTab
);
588 ppUsed
[nArrX
][nArrY
] = sal_True
;
589 ppRefs
[nArrX
][nArrY
].Init();
590 ppRefs
[nArrX
][nArrY
].AddEntry( nCol
, nRow
, nTab
);
596 pSrcDoc
->GetValue( nCol
, nRow
, nTab
, nVal
);
597 if (ppUsed
[nArrX
][nArrY
])
598 lcl_UpdateArray( eFunction
, ppCount
[nArrX
][nArrY
],
599 ppSum
[nArrX
][nArrY
], ppSumSqr
[nArrX
][nArrY
],
603 ppUsed
[nArrX
][nArrY
] = sal_True
;
604 lcl_InitArray( eFunction
, ppCount
[nArrX
][nArrY
],
606 ppSumSqr
[nArrX
][nArrY
], nVal
);
618 // vorher testen, wieviele Zeilen eingefuegt werden (fuer Undo)
620 SCROW
ScConsData::GetInsertCount() const
625 if ( ppRefs
&& ppUsed
)
627 for (nArrY
=0; nArrY
<nRowCount
; nArrY
++)
630 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
631 if (ppUsed
[nArrX
][nArrY
])
632 nNeeded
= std::max( nNeeded
, ppRefs
[nArrX
][nArrY
].GetCount() );
640 // fertige Daten ins Dokument schreiben
641 //! optimieren nach Spalten?
643 void ScConsData::OutputToDocument( ScDocument
* pDestDoc
, SCCOL nCol
, SCROW nRow
, SCTAB nTab
)
645 OpCode eOpCode
= eOpCodeTable
[eFunction
];
652 if ( bColByName
&& bRowByName
&& !aCornerText
.isEmpty() )
653 pDestDoc
->SetString( nCol
, nRow
, nTab
, aCornerText
);
657 SCCOL nStartCol
= nCol
;
658 SCROW nStartRow
= nRow
;
659 if (bColByName
) ++nStartRow
;
660 if (bRowByName
) ++nStartCol
;
663 for (SCSIZE i
=0; i
<nColCount
; i
++)
664 pDestDoc
->SetString( sal::static_int_cast
<SCCOL
>(nStartCol
+i
), nRow
, nTab
, mpColHeaders
[i
] );
666 for (SCSIZE j
=0; j
<nRowCount
; j
++)
667 pDestDoc
->SetString( nCol
, sal::static_int_cast
<SCROW
>(nStartRow
+j
), nTab
, mpRowHeaders
[j
] );
674 if ( ppCount
&& ppUsed
) // Werte direkt einfuegen
676 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
677 for (nArrY
=0; nArrY
<nRowCount
; nArrY
++)
678 if (ppUsed
[nArrX
][nArrY
])
680 double fVal
= lcl_CalcData( eFunction
, ppCount
[nArrX
][nArrY
],
682 ppSumSqr
[nArrX
][nArrY
]);
683 if (ppCount
[nArrX
][nArrY
] < 0.0)
684 pDestDoc
->SetError( sal::static_int_cast
<SCCOL
>(nCol
+nArrX
),
685 sal::static_int_cast
<SCROW
>(nRow
+nArrY
), nTab
, errNoValue
);
687 pDestDoc
->SetValue( sal::static_int_cast
<SCCOL
>(nCol
+nArrX
),
688 sal::static_int_cast
<SCROW
>(nRow
+nArrY
), nTab
, fVal
);
692 if ( ppRefs
&& ppUsed
) // Referenzen einfuegen
694 //! unterscheiden, ob nach Kategorien aufgeteilt
697 ScSingleRefData aSRef
; // Daten fuer Referenz-Formelzellen
698 aSRef
.InitFlags(); // This reference is absolute at all times.
699 aSRef
.SetFlag3D(true);
701 ScComplexRefData aCRef
; // Daten fuer Summen-Zellen
703 aCRef
.Ref1
.SetColRel(sal_True
); aCRef
.Ref1
.SetRowRel(sal_True
); aCRef
.Ref1
.SetTabRel(sal_True
);
704 aCRef
.Ref2
.SetColRel(sal_True
); aCRef
.Ref2
.SetRowRel(sal_True
); aCRef
.Ref2
.SetTabRel(sal_True
);
706 for (nArrY
=0; nArrY
<nRowCount
; nArrY
++)
709 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
710 if (ppUsed
[nArrX
][nArrY
])
711 nNeeded
= std::max( nNeeded
, ppRefs
[nArrX
][nArrY
].GetCount() );
715 pDestDoc
->InsertRow( 0,nTab
, MAXCOL
,nTab
, nRow
+nArrY
, nNeeded
);
717 for (nArrX
=0; nArrX
<nColCount
; nArrX
++)
718 if (ppUsed
[nArrX
][nArrY
])
720 ScReferenceList
& rList
= ppRefs
[nArrX
][nArrY
];
721 SCSIZE nCount
= rList
.GetCount();
724 for (SCSIZE nPos
=0; nPos
<nCount
; nPos
++)
726 ScReferenceEntry aRef
= rList
.GetEntry(nPos
);
727 if (aRef
.nTab
!= SC_CONS_NOTFOUND
)
729 // Referenz einfuegen (absolut, 3d)
731 aSRef
.SetAddress(ScAddress(aRef
.nCol
,aRef
.nRow
,aRef
.nTab
), ScAddress());
733 ScTokenArray aRefArr
;
734 aRefArr
.AddSingleReference(aSRef
);
735 aRefArr
.AddOpCode(ocStop
);
736 ScAddress
aDest( sal::static_int_cast
<SCCOL
>(nCol
+nArrX
),
737 sal::static_int_cast
<SCROW
>(nRow
+nArrY
+nPos
), nTab
);
738 ScFormulaCell
* pCell
= new ScFormulaCell(pDestDoc
, aDest
, aRefArr
);
739 pDestDoc
->SetFormulaCell(aDest
, pCell
);
743 // Summe einfuegen (relativ, nicht 3d)
745 ScAddress
aDest( sal::static_int_cast
<SCCOL
>(nCol
+nArrX
),
746 sal::static_int_cast
<SCROW
>(nRow
+nArrY
+nNeeded
), nTab
);
748 ScRange
aRange(sal::static_int_cast
<SCCOL
>(nCol
+nArrX
), nRow
+nArrY
, nTab
);
749 aRange
.aEnd
.SetRow(nRow
+nArrY
+nNeeded
-1);
750 aCRef
.SetRange(aRange
, aDest
);
753 aArr
.AddOpCode(eOpCode
); // ausgewaehlte Funktion
754 aArr
.AddOpCode(ocOpen
);
755 aArr
.AddDoubleReference(aCRef
);
756 aArr
.AddOpCode(ocClose
);
757 aArr
.AddOpCode(ocStop
);
758 ScFormulaCell
* pCell
= new ScFormulaCell(pDestDoc
, aDest
, aArr
);
759 pDestDoc
->SetFormulaCell(aDest
, pCell
);
763 // Gliederung einfuegen
765 ScOutlineArray
* pOutArr
= pDestDoc
->GetOutlineTable( nTab
, sal_True
)->GetRowArray();
766 SCROW nOutStart
= nRow
+nArrY
;
767 SCROW nOutEnd
= nRow
+nArrY
+nNeeded
-1;
769 pOutArr
->Insert( nOutStart
, nOutEnd
, bSize
);
770 for (SCROW nOutRow
=nOutStart
; nOutRow
<=nOutEnd
; nOutRow
++)
771 pDestDoc
->ShowRow( nOutRow
, nTab
, false );
772 pDestDoc
->SetDrawPageSize(nTab
);
773 pDestDoc
->UpdateOutlineRow( nOutStart
, nOutEnd
, nTab
, false );
777 if (ppTitlePos
&& mpTitles
&& mpRowHeaders
)
779 OUString
aDelim( " / " );
780 for (SCSIZE nPos
=0; nPos
<nDataCount
; nPos
++)
782 SCSIZE nTPos
= ppTitlePos
[nArrY
][nPos
];
784 if (nPos
+1<nDataCount
)
785 if (ppTitlePos
[nArrY
][nPos
+1] == nTPos
)
787 if ( bDo
&& nTPos
< nNeeded
)
789 aString
= mpRowHeaders
[nArrY
];
791 aString
+= mpTitles
[nPos
];
792 pDestDoc
->SetString( nCol
-1, nRow
+nArrY
+nTPos
, nTab
, aString
);
803 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */