LibreOffice Module sc (master) 1
chartpos.cxx
Go to the documentation of this file.
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 <chartpos.hxx>
21#include <document.hxx>
22#include <osl/diagnose.h>
23#include <svl/numformat.hxx>
24
25#include <memory>
26#include <utility>
27
28namespace
29{
30 bool lcl_hasValueDataButNoDates( const ScDocument& rDocument, SCCOL nCol, SCROW nRow, SCTAB nTab )
31 {
32 bool bReturn = false;
33 if (rDocument.HasValueData( nCol, nRow, nTab ))
34 {
35 //treat dates like text #i25706#
36 sal_uInt32 nNumberFormat = rDocument.GetNumberFormat( ScAddress( nCol, nRow, nTab ) );
37 SvNumFormatType nType = rDocument.GetFormatTable()->GetType(nNumberFormat);
38 bool bIsDate(nType & SvNumFormatType::DATE);
39 bReturn = !bIsDate;
40 }
41 return bReturn;
42 }
43}
44
46 SCCOL nStartColP, SCROW nStartRowP, SCCOL nEndColP, SCROW nEndRowP) :
47 rDocument( rDoc ),
48 eGlue( ScChartGlue::NA ),
49 nStartCol(0),
50 nStartRow(0),
51 bColHeaders( false ),
52 bRowHeaders( false ),
53 bDummyUpperLeft( false )
54{
55 SetRangeList( ScRange( nStartColP, nStartRowP, nTab, nEndColP, nEndRowP, nTab ) );
57}
58
60 aRangeListRef(std::move( xRangeList )),
61 rDocument( rDoc ),
62 eGlue( ScChartGlue::NA ),
63 nStartCol(0),
64 nStartRow(0),
65 bColHeaders( false ),
66 bRowHeaders( false ),
67 bDummyUpperLeft( false )
68{
69 if ( aRangeListRef.is() )
71}
72
74 aRangeListRef( rPositioner.aRangeListRef ),
75 rDocument(rPositioner.rDocument),
76 eGlue(rPositioner.eGlue),
77 nStartCol(rPositioner.nStartCol),
78 nStartRow(rPositioner.nStartRow),
79 bColHeaders(rPositioner.bColHeaders),
80 bRowHeaders(rPositioner.bRowHeaders),
81 bDummyUpperLeft( rPositioner.bDummyUpperLeft )
82{
83}
84
86{
87}
88
90{
91 aRangeListRef = new ScRangeList( rRange );
93}
94
96{
97 if ( eGlue != ScChartGlue::NA )
98 return;
99 bDummyUpperLeft = false;
100 ScRange* pR;
101 if ( aRangeListRef->size() <= 1 )
102 {
103 if ( !aRangeListRef->empty() )
104 {
105 pR = &aRangeListRef->front();
106 if ( pR->aStart.Tab() == pR->aEnd.Tab() )
108 else
109 eGlue = ScChartGlue::Cols; // several tables column by column
110 nStartCol = pR->aStart.Col();
111 nStartRow = pR->aStart.Row();
112 }
113 else
114 {
116 nStartCol = 0;
117 nStartRow = 0;
118 }
119 return;
120 }
121
122 pR = &aRangeListRef->front();
123 nStartCol = pR->aStart.Col();
124 nStartRow = pR->aStart.Row();
125 SCCOL nMaxCols, nEndCol;
126 SCROW nMaxRows, nEndRow;
127 nMaxCols = nEndCol = 0;
128 nMaxRows = nEndRow = 0;
129
130 // <= so 1 extra pass after last item
131 for ( size_t i = 1, nRanges = aRangeListRef->size(); i <= nRanges; ++i )
132 { // detect spanning/surrounding area etc.
133 SCCOLROW nTmp, n1, n2;
134 if ( (n1 = pR->aStart.Col()) < nStartCol ) nStartCol = static_cast<SCCOL>(n1 );
135 if ( (n2 = pR->aEnd.Col() ) > nEndCol ) nEndCol = static_cast<SCCOL>(n2 );
136 if ( (nTmp = n2 - n1 + 1 ) > nMaxCols ) nMaxCols = static_cast<SCCOL>(nTmp);
137 if ( (n1 = pR->aStart.Row()) < nStartRow ) nStartRow = static_cast<SCROW>(n1 );
138 if ( (n2 = pR->aEnd.Row() ) > nEndRow ) nEndRow = static_cast<SCROW>(n2 );
139 if ( (nTmp = n2 - n1 + 1 ) > nMaxRows ) nMaxRows = static_cast<SCROW>(nTmp);
140
141 // in last pass; i = nRanges so don't use at()
142 if ( i < nRanges )
143 pR = &(*aRangeListRef)[i];
144 }
145 SCCOL nC = nEndCol - nStartCol + 1;
146 if ( nC == 1 )
147 {
149 return;
150 }
151 SCROW nR = nEndRow - nStartRow + 1;
152 if ( nR == 1 )
153 {
155 return;
156 }
157 sal_uLong nCR = static_cast<sal_uLong>(nC) * nR;
158
159 /*
160 TODO:
161 First do it simple without bit masking. A maximum of 8MB could be allocated
162 this way (256 Cols x 32000 Rows). That could be reduced to 2MB by
163 using 2 Bits per entry, but it is faster this way.
164 Another optimization would be to store only used rows/columns in the array, but
165 would mean another iteration of the RangeList indirect access to the array. */
166
167 enum class CellState : sal_uInt8 { Hole, Occupied, Free, Glue };
168 CellState* p;
169 std::unique_ptr<CellState[]> pA(new CellState[ nCR ]);
170 memset( pA.get(), 0, nCR * sizeof(CellState) );
171
172 SCCOL nCol, nCol1, nCol2;
173 SCROW nRow, nRow1, nRow2;
174 for ( size_t i = 0, nRanges = aRangeListRef->size(); i < nRanges; ++i )
175 { // mark selections as used in 2D
176 pR = &(*aRangeListRef)[i];
177 nCol1 = pR->aStart.Col() - nStartCol;
178 nCol2 = pR->aEnd.Col() - nStartCol;
179 nRow1 = pR->aStart.Row() - nStartRow;
180 nRow2 = pR->aEnd.Row() - nStartRow;
181 for ( nCol = nCol1; nCol <= nCol2; nCol++ )
182 {
183 p = pA.get() + static_cast<sal_uLong>(nCol) * nR + nRow1;
184 for ( nRow = nRow1; nRow <= nRow2; nRow++, p++ )
185 *p = CellState::Occupied;
186 }
187 }
188 bool bGlue = true;
189
190 bool bGlueCols = false;
191 for ( nCol = 0; bGlue && nCol < nC; nCol++ )
192 { // iterate columns and try to mark as unused
193 p = pA.get() + static_cast<sal_uLong>(nCol) * nR;
194 for ( nRow = 0; bGlue && nRow < nR; nRow++, p++ )
195 {
196 if ( *p == CellState::Occupied )
197 { // If there's one right in the middle, we can't combine.
198 // If it were at the edge, we could combine, if in this Column
199 // in every set line, one is set.
200 if ( nRow > 0 && nCol > 0 )
201 bGlue = false; // nCol==0 can be DummyUpperLeft
202 else
203 nRow = nR;
204 }
205 else
206 *p = CellState::Free;
207 }
208 if ( bGlue )
209 {
210 p = pA.get() + (((static_cast<sal_uLong>(nCol)+1) * nR) - 1);
211 if (*p == CellState::Free)
212 { // mark column as totally unused
213 *p = CellState::Glue;
214 bGlueCols = true; // one unused column at least
215 }
216 }
217 }
218
219 bool bGlueRows = false;
220 for ( nRow = 0; bGlue && nRow < nR; nRow++ )
221 { // iterate rows and try to mark as unused
222 p = pA.get() + nRow;
223 for ( nCol = 0; bGlue && nCol < nC; nCol++, p+=nR )
224 {
225 if ( *p == CellState::Occupied )
226 {
227 if ( nCol > 0 && nRow > 0 )
228 bGlue = false; // nRow==0 can be DummyUpperLeft
229 else
230 nCol = nC;
231 }
232 else
233 *p = CellState::Free;
234 }
235 if ( bGlue )
236 {
237 p = pA.get() + (((static_cast<sal_uLong>(nC)-1) * nR) + nRow);
238 if (*p == CellState::Free )
239 { // mark row as totally unused
240 *p = CellState::Glue;
241 bGlueRows = true; // one unused row at least
242 }
243 }
244 }
245
246 // If n=1: The upper left corner could be automagically pulled in for labeling
247 p = pA.get() + 1;
248 for ( sal_uLong n = 1; bGlue && n < nCR; n++, p++ )
249 { // An untouched field means we could neither reach it through rows nor columns,
250 // thus we can't combine anything
251 if ( *p == CellState::Hole )
252 bGlue = false;
253 }
254 if ( bGlue )
255 {
256 if ( bGlueCols && bGlueRows )
258 else if ( bGlueRows )
260 else
262 if ( pA[0] != CellState::Occupied )
263 bDummyUpperLeft = true;
264 }
265 else
266 {
268 }
269}
270
272{
273 SCCOL nCol1, nCol2, iCol;
274 SCROW nRow1, nRow2, iRow;
275 SCTAB nTab1, nTab2;
276
277 bool bColStrings = true;
278 bool bRowStrings = true;
279 GlueState();
280 if ( aRangeListRef->size() == 1 )
281 {
282 aRangeListRef->front().GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
283 if ( nCol1 > nCol2 || nRow1 > nRow2 )
284 bColStrings = bRowStrings = false;
285 else
286 {
287 for (iCol=nCol1; iCol<=nCol2 && bColStrings; iCol++)
288 {
289 if (lcl_hasValueDataButNoDates( rDocument, iCol, nRow1, nTab1 ))
290 bColStrings = false;
291 }
292 for (iRow=nRow1; iRow<=nRow2 && bRowStrings; iRow++)
293 {
294 if (lcl_hasValueDataButNoDates( rDocument, nCol1, iRow, nTab1 ))
295 bRowStrings = false;
296 }
297 }
298 }
299 else
300 {
301 bool bVert = (eGlue == ScChartGlue::NONE || eGlue == ScChartGlue::Rows);
302 for ( size_t i = 0, nRanges = aRangeListRef->size();
303 (i < nRanges) && (bColStrings || bRowStrings);
304 ++i
305 )
306 {
307 const ScRange & rR = (*aRangeListRef)[i];
308 rR.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
309 bool bTopRow = (nRow1 == nStartRow);
310 if ( bRowStrings && (bVert || nCol1 == nStartCol) )
311 { // NONE or ROWS: RowStrings in every selection possible
312 // COLS or BOTH: only from first column
313 if ( nCol1 <= nCol2 )
314 for (iRow=nRow1; iRow<=nRow2 && bRowStrings; iRow++)
315 {
316 if (lcl_hasValueDataButNoDates( rDocument, nCol1, iRow, nTab1 ))
317 bRowStrings = false;
318 }
319 }
320 if ( bColStrings && bTopRow )
321 { // ColStrings only from first row
322 if ( nRow1 <= nRow2 )
323 for (iCol=nCol1; iCol<=nCol2 && bColStrings; iCol++)
324 {
325 if (lcl_hasValueDataButNoDates( rDocument, iCol, nRow1, nTab1 ))
326 bColStrings = false;
327 }
328 }
329 }
330 }
331 bColHeaders = bColStrings;
332 bRowHeaders = bRowStrings;
333}
334
336{
338 return pPositionMap.get();
339}
340
342{
344 {
345 pPositionMap.reset();
346 }
347
348 if ( pPositionMap )
349 return ;
350
351 SCSIZE nColAdd = bRowHeaders ? 1 : 0;
352 SCSIZE nRowAdd = bColHeaders ? 1 : 0;
353
354 SCCOL nCol, nCol1, nCol2;
355 SCROW nRow, nRow1, nRow2;
356 SCTAB nTab, nTab1, nTab2;
357
358 // real size (without hidden rows/columns)
359
360 SCSIZE nColCount = 0;
361 SCSIZE nRowCount = 0;
362
363 GlueState();
364
365 const bool bNoGlue = (eGlue == ScChartGlue::NONE);
366 ColumnMap aColMap;
367 SCROW nNoGlueRow = 0;
368 for ( size_t i = 0, nRanges = aRangeListRef->size(); i < nRanges; ++i )
369 {
370 const ScRange & rR = (*aRangeListRef)[i];
371 rR.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
372 for ( nTab = nTab1; nTab <= nTab2; nTab++ )
373 {
374 // nTab in ColKey to allow to have the same col/row in another table
375 sal_uLong nInsCol = (static_cast<sal_uLong>(nTab) << 16) | (bNoGlue ? 0 :
376 static_cast<sal_uLong>(nCol1));
377 for ( nCol = nCol1; nCol <= nCol2; ++nCol, ++nInsCol )
378 {
379 RowMap* pCol = &aColMap[nInsCol];
380
381 // in other table a new ColKey already was created,
382 // the rows must be equal to be filled with Dummy
383 sal_uLong nInsRow = (bNoGlue ? nNoGlueRow : nRow1);
384 for ( nRow = nRow1; nRow <= nRow2; nRow++, nInsRow++ )
385 {
386 if ( pCol->find( nInsRow ) == pCol->end() )
387 {
388 pCol->emplace( nInsRow, std::make_unique<ScAddress>( nCol, nRow, nTab ) );
389 }
390 }
391 }
392 }
393 // For NoGlue: associated tables will be rendered as ColGlue
394 nNoGlueRow += nRow2 - nRow1 + 1;
395 }
396
397 // count of data
398 nColCount = static_cast< SCSIZE >( aColMap.size());
399 if ( !aColMap.empty() )
400 {
401 RowMap& rCol = aColMap.begin()->second;
402 if ( bDummyUpperLeft )
403 rCol[ 0 ] = nullptr; // Dummy for labeling
404 nRowCount = static_cast< SCSIZE >( rCol.size());
405 }
406 else
407 nRowCount = 0;
408 if ( nColCount > 0 )
409 nColCount -= nColAdd;
410 if ( nRowCount > 0 )
411 nRowCount -= nRowAdd;
412
413 if ( nColCount==0 || nRowCount==0 )
414 { // create an entry without data
415 RowMap& rCol = aColMap[0];
416 nColCount = 1;
417 rCol[ 0 ] = nullptr;
418 nRowCount = 1;
419 nColAdd = 0;
420 nRowAdd = 0;
421 }
422 else
423 {
424 if ( bNoGlue )
425 { // fill gaps with Dummies, first column is master
426 RowMap& rFirstCol = aColMap.begin()->second;
427
428 for ( const auto& it1 : rFirstCol )
429 {
430 sal_uLong nKey = it1.first;
431 for (ColumnMap::iterator it2 = ++aColMap.begin(); it2 != aColMap.end(); ++it2 )
432 it2->second.emplace( nKey, nullptr ); // no data
433 }
434 }
435 }
436
437 pPositionMap.reset( new ScChartPositionMap( static_cast<SCCOL>(nColCount), static_cast<SCROW>(nRowCount),
438 static_cast<SCCOL>(nColAdd), static_cast<SCROW>(nRowAdd), aColMap ) );
439}
440
442{
444 pPositionMap.reset();
445}
446
448 SCCOL nColAdd, SCROW nRowAdd, ColumnMap& rCols ) :
449 ppData( new std::unique_ptr<ScAddress> [ nChartCols * nChartRows ] ),
450 ppColHeader( new std::unique_ptr<ScAddress> [ nChartCols ] ),
451 ppRowHeader( new std::unique_ptr<ScAddress> [ nChartRows ] ),
452 nCount( static_cast<sal_uLong>(nChartCols) * nChartRows ),
453 nColCount( nChartCols ),
454 nRowCount( nChartRows )
455{
456 OSL_ENSURE( nColCount && nRowCount, "ScChartPositionMap without dimension" );
457
458 ColumnMap::iterator pColIter = rCols.begin();
459 RowMap& rCol1 = pColIter->second;
460
461 // row header
462 auto pPos1Iter = rCol1.begin();
463 if ( nRowAdd )
464 ++pPos1Iter;
465 if ( nColAdd )
466 { // independent
467 SCROW nRow = 0;
468 for ( ; nRow < nRowCount && pPos1Iter != rCol1.end(); nRow++ )
469 {
470 ppRowHeader[ nRow ] = std::move(pPos1Iter->second);
471 ++pPos1Iter;
472 }
473 }
474 else
475 { // copy
476 SCROW nRow = 0;
477 for ( ; nRow < nRowCount && pPos1Iter != rCol1.end(); nRow++ )
478 {
479 if (pPos1Iter->second)
480 ppRowHeader[ nRow ].reset(new ScAddress( *pPos1Iter->second ));
481 ++pPos1Iter;
482 }
483 }
484 if ( nColAdd )
485 {
486 ++pColIter;
487 }
488
489 // data column by column and column-header
490 sal_uLong nIndex = 0;
491 for ( SCCOL nCol = 0; nCol < nColCount; nCol++ )
492 {
493 if ( pColIter != rCols.end() )
494 {
495 RowMap& rCol2 = pColIter->second;
496 RowMap::iterator pPosIter = rCol2.begin();
497 if ( pPosIter != rCol2.end() )
498 {
499 if ( nRowAdd )
500 {
501 ppColHeader[ nCol ] = std::move(pPosIter->second); // independent
502 ++pPosIter;
503 }
504 else if ( pPosIter->second )
505 ppColHeader[ nCol ].reset( new ScAddress( *pPosIter->second ) );
506 }
507
508 SCROW nRow = 0;
509 for ( ; nRow < nRowCount && pPosIter != rCol2.end(); nRow++, nIndex++ )
510 {
511 ppData[ nIndex ] = std::move(pPosIter->second);
512 ++pPosIter;
513 }
514
515 ++pColIter;
516 }
517 }
518}
519
521{
522}
523
524/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
size_t SCSIZE
size_t typedef to be able to find places where code was changed from USHORT to size_t and is used to ...
Definition: address.hxx:44
ScChartGlue
Definition: chartpos.hxx:92
std::map< SCCOL, RowMap > ColumnMap
Definition: chartpos.hxx:30
std::map< SCROW, std::unique_ptr< ScAddress > > RowMap
Definition: chartpos.hxx:28
SCTAB Tab() const
Definition: address.hxx:283
SCROW Row() const
Definition: address.hxx:274
SCCOL Col() const
Definition: address.hxx:279
std::unique_ptr< std::unique_ptr< ScAddress >[]> ppRowHeader
Definition: chartpos.hxx:38
std::unique_ptr< std::unique_ptr< ScAddress >[]> ppColHeader
Definition: chartpos.hxx:37
ScChartPositionMap(SCCOL nChartCols, SCROW nChartRows, SCCOL nColAdd, SCROW nRowAdd, ColumnMap &rCols)
Definition: chartpos.cxx:447
std::unique_ptr< std::unique_ptr< ScAddress >[]> ppData
Definition: chartpos.hxx:36
ScChartGlue eGlue
Definition: chartpos.hxx:107
void CheckColRowHeaders()
Definition: chartpos.cxx:271
const ScChartPositionMap * GetPositionMap()
Definition: chartpos.cxx:335
void SetRangeList(const ScRange &rNew)
Definition: chartpos.cxx:89
void CreatePositionMap()
Definition: chartpos.cxx:341
ScChartPositioner(ScDocument &rDoc, SCTAB nTab, SCCOL nStartColP, SCROW nStartRowP, SCCOL nEndColP, SCROW nEndRowP)
Definition: chartpos.cxx:45
ScRangeListRef aRangeListRef
Definition: chartpos.hxx:104
void InvalidateGlue()
Definition: chartpos.cxx:441
ScDocument & rDocument
Definition: chartpos.hxx:105
std::unique_ptr< ScChartPositionMap > pPositionMap
Definition: chartpos.hxx:106
SC_DLLPUBLIC sal_uInt32 GetNumberFormat(SCCOL nCol, SCROW nRow, SCTAB nTab) const
Definition: document.cxx:3640
SC_DLLPUBLIC bool HasValueData(SCCOL nCol, SCROW nRow, SCTAB nTab) const
Definition: document.cxx:3750
SC_DLLPUBLIC SvNumberFormatter * GetFormatTable() const
Definition: documen2.cxx:467
void GetVars(SCCOL &nCol1, SCROW &nRow1, SCTAB &nTab1, SCCOL &nCol2, SCROW &nRow2, SCTAB &nTab2) const
Definition: address.hxx:690
ScAddress aEnd
Definition: address.hxx:498
ScAddress aStart
Definition: address.hxx:497
SvNumFormatType GetType(sal_uInt32 nFIndex) const
bool is() const
const int nMaxCols
int nCount
sal_Int32 nIndex
void * p
sal_Int64 n
int n2
int n1
int i
QPRO_FUNC_TYPE nType
Definition: qproform.cxx:398
sal_uIntPtr sal_uLong
unsigned char sal_uInt8
sal_Int32 SCCOLROW
a type capable of holding either SCCOL or SCROW
Definition: types.hxx:23
sal_Int16 SCTAB
Definition: types.hxx:22
sal_Int16 SCCOL
Definition: types.hxx:21
sal_Int32 SCROW
Definition: types.hxx:17
SvNumFormatType