Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / core / tool / chartpos.cxx
bloba64c5ffdf13e4cfed23fadde9d8e453ee185fd1c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 "chartpos.hxx"
21 #include "document.hxx"
22 #include "rechead.hxx"
24 namespace
26 bool lcl_hasValueDataButNoDates( ScDocument* pDocument, SCCOL nCol, SCROW nRow, SCTAB nTab )
28 bool bReturn = false;
29 if (pDocument->HasValueData( nCol, nRow, nTab ))
31 //treat dates like text #i25706#
32 sal_uInt32 nNumberFormat = pDocument->GetNumberFormat( ScAddress( nCol, nRow, nTab ) );
33 short nType = pDocument->GetFormatTable()->GetType(nNumberFormat);
34 bool bIsDate = (nType & NUMBERFORMAT_DATE);
35 bReturn = !bIsDate;
37 return bReturn;
41 ScChartPositioner::ScChartPositioner( ScDocument* pDoc, SCTAB nTab,
42 SCCOL nStartColP, SCROW nStartRowP, SCCOL nEndColP, SCROW nEndRowP) :
43 pDocument( pDoc ),
44 pPositionMap( NULL ),
45 eGlue( SC_CHARTGLUE_NA ),
46 nStartCol(0),
47 nStartRow(0),
48 bColHeaders( false ),
49 bRowHeaders( false ),
50 bDummyUpperLeft( false )
52 SetRangeList( ScRange( nStartColP, nStartRowP, nTab, nEndColP, nEndRowP, nTab ) );
53 CheckColRowHeaders();
56 ScChartPositioner::ScChartPositioner( ScDocument* pDoc, const ScRangeListRef& rRangeList ) :
57 aRangeListRef( rRangeList ),
58 pDocument( pDoc ),
59 pPositionMap( NULL ),
60 eGlue( SC_CHARTGLUE_NA ),
61 nStartCol(0),
62 nStartRow(0),
63 bColHeaders( false ),
64 bRowHeaders( false ),
65 bDummyUpperLeft( false )
67 if ( aRangeListRef.Is() )
68 CheckColRowHeaders();
71 ScChartPositioner::ScChartPositioner( const ScChartPositioner& rPositioner ) :
72 aRangeListRef( rPositioner.aRangeListRef ),
73 pDocument(rPositioner.pDocument),
74 pPositionMap( NULL ),
75 eGlue(rPositioner.eGlue),
76 nStartCol(rPositioner.nStartCol),
77 nStartRow(rPositioner.nStartRow),
78 bColHeaders(rPositioner.bColHeaders),
79 bRowHeaders(rPositioner.bRowHeaders),
80 bDummyUpperLeft( rPositioner.bDummyUpperLeft )
84 ScChartPositioner::~ScChartPositioner()
86 delete pPositionMap;
89 sal_Bool ScChartPositioner::operator==(const ScChartPositioner& rCmp) const
91 return bColHeaders == rCmp.bColHeaders
92 && bRowHeaders == rCmp.bRowHeaders
93 && *aRangeListRef == *rCmp.aRangeListRef;
96 void ScChartPositioner::SetRangeList( const ScRange& rRange )
98 aRangeListRef = new ScRangeList;
99 aRangeListRef->Append( rRange );
100 InvalidateGlue();
103 void ScChartPositioner::GlueState()
105 if ( eGlue != SC_CHARTGLUE_NA )
106 return;
107 bDummyUpperLeft = false;
108 ScRange* pR;
109 if ( aRangeListRef->size() <= 1 )
111 if ( !aRangeListRef->empty() )
113 pR = aRangeListRef->front();
114 if ( pR->aStart.Tab() == pR->aEnd.Tab() )
115 eGlue = SC_CHARTGLUE_NONE;
116 else
117 eGlue = SC_CHARTGLUE_COLS; // several tables column by column
118 nStartCol = pR->aStart.Col();
119 nStartRow = pR->aStart.Row();
121 else
123 InvalidateGlue();
124 nStartCol = 0;
125 nStartRow = 0;
127 return;
130 pR = aRangeListRef->front();
131 nStartCol = pR->aStart.Col();
132 nStartRow = pR->aStart.Row();
133 SCCOL nMaxCols, nEndCol;
134 SCROW nMaxRows, nEndRow;
135 nMaxCols = nEndCol = 0;
136 nMaxRows = nEndRow = 0;
138 // <= so 1 extra pass after last item
139 for ( size_t i = 1, nRanges = aRangeListRef->size(); i <= nRanges; ++i )
140 { // detect spanning/surrounding area etc.
141 SCCOLROW nTmp, n1, n2;
142 if ( (n1 = pR->aStart.Col()) < nStartCol ) nStartCol = static_cast<SCCOL>(n1 );
143 if ( (n2 = pR->aEnd.Col() ) > nEndCol ) nEndCol = static_cast<SCCOL>(n2 );
144 if ( (nTmp = n2 - n1 + 1 ) > nMaxCols ) nMaxCols = static_cast<SCCOL>(nTmp);
145 if ( (n1 = pR->aStart.Row()) < nStartRow ) nStartRow = static_cast<SCROW>(n1 );
146 if ( (n2 = pR->aEnd.Row() ) > nEndRow ) nEndRow = static_cast<SCROW>(n2 );
147 if ( (nTmp = n2 - n1 + 1 ) > nMaxRows ) nMaxRows = static_cast<SCROW>(nTmp);
149 // in last pass; i = nRanges so don't use at()
150 if ( i < nRanges )
151 pR = (*aRangeListRef)[i];
153 SCCOL nC = nEndCol - nStartCol + 1;
154 if ( nC == 1 )
156 eGlue = SC_CHARTGLUE_ROWS;
157 return;
159 SCROW nR = nEndRow - nStartRow + 1;
160 if ( nR == 1 )
162 eGlue = SC_CHARTGLUE_COLS;
163 return;
165 sal_uLong nCR = (sal_uLong)nC * nR;
168 TODO:
169 First do it simple without bit masking. A maximum of 8MB could be allocated
170 this way (256 Cols x 32000 Rows). That could be reduced to 2MB by
171 using 2 Bits per entry, but it is faster this way.
172 Another optimization would be to store only used rows/columns in the array, but
173 would mean another iteration of the RangeList indirect access to the array. */
175 const sal_uInt8 nHole = 0;
176 const sal_uInt8 nOccu = 1;
177 const sal_uInt8 nFree = 2;
178 const sal_uInt8 nGlue = 3;
179 sal_uInt8* p;
180 sal_uInt8* pA = new sal_uInt8[ nCR ];
181 memset( pA, 0, nCR * sizeof(sal_uInt8) );
183 SCCOL nCol, nCol1, nCol2;
184 SCROW nRow, nRow1, nRow2;
185 for ( size_t i = 0, nRanges = aRangeListRef->size(); i < nRanges; ++i )
186 { // mark selections as used in 2D
187 pR = (*aRangeListRef)[i];
188 nCol1 = pR->aStart.Col() - nStartCol;
189 nCol2 = pR->aEnd.Col() - nStartCol;
190 nRow1 = pR->aStart.Row() - nStartRow;
191 nRow2 = pR->aEnd.Row() - nStartRow;
192 for ( nCol = nCol1; nCol <= nCol2; nCol++ )
194 p = pA + (sal_uLong)nCol * nR + nRow1;
195 for ( nRow = nRow1; nRow <= nRow2; nRow++, p++ )
196 *p = nOccu;
199 sal_Bool bGlue = sal_True;
201 sal_Bool bGlueCols = false;
202 for ( nCol = 0; bGlue && nCol < nC; nCol++ )
203 { // iterate columns and try to mark as unused
204 p = pA + (sal_uLong)nCol * nR;
205 for ( nRow = 0; bGlue && nRow < nR; nRow++, p++ )
207 if ( *p == nOccu )
208 { // If there's one right in the middle, we can't combine.
209 // If it were at the edge, we could combine, if in this Column
210 // in every set line, one is set.
211 if ( nRow > 0 && nCol > 0 )
212 bGlue = false; // nCol==0 can be DummyUpperLeft
213 else
214 nRow = nR;
216 else
217 *p = nFree;
219 if ( bGlue && *(p = (pA + ((((sal_uLong)nCol+1) * nR) - 1))) == nFree )
220 { // mark column as totally unused
221 *p = nGlue;
222 bGlueCols = sal_True; // one unused column at least
226 sal_Bool bGlueRows = false;
227 for ( nRow = 0; bGlue && nRow < nR; nRow++ )
228 { // iterate rows and try to mark as unused
229 p = pA + nRow;
230 for ( nCol = 0; bGlue && nCol < nC; nCol++, p+=nR )
232 if ( *p == nOccu )
234 if ( nCol > 0 && nRow > 0 )
235 bGlue = false; // nRow==0 can be DummyUpperLeft
236 else
237 nCol = nC;
239 else
240 *p = nFree;
242 if ( bGlue && *(p = (pA + ((((sal_uLong)nC-1) * nR) + nRow))) == nFree )
243 { // mark row as totally unused
244 *p = nGlue;
245 bGlueRows = sal_True; // one unused row at least
249 // If n=1: The upper left corner could be automagically pulled in for labeling
250 p = pA + 1;
251 for ( sal_uLong n = 1; bGlue && n < nCR; n++, p++ )
252 { // An untouched field means we could neither reach it through rows nor columns,
253 // thus we can't combine anything
254 if ( *p == nHole )
255 bGlue = false;
257 if ( bGlue )
259 if ( bGlueCols && bGlueRows )
260 eGlue = SC_CHARTGLUE_BOTH;
261 else if ( bGlueRows )
262 eGlue = SC_CHARTGLUE_ROWS;
263 else
264 eGlue = SC_CHARTGLUE_COLS;
265 if ( *pA != nOccu )
266 bDummyUpperLeft = sal_True;
268 else
270 eGlue = SC_CHARTGLUE_NONE;
273 delete [] pA;
276 void ScChartPositioner::CheckColRowHeaders()
278 SCCOL nCol1, nCol2, iCol;
279 SCROW nRow1, nRow2, iRow;
280 SCTAB nTab1, nTab2;
282 sal_Bool bColStrings = sal_True;
283 sal_Bool bRowStrings = sal_True;
284 GlueState();
285 if ( aRangeListRef->size() == 1 )
287 aRangeListRef->front()->GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
288 if ( nCol1 > nCol2 || nRow1 > nRow2 )
289 bColStrings = bRowStrings = false;
290 else
292 for (iCol=nCol1; iCol<=nCol2 && bColStrings; iCol++)
294 if (lcl_hasValueDataButNoDates( pDocument, iCol, nRow1, nTab1 ))
295 bColStrings = false;
297 for (iRow=nRow1; iRow<=nRow2 && bRowStrings; iRow++)
299 if (lcl_hasValueDataButNoDates( pDocument, nCol1, iRow, nTab1 ))
300 bRowStrings = false;
304 else
306 sal_Bool bVert = (eGlue == SC_CHARTGLUE_NONE || eGlue == SC_CHARTGLUE_ROWS);
307 for ( size_t i = 0, nRanges = aRangeListRef->size();
308 (i < nRanges) && (bColStrings || bRowStrings);
312 ScRange* pR = (*aRangeListRef)[i];
313 pR->GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
314 sal_Bool bTopRow = (nRow1 == nStartRow);
315 if ( bRowStrings && (bVert || nCol1 == nStartCol) )
316 { // NONE or ROWS: RowStrings in every selection possible
317 // COLS or BOTH: only from first column
318 if ( nCol1 <= nCol2 )
319 for (iRow=nRow1; iRow<=nRow2 && bRowStrings; iRow++)
321 if (lcl_hasValueDataButNoDates( pDocument, nCol1, iRow, nTab1 ))
322 bRowStrings = false;
325 if ( bColStrings && bTopRow )
326 { // ColStrings only from first row
327 if ( nRow1 <= nRow2 )
328 for (iCol=nCol1; iCol<=nCol2 && bColStrings; iCol++)
330 if (lcl_hasValueDataButNoDates( pDocument, iCol, nRow1, nTab1 ))
331 bColStrings = false;
336 bColHeaders = bColStrings;
337 bRowHeaders = bRowStrings;
340 const ScChartPositionMap* ScChartPositioner::GetPositionMap()
342 CreatePositionMap();
343 return pPositionMap;
347 void ScChartPositioner::CreatePositionMap()
349 if ( eGlue == SC_CHARTGLUE_NA && pPositionMap )
351 delete pPositionMap;
352 pPositionMap = NULL;
355 if ( pPositionMap )
356 return ;
358 SCSIZE nColAdd = bRowHeaders ? 1 : 0;
359 SCSIZE nRowAdd = bColHeaders ? 1 : 0;
361 SCCOL nCol, nCol1, nCol2;
362 SCROW nRow, nRow1, nRow2;
363 SCTAB nTab, nTab1, nTab2;
366 // real size (without hidden rows/columns)
369 SCSIZE nColCount = 0;
370 SCSIZE nRowCount = 0;
372 GlueState();
374 const sal_Bool bNoGlue = (eGlue == SC_CHARTGLUE_NONE);
375 ColumnMap* pCols = new ColumnMap;
376 SCROW nNoGlueRow = 0;
377 for ( size_t i = 0, nRanges = aRangeListRef->size(); i < nRanges; ++i )
379 ScRange* pR = (*aRangeListRef)[i];
380 pR->GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
381 for ( nTab = nTab1; nTab <= nTab2; nTab++ )
383 // nTab in ColKey to allow to have the same col/row in another tabe
384 sal_uLong nInsCol = (static_cast<sal_uLong>(nTab) << 16) | (bNoGlue ? 0 :
385 static_cast<sal_uLong>(nCol1));
386 for ( nCol = nCol1; nCol <= nCol2; ++nCol, ++nInsCol )
388 RowMap* pCol = NULL;
389 ColumnMap::const_iterator it = pCols->find( nInsCol );
390 if ( it == pCols->end() )
392 pCol = new RowMap;
393 pCols->insert( ColumnMap::value_type( nInsCol, pCol ) );
395 else
396 pCol = it->second;
398 // in other table a new ColKey already was created,
399 // the rows must be equal to be filled with Dummy
400 sal_uLong nInsRow = (bNoGlue ? nNoGlueRow : nRow1);
401 for ( nRow = nRow1; nRow <= nRow2; nRow++, nInsRow++ )
403 if ( pCol->find( nInsRow ) == pCol->end() )
405 pCol->insert( RowMap::value_type( nInsRow, new ScAddress( nCol, nRow, nTab ) ) );
410 // For NoGlue: associated tables will be rendered as ColGlue
411 nNoGlueRow += nRow2 - nRow1 + 1;
414 // count of data
415 nColCount = static_cast< SCSIZE >( pCols->size());
416 if ( !pCols->empty() )
418 RowMap* pCol = pCols->begin()->second;
419 if ( bDummyUpperLeft )
420 (*pCol)[ 0 ] = NULL; // Dummy for labeling
421 nRowCount = static_cast< SCSIZE >( pCol->size());
423 else
424 nRowCount = 0;
425 if ( nColCount > 0 )
426 nColCount -= nColAdd;
427 if ( nRowCount > 0 )
428 nRowCount -= nRowAdd;
430 if ( nColCount==0 || nRowCount==0 )
431 { // create an entry without data
432 RowMap* pCol;
433 if ( !pCols->empty() )
434 pCol = pCols->begin()->second;
435 else
437 pCol = new RowMap;
438 (*pCols)[ 0 ] = pCol;
440 nColCount = 1;
441 if ( !pCol->empty() )
442 { // cannot be if nColCount==0 || nRowCount==0
443 ScAddress* pPos = pCol->begin()->second;
444 if ( pPos )
446 sal_uLong nCurrentKey = pCol->begin()->first;
447 delete pPos;
448 (*pCol)[ nCurrentKey ] = NULL;
451 else
452 (*pCol)[ 0 ] = NULL;
453 nRowCount = 1;
454 nColAdd = 0;
455 nRowAdd = 0;
457 else
459 if ( bNoGlue )
460 { // fill gaps with Dummies, first column is master
461 RowMap* pFirstCol = pCols->begin()->second;
462 sal_uLong nCount = pFirstCol->size();
463 RowMap::const_iterator it1 = pFirstCol->begin();
464 for ( sal_uLong n = 0; n < nCount; n++, ++it1 )
466 sal_uLong nKey = it1->first;
467 for (ColumnMap::const_iterator it2 = ++pCols->begin(); it2 != pCols->end(); ++it2 )
468 it2->second->insert( RowMap::value_type( nKey, (ScAddress *)NULL )); // no data
473 pPositionMap = new ScChartPositionMap( static_cast<SCCOL>(nColCount), static_cast<SCROW>(nRowCount),
474 static_cast<SCCOL>(nColAdd), static_cast<SCROW>(nRowAdd), *pCols );
476 // cleanup
477 for (ColumnMap::const_iterator it = pCols->begin(); it != pCols->end(); ++it )
478 { // Only delete tables, not the ScAddress*!
479 delete it->second;
481 delete pCols;
485 ScChartPositionMap::ScChartPositionMap( SCCOL nChartCols, SCROW nChartRows,
486 SCCOL nColAdd, SCROW nRowAdd, ColumnMap& rCols ) :
487 ppData( new ScAddress* [ nChartCols * nChartRows ] ),
488 ppColHeader( new ScAddress* [ nChartCols ] ),
489 ppRowHeader( new ScAddress* [ nChartRows ] ),
490 nCount( (sal_uLong) nChartCols * nChartRows ),
491 nColCount( nChartCols ),
492 nRowCount( nChartRows )
494 OSL_ENSURE( nColCount && nRowCount, "ScChartPositionMap without dimension" );
496 ColumnMap::const_iterator pColIter = rCols.begin();
497 RowMap* pCol1 = pColIter->second;
498 RowMap::const_iterator pPos1Iter;
500 // row header
501 pPos1Iter = pCol1->begin();
502 if ( nRowAdd )
503 ++pPos1Iter;
504 if ( nColAdd )
505 { // independent
506 SCROW nRow = 0;
507 for ( ; nRow < nRowCount && pPos1Iter != pCol1->end(); nRow++ )
509 ppRowHeader[ nRow ] = pPos1Iter->second;
510 ++pPos1Iter;
512 for ( ; nRow < nRowCount; nRow++ )
513 ppRowHeader[ nRow ] = NULL;
515 else
516 { // copy
517 SCROW nRow = 0;
518 for ( ; nRow < nRowCount && pPos1Iter != pCol1->end(); nRow++ )
520 ppRowHeader[ nRow ] = pPos1Iter->second ?
521 new ScAddress( *pPos1Iter->second ) : NULL;
522 ++pPos1Iter;
524 for ( ; nRow < nRowCount; nRow++ )
525 ppRowHeader[ nRow ] = NULL;
527 if ( nColAdd )
529 ++pColIter;
532 // data column by column and column-header
533 sal_uLong nIndex = 0;
534 for ( SCCOL nCol = 0; nCol < nColCount; nCol++ )
536 if ( pColIter != rCols.end() )
538 RowMap* pCol2 = pColIter->second;
539 RowMap::const_iterator pPosIter = pCol2->begin();
540 if ( pPosIter != pCol2->end() )
542 if ( nRowAdd )
544 ppColHeader[ nCol ] = pPosIter->second; // independent
545 ++pPosIter;
547 else
548 ppColHeader[ nCol ] = pPosIter->second ?
549 new ScAddress( *pPosIter->second ) : NULL;
552 SCROW nRow = 0;
553 for ( ; nRow < nRowCount && pPosIter != pCol2->end(); nRow++, nIndex++ )
555 ppData[ nIndex ] = pPosIter->second;
556 ++pPosIter;
558 for ( ; nRow < nRowCount; nRow++, nIndex++ )
559 ppData[ nIndex ] = NULL;
561 ++pColIter;
563 else
565 ppColHeader[ nCol ] = NULL;
566 for ( SCROW nRow = 0; nRow < nRowCount; nRow++, nIndex++ )
568 ppData[ nIndex ] = NULL;
575 ScChartPositionMap::~ScChartPositionMap()
577 for ( sal_uLong nIndex=0; nIndex < nCount; nIndex++ )
579 delete ppData[nIndex];
581 delete [] ppData;
583 SCCOL j;
584 for ( j=0; j < nColCount; j++ )
586 delete ppColHeader[j];
588 delete [] ppColHeader;
589 SCROW i;
590 for ( i=0; i < nRowCount; i++ )
592 delete ppRowHeader[i];
594 delete [] ppRowHeader;
597 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */