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