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