tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / unoobj / chart2uno.cxx
blobaa96806886bb8b7b45f0d945b5d1118d836d90ba
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 <memory>
21 #include <sal/config.h>
22 #include <sal/log.hxx>
24 #include <algorithm>
25 #include <utility>
27 #include <chart2uno.hxx>
28 #include <miscuno.hxx>
29 #include <document.hxx>
30 #include <docsh.hxx>
31 #include <formulacell.hxx>
32 #include <unonames.hxx>
33 #include <globstr.hrc>
34 #include <scresid.hxx>
35 #include <rangeutl.hxx>
36 #include <hints.hxx>
37 #include <unoreflist.hxx>
38 #include <compiler.hxx>
39 #include <reftokenhelper.hxx>
40 #include <chartlis.hxx>
41 #include <tokenuno.hxx>
42 #include <cellvalue.hxx>
43 #include <tokenarray.hxx>
44 #include <scmatrix.hxx>
45 #include <brdcst.hxx>
46 #include <mtvelements.hxx>
48 #include <formula/opcode.hxx>
49 #include <o3tl/safeint.hxx>
50 #include <svl/numformat.hxx>
51 #include <svl/sharedstring.hxx>
53 #include <vcl/svapp.hxx>
55 #include <com/sun/star/beans/UnknownPropertyException.hpp>
56 #include <com/sun/star/chart/ChartDataRowSource.hpp>
57 #include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
58 #include <com/sun/star/frame/XModel.hpp>
59 #include <comphelper/extract.hxx>
60 #include <comphelper/processfactory.hxx>
61 #include <comphelper/sequence.hxx>
63 #include <limits>
65 SC_SIMPLE_SERVICE_INFO( ScChart2DataProvider, u"ScChart2DataProvider"_ustr,
66 u"com.sun.star.chart2.data.DataProvider"_ustr)
67 SC_SIMPLE_SERVICE_INFO( ScChart2DataSource, u"ScChart2DataSource"_ustr,
68 u"com.sun.star.chart2.data.DataSource"_ustr)
69 SC_SIMPLE_SERVICE_INFO( ScChart2DataSequence, u"ScChart2DataSequence"_ustr,
70 u"com.sun.star.chart2.data.DataSequence"_ustr)
72 using namespace ::com::sun::star;
73 using namespace ::formula;
74 using ::com::sun::star::uno::Sequence;
75 using ::std::unique_ptr;
76 using ::std::vector;
77 using ::std::distance;
78 using ::std::shared_ptr;
80 namespace
82 std::span<const SfxItemPropertyMapEntry> lcl_GetDataProviderPropertyMap()
84 static const SfxItemPropertyMapEntry aDataProviderPropertyMap_Impl[] =
86 { SC_UNONAME_INCLUDEHIDDENCELLS, 0, cppu::UnoType<bool>::get(), 0, 0 },
87 { SC_UNONAME_USE_INTERNAL_DATA_PROVIDER, 0, cppu::UnoType<bool>::get(), 0, 0 },
89 return aDataProviderPropertyMap_Impl;
92 std::span<const SfxItemPropertyMapEntry> lcl_GetDataSequencePropertyMap()
94 static const SfxItemPropertyMapEntry aDataSequencePropertyMap_Impl[] =
96 { SC_UNONAME_HIDDENVALUES, 0, cppu::UnoType<uno::Sequence<sal_Int32>>::get(), 0, 0 },
97 { SC_UNONAME_ROLE, 0, cppu::UnoType<css::chart2::data::DataSequenceRole>::get(), 0, 0 },
98 { SC_UNONAME_INCLUDEHIDDENCELLS, 0, cppu::UnoType<bool>::get(), 0, 0 },
100 return aDataSequencePropertyMap_Impl;
103 struct lcl_appendTableNumber
105 explicit lcl_appendTableNumber( OUStringBuffer & rBuffer ) :
106 m_rBuffer( rBuffer )
108 void operator() ( SCTAB nTab )
110 // there is no append with SCTAB or sal_Int16
111 m_rBuffer.append( static_cast< sal_Int32 >( nTab ));
112 m_rBuffer.append( ' ' );
114 private:
115 OUStringBuffer & m_rBuffer;
118 OUString lcl_createTableNumberList( const ::std::vector< SCTAB > & rTableVector )
120 OUStringBuffer aBuffer;
121 ::std::for_each( rTableVector.begin(), rTableVector.end(), lcl_appendTableNumber( aBuffer ));
122 // remove last trailing ' '
123 if( !aBuffer.isEmpty() )
124 aBuffer.setLength( aBuffer.getLength() - 1 );
125 return aBuffer.makeStringAndClear();
128 uno::Reference< frame::XModel > lcl_GetXModel( const ScDocument * pDoc )
130 uno::Reference< frame::XModel > xModel;
131 ScDocShell * pObjSh( pDoc ? pDoc->GetDocumentShell() : nullptr );
132 if( pObjSh )
133 xModel.set( pObjSh->GetModel());
134 return xModel;
137 struct TokenTable
139 SCROW mnRowCount;
140 SCCOL mnColCount;
141 vector<std::unique_ptr<FormulaToken>> maTokens;
143 // noncopyable
144 TokenTable(const TokenTable&) = delete;
145 const TokenTable& operator=(const TokenTable&) = delete;
147 TokenTable()
148 : mnRowCount(0)
149 , mnColCount(0)
153 void init( SCCOL nColCount, SCROW nRowCount )
155 mnColCount = nColCount;
156 mnRowCount = nRowCount;
157 maTokens.reserve(mnColCount*mnRowCount);
159 void clear()
161 for (auto & rToken : maTokens)
162 rToken.reset();
165 void push_back( std::unique_ptr<FormulaToken> pToken )
167 maTokens.push_back( std::move(pToken) );
168 OSL_ENSURE( maTokens.size()<= o3tl::make_unsigned( mnColCount*mnRowCount ), "too many tokens" );
171 sal_uInt32 getIndex(SCCOL nCol, SCROW nRow) const
173 OSL_ENSURE( nCol<mnColCount, "wrong column index" );
174 OSL_ENSURE( nRow<mnRowCount, "wrong row index" );
175 sal_uInt32 nRet = static_cast<sal_uInt32>(nCol*mnRowCount + nRow);
176 OSL_ENSURE( maTokens.size()>= o3tl::make_unsigned( mnColCount*mnRowCount ), "too few tokens" );
177 return nRet;
180 vector<ScTokenRef> getColRanges(const ScDocument* pDoc, SCCOL nCol) const;
181 vector<ScTokenRef> getRowRanges(const ScDocument* pDoc, SCROW nRow) const;
182 vector<ScTokenRef> getAllRanges(const ScDocument* pDoc) const;
185 vector<ScTokenRef> TokenTable::getColRanges(const ScDocument* pDoc, SCCOL nCol) const
187 if (nCol >= mnColCount)
188 return vector<ScTokenRef>();
189 if( mnRowCount<=0 )
190 return vector<ScTokenRef>();
192 vector<ScTokenRef> aTokens;
193 sal_uInt32 nLast = getIndex(nCol, mnRowCount-1);
194 for (sal_uInt32 i = getIndex(nCol, 0); i <= nLast; ++i)
196 FormulaToken* p = maTokens[i].get();
197 if (!p)
198 continue;
200 ScTokenRef pCopy(p->Clone());
201 ScRefTokenHelper::join(pDoc, aTokens, pCopy, ScAddress());
203 return aTokens;
206 vector<ScTokenRef> TokenTable::getRowRanges(const ScDocument* pDoc, SCROW nRow) const
208 if (nRow >= mnRowCount)
209 return vector<ScTokenRef>();
210 if( mnColCount<=0 )
211 return vector<ScTokenRef>();
213 vector<ScTokenRef> aTokens;
214 sal_uInt32 nLast = getIndex(mnColCount-1, nRow);
215 for (sal_uInt32 i = getIndex(0, nRow); i <= nLast; i += mnRowCount)
217 FormulaToken* p = maTokens[i].get();
218 if (!p)
219 continue;
221 ScTokenRef p2(p->Clone());
222 ScRefTokenHelper::join(pDoc, aTokens, p2, ScAddress());
224 return aTokens;
227 vector<ScTokenRef> TokenTable::getAllRanges(const ScDocument* pDoc) const
229 vector<ScTokenRef> aTokens;
230 sal_uInt32 nStop = mnColCount*mnRowCount;
231 for (sal_uInt32 i = 0; i < nStop; i++)
233 FormulaToken* p = maTokens[i].get();
234 if (!p)
235 continue;
237 ScTokenRef p2(p->Clone());
238 ScRefTokenHelper::join(pDoc, aTokens, p2, ScAddress());
240 return aTokens;
243 typedef std::map<SCROW, std::unique_ptr<FormulaToken>> FormulaTokenMap;
244 typedef std::map<sal_uInt32, FormulaTokenMap> FormulaTokenMapMap;
246 class Chart2PositionMap
248 public:
249 Chart2PositionMap(SCCOL nColCount, SCROW nRowCount,
250 bool bFillRowHeader, bool bFillColumnHeader, FormulaTokenMapMap& rCols,
251 ScDocument* pDoc );
252 ~Chart2PositionMap();
254 SCCOL getDataColCount() const { return mnDataColCount; }
255 SCROW getDataRowCount() const { return mnDataRowCount; }
257 vector<ScTokenRef> getLeftUpperCornerRanges() const;
258 vector<ScTokenRef> getAllColHeaderRanges() const;
259 vector<ScTokenRef> getAllRowHeaderRanges() const;
261 vector<ScTokenRef> getColHeaderRanges(SCCOL nChartCol) const;
262 vector<ScTokenRef> getRowHeaderRanges(SCROW nChartRow) const;
264 vector<ScTokenRef> getDataColRanges(SCCOL nCol) const;
265 vector<ScTokenRef> getDataRowRanges(SCROW nRow) const;
267 private:
268 const ScDocument* mpDoc;
269 SCCOL mnDataColCount;
270 SCROW mnDataRowCount;
272 TokenTable maLeftUpperCorner; //nHeaderColCount*nHeaderRowCount
273 TokenTable maColHeaders; //mnDataColCount*nHeaderRowCount
274 TokenTable maRowHeaders; //nHeaderColCount*mnDataRowCount
275 TokenTable maData;//mnDataColCount*mnDataRowCount
278 Chart2PositionMap::Chart2PositionMap(SCCOL nAllColCount, SCROW nAllRowCount,
279 bool bFillRowHeader, bool bFillColumnHeader, FormulaTokenMapMap& rCols, ScDocument* pDoc)
281 mpDoc = pDoc;
282 // if bFillRowHeader is true, at least the first column serves as a row header.
283 // If more than one column is pure text all the first pure text columns are used as header.
284 // Likewise, if bFillColumnHeader is true, at least the first row serves as a column header.
285 // If more than one row is pure text all the first pure text rows are used as header.
287 SCROW nHeaderRowCount = (bFillColumnHeader && nAllColCount && nAllRowCount) ? 1 : 0;
288 SCCOL nHeaderColCount = (bFillRowHeader && nAllColCount && nAllRowCount) ? 1 : 0;
290 if( pDoc && (nHeaderColCount || nHeaderRowCount ) )
292 //check whether there is more than one text column or row that should be added to the headers
293 SCROW nMaxHeaderRow = nAllRowCount;
294 SCCOL nCol = 0;
295 for (auto it = rCols.begin(); it != rCols.end(); ++it, ++nCol)
297 // Skip header columns
298 if (nCol < nHeaderColCount)
299 continue;
301 const auto& rCol = *it;
303 bool bFoundValuesInCol = false;
304 bool bFoundAnythingInCol = false;
305 SCROW nRow = 0;
306 for (auto it2 = rCol.second.begin(); it2 != rCol.second.end(); ++it2, ++nRow)
308 const auto& rCell = *it2;
310 // Skip header rows
311 if (nRow < nHeaderRowCount || !rCell.second)
312 continue;
314 ScRange aRange;
315 bool bExternal = false;
316 StackVar eType = rCell.second->GetType();
317 if( eType==svExternal || eType==svExternalSingleRef || eType==svExternalDoubleRef || eType==svExternalName )
318 bExternal = true;//lllll todo correct?
319 ScTokenRef pSharedToken(rCell.second->Clone());
320 ScRefTokenHelper::getRangeFromToken(pDoc, aRange, pSharedToken, ScAddress(), bExternal);
321 SCCOL nCol1=0, nCol2=0;
322 SCROW nRow1=0, nRow2=0;
323 SCTAB nTab1=0, nTab2=0;
324 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
325 if ( pDoc->HasValueData( nCol1, nRow1, nTab1 ) )
327 // Found some numeric data
328 bFoundValuesInCol = true;
329 nMaxHeaderRow = std::min(nMaxHeaderRow, nRow);
330 break;
332 if ( pDoc->HasData( nCol1, nRow1, nTab1 ) )
334 // Found some other data (non-numeric)
335 bFoundAnythingInCol = true;
337 else
339 // If cell is empty, it belongs to data
340 nMaxHeaderRow = std::min(nMaxHeaderRow, nRow);
344 if (nHeaderColCount && !bFoundValuesInCol && bFoundAnythingInCol && nCol == nHeaderColCount)
346 // There is no values in row, but some data. And this column is next to header
347 // So let's put it to header
348 nHeaderColCount++;
352 if (nHeaderRowCount)
354 nHeaderRowCount = nMaxHeaderRow;
358 mnDataColCount = nAllColCount - nHeaderColCount;
359 mnDataRowCount = nAllRowCount - nHeaderRowCount;
361 maLeftUpperCorner.init(nHeaderColCount,nHeaderRowCount);
362 maColHeaders.init(mnDataColCount,nHeaderRowCount);
363 maRowHeaders.init(nHeaderColCount,mnDataRowCount);
364 maData.init(mnDataColCount,mnDataRowCount);
366 FormulaTokenMapMap::iterator it1 = rCols.begin();
367 for (SCCOL nCol = 0; nCol < nAllColCount; ++nCol)
369 if (it1 != rCols.end())
371 FormulaTokenMap& rCol = it1->second;
372 FormulaTokenMap::iterator it2 = rCol.begin();
373 for (SCROW nRow = 0; nRow < nAllRowCount; ++nRow)
375 std::unique_ptr<FormulaToken> pToken;
376 if (it2 != rCol.end())
378 pToken = std::move(it2->second);
379 ++it2;
382 if( nCol < nHeaderColCount )
384 if( nRow < nHeaderRowCount )
385 maLeftUpperCorner.push_back(std::move(pToken));
386 else
387 maRowHeaders.push_back(std::move(pToken));
389 else if( nRow < nHeaderRowCount )
390 maColHeaders.push_back(std::move(pToken));
391 else
392 maData.push_back(std::move(pToken));
394 ++it1;
399 Chart2PositionMap::~Chart2PositionMap()
401 maLeftUpperCorner.clear();
402 maColHeaders.clear();
403 maRowHeaders.clear();
404 maData.clear();
407 vector<ScTokenRef> Chart2PositionMap::getLeftUpperCornerRanges() const
409 return maLeftUpperCorner.getAllRanges(mpDoc);
411 vector<ScTokenRef> Chart2PositionMap::getAllColHeaderRanges() const
413 return maColHeaders.getAllRanges(mpDoc);
415 vector<ScTokenRef> Chart2PositionMap::getAllRowHeaderRanges() const
417 return maRowHeaders.getAllRanges(mpDoc);
419 vector<ScTokenRef> Chart2PositionMap::getColHeaderRanges(SCCOL nCol) const
421 return maColHeaders.getColRanges(mpDoc, nCol);
423 vector<ScTokenRef> Chart2PositionMap::getRowHeaderRanges(SCROW nRow) const
425 return maRowHeaders.getRowRanges(mpDoc, nRow);
428 vector<ScTokenRef> Chart2PositionMap::getDataColRanges(SCCOL nCol) const
430 return maData.getColRanges(mpDoc, nCol);
433 vector<ScTokenRef> Chart2PositionMap::getDataRowRanges(SCROW nRow) const
435 return maData.getRowRanges(mpDoc, nRow);
439 * Designed to be a drop-in replacement for ScChartPositioner, in order to
440 * handle external references.
442 class Chart2Positioner
444 enum GlueType
446 GLUETYPE_NA,
447 GLUETYPE_NONE,
448 GLUETYPE_COLS,
449 GLUETYPE_ROWS,
450 GLUETYPE_BOTH
453 public:
454 Chart2Positioner(const Chart2Positioner&) = delete;
455 const Chart2Positioner& operator=(const Chart2Positioner&) = delete;
457 Chart2Positioner(ScDocument* pDoc, const vector<ScTokenRef>& rRefTokens) :
458 mrRefTokens(rRefTokens),
459 meGlue(GLUETYPE_NA),
460 mnStartCol(0),
461 mnStartRow(0),
462 mpDoc(pDoc),
463 mbColHeaders(false),
464 mbRowHeaders(false),
465 mbDummyUpperLeft(false)
469 void setHeaders(bool bColHeaders, bool bRowHeaders)
471 mbColHeaders = bColHeaders;
472 mbRowHeaders = bRowHeaders;
475 Chart2PositionMap* getPositionMap()
477 createPositionMap();
478 return mpPositionMap.get();
481 private:
482 void invalidateGlue();
483 void glueState();
484 void calcGlueState(SCCOL nCols, SCROW nRows);
485 void createPositionMap();
487 private:
488 const vector<ScTokenRef>& mrRefTokens;
489 std::unique_ptr<Chart2PositionMap> mpPositionMap;
490 GlueType meGlue;
491 SCCOL mnStartCol;
492 SCROW mnStartRow;
493 ScDocument* mpDoc;
494 bool mbColHeaders:1;
495 bool mbRowHeaders:1;
496 bool mbDummyUpperLeft:1;
499 void Chart2Positioner::invalidateGlue()
501 meGlue = GLUETYPE_NA;
502 mpPositionMap.reset();
505 void Chart2Positioner::glueState()
507 if (meGlue != GLUETYPE_NA)
508 return;
510 mbDummyUpperLeft = false;
511 if (mrRefTokens.size() <= 1)
513 // Source data consists of only one data range.
514 const ScTokenRef& p = mrRefTokens.front();
515 ScComplexRefData aData;
516 if (ScRefTokenHelper::getDoubleRefDataFromToken(aData, p))
518 if (aData.Ref1.Tab() == aData.Ref2.Tab())
519 meGlue = GLUETYPE_NONE;
520 else
521 meGlue = GLUETYPE_COLS;
522 mnStartCol = aData.Ref1.Col();
523 mnStartRow = aData.Ref1.Row();
525 else
527 invalidateGlue();
528 mnStartCol = 0;
529 mnStartRow = 0;
531 return;
534 ScComplexRefData aData;
535 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, mrRefTokens.front()))
537 SAL_WARN("sc", "Chart2Positioner::glueState getDoubleRefDataFromToken failed");
538 invalidateGlue();
539 mnStartCol = 0;
540 mnStartRow = 0;
541 return;
543 mnStartCol = aData.Ref1.Col();
544 mnStartRow = aData.Ref1.Row();
546 SCCOL nEndCol = 0;
547 SCROW nEndRow = 0;
548 for (const auto& rxToken : mrRefTokens)
550 ScRefTokenHelper::getDoubleRefDataFromToken(aData, rxToken);
551 SCCOLROW n1 = aData.Ref1.Col();
552 SCCOLROW n2 = aData.Ref2.Col();
553 if (n1 > mpDoc->MaxCol())
554 n1 = mpDoc->MaxCol();
555 if (n2 > mpDoc->MaxCol())
556 n2 = mpDoc->MaxCol();
557 if (n1 < mnStartCol)
558 mnStartCol = static_cast<SCCOL>(n1);
559 if (n2 > nEndCol)
560 nEndCol = static_cast<SCCOL>(n2);
562 n1 = aData.Ref1.Row();
563 n2 = aData.Ref2.Row();
564 if (n1 > mpDoc->MaxRow())
565 n1 = mpDoc->MaxRow();
566 if (n2 > mpDoc->MaxRow())
567 n2 = mpDoc->MaxRow();
569 if (n1 < mnStartRow)
570 mnStartRow = static_cast<SCROW>(n1);
571 if (n2 > nEndRow)
572 nEndRow = static_cast<SCROW>(n2);
575 if (mnStartCol == nEndCol)
577 // All source data is in a single column.
578 meGlue = GLUETYPE_ROWS;
579 return;
582 if (mnStartRow == nEndRow)
584 // All source data is in a single row.
585 meGlue = GLUETYPE_COLS;
586 return;
589 // total column size
590 SCCOL nC = nEndCol - mnStartCol + 1;
592 // total row size
593 SCROW nR = nEndRow - mnStartRow + 1;
595 // #i103540# prevent invalid vector size
596 if ((nC <= 0) || (nR <= 0))
598 invalidateGlue();
599 mnStartCol = 0;
600 mnStartRow = 0;
601 return;
604 calcGlueState(nC, nR);
607 enum State { Hole = 0, Occupied = 1, Free = 2, Glue = 3 };
609 void Chart2Positioner::calcGlueState(SCCOL nColSize, SCROW nRowSize)
611 // TODO: This code can use some space optimization. Using an array to
612 // store individual cell's states is terribly inefficient esp for large
613 // data ranges; let's use flat_segment_tree to reduce memory usage here.
615 sal_uInt32 nCR = static_cast<sal_uInt32>(nColSize*nRowSize);
617 vector<State> aCellStates(nCR, Hole);
619 // Mark all referenced cells "occupied".
620 for (const auto& rxToken : mrRefTokens)
622 ScComplexRefData aData;
623 ScRefTokenHelper::getDoubleRefDataFromToken(aData, rxToken);
624 auto nCol1 = aData.Ref1.Col() - mnStartCol;
625 auto nCol2 = aData.Ref2.Col() - mnStartCol;
626 SCROW nRow1 = aData.Ref1.Row() - mnStartRow;
627 SCROW nRow2 = aData.Ref2.Row() - mnStartRow;
628 for (SCCOLROW nCol = nCol1; nCol <= nCol2 && nCol >= 0; ++nCol)
629 for (SCCOLROW nRow = nRow1; nRow <= nRow2 && nRow >= 0; ++nRow)
631 size_t i = nCol*nRowSize + nRow;
632 aCellStates[i] = Occupied;
636 // If at least one cell in either the first column or first row is empty,
637 // we don't glue at all unless the whole column or row is empty; we expect
638 // all cells in the first column / row to be fully populated. If we have
639 // empty column or row, then we do glue by the column or row,
640 // respectively.
642 bool bGlue = true;
643 bool bGlueCols = false;
644 for (auto nCol = 0; bGlue && nCol < nColSize; ++nCol)
646 for (SCROW nRow = 0; bGlue && nRow < nRowSize; ++nRow)
648 size_t i = nCol*nRowSize + nRow;
649 if (aCellStates[i] == Occupied)
651 if (nCol == 0 || nRow == 0)
652 break;
654 bGlue = false;
656 else
657 aCellStates[i] = Free;
659 size_t nLast = (nCol+1)*nRowSize - 1; // index for the last cell in the column.
660 if (bGlue && aCellStates[nLast] == Free)
662 // Whole column is empty.
663 aCellStates[nLast] = Glue;
664 bGlueCols = true;
668 bool bGlueRows = false;
669 for (SCROW nRow = 0; bGlue && nRow < nRowSize; ++nRow)
671 size_t i = nRow;
672 for (SCCOL nCol = 0; bGlue && nCol < nColSize; ++nCol, i += nRowSize)
674 if (aCellStates[i] == Occupied)
676 if (nCol == 0 || nRow == 0)
677 break;
679 bGlue = false;
681 else
682 aCellStates[i] = Free;
684 i = (nColSize-1)*nRowSize + nRow; // index for the row position in the last column.
685 if (bGlue && aCellStates[i] == Free)
687 // Whole row is empty.
688 aCellStates[i] = Glue;
689 bGlueRows = true;
693 size_t i = 1;
694 for (sal_uInt32 n = 1; bGlue && n < nCR; ++n, ++i)
695 if (aCellStates[i] == Hole)
696 bGlue = false;
698 if (bGlue)
700 if (bGlueCols && bGlueRows)
701 meGlue = GLUETYPE_BOTH;
702 else if (bGlueRows)
703 meGlue = GLUETYPE_ROWS;
704 else
705 meGlue = GLUETYPE_COLS;
706 if (aCellStates.front() != Occupied)
707 mbDummyUpperLeft = true;
709 else
710 meGlue = GLUETYPE_NONE;
713 void Chart2Positioner::createPositionMap()
715 if (meGlue == GLUETYPE_NA && mpPositionMap)
716 mpPositionMap.reset();
718 if (mpPositionMap)
719 return;
721 glueState();
723 bool bNoGlue = (meGlue == GLUETYPE_NONE);
724 FormulaTokenMapMap aCols;
725 SCROW nNoGlueRow = 0;
726 for (const ScTokenRef& pToken : mrRefTokens)
728 bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
729 sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
730 svl::SharedString aTabName = svl::SharedString::getEmptyString();
731 if (bExternal)
732 aTabName = pToken->GetString();
734 ScComplexRefData aData;
735 if( !ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken) )
736 break;
737 const ScSingleRefData& s = aData.Ref1;
738 const ScSingleRefData& e = aData.Ref2;
739 SCCOL nCol1 = s.Col(), nCol2 = e.Col();
740 SCROW nRow1 = s.Row(), nRow2 = e.Row();
741 SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
743 for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
745 assert (nCol1 >= 0);
746 sal_uInt32 nInsCol = bNoGlue ? 0 : static_cast<sal_uInt32>(nCol1) & 0xFFFF;
748 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol, ++nInsCol)
750 // columns on secondary sheets are appended; we treat them as if
751 // all columns are on the same sheet. TODO: We can't assume that
752 // the column range is 16-bit; remove that restriction.
753 sal_uInt32 nInsKey = (static_cast<sal_uInt32>(nTab) << 16) | nInsCol;
754 FormulaTokenMap& rCol = aCols[nInsKey];
756 auto nInsRow = bNoGlue ? nNoGlueRow : nRow1;
757 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow, ++nInsRow)
759 ScSingleRefData aCellData;
760 aCellData.InitFlags();
761 aCellData.SetFlag3D(true);
762 aCellData.SetColRel(false);
763 aCellData.SetRowRel(false);
764 aCellData.SetTabRel(false);
765 aCellData.SetAbsCol(nCol);
766 aCellData.SetAbsRow(nRow);
767 aCellData.SetAbsTab(nTab);
769 auto& rCell = rCol[nInsRow];
770 if (!rCell)
772 if (bExternal)
773 rCell.reset(new ScExternalSingleRefToken(nFileId, aTabName, aCellData));
774 else
775 rCell.reset(new ScSingleRefToken(mpDoc->GetSheetLimits(), aCellData));
780 nNoGlueRow += nRow2 - nRow1 + 1;
783 bool bFillRowHeader = mbRowHeaders;
784 bool bFillColumnHeader = mbColHeaders;
786 SCSIZE nAllColCount = static_cast<SCSIZE>(aCols.size());
787 SCSIZE nAllRowCount = 0;
788 if (!aCols.empty())
790 FormulaTokenMap& rCol = aCols.begin()->second;
791 if (mbDummyUpperLeft)
792 rCol.try_emplace( 0, nullptr ); // dummy for labeling
793 nAllRowCount = static_cast<SCSIZE>(rCol.size());
796 if( nAllColCount!=0 && nAllRowCount!=0 )
798 if (bNoGlue)
800 FormulaTokenMap& rFirstCol = aCols.begin()->second;
801 for (const auto& rFirstColEntry : rFirstCol)
803 SCROW nKey = rFirstColEntry.first;
804 for (auto& rEntry : aCols)
806 FormulaTokenMap& rCol = rEntry.second;
807 rCol.try_emplace( nKey, nullptr );
812 mpPositionMap.reset(
813 new Chart2PositionMap(
814 static_cast<SCCOL>(nAllColCount), static_cast<SCROW>(nAllRowCount),
815 bFillRowHeader, bFillColumnHeader, aCols, mpDoc));
819 * Function object to create a range string from a token list.
821 class Tokens2RangeString
823 public:
824 Tokens2RangeString(ScDocument& rDoc, FormulaGrammar::Grammar eGram, sal_Unicode cRangeSep) :
825 mpRangeStr(std::make_shared<OUStringBuffer>()),
826 mpDoc(&rDoc),
827 meGrammar(eGram),
828 mcRangeSep(cRangeSep),
829 mbFirst(true)
833 void operator() (const ScTokenRef& rToken)
835 ScCompiler aCompiler(*mpDoc, ScAddress(0,0,0), meGrammar);
836 OUString aStr;
837 aCompiler.CreateStringFromToken(aStr, rToken.get());
838 if (mbFirst)
839 mbFirst = false;
840 else
841 mpRangeStr->append(mcRangeSep);
842 mpRangeStr->append(aStr);
845 void getString(OUString& rStr)
847 rStr = mpRangeStr->makeStringAndClear();
850 private:
851 shared_ptr<OUStringBuffer> mpRangeStr;
852 ScDocument* mpDoc;
853 FormulaGrammar::Grammar meGrammar;
854 sal_Unicode mcRangeSep;
855 bool mbFirst;
859 * Function object to convert a list of tokens into a string form suitable
860 * for ODF export. In ODF, a range is expressed as
862 * (start cell address):(end cell address)
864 * and each address doesn't include any '$' symbols.
866 class Tokens2RangeStringXML
868 public:
869 explicit Tokens2RangeStringXML(ScDocument& rDoc) :
870 mpRangeStr(std::make_shared<OUStringBuffer>()),
871 mpDoc(&rDoc),
872 mbFirst(true)
876 void operator() (const ScTokenRef& rToken)
878 if (mbFirst)
879 mbFirst = false;
880 else
881 mpRangeStr->append(mcRangeSep);
883 ScTokenRef aStart, aEnd;
884 bool bValidToken = splitRangeToken(*mpDoc, rToken, aStart, aEnd);
885 // Check there is a valid reference in named range
886 if (!bValidToken && rToken->GetType() == svIndex && rToken->GetOpCode() == ocName)
888 ScRangeData* pNameRange = mpDoc->FindRangeNameBySheetAndIndex(rToken->GetSheet(), rToken->GetIndex());
889 if (pNameRange->HasReferences())
891 const ScTokenRef aTempToken = pNameRange->GetCode()->FirstToken();
892 bValidToken = splitRangeToken(*mpDoc, aTempToken, aStart, aEnd);
896 OSL_ENSURE(bValidToken, "invalid token");
897 if (!bValidToken)
898 return;
900 ScCompiler aCompiler(*mpDoc, ScAddress(0,0,0), FormulaGrammar::GRAM_ENGLISH);
902 OUString aStr;
903 aCompiler.CreateStringFromToken(aStr, aStart.get());
904 mpRangeStr->append(aStr);
906 mpRangeStr->append(mcAddrSep);
908 OUString aStr;
909 aCompiler.CreateStringFromToken(aStr, aEnd.get());
910 mpRangeStr->append(aStr);
914 void getString(OUString& rStr)
916 rStr = mpRangeStr->makeStringAndClear();
919 private:
920 static bool splitRangeToken(const ScDocument& rDoc, const ScTokenRef& pToken, ScTokenRef& rStart, ScTokenRef& rEnd)
922 ScComplexRefData aData;
923 bool bIsRefToken = ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken);
924 OSL_ENSURE(bIsRefToken, "invalid token");
925 if (!bIsRefToken)
926 return false;
927 bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
928 sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
929 const svl::SharedString aTabName = bExternal ? pToken->GetString() : svl::SharedString::getEmptyString();
931 // In saving to XML, we don't prepend address with '$'.
932 setRelative(aData.Ref1);
933 setRelative(aData.Ref2);
935 // In XML, the range must explicitly specify sheet name.
936 aData.Ref1.SetFlag3D(true);
937 aData.Ref2.SetFlag3D(true);
939 if (bExternal)
940 rStart.reset(new ScExternalSingleRefToken(nFileId, aTabName, aData.Ref1));
941 else
942 rStart.reset(new ScSingleRefToken(rDoc.GetSheetLimits(), aData.Ref1));
944 if (bExternal)
945 rEnd.reset(new ScExternalSingleRefToken(nFileId, aTabName, aData.Ref2));
946 else
947 rEnd.reset(new ScSingleRefToken(rDoc.GetSheetLimits(), aData.Ref2));
948 return true;
951 static void setRelative(ScSingleRefData& rData)
953 rData.SetColRel(true);
954 rData.SetRowRel(true);
955 rData.SetTabRel(true);
958 private:
959 shared_ptr<OUStringBuffer> mpRangeStr;
960 ScDocument* mpDoc;
961 static const sal_Unicode mcRangeSep = ' ';
962 static const sal_Unicode mcAddrSep = ':';
963 bool mbFirst;
966 void lcl_convertTokensToString(OUString& rStr, const vector<ScTokenRef>& rTokens, ScDocument& rDoc)
968 const sal_Unicode cRangeSep = ScCompiler::GetNativeSymbolChar(ocSep);
969 FormulaGrammar::Grammar eGrammar = rDoc.GetGrammar();
970 Tokens2RangeString func(rDoc, eGrammar, cRangeSep);
971 func = ::std::for_each(rTokens.begin(), rTokens.end(), func);
972 func.getString(rStr);
975 } // anonymous namespace
977 // DataProvider ==============================================================
979 ScChart2DataProvider::ScChart2DataProvider( ScDocument* pDoc )
980 : m_pDocument( pDoc)
981 , m_aPropSet(lcl_GetDataProviderPropertyMap())
982 , m_bIncludeHiddenCells( true)
984 if ( m_pDocument )
985 m_pDocument->AddUnoObject( *this);
988 ScChart2DataProvider::~ScChart2DataProvider()
990 SolarMutexGuard g;
992 if ( m_pDocument )
993 m_pDocument->RemoveUnoObject( *this);
996 void ScChart2DataProvider::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
998 if ( rHint.GetId() == SfxHintId::Dying )
1000 m_pDocument = nullptr;
1004 sal_Bool SAL_CALL ScChart2DataProvider::createDataSourcePossible( const uno::Sequence< beans::PropertyValue >& aArguments )
1006 SolarMutexGuard aGuard;
1007 if( ! m_pDocument )
1008 return false;
1010 OUString aRangeRepresentation;
1011 for(const auto& rArgument : aArguments)
1013 if ( rArgument.Name == "CellRangeRepresentation" )
1015 rArgument.Value >>= aRangeRepresentation;
1019 vector<ScTokenRef> aTokens;
1020 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1021 ScRefTokenHelper::compileRangeRepresentation(
1022 aTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
1023 return !aTokens.empty();
1026 namespace
1029 uno::Reference< chart2::data::XLabeledDataSequence > lcl_createLabeledDataSequenceFromTokens(
1030 vector< ScTokenRef > && aValueTokens, vector< ScTokenRef > && aLabelTokens,
1031 ScDocument* pDoc, bool bIncludeHiddenCells )
1033 uno::Reference< chart2::data::XLabeledDataSequence > xResult;
1034 bool bHasValues = !aValueTokens.empty();
1035 bool bHasLabel = !aLabelTokens.empty();
1036 if( bHasValues || bHasLabel )
1040 const uno::Reference< uno::XComponentContext >& xContext( ::comphelper::getProcessComponentContext() );
1041 if ( xContext.is() )
1043 xResult.set( chart2::data::LabeledDataSequence::create(xContext), uno::UNO_QUERY_THROW );
1045 if ( bHasValues )
1047 uno::Reference< chart2::data::XDataSequence > xSeq( new ScChart2DataSequence( pDoc, std::move(aValueTokens), bIncludeHiddenCells ) );
1048 xResult->setValues( xSeq );
1050 if ( bHasLabel )
1052 //Labels should always include hidden cells, regardless of the bIncludeHiddenCells setting
1053 uno::Reference< chart2::data::XDataSequence > xLabelSeq( new ScChart2DataSequence( pDoc, std::move(aLabelTokens), true ) );
1054 xResult->setLabel( xLabelSeq );
1057 catch( const uno::Exception& )
1061 return xResult;
1065 * Check the current list of reference tokens, and add the upper left
1066 * corner of the minimum range that encloses all ranges if certain
1067 * conditions are met.
1069 * @param rRefTokens list of reference tokens
1071 * @return true if the corner was added, false otherwise.
1073 bool lcl_addUpperLeftCornerIfMissing(const ScDocument* pDoc, vector<ScTokenRef>& rRefTokens,
1074 SCROW nCornerRowCount, SCCOL nCornerColumnCount)
1076 using ::std::max;
1077 using ::std::min;
1079 if (rRefTokens.empty())
1080 return false;
1082 SCCOL nMinCol = pDoc->GetSheetLimits().GetMaxColCount();
1083 SCROW nMinRow = pDoc->GetSheetLimits().GetMaxRowCount();
1084 SCCOL nMaxCol = 0;
1085 SCROW nMaxRow = 0;
1086 SCTAB nTab = 0;
1088 sal_uInt16 nFileId = 0;
1089 svl::SharedString aExtTabName;
1090 bool bExternal = false;
1092 vector<ScTokenRef>::const_iterator itr = rRefTokens.begin(), itrEnd = rRefTokens.end();
1094 // Get the first ref token.
1095 ScTokenRef pToken = *itr;
1096 switch (pToken->GetType())
1098 case svSingleRef:
1100 const ScSingleRefData& rData = *pToken->GetSingleRef();
1101 nMinCol = rData.Col();
1102 nMinRow = rData.Row();
1103 nMaxCol = rData.Col();
1104 nMaxRow = rData.Row();
1105 nTab = rData.Tab();
1107 break;
1108 case svDoubleRef:
1110 const ScComplexRefData& rData = *pToken->GetDoubleRef();
1111 nMinCol = min(rData.Ref1.Col(), rData.Ref2.Col());
1112 nMinRow = min(rData.Ref1.Row(), rData.Ref2.Row());
1113 nMaxCol = max(rData.Ref1.Col(), rData.Ref2.Col());
1114 nMaxRow = max(rData.Ref1.Row(), rData.Ref2.Row());
1115 nTab = rData.Ref1.Tab();
1117 break;
1118 case svExternalSingleRef:
1120 const ScSingleRefData& rData = *pToken->GetSingleRef();
1121 nMinCol = rData.Col();
1122 nMinRow = rData.Row();
1123 nMaxCol = rData.Col();
1124 nMaxRow = rData.Row();
1125 nTab = rData.Tab();
1126 nFileId = pToken->GetIndex();
1127 aExtTabName = pToken->GetString();
1128 bExternal = true;
1130 break;
1131 case svExternalDoubleRef:
1133 const ScComplexRefData& rData = *pToken->GetDoubleRef();
1134 nMinCol = min(rData.Ref1.Col(), rData.Ref2.Col());
1135 nMinRow = min(rData.Ref1.Row(), rData.Ref2.Row());
1136 nMaxCol = max(rData.Ref1.Col(), rData.Ref2.Col());
1137 nMaxRow = max(rData.Ref1.Row(), rData.Ref2.Row());
1138 nTab = rData.Ref1.Tab();
1139 nFileId = pToken->GetIndex();
1140 aExtTabName = pToken->GetString();
1141 bExternal = true;
1143 break;
1144 default:
1148 // Determine the minimum range enclosing all data ranges. Also make sure
1149 // that they are all on the same table.
1151 for (++itr; itr != itrEnd; ++itr)
1153 pToken = *itr;
1154 switch (pToken->GetType())
1156 case svSingleRef:
1158 const ScSingleRefData& rData = *pToken->GetSingleRef();
1160 nMinCol = min(nMinCol, rData.Col());
1161 nMinRow = min(nMinRow, rData.Row());
1162 nMaxCol = max(nMaxCol, rData.Col());
1163 nMaxRow = max(nMaxRow, rData.Row());
1164 if (nTab != rData.Tab() || bExternal)
1165 return false;
1167 break;
1168 case svDoubleRef:
1170 const ScComplexRefData& rData = *pToken->GetDoubleRef();
1172 nMinCol = min(nMinCol, rData.Ref1.Col());
1173 nMinCol = min(nMinCol, rData.Ref2.Col());
1174 nMinRow = min(nMinRow, rData.Ref1.Row());
1175 nMinRow = min(nMinRow, rData.Ref2.Row());
1177 nMaxCol = max(nMaxCol, rData.Ref1.Col());
1178 nMaxCol = max(nMaxCol, rData.Ref2.Col());
1179 nMaxRow = max(nMaxRow, rData.Ref1.Row());
1180 nMaxRow = max(nMaxRow, rData.Ref2.Row());
1182 if (nTab != rData.Ref1.Tab() || bExternal)
1183 return false;
1185 break;
1186 case svExternalSingleRef:
1188 if (!bExternal)
1189 return false;
1191 if (nFileId != pToken->GetIndex() || aExtTabName != pToken->GetString())
1192 return false;
1194 const ScSingleRefData& rData = *pToken->GetSingleRef();
1196 nMinCol = min(nMinCol, rData.Col());
1197 nMinRow = min(nMinRow, rData.Row());
1198 nMaxCol = max(nMaxCol, rData.Col());
1199 nMaxRow = max(nMaxRow, rData.Row());
1201 break;
1202 case svExternalDoubleRef:
1204 if (!bExternal)
1205 return false;
1207 if (nFileId != pToken->GetIndex() || aExtTabName != pToken->GetString())
1208 return false;
1210 const ScComplexRefData& rData = *pToken->GetDoubleRef();
1212 nMinCol = min(nMinCol, rData.Ref1.Col());
1213 nMinCol = min(nMinCol, rData.Ref2.Col());
1214 nMinRow = min(nMinRow, rData.Ref1.Row());
1215 nMinRow = min(nMinRow, rData.Ref2.Row());
1217 nMaxCol = max(nMaxCol, rData.Ref1.Col());
1218 nMaxCol = max(nMaxCol, rData.Ref2.Col());
1219 nMaxRow = max(nMaxRow, rData.Ref1.Row());
1220 nMaxRow = max(nMaxRow, rData.Ref2.Row());
1222 break;
1223 default:
1228 const auto & rSheetLimits = pDoc->GetSheetLimits();
1229 if (nMinRow >= nMaxRow || nMinCol >= nMaxCol ||
1230 nMinRow >= rSheetLimits.GetMaxRowCount() || nMinCol >= rSheetLimits.GetMaxColCount() ||
1231 nMaxRow >= rSheetLimits.GetMaxRowCount() || nMaxCol >= rSheetLimits.GetMaxColCount())
1233 // Invalid range. Bail out.
1234 return false;
1237 // Check if the following conditions are met:
1239 // 1) The upper-left corner cell is not included.
1240 // 2) The three adjacent cells of that corner cell are included.
1242 bool bRight = false, bBottom = false, bDiagonal = false;
1243 for (const auto& rxToken : rRefTokens)
1245 switch (rxToken->GetType())
1247 case svSingleRef:
1248 case svExternalSingleRef:
1250 const ScSingleRefData& rData = *rxToken->GetSingleRef();
1251 if (rData.Col() == nMinCol && rData.Row() == nMinRow)
1252 // The corner cell is contained.
1253 return false;
1255 if (rData.Col() == nMinCol+nCornerColumnCount && rData.Row() == nMinRow)
1256 bRight = true;
1258 if (rData.Col() == nMinCol && rData.Row() == nMinRow+nCornerRowCount)
1259 bBottom = true;
1261 if (rData.Col() == nMinCol+nCornerColumnCount && rData.Row() == nMinRow+nCornerRowCount)
1262 bDiagonal = true;
1264 break;
1265 case svDoubleRef:
1266 case svExternalDoubleRef:
1268 const ScComplexRefData& rData = *rxToken->GetDoubleRef();
1269 const ScSingleRefData& r1 = rData.Ref1;
1270 const ScSingleRefData& r2 = rData.Ref2;
1271 if (r1.Col() <= nMinCol && nMinCol <= r2.Col() &&
1272 r1.Row() <= nMinRow && nMinRow <= r2.Row())
1273 // The corner cell is contained.
1274 return false;
1276 if (r1.Col() <= nMinCol+nCornerColumnCount && nMinCol+nCornerColumnCount <= r2.Col() &&
1277 r1.Row() <= nMinRow && nMinRow <= r2.Row())
1278 bRight = true;
1280 if (r1.Col() <= nMinCol && nMinCol <= r2.Col() &&
1281 r1.Row() <= nMinRow+nCornerRowCount && nMinRow+nCornerRowCount <= r2.Row())
1282 bBottom = true;
1284 if (r1.Col() <= nMinCol+nCornerColumnCount && nMinCol+nCornerColumnCount <= r2.Col() &&
1285 r1.Row() <= nMinRow+nCornerRowCount && nMinRow+nCornerRowCount <= r2.Row())
1286 bDiagonal = true;
1288 break;
1289 default:
1294 if (!bRight || !bBottom || !bDiagonal)
1295 // Not all the adjacent cells are included. Bail out.
1296 return false;
1298 ScSingleRefData aData;
1299 aData.InitFlags();
1300 aData.SetFlag3D(true);
1301 aData.SetAbsCol(nMinCol);
1302 aData.SetAbsRow(nMinRow);
1303 aData.SetAbsTab(nTab);
1305 if( nCornerRowCount==1 && nCornerColumnCount==1 )
1307 if (bExternal)
1309 ScTokenRef pCorner(
1310 new ScExternalSingleRefToken(nFileId, std::move(aExtTabName), aData));
1311 ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress());
1313 else
1315 ScTokenRef pCorner(new ScSingleRefToken(pDoc->GetSheetLimits(), aData));
1316 ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress());
1319 else
1321 ScSingleRefData aDataEnd(aData);
1322 aDataEnd.IncCol(nCornerColumnCount-1);
1323 aDataEnd.IncRow(nCornerRowCount-1);
1324 ScComplexRefData r;
1325 r.Ref1=aData;
1326 r.Ref2=aDataEnd;
1327 if (bExternal)
1329 ScTokenRef pCorner(
1330 new ScExternalDoubleRefToken(nFileId, std::move(aExtTabName), r));
1331 ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress());
1333 else
1335 ScTokenRef pCorner(new ScDoubleRefToken(pDoc->GetSheetLimits(), r));
1336 ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress());
1340 return true;
1343 #define SHRINK_RANGE_THRESHOLD 10000
1345 class ShrinkRefTokenToDataRange
1347 ScDocument* mpDoc;
1348 public:
1349 explicit ShrinkRefTokenToDataRange(ScDocument* pDoc) : mpDoc(pDoc) {}
1350 void operator() (const ScTokenRef& rRef)
1352 if (ScRefTokenHelper::isExternalRef(rRef))
1353 return;
1355 // Don't assume an ScDoubleRefToken if it isn't. It can be at least an
1356 // ScSingleRefToken, then there isn't anything to shrink.
1357 if (rRef->GetType() != svDoubleRef)
1358 return;
1360 ScComplexRefData& rData = *rRef->GetDoubleRef();
1361 ScSingleRefData& s = rData.Ref1;
1362 ScSingleRefData& e = rData.Ref2;
1364 if(abs((e.Col()-s.Col())*(e.Row()-s.Row())) < SHRINK_RANGE_THRESHOLD)
1365 return;
1367 SCCOL nMinCol = mpDoc->MaxCol(), nMaxCol = 0;
1368 SCROW nMinRow = mpDoc->MaxRow(), nMaxRow = 0;
1370 // Determine the smallest range that encompasses the data ranges of all sheets.
1371 SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
1372 for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
1374 SCCOL nCol1 = 0, nCol2 = mpDoc->MaxCol();
1375 SCROW nRow1 = 0, nRow2 = mpDoc->MaxRow();
1376 mpDoc->ShrinkToDataArea(nTab, nCol1, nRow1, nCol2, nRow2);
1377 nMinCol = std::min(nMinCol, nCol1);
1378 nMinRow = std::min(nMinRow, nRow1);
1379 nMaxCol = std::max(nMaxCol, nCol2);
1380 nMaxRow = std::max(nMaxRow, nRow2);
1383 // Shrink range to the data range if applicable.
1384 if (s.Col() < nMinCol)
1385 s.SetAbsCol(nMinCol);
1386 if (s.Row() < nMinRow)
1387 s.SetAbsRow(nMinRow);
1388 if (e.Col() > nMaxCol)
1389 e.SetAbsCol(nMaxCol);
1390 if (e.Row() > nMaxRow)
1391 e.SetAbsRow(nMaxRow);
1395 void shrinkToDataRange(ScDocument* pDoc, vector<ScTokenRef>& rRefTokens)
1397 std::for_each(rRefTokens.begin(), rRefTokens.end(), ShrinkRefTokenToDataRange(pDoc));
1402 uno::Reference< chart2::data::XDataSource> SAL_CALL
1403 ScChart2DataProvider::createDataSource(
1404 const uno::Sequence< beans::PropertyValue >& aArguments )
1406 SolarMutexGuard aGuard;
1407 if ( ! m_pDocument )
1408 throw uno::RuntimeException();
1410 uno::Reference< chart2::data::XDataSource> xResult;
1411 bool bLabel = true;
1412 bool bCategories = false;
1413 bool bOrientCol = true;
1414 OUString aRangeRepresentation;
1415 uno::Sequence< sal_Int32 > aSequenceMapping;
1416 bool bTimeBased = false;
1417 for(const auto& rArgument : aArguments)
1419 if ( rArgument.Name == "DataRowSource" )
1421 chart::ChartDataRowSource eSource = chart::ChartDataRowSource_COLUMNS;
1422 if( ! (rArgument.Value >>= eSource))
1424 sal_Int32 nSource(0);
1425 if( rArgument.Value >>= nSource )
1426 eSource = static_cast< chart::ChartDataRowSource >( nSource );
1428 bOrientCol = (eSource == chart::ChartDataRowSource_COLUMNS);
1430 else if ( rArgument.Name == "FirstCellAsLabel" )
1432 bLabel = ::cppu::any2bool(rArgument.Value);
1434 else if ( rArgument.Name == "HasCategories" )
1436 bCategories = ::cppu::any2bool(rArgument.Value);
1438 else if ( rArgument.Name == "CellRangeRepresentation" )
1440 rArgument.Value >>= aRangeRepresentation;
1442 else if ( rArgument.Name == "SequenceMapping" )
1444 rArgument.Value >>= aSequenceMapping;
1446 else if ( rArgument.Name == "TimeBased" )
1448 rArgument.Value >>= bTimeBased;
1452 vector<ScTokenRef> aRefTokens;
1453 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1454 ScRefTokenHelper::compileRangeRepresentation(
1455 aRefTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
1456 if (aRefTokens.empty())
1457 // Invalid range representation. Bail out.
1458 throw lang::IllegalArgumentException();
1460 SCTAB nTimeBasedStart = MAXTAB;
1461 SCTAB nTimeBasedEnd = 0;
1462 if(bTimeBased)
1464 // limit to first sheet
1465 for(const auto& rxToken : aRefTokens)
1467 if (rxToken->GetType() != svDoubleRef)
1468 continue;
1470 ScComplexRefData& rData = *rxToken->GetDoubleRef();
1471 ScSingleRefData& s = rData.Ref1;
1472 ScSingleRefData& e = rData.Ref2;
1474 nTimeBasedStart = std::min(nTimeBasedStart, s.Tab());
1475 nTimeBasedEnd = std::min(nTimeBasedEnd, e.Tab());
1477 if(s.Tab() != e.Tab())
1478 e.SetAbsTab(s.Tab());
1482 if(!bTimeBased)
1483 shrinkToDataRange(m_pDocument, aRefTokens);
1485 if (bLabel)
1486 lcl_addUpperLeftCornerIfMissing(m_pDocument, aRefTokens, 1, 1); //#i90669#
1488 bool bColHeaders = (bOrientCol ? bLabel : bCategories );
1489 bool bRowHeaders = (bOrientCol ? bCategories : bLabel );
1491 Chart2Positioner aChPositioner(m_pDocument, aRefTokens);
1492 aChPositioner.setHeaders(bColHeaders, bRowHeaders);
1494 const Chart2PositionMap* pChartMap = aChPositioner.getPositionMap();
1495 if (!pChartMap)
1496 // No chart position map instance. Bail out.
1497 return xResult;
1499 rtl::Reference<ScChart2DataSource> pDS;
1500 ::std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSeqs;
1502 // Fill Categories
1503 if( bCategories )
1505 vector<ScTokenRef> aValueTokens;
1506 if (bOrientCol)
1507 aValueTokens = pChartMap->getAllRowHeaderRanges();
1508 else
1509 aValueTokens = pChartMap->getAllColHeaderRanges();
1511 vector<ScTokenRef> aLabelTokens(
1512 pChartMap->getLeftUpperCornerRanges());
1514 uno::Reference< chart2::data::XLabeledDataSequence > xCategories = lcl_createLabeledDataSequenceFromTokens(
1515 std::move(aValueTokens), std::move(aLabelTokens), m_pDocument, m_bIncludeHiddenCells ); //ownership of pointers is transferred!
1516 if ( xCategories.is() )
1518 aSeqs.push_back( xCategories );
1522 // Fill Series (values and label)
1523 sal_Int32 nCount = bOrientCol ? pChartMap->getDataColCount() : pChartMap->getDataRowCount();
1524 for (sal_Int32 i = 0; i < nCount; ++i)
1526 vector<ScTokenRef> aValueTokens;
1527 vector<ScTokenRef> aLabelTokens;
1528 if (bOrientCol)
1530 aValueTokens = pChartMap->getDataColRanges(static_cast<SCCOL>(i));
1531 aLabelTokens = pChartMap->getColHeaderRanges(static_cast<SCCOL>(i));
1533 else
1535 aValueTokens = pChartMap->getDataRowRanges(static_cast<SCROW>(i));
1536 aLabelTokens = pChartMap->getRowHeaderRanges(static_cast<SCROW>(i));
1538 uno::Reference< chart2::data::XLabeledDataSequence > xChartSeries = lcl_createLabeledDataSequenceFromTokens(
1539 std::move(aValueTokens), std::move(aLabelTokens), m_pDocument, m_bIncludeHiddenCells ); //ownership of pointers is transferred!
1540 if ( xChartSeries.is() && xChartSeries->getValues().is() && xChartSeries->getValues()->getData().hasElements() )
1542 aSeqs.push_back( xChartSeries );
1546 pDS = new ScChart2DataSource(m_pDocument);
1548 //reorder labeled sequences according to aSequenceMapping
1549 ::std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSeqVector;
1550 aSeqVector.reserve(aSeqs.size());
1551 for (auto const& aSeq : aSeqs)
1553 aSeqVector.push_back(aSeq);
1556 for (const sal_Int32 nNewIndex : aSequenceMapping)
1558 // note: assuming that the values in the sequence mapping are always non-negative
1559 ::std::vector< uno::Reference< chart2::data::XLabeledDataSequence > >::size_type nOldIndex( static_cast< sal_uInt32 >( nNewIndex ) );
1560 if( nOldIndex < aSeqVector.size() )
1562 pDS->AddLabeledSequence( aSeqVector[nOldIndex] );
1563 aSeqVector[nOldIndex] = nullptr;
1567 for(const uno::Reference< chart2::data::XLabeledDataSequence >& xSeq : aSeqVector)
1569 if ( xSeq.is() )
1571 pDS->AddLabeledSequence( xSeq );
1575 xResult.set( pDS );
1576 return xResult;
1579 namespace
1583 * Function object to create a list of table numbers from a token list.
1585 class InsertTabNumber
1587 public:
1588 InsertTabNumber() :
1589 mpTabNumVector(std::make_shared<vector<SCTAB>>())
1593 void operator() (const ScTokenRef& pToken) const
1595 if (!ScRefTokenHelper::isRef(pToken))
1596 return;
1598 const ScSingleRefData& r = *pToken->GetSingleRef();
1599 mpTabNumVector->push_back(r.Tab());
1602 void getVector(vector<SCTAB>& rVector)
1604 mpTabNumVector->swap(rVector);
1606 private:
1607 shared_ptr< vector<SCTAB> > mpTabNumVector;
1610 class RangeAnalyzer
1612 public:
1613 RangeAnalyzer();
1614 void initRangeAnalyzer( const ScDocument* pDoc, const vector<ScTokenRef>& rTokens );
1615 void analyzeRange( sal_Int32& rnDataInRows, sal_Int32& rnDataInCols,
1616 bool& rbRowSourceAmbiguous ) const;
1617 bool inSameSingleRow( const RangeAnalyzer& rOther );
1618 bool inSameSingleColumn( const RangeAnalyzer& rOther );
1619 SCROW getRowCount() const { return mnRowCount; }
1620 SCCOL getColumnCount() const { return mnColumnCount; }
1622 private:
1623 bool mbEmpty;
1624 bool mbAmbiguous;
1625 SCROW mnRowCount;
1626 SCCOL mnColumnCount;
1628 SCCOL mnStartColumn;
1629 SCROW mnStartRow;
1632 RangeAnalyzer::RangeAnalyzer()
1633 : mbEmpty(true)
1634 , mbAmbiguous(false)
1635 , mnRowCount(0)
1636 , mnColumnCount(0)
1637 , mnStartColumn(-1)
1638 , mnStartRow(-1)
1642 void RangeAnalyzer::initRangeAnalyzer( const ScDocument* pDoc, const vector<ScTokenRef>& rTokens )
1644 mnRowCount=0;
1645 mnColumnCount=0;
1646 mnStartColumn = -1;
1647 mnStartRow = -1;
1648 mbAmbiguous=false;
1649 if( rTokens.empty() )
1651 mbEmpty=true;
1652 return;
1654 mbEmpty=false;
1656 for (const ScTokenRef& aRefToken : rTokens)
1658 StackVar eVar = aRefToken->GetType();
1659 if (eVar == svDoubleRef || eVar == svExternalDoubleRef)
1661 const ScComplexRefData& r = *aRefToken->GetDoubleRef();
1662 if (r.Ref1.Tab() == r.Ref2.Tab())
1664 mnColumnCount = std::max<SCCOL>(mnColumnCount, static_cast<SCCOL>(abs(r.Ref2.Col() - r.Ref1.Col())+1));
1665 mnRowCount = std::max<SCROW>(mnRowCount, static_cast<SCROW>(abs(r.Ref2.Row() - r.Ref1.Row())+1));
1666 if( mnStartColumn == -1 )
1668 mnStartColumn = r.Ref1.Col();
1669 mnStartRow = r.Ref1.Row();
1671 else
1673 if (mnStartColumn != r.Ref1.Col() && mnStartRow != r.Ref1.Row())
1674 mbAmbiguous=true;
1677 else
1678 mbAmbiguous=true;
1680 else if (eVar == svSingleRef || eVar == svExternalSingleRef)
1682 const ScSingleRefData& r = *aRefToken->GetSingleRef();
1683 mnColumnCount = std::max<SCCOL>( mnColumnCount, 1);
1684 mnRowCount = std::max<SCROW>( mnRowCount, 1);
1685 if( mnStartColumn == -1 )
1687 mnStartColumn = r.Col();
1688 mnStartRow = r.Row();
1690 else
1692 if (mnStartColumn != r.Col() && mnStartRow != r.Row())
1693 mbAmbiguous=true;
1696 else if (eVar == svIndex && aRefToken->GetOpCode() == ocName)
1698 ScRangeData* pNameRange = pDoc->FindRangeNameBySheetAndIndex(aRefToken->GetSheet(), aRefToken->GetIndex());
1699 ScRange aRange;
1700 if (pNameRange->IsReference(aRange))
1702 mnColumnCount = std::max<SCCOL>(mnColumnCount, static_cast<SCCOL>(abs(aRange.aEnd.Col() - aRange.aStart.Col()) + 1));
1703 mnRowCount = std::max<SCROW>(mnRowCount, static_cast<SCROW>(abs(aRange.aEnd.Row() - aRange.aStart.Row()) + 1));
1704 if (mnStartColumn == -1)
1706 mnStartColumn = aRange.aStart.Col();
1707 mnStartRow = aRange.aStart.Row();
1709 else
1711 if (mnStartColumn != aRange.aStart.Col() && mnStartRow != aRange.aStart.Row())
1712 mbAmbiguous = true;
1715 else
1716 mbAmbiguous = true;
1718 else
1719 mbAmbiguous=true;
1723 void RangeAnalyzer::analyzeRange( sal_Int32& rnDataInRows,
1724 sal_Int32& rnDataInCols,
1725 bool& rbRowSourceAmbiguous ) const
1727 if(!mbEmpty && !mbAmbiguous)
1729 if( mnRowCount==1 && mnColumnCount>1 )
1730 ++rnDataInRows;
1731 else if( mnColumnCount==1 && mnRowCount>1 )
1732 ++rnDataInCols;
1733 else if( mnRowCount>1 && mnColumnCount>1 )
1734 rbRowSourceAmbiguous = true;
1736 else if( !mbEmpty )
1737 rbRowSourceAmbiguous = true;
1740 bool RangeAnalyzer::inSameSingleRow( const RangeAnalyzer& rOther )
1742 return mnStartRow==rOther.mnStartRow &&
1743 mnRowCount==1 && rOther.mnRowCount==1;
1746 bool RangeAnalyzer::inSameSingleColumn( const RangeAnalyzer& rOther )
1748 return mnStartColumn==rOther.mnStartColumn &&
1749 mnColumnCount==1 && rOther.mnColumnCount==1;
1752 std::pair<OUString, OUString> constructKey(const uno::Reference< chart2::data::XLabeledDataSequence>& xNew)
1754 std::pair<OUString, OUString> aKey;
1755 if( xNew->getLabel().is() )
1756 aKey.first = xNew->getLabel()->getSourceRangeRepresentation();
1757 if( xNew->getValues().is() )
1758 aKey.second = xNew->getValues()->getSourceRangeRepresentation();
1759 return aKey;
1763 } //end anonymous namespace
1765 uno::Sequence< beans::PropertyValue > SAL_CALL ScChart2DataProvider::detectArguments(
1766 const uno::Reference< chart2::data::XDataSource >& xDataSource )
1768 ::std::vector< beans::PropertyValue > aResult;
1769 bool bRowSourceDetected = false;
1770 bool bFirstCellAsLabel = false;
1771 bool bHasCategories = false;
1772 OUString sRangeRep;
1774 bool bHasCategoriesLabels = false;
1775 vector<ScTokenRef> aAllCategoriesValuesTokens;
1776 vector<ScTokenRef> aAllSeriesLabelTokens;
1778 chart::ChartDataRowSource eRowSource = chart::ChartDataRowSource_COLUMNS;
1780 vector<ScTokenRef> aAllTokens;
1782 // parse given data source and collect infos
1784 SolarMutexGuard aGuard;
1785 OSL_ENSURE( m_pDocument, "No Document -> no detectArguments" );
1786 if(!m_pDocument ||!xDataSource.is())
1787 return comphelper::containerToSequence( aResult );
1789 sal_Int32 nDataInRows = 0;
1790 sal_Int32 nDataInCols = 0;
1791 bool bRowSourceAmbiguous = false;
1793 const Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aSequences( xDataSource->getDataSequences());
1794 const sal_Int32 nCount( aSequences.getLength());
1795 RangeAnalyzer aPrevLabel,aPrevValues;
1796 for( const uno::Reference< chart2::data::XLabeledDataSequence >& xLS : aSequences )
1798 if( xLS.is() )
1800 bool bThisIsCategories = false;
1801 if(!bHasCategories)
1803 uno::Reference< beans::XPropertySet > xSeqProp( xLS->getValues(), uno::UNO_QUERY );
1804 OUString aRole;
1805 if( xSeqProp.is() && (xSeqProp->getPropertyValue(u"Role"_ustr) >>= aRole) &&
1806 aRole == "categories" )
1807 bThisIsCategories = bHasCategories = true;
1810 RangeAnalyzer aLabel,aValues;
1811 // label
1812 uno::Reference< chart2::data::XDataSequence > xLabel( xLS->getLabel());
1813 if( xLabel.is())
1815 bFirstCellAsLabel = true;
1816 vector<ScTokenRef> aTokens;
1817 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1818 ScRefTokenHelper::compileRangeRepresentation(
1819 aTokens, xLabel->getSourceRangeRepresentation(), *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
1820 aLabel.initRangeAnalyzer(m_pDocument, aTokens);
1821 for (const auto& rxToken : aTokens)
1823 if (rxToken->GetType() == svIndex && rxToken->GetOpCode() == ocName)
1825 ScRangeData* pNameRange = m_pDocument->FindRangeNameBySheetAndIndex(rxToken->GetSheet(), rxToken->GetIndex());
1826 if (pNameRange->HasReferences())
1828 const ScTokenRef aTempToken = pNameRange->GetCode()->FirstToken();
1829 ScRefTokenHelper::join(m_pDocument, aAllTokens, aTempToken, ScAddress());
1831 else
1832 ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress());
1834 else
1835 ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress());
1836 if(!bThisIsCategories)
1837 ScRefTokenHelper::join(m_pDocument, aAllSeriesLabelTokens, rxToken, ScAddress());
1839 if(bThisIsCategories)
1840 bHasCategoriesLabels=true;
1842 // values
1843 uno::Reference< chart2::data::XDataSequence > xValues( xLS->getValues());
1844 if( xValues.is())
1846 vector<ScTokenRef> aTokens;
1847 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1848 ScRefTokenHelper::compileRangeRepresentation(
1849 aTokens, xValues->getSourceRangeRepresentation(), *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
1850 aValues.initRangeAnalyzer(m_pDocument, aTokens);
1851 for (const auto& rxToken : aTokens)
1853 if (rxToken->GetType() == svIndex && rxToken->GetOpCode() == ocName)
1855 ScRangeData* pNameRange = m_pDocument->FindRangeNameBySheetAndIndex(rxToken->GetSheet(), rxToken->GetIndex());
1856 if (pNameRange->HasReferences())
1858 const ScTokenRef aTempToken = pNameRange->GetCode()->FirstToken();
1859 ScRefTokenHelper::join(m_pDocument, aAllTokens, aTempToken, ScAddress());
1861 else
1862 ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress());
1864 else
1865 ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress());
1866 if(bThisIsCategories)
1867 ScRefTokenHelper::join(m_pDocument, aAllCategoriesValuesTokens, rxToken, ScAddress());
1870 //detect row source
1871 if(!bThisIsCategories || nCount==1) //categories might span multiple rows *and* columns, so they should be used for detection only if nothing else is available
1873 if (!bRowSourceAmbiguous)
1875 aValues.analyzeRange(nDataInRows,nDataInCols,bRowSourceAmbiguous);
1876 aLabel.analyzeRange(nDataInRows,nDataInCols,bRowSourceAmbiguous);
1877 if (nDataInRows > 1 && nDataInCols > 1)
1878 bRowSourceAmbiguous = true;
1879 else if( !bRowSourceAmbiguous && !nDataInRows && !nDataInCols )
1881 if( aValues.inSameSingleColumn( aLabel ) )
1882 nDataInCols++;
1883 else if( aValues.inSameSingleRow( aLabel ) )
1884 nDataInRows++;
1885 else
1887 //#i86188# also detect a single column split into rows correctly
1888 if( aValues.inSameSingleColumn( aPrevValues ) )
1889 nDataInRows++;
1890 else if( aValues.inSameSingleRow( aPrevValues ) )
1891 nDataInCols++;
1892 else if( aLabel.inSameSingleColumn( aPrevLabel ) )
1893 nDataInRows++;
1894 else if( aLabel.inSameSingleRow( aPrevLabel ) )
1895 nDataInCols++;
1900 aPrevValues=aValues;
1901 aPrevLabel=aLabel;
1905 if (!bRowSourceAmbiguous)
1907 bRowSourceDetected = true;
1908 eRowSource = ( nDataInCols > 0
1909 ? chart::ChartDataRowSource_COLUMNS
1910 : chart::ChartDataRowSource_ROWS );
1912 else
1914 // set DataRowSource to the better of the two ambiguities
1915 eRowSource = ( nDataInRows > nDataInCols
1916 ? chart::ChartDataRowSource_ROWS
1917 : chart::ChartDataRowSource_COLUMNS );
1922 // TableNumberList
1924 vector<SCTAB> aTableNumVector;
1925 InsertTabNumber func;
1926 func = ::std::for_each(aAllTokens.begin(), aAllTokens.end(), func);
1927 func.getVector(aTableNumVector);
1928 aResult.emplace_back( "TableNumberList", -1,
1929 uno::Any( lcl_createTableNumberList( aTableNumVector ) ),
1930 beans::PropertyState_DIRECT_VALUE );
1933 if( bRowSourceDetected )
1935 // DataRowSource (calculated before)
1936 aResult.emplace_back( "DataRowSource", -1,
1937 uno::Any( eRowSource ), beans::PropertyState_DIRECT_VALUE );
1938 // HasCategories
1939 aResult.emplace_back( "HasCategories", -1,
1940 uno::Any( bHasCategories ), beans::PropertyState_DIRECT_VALUE );
1941 // FirstCellAsLabel
1942 aResult.emplace_back( "FirstCellAsLabel", -1,
1943 uno::Any( bFirstCellAsLabel ), beans::PropertyState_DIRECT_VALUE );
1946 // Add the left upper corner to the range if it is missing.
1947 if (bRowSourceDetected && bFirstCellAsLabel && bHasCategories && !bHasCategoriesLabels )
1949 RangeAnalyzer aTop,aLeft;
1950 if( eRowSource==chart::ChartDataRowSource_COLUMNS )
1952 aTop.initRangeAnalyzer(m_pDocument, aAllSeriesLabelTokens);
1953 aLeft.initRangeAnalyzer(m_pDocument, aAllCategoriesValuesTokens);
1955 else
1957 aTop.initRangeAnalyzer(m_pDocument, aAllCategoriesValuesTokens);
1958 aLeft.initRangeAnalyzer(m_pDocument, aAllSeriesLabelTokens);
1960 lcl_addUpperLeftCornerIfMissing(m_pDocument, aAllTokens, aTop.getRowCount(), aLeft.getColumnCount());//e.g. #i91212#
1963 // Get range string.
1964 lcl_convertTokensToString(sRangeRep, aAllTokens, *m_pDocument);
1966 // add cell range property
1967 aResult.emplace_back( "CellRangeRepresentation", -1,
1968 uno::Any( sRangeRep ), beans::PropertyState_DIRECT_VALUE );
1970 //Sequence Mapping
1971 bool const bSequencesReordered = true;//todo detect this above or detect this sequence mapping cheaper ...
1972 if( bSequencesReordered && bRowSourceDetected )
1974 bool bDifferentIndexes = false;
1976 std::vector< sal_Int32 > aSequenceMappingVector;
1978 uno::Reference< chart2::data::XDataSource > xCompareDataSource;
1981 xCompareDataSource.set( createDataSource( comphelper::containerToSequence( aResult ) ) );
1983 catch( const lang::IllegalArgumentException & )
1985 // creation of data source to compare didn't work, so we cannot
1986 // create a sequence mapping
1989 if( xDataSource.is() && xCompareDataSource.is() )
1991 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> > aOldSequences =
1992 xCompareDataSource->getDataSequences();
1993 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> > aNewSequences =
1994 xDataSource->getDataSequences();
1996 std::map<std::pair<OUString, OUString>,sal_Int32> aOldEntryToIndex;
1997 for( sal_Int32 nIndex = 0, n = aOldSequences.getLength(); nIndex < n; nIndex++ )
1999 const uno::Reference< chart2::data::XLabeledDataSequence>& xOld( aOldSequences[nIndex] );
2000 if( xOld.is() )
2002 std::pair<OUString, OUString> aKey = constructKey(xOld);
2003 aOldEntryToIndex[aKey] = nIndex;
2007 for( sal_Int32 nNewIndex = 0, n = aNewSequences.getLength(); nNewIndex < n; nNewIndex++ )
2009 const uno::Reference< chart2::data::XLabeledDataSequence>& xNew( aNewSequences[nNewIndex] );
2010 if( !xNew.is() )
2011 continue;
2013 std::pair<OUString, OUString> aKey = constructKey(xNew);
2014 if (aOldEntryToIndex.find(aKey) == aOldEntryToIndex.end())
2015 continue;
2017 sal_Int32 nOldIndex = aOldEntryToIndex[aKey];
2018 if( nOldIndex != nNewIndex )
2019 bDifferentIndexes = true;
2021 aSequenceMappingVector.push_back(nOldIndex);
2025 if( bDifferentIndexes && !aSequenceMappingVector.empty() )
2027 aResult.emplace_back( "SequenceMapping", -1,
2028 uno::Any( comphelper::containerToSequence(aSequenceMappingVector) )
2029 , beans::PropertyState_DIRECT_VALUE );
2033 return comphelper::containerToSequence( aResult );
2036 sal_Bool SAL_CALL ScChart2DataProvider::createDataSequenceByRangeRepresentationPossible( const OUString& aRangeRepresentation )
2038 SolarMutexGuard aGuard;
2039 if( ! m_pDocument )
2040 return false;
2042 vector<ScTokenRef> aTokens;
2043 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
2044 ScRefTokenHelper::compileRangeRepresentation(
2045 aTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
2046 return !aTokens.empty();
2049 uno::Reference< chart2::data::XDataSequence > SAL_CALL
2050 ScChart2DataProvider::createDataSequenceByRangeRepresentation(
2051 const OUString& aRangeRepresentation )
2053 SolarMutexGuard aGuard;
2054 uno::Reference< chart2::data::XDataSequence > xResult;
2056 OSL_ENSURE( m_pDocument, "No Document -> no createDataSequenceByRangeRepresentation" );
2057 if(!m_pDocument || aRangeRepresentation.isEmpty())
2058 return xResult;
2060 vector<ScTokenRef> aRefTokens;
2061 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
2062 ScRefTokenHelper::compileRangeRepresentation(
2063 aRefTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
2064 if (aRefTokens.empty())
2065 return xResult;
2067 shrinkToDataRange(m_pDocument, aRefTokens);
2069 xResult.set(new ScChart2DataSequence(m_pDocument, std::move(aRefTokens), m_bIncludeHiddenCells));
2071 return xResult;
2074 uno::Reference<chart2::data::XDataSequence> SAL_CALL
2075 ScChart2DataProvider::createDataSequenceByValueArray(
2076 const OUString& /*aRole*/, const OUString& /*aRangeRepresentation*/,
2077 const OUString& /*aRoleQualifier*/ )
2079 return uno::Reference<chart2::data::XDataSequence>();
2082 uno::Reference< sheet::XRangeSelection > SAL_CALL ScChart2DataProvider::getRangeSelection()
2084 uno::Reference< sheet::XRangeSelection > xResult;
2086 uno::Reference< frame::XModel > xModel( lcl_GetXModel( m_pDocument ));
2087 if( xModel.is())
2088 xResult.set( xModel->getCurrentController(), uno::UNO_QUERY );
2090 return xResult;
2093 sal_Bool SAL_CALL ScChart2DataProvider::createDataSequenceByFormulaTokensPossible(
2094 const Sequence<sheet::FormulaToken>& aTokens )
2096 if (!aTokens.hasElements())
2097 return false;
2099 ScTokenArray aCode(*m_pDocument);
2100 if (!ScTokenConversion::ConvertToTokenArray(*m_pDocument, aCode, aTokens))
2101 return false;
2103 sal_uInt16 n = aCode.GetLen();
2104 if (!n)
2105 return false;
2107 formula::FormulaTokenArrayPlainIterator aIter(aCode);
2108 const formula::FormulaToken* pFirst = aIter.First();
2109 const formula::FormulaToken* pLast = aCode.GetArray()[n-1];
2110 for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
2112 switch (p->GetType())
2114 case svSep:
2116 switch (p->GetOpCode())
2118 case ocSep:
2119 // separators are allowed.
2120 break;
2121 case ocOpen:
2122 if (p != pFirst)
2123 // open paran is allowed only as the first token.
2124 return false;
2125 break;
2126 case ocClose:
2127 if (p != pLast)
2128 // close paren is allowed only as the last token.
2129 return false;
2130 break;
2131 default:
2132 return false;
2135 break;
2136 case svSingleRef:
2137 case svDoubleRef:
2138 case svExternalSingleRef:
2139 case svExternalDoubleRef:
2140 break;
2141 default:
2142 return false;
2146 return true;
2149 uno::Reference<chart2::data::XDataSequence> SAL_CALL
2150 ScChart2DataProvider::createDataSequenceByFormulaTokens(
2151 const Sequence<sheet::FormulaToken>& aTokens )
2153 uno::Reference<chart2::data::XDataSequence> xResult;
2154 if (!aTokens.hasElements())
2155 return xResult;
2157 ScTokenArray aCode(*m_pDocument);
2158 if (!ScTokenConversion::ConvertToTokenArray(*m_pDocument, aCode, aTokens))
2159 return xResult;
2161 sal_uInt16 n = aCode.GetLen();
2162 if (!n)
2163 return xResult;
2165 vector<ScTokenRef> aRefTokens;
2166 formula::FormulaTokenArrayPlainIterator aIter(aCode);
2167 const formula::FormulaToken* pFirst = aIter.First();
2168 const formula::FormulaToken* pLast = aCode.GetArray()[n-1];
2169 for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
2171 switch (p->GetType())
2173 case svSep:
2175 switch (p->GetOpCode())
2177 case ocSep:
2178 // separators are allowed.
2179 break;
2180 case ocOpen:
2181 if (p != pFirst)
2182 // open paran is allowed only as the first token.
2183 throw lang::IllegalArgumentException();
2184 break;
2185 case ocClose:
2186 if (p != pLast)
2187 // close paren is allowed only as the last token.
2188 throw lang::IllegalArgumentException();
2189 break;
2190 default:
2191 throw lang::IllegalArgumentException();
2194 break;
2195 case svIndex:
2196 case svString:
2197 case svSingleRef:
2198 case svDoubleRef:
2199 case svExternalSingleRef:
2200 case svExternalDoubleRef:
2202 ScTokenRef pNew(p->Clone());
2203 aRefTokens.push_back(pNew);
2205 break;
2206 default:
2207 throw lang::IllegalArgumentException();
2211 if (aRefTokens.empty())
2212 return xResult;
2214 shrinkToDataRange(m_pDocument, aRefTokens);
2216 xResult.set(new ScChart2DataSequence(m_pDocument, std::move(aRefTokens), m_bIncludeHiddenCells));
2217 return xResult;
2220 // XRangeXMLConversion ---------------------------------------------------
2222 OUString SAL_CALL ScChart2DataProvider::convertRangeToXML( const OUString& sRangeRepresentation )
2224 OUString aRet;
2225 if (!m_pDocument)
2226 return aRet;
2228 if (sRangeRepresentation.isEmpty())
2229 // Empty data range is allowed.
2230 return aRet;
2232 vector<ScTokenRef> aRefTokens;
2233 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
2234 ScRefTokenHelper::compileRangeRepresentation(
2235 aRefTokens, sRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
2236 if (aRefTokens.empty())
2238 SAL_WARN("sc", "convertRangeToXML throw IllegalArgumentException from input of: " << sRangeRepresentation);
2239 throw lang::IllegalArgumentException();
2242 Tokens2RangeStringXML converter(*m_pDocument);
2243 converter = ::std::for_each(aRefTokens.begin(), aRefTokens.end(), converter);
2244 converter.getString(aRet);
2246 return aRet;
2249 OUString SAL_CALL ScChart2DataProvider::convertRangeFromXML( const OUString& sXMLRange )
2251 if (!m_pDocument)
2253 // #i74062# When loading flat XML, this is called before the referenced sheets are in the document,
2254 // so the conversion has to take place directly with the strings, without looking up the sheets.
2256 OUStringBuffer sRet;
2257 sal_Int32 nOffset = 0;
2258 while( nOffset >= 0 )
2260 OUString sToken;
2261 ScRangeStringConverter::GetTokenByOffset( sToken, sXMLRange, nOffset );
2262 if( nOffset >= 0 )
2264 // convert one address (remove dots)
2266 OUString aUIString(sToken);
2268 sal_Int32 nIndex = ScRangeStringConverter::IndexOf( sToken, ':', 0 );
2269 if ( nIndex >= 0 && nIndex < aUIString.getLength() - 1 &&
2270 aUIString[nIndex + 1] == '.' )
2271 aUIString = aUIString.replaceAt( nIndex + 1, 1, u"" );
2273 if ( aUIString[0] == '.' )
2274 aUIString = aUIString.copy( 1 );
2276 if( !sRet.isEmpty() )
2277 sRet.append( ';' );
2278 sRet.append( aUIString );
2282 return sRet.makeStringAndClear();
2285 OUString aRet;
2286 ScRangeStringConverter::GetStringFromXMLRangeString(aRet, sXMLRange, *m_pDocument);
2287 return aRet;
2290 // DataProvider XPropertySet -------------------------------------------------
2292 uno::Reference< beans::XPropertySetInfo> SAL_CALL
2293 ScChart2DataProvider::getPropertySetInfo()
2295 SolarMutexGuard aGuard;
2296 static uno::Reference<beans::XPropertySetInfo> aRef =
2297 new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() );
2298 return aRef;
2301 void SAL_CALL ScChart2DataProvider::setPropertyValue(
2302 const OUString& rPropertyName, const uno::Any& rValue)
2304 if ( rPropertyName != SC_UNONAME_INCLUDEHIDDENCELLS )
2305 throw beans::UnknownPropertyException(rPropertyName);
2307 if ( !(rValue >>= m_bIncludeHiddenCells))
2308 throw lang::IllegalArgumentException();
2312 uno::Any SAL_CALL ScChart2DataProvider::getPropertyValue(
2313 const OUString& rPropertyName)
2315 uno::Any aRet;
2316 if ( rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS )
2317 aRet <<= m_bIncludeHiddenCells;
2318 else if (rPropertyName == SC_UNONAME_USE_INTERNAL_DATA_PROVIDER)
2320 // This is a read-only property.
2321 aRet <<= m_pDocument->PastingDrawFromOtherDoc();
2323 else
2324 throw beans::UnknownPropertyException(rPropertyName);
2325 return aRet;
2328 void SAL_CALL ScChart2DataProvider::addPropertyChangeListener(
2329 const OUString& /*rPropertyName*/,
2330 const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/)
2332 OSL_FAIL( "Not yet implemented" );
2335 void SAL_CALL ScChart2DataProvider::removePropertyChangeListener(
2336 const OUString& /*rPropertyName*/,
2337 const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/)
2339 OSL_FAIL( "Not yet implemented" );
2342 void SAL_CALL ScChart2DataProvider::addVetoableChangeListener(
2343 const OUString& /*rPropertyName*/,
2344 const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/)
2346 OSL_FAIL( "Not yet implemented" );
2349 void SAL_CALL ScChart2DataProvider::removeVetoableChangeListener(
2350 const OUString& /*rPropertyName*/,
2351 const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/ )
2353 OSL_FAIL( "Not yet implemented" );
2356 // DataSource ================================================================
2358 ScChart2DataSource::ScChart2DataSource( ScDocument* pDoc)
2359 : m_pDocument( pDoc)
2361 if ( m_pDocument )
2362 m_pDocument->AddUnoObject( *this);
2365 ScChart2DataSource::~ScChart2DataSource()
2367 SolarMutexGuard g;
2369 if ( m_pDocument )
2370 m_pDocument->RemoveUnoObject( *this);
2373 void ScChart2DataSource::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
2375 if ( rHint.GetId() == SfxHintId::Dying )
2377 m_pDocument = nullptr;
2381 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> > SAL_CALL
2382 ScChart2DataSource::getDataSequences()
2384 SolarMutexGuard aGuard;
2385 return comphelper::containerToSequence(m_aLabeledSequences);
2388 void ScChart2DataSource::AddLabeledSequence(const uno::Reference < chart2::data::XLabeledDataSequence >& xNew)
2390 m_aLabeledSequences.push_back(xNew);
2393 // DataSequence ==============================================================
2395 ScChart2DataSequence::Item::Item()
2396 : mfValue(std::numeric_limits<double>::quiet_NaN())
2397 , mbIsValue(false)
2401 ScChart2DataSequence::HiddenRangeListener::HiddenRangeListener(ScChart2DataSequence& rParent) :
2402 mrParent(rParent)
2406 ScChart2DataSequence::HiddenRangeListener::~HiddenRangeListener()
2410 void ScChart2DataSequence::HiddenRangeListener::notify()
2412 mrParent.setDataChangedHint(true);
2415 ScChart2DataSequence::ScChart2DataSequence( ScDocument* pDoc,
2416 vector<ScTokenRef>&& rTokens,
2417 bool bIncludeHiddenCells )
2418 : m_xDataArray(new std::vector<Item>)
2419 , m_bIncludeHiddenCells( bIncludeHiddenCells)
2420 , m_nObjectId( 0 )
2421 , m_pDocument( pDoc)
2422 , m_aTokens(std::move(rTokens))
2423 , m_aPropSet(lcl_GetDataSequencePropertyMap())
2424 , m_bGotDataChangedHint(false)
2425 , m_bExtDataRebuildQueued(false)
2426 , mbTimeBased(false)
2427 , mnTimeBasedStart(0)
2428 , mnTimeBasedEnd(0)
2429 , mnCurrentTab(0)
2431 if ( m_pDocument )
2433 m_pDocument->AddUnoObject( *this);
2434 m_nObjectId = m_pDocument->GetNewUnoId();
2436 // FIXME: real implementation of identifier and it's mapping to ranges.
2437 // Reuse ScChartListener?
2439 // BM: don't use names of named ranges but the UI range strings
2440 // String aStr;
2441 // rRangeList->Format( aStr, ScRefFlags::RANGE_ABS_3D, m_pDocument );
2442 // m_aIdentifier = aStr;
2444 // m_aIdentifier = "ID_";
2445 // static sal_Int32 nID = 0;
2446 // m_aIdentifier += OUString::valueOf( ++nID);
2449 /** called from Clone() */
2450 ScChart2DataSequence::ScChart2DataSequence(ScDocument* pDoc, const ScChart2DataSequence& r)
2451 : m_xDataArray(r.m_xDataArray)
2452 , m_aHiddenValues(r.m_aHiddenValues)
2453 , m_aRole(r.m_aRole)
2454 , m_bIncludeHiddenCells( r.m_bIncludeHiddenCells)
2455 , m_nObjectId( 0 )
2456 , m_pDocument( pDoc)
2457 , m_aPropSet(lcl_GetDataSequencePropertyMap())
2458 , m_bGotDataChangedHint(false)
2459 , m_bExtDataRebuildQueued(false)
2460 , mbTimeBased(false)
2461 , mnTimeBasedStart(0)
2462 , mnTimeBasedEnd(0)
2463 , mnCurrentTab(0)
2465 assert(pDoc);
2467 // Clone tokens.
2468 m_aTokens.reserve(r.m_aTokens.size());
2469 for (const auto& rxToken : r.m_aTokens)
2471 ScTokenRef p(rxToken->Clone());
2472 m_aTokens.push_back(p);
2475 m_pDocument->AddUnoObject( *this);
2476 m_nObjectId = m_pDocument->GetNewUnoId();
2478 if (r.m_oRangeIndices)
2479 m_oRangeIndices = *r.m_oRangeIndices;
2481 if (!r.m_pExtRefListener)
2482 return;
2484 // Re-register all external files that the old instance was
2485 // listening to.
2487 ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager();
2488 m_pExtRefListener.reset(new ExternalRefListener(*this, m_pDocument));
2489 const std::unordered_set<sal_uInt16>& rFileIds = r.m_pExtRefListener->getAllFileIds();
2490 for (const auto& rFileId : rFileIds)
2492 pRefMgr->addLinkListener(rFileId, m_pExtRefListener.get());
2493 m_pExtRefListener->addFileId(rFileId);
2497 ScChart2DataSequence::~ScChart2DataSequence()
2499 SolarMutexGuard g;
2501 if ( m_pDocument )
2503 m_pDocument->RemoveUnoObject( *this);
2504 if (m_pHiddenListener)
2506 ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection();
2507 if (pCLC)
2508 pCLC->EndListeningHiddenRange(m_pHiddenListener.get());
2510 StopListeningToAllExternalRefs();
2513 m_pValueListener.reset();
2516 void ScChart2DataSequence::RefChanged()
2518 if( !m_pValueListener || m_aValueListeners.empty() )
2519 return;
2521 m_pValueListener->EndListeningAll();
2523 if( !m_pDocument )
2524 return;
2526 ScChartListenerCollection* pCLC = nullptr;
2527 if (m_pHiddenListener)
2529 pCLC = m_pDocument->GetChartListenerCollection();
2530 if (pCLC)
2531 pCLC->EndListeningHiddenRange(m_pHiddenListener.get());
2534 for (const auto& rxToken : m_aTokens)
2536 ScRange aRange;
2537 if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, rxToken, ScAddress()))
2538 continue;
2540 m_pDocument->StartListeningArea(aRange, false, m_pValueListener.get());
2541 if (pCLC)
2542 pCLC->StartListeningHiddenRange(aRange, m_pHiddenListener.get());
2546 void ScChart2DataSequence::BuildDataCache()
2548 m_bExtDataRebuildQueued = false;
2550 if (!m_xDataArray->empty())
2551 return;
2553 StopListeningToAllExternalRefs();
2555 ::std::vector<sal_Int32> aHiddenValues;
2556 sal_Int32 nDataCount = 0;
2558 for (const auto& rxToken : m_aTokens)
2560 if (ScRefTokenHelper::isExternalRef(rxToken))
2562 nDataCount += FillCacheFromExternalRef(rxToken);
2564 else
2566 ScRange aRange;
2567 if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, rxToken, ScAddress()))
2568 continue;
2570 SCCOL nLastCol = -1;
2571 SCROW nLastRow = -1;
2572 for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
2574 for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol)
2576 sc::ColumnBlockPosition hint;
2577 m_pDocument->InitColumnBlockPosition( hint, nTab, nCol );
2578 for (SCROW nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow)
2580 if (nRow == aRange.aEnd.Row())
2582 // Excel behavior: if the last row is the totals row, the data
2583 // is not added to the chart. If it's not the last row, the data
2584 // is added like normal.
2585 const auto* pData = m_pDocument->GetDBAtCursor(
2586 nCol, nRow, nTab,
2587 ScDBDataPortion::AREA
2589 if (pData && pData->HasTotals())
2591 ScRange aTempRange;
2592 pData->GetArea(aTempRange);
2593 if (aTempRange.aEnd.Row() == nRow)
2595 // Current row is totals row, skip
2596 break;
2600 bool bColHidden = m_pDocument->ColHidden(nCol, nTab, nullptr, &nLastCol);
2601 bool bRowHidden = m_pDocument->RowHidden(nRow, nTab, nullptr, &nLastRow);
2603 if (bColHidden || bRowHidden)
2605 // hidden cell
2606 aHiddenValues.push_back(nDataCount-1);
2608 if( !m_bIncludeHiddenCells )
2609 continue;
2612 Item aItem;
2614 ScAddress aAdr(nCol, nRow, nTab);
2615 aItem.maString = m_pDocument->GetString(aAdr);
2617 ScRefCellValue aCell(*m_pDocument, aAdr, hint);
2618 switch (aCell.getType())
2620 case CELLTYPE_VALUE:
2621 aItem.mfValue = aCell.getValue();
2622 aItem.mbIsValue = true;
2623 break;
2624 case CELLTYPE_FORMULA:
2626 ScFormulaCell* pFCell = aCell.getFormula();
2627 FormulaError nErr = pFCell->GetErrCode();
2628 if (nErr != FormulaError::NONE)
2629 break;
2631 if (pFCell->IsValue())
2633 aItem.mfValue = pFCell->GetValue();
2634 aItem.mbIsValue = true;
2637 break;
2638 case CELLTYPE_EDIT:
2639 case CELLTYPE_NONE:
2640 case CELLTYPE_STRING:
2641 default:
2642 ; // do nothing
2645 aItem.mAddress = ScAddress(nCol, nRow, nTab);
2647 m_xDataArray->push_back(std::move(aItem));
2648 ++nDataCount;
2655 // convert the hidden cell list to sequence.
2656 m_aHiddenValues.realloc(aHiddenValues.size());
2657 std::copy(
2658 aHiddenValues.begin(), aHiddenValues.end(), m_aHiddenValues.getArray());
2660 // Clear the data series cache when the array is re-built.
2661 m_aMixedDataCache.realloc(0);
2664 void ScChart2DataSequence::RebuildDataCache()
2666 if (!m_bExtDataRebuildQueued)
2668 m_xDataArray.reset(new std::vector<Item>);
2669 m_pDocument->BroadcastUno(ScHint(SfxHintId::ScDataChanged, ScAddress()));
2670 m_bExtDataRebuildQueued = true;
2671 m_bGotDataChangedHint = true;
2675 sal_Int32 ScChart2DataSequence::FillCacheFromExternalRef(const ScTokenRef& pToken)
2677 ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager();
2678 ScRange aRange;
2679 if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, pToken, ScAddress(), true))
2680 return 0;
2682 sal_uInt16 nFileId = pToken->GetIndex();
2683 OUString aTabName = pToken->GetString().getString();
2684 ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens(nFileId, aTabName, aRange, nullptr);
2685 if (!pArray)
2686 // no external data exists for this range.
2687 return 0;
2689 // Start listening for this external document.
2690 ExternalRefListener* pExtRefListener = GetExtRefListener();
2691 pRefMgr->addLinkListener(nFileId, pExtRefListener);
2692 pExtRefListener->addFileId(nFileId);
2694 m_xDataArray.reset(new std::vector<Item>);
2695 ScExternalRefCache::TableTypeRef pTable = pRefMgr->getCacheTable(nFileId, aTabName, false);
2696 sal_Int32 nDataCount = 0;
2697 FormulaTokenArrayPlainIterator aIter(*pArray);
2698 for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
2700 // Cached external range is always represented as a single
2701 // matrix token, although that might change in the future when
2702 // we introduce a new token type to store multi-table range
2703 // data.
2705 if (p->GetType() != svMatrix)
2707 OSL_FAIL("Cached array is not a matrix token.");
2708 continue;
2711 const ScMatrix* pMat = p->GetMatrix();
2712 SCSIZE nCSize, nRSize;
2713 pMat->GetDimensions(nCSize, nRSize);
2714 for (SCSIZE nC = 0; nC < nCSize; ++nC)
2716 for (SCSIZE nR = 0; nR < nRSize; ++nR)
2718 if (pMat->IsValue(nC, nR) || pMat->IsBoolean(nC, nR))
2720 Item aItem;
2722 aItem.mbIsValue = true;
2723 aItem.mfValue = pMat->GetDouble(nC, nR);
2725 SvNumberFormatter* pFormatter = m_pDocument->GetFormatTable();
2726 if (pFormatter)
2728 const double fVal = aItem.mfValue;
2729 const Color* pColor = nullptr;
2730 sal_uInt32 nFmt = 0;
2731 if (pTable)
2733 // Get the correct format index from the cache.
2734 SCCOL nCol = aRange.aStart.Col() + static_cast<SCCOL>(nC);
2735 SCROW nRow = aRange.aStart.Row() + static_cast<SCROW>(nR);
2736 pTable->getCell(nCol, nRow, &nFmt);
2738 pFormatter->GetOutputString(fVal, nFmt, aItem.maString, &pColor);
2741 m_xDataArray->push_back(std::move(aItem));
2742 ++nDataCount;
2744 else if (pMat->IsStringOrEmpty(nC, nR))
2746 Item aItem;
2748 aItem.mbIsValue = false;
2749 aItem.maString = pMat->GetString(nC, nR).getString();
2751 m_xDataArray->push_back(std::move(aItem));
2752 ++nDataCount;
2757 return nDataCount;
2760 void ScChart2DataSequence::UpdateTokensFromRanges(const ScRangeList& rRanges)
2762 if (!m_oRangeIndices)
2763 return;
2765 for ( size_t i = 0, nCount = rRanges.size(); i < nCount; ++i )
2767 ScTokenRef pToken;
2768 const ScRange & rRange = rRanges[i];
2770 ScRefTokenHelper::getTokenFromRange(m_pDocument, pToken, rRange);
2771 sal_uInt32 nOrigPos = (*m_oRangeIndices)[i];
2772 m_aTokens[nOrigPos] = std::move(pToken);
2775 RefChanged();
2777 // any change of the range address is broadcast to value (modify) listeners
2778 if ( !m_aValueListeners.empty() )
2779 m_bGotDataChangedHint = true;
2782 ScChart2DataSequence::ExternalRefListener* ScChart2DataSequence::GetExtRefListener()
2784 if (!m_pExtRefListener)
2785 m_pExtRefListener.reset(new ExternalRefListener(*this, m_pDocument));
2787 return m_pExtRefListener.get();
2790 void ScChart2DataSequence::StopListeningToAllExternalRefs()
2792 if (!m_pExtRefListener)
2793 return;
2795 const std::unordered_set<sal_uInt16>& rFileIds = m_pExtRefListener->getAllFileIds();
2796 ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager();
2797 for (const auto& rFileId : rFileIds)
2798 pRefMgr->removeLinkListener(rFileId, m_pExtRefListener.get());
2800 m_pExtRefListener.reset();
2804 void ScChart2DataSequence::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
2806 if ( rHint.GetId() == SfxHintId::ScUpdateRef )
2808 // Create a range list from the token list, have the range list
2809 // updated, and bring the change back to the token list.
2811 ScRangeList aRanges;
2812 m_oRangeIndices.emplace();
2813 vector<ScTokenRef>::const_iterator itrBeg = m_aTokens.begin(), itrEnd = m_aTokens.end();
2814 for (vector<ScTokenRef>::const_iterator itr = itrBeg ;itr != itrEnd; ++itr)
2816 if (!ScRefTokenHelper::isExternalRef(*itr))
2818 ScRange aRange;
2819 ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, *itr, ScAddress());
2820 aRanges.push_back(aRange);
2821 sal_uInt32 nPos = distance(itrBeg, itr);
2822 m_oRangeIndices->push_back(nPos);
2826 assert(m_oRangeIndices->size() == aRanges.size() &&
2827 "range list and range index list have different sizes.");
2829 unique_ptr<ScRangeList> pUndoRanges;
2830 if ( m_pDocument->HasUnoRefUndo() )
2831 pUndoRanges.reset(new ScRangeList(aRanges));
2833 const ScUpdateRefHint& rRef = static_cast<const ScUpdateRefHint&>(rHint);
2834 bool bChanged = aRanges.UpdateReference(
2835 rRef.GetMode(), m_pDocument, rRef.GetRange(), rRef.GetDx(), rRef.GetDy(), rRef.GetDz());
2837 if (bChanged)
2839 // TODO: This should be an assert, but tdf#144537 triggers it.
2840 SAL_WARN_IF(m_oRangeIndices->size() == aRanges.size(),
2841 "sc.ui", "range list and range index list have different sizes after the reference update.");
2843 // Bring the change back from the range list to the token list.
2844 UpdateTokensFromRanges(aRanges);
2846 if (pUndoRanges)
2847 m_pDocument->AddUnoRefChange(m_nObjectId, *pUndoRanges);
2850 else if ( rHint.GetId() == SfxHintId::ScUnoRefUndo )
2852 auto pUndoHint = static_cast<const ScUnoRefUndoHint*>(&rHint);
2855 if (pUndoHint->GetObjectId() != m_nObjectId)
2856 break;
2858 // The hint object provides the old ranges. Restore the old state
2859 // from these ranges.
2861 if (!m_oRangeIndices || m_oRangeIndices->empty())
2863 assert(false && " faulty range indices");
2864 break;
2867 const ScRangeList& rRanges = pUndoHint->GetRanges();
2869 size_t nCount = rRanges.size();
2870 if (nCount != m_oRangeIndices->size())
2872 assert(false && "range count and range index count differ.");
2873 break;
2876 UpdateTokensFromRanges(rRanges);
2878 while (false);
2880 else
2882 const SfxHintId nId = rHint.GetId();
2883 if ( nId ==SfxHintId::Dying )
2885 m_pDocument = nullptr;
2887 else if ( nId == SfxHintId::DataChanged )
2889 // delayed broadcast as in ScCellRangesBase
2891 if ( m_bGotDataChangedHint && m_pDocument )
2893 m_xDataArray.reset(new std::vector<Item>);
2894 lang::EventObject aEvent;
2895 aEvent.Source = getXWeak();
2897 if( m_pDocument )
2899 for (const uno::Reference<util::XModifyListener> & xListener: m_aValueListeners)
2900 m_pDocument->AddUnoListenerCall( xListener, aEvent );
2903 m_bGotDataChangedHint = false;
2906 else if ( nId == SfxHintId::ScCalcAll )
2908 // broadcast from DoHardRecalc - set m_bGotDataChangedHint
2909 // (SfxHintId::DataChanged follows separately)
2911 if ( !m_aValueListeners.empty() )
2912 m_bGotDataChangedHint = true;
2914 else if (nId == SfxHintId::ScClearCache)
2916 // necessary after import
2917 m_xDataArray.reset(new std::vector<Item>);
2922 IMPL_LINK( ScChart2DataSequence, ValueListenerHdl, const SfxHint&, rHint, void )
2924 if ( m_pDocument && (rHint.GetId() == SfxHintId::ScDataChanged) )
2926 // This may be called several times for a single change, if several formulas
2927 // in the range are notified. So only a flag is set that is checked when
2928 // SfxHintId::DataChanged is received.
2930 setDataChangedHint(true);
2934 ScChart2DataSequence::ExternalRefListener::ExternalRefListener(
2935 ScChart2DataSequence& rParent, ScDocument* pDoc) :
2936 mrParent(rParent),
2937 mpDoc(pDoc)
2941 ScChart2DataSequence::ExternalRefListener::~ExternalRefListener()
2943 if (!mpDoc || mpDoc->IsInDtorClear())
2944 // The document is being destroyed. Do nothing.
2945 return;
2947 // Make sure to remove all pointers to this object.
2948 mpDoc->GetExternalRefManager()->removeLinkListener(this);
2951 void ScChart2DataSequence::ExternalRefListener::notify(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType)
2953 switch (eType)
2955 case ScExternalRefManager::LINK_MODIFIED:
2957 if (maFileIds.count(nFileId))
2958 // We are listening to this external document.
2959 mrParent.RebuildDataCache();
2961 break;
2962 case ScExternalRefManager::LINK_BROKEN:
2963 maFileIds.erase(nFileId);
2964 break;
2965 case ScExternalRefManager::OH_NO_WE_ARE_GOING_TO_DIE:
2966 mpDoc = nullptr;
2967 break;
2971 void ScChart2DataSequence::ExternalRefListener::addFileId(sal_uInt16 nFileId)
2973 maFileIds.insert(nFileId);
2976 uno::Sequence< uno::Any> SAL_CALL ScChart2DataSequence::getData()
2978 SolarMutexGuard aGuard;
2979 if ( !m_pDocument)
2980 throw uno::RuntimeException();
2982 BuildDataCache();
2984 if (!m_aMixedDataCache.hasElements())
2986 // Build a cache for the 1st time...
2988 sal_Int32 nCount = m_xDataArray->size();
2989 m_aMixedDataCache.realloc(nCount);
2990 uno::Any* pArr = m_aMixedDataCache.getArray();
2991 for (const Item &rItem : *m_xDataArray)
2993 if (rItem.mbIsValue)
2994 *pArr <<= rItem.mfValue;
2995 else if (rItem.maString.isEmpty())
2997 ScRefCellValue aCell(*m_pDocument, rItem.mAddress);
2998 if (aCell.isEmpty())
2999 *pArr = uno::Any();
3000 else
3001 *pArr <<= rItem.maString;
3003 else
3004 *pArr <<= rItem.maString;
3005 ++pArr;
3008 return m_aMixedDataCache;
3011 // XNumericalDataSequence --------------------------------------------------
3013 uno::Sequence< double > SAL_CALL ScChart2DataSequence::getNumericalData()
3015 SolarMutexGuard aGuard;
3016 if ( !m_pDocument)
3017 throw uno::RuntimeException();
3019 BuildDataCache();
3021 sal_Int32 nCount = m_xDataArray->size();
3022 uno::Sequence<double> aSeq(nCount);
3023 double* pArr = aSeq.getArray();
3024 for (const Item& rItem : *m_xDataArray)
3026 *pArr = rItem.mbIsValue ? rItem.mfValue : std::numeric_limits<double>::quiet_NaN();
3027 ++pArr;
3030 return aSeq;
3033 // XTextualDataSequence --------------------------------------------------
3035 uno::Sequence< OUString > SAL_CALL ScChart2DataSequence::getTextualData()
3037 SolarMutexGuard aGuard;
3038 uno::Sequence<OUString> aSeq;
3039 if ( !m_pDocument )
3040 throw uno::RuntimeException();
3042 BuildDataCache();
3044 sal_Int32 nCount = m_xDataArray->size();
3045 if ( nCount > 0 )
3047 aSeq = uno::Sequence<OUString>(nCount);
3048 OUString* pArr = aSeq.getArray();
3049 for (const Item& rItem : *m_xDataArray)
3051 *pArr = rItem.maString;
3052 ++pArr;
3055 else if ( m_aTokens.front() )
3057 if( m_aTokens.front()->GetType() == svString )
3059 aSeq = uno::Sequence<OUString> { m_aTokens.front()->GetString().getString() };
3063 return aSeq;
3066 OUString SAL_CALL ScChart2DataSequence::getSourceRangeRepresentation()
3068 SolarMutexGuard aGuard;
3069 OUString aStr;
3070 OSL_ENSURE( m_pDocument, "No Document -> no SourceRangeRepresentation" );
3071 if (m_pDocument)
3072 lcl_convertTokensToString(aStr, m_aTokens, *m_pDocument);
3074 return aStr;
3077 namespace {
3080 * This function object is used to accumulatively count the numbers of
3081 * columns and rows in all reference tokens.
3083 class AccumulateRangeSize
3085 public:
3086 AccumulateRangeSize(const ScDocument* pDoc) :
3087 mpDoc(pDoc), mnCols(0), mnRows(0) {}
3089 void operator() (const ScTokenRef& pToken)
3091 ScRange r;
3092 bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
3093 ScRefTokenHelper::getRangeFromToken(mpDoc, r, pToken, ScAddress(), bExternal);
3094 r.PutInOrder();
3095 mnCols += r.aEnd.Col() - r.aStart.Col() + 1;
3096 mnRows += r.aEnd.Row() - r.aStart.Row() + 1;
3099 SCCOL getCols() const { return mnCols; }
3100 SCROW getRows() const { return mnRows; }
3101 private:
3102 const ScDocument* mpDoc;
3103 SCCOL mnCols;
3104 SCROW mnRows;
3108 * This function object is used to generate label strings from a list of
3109 * reference tokens.
3111 class GenerateLabelStrings
3113 public:
3114 GenerateLabelStrings(const ScDocument* pDoc, sal_Int32 nSize, chart2::data::LabelOrigin eOrigin, bool bColumn) :
3115 mpDoc(pDoc),
3116 mpLabels(std::make_shared<Sequence<OUString>>(nSize)),
3117 meOrigin(eOrigin),
3118 mnCount(0),
3119 mbColumn(bColumn) {}
3121 void operator() (const ScTokenRef& pToken)
3123 bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
3124 ScRange aRange;
3125 ScRefTokenHelper::getRangeFromToken(mpDoc, aRange, pToken, ScAddress(), bExternal);
3126 OUString* pArr = mpLabels->getArray();
3127 if (mbColumn)
3129 for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol)
3131 if ( meOrigin != chart2::data::LabelOrigin_LONG_SIDE)
3133 OUString aString = ScResId(STR_COLUMN) + " ";
3134 ScAddress aPos( nCol, 0, 0 );
3135 OUString aColStr(aPos.Format(ScRefFlags::COL_VALID));
3136 aString += aColStr;
3137 pArr[mnCount] = aString;
3139 else //only indices for categories
3140 pArr[mnCount] = OUString::number( mnCount+1 );
3141 ++mnCount;
3144 else
3146 for (sal_Int32 nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow)
3148 if (meOrigin != chart2::data::LabelOrigin_LONG_SIDE)
3149 pArr[mnCount] = ScResId(STR_ROW) + " " + OUString::number(nRow + 1);
3150 else //only indices for categories
3151 pArr[mnCount] = OUString::number( mnCount+1 );
3152 ++mnCount;
3157 const Sequence<OUString>& getLabels() const { return *mpLabels; }
3159 private:
3160 const ScDocument* mpDoc;
3161 shared_ptr< Sequence<OUString> > mpLabels;
3162 chart2::data::LabelOrigin meOrigin;
3163 sal_Int32 mnCount;
3164 bool mbColumn;
3169 uno::Sequence< OUString > SAL_CALL ScChart2DataSequence::generateLabel(chart2::data::LabelOrigin eOrigin)
3171 SolarMutexGuard aGuard;
3172 if ( !m_pDocument)
3173 throw uno::RuntimeException();
3175 // Determine the total size of all ranges.
3176 AccumulateRangeSize func(m_pDocument);
3177 func = ::std::for_each(m_aTokens.begin(), m_aTokens.end(), func);
3178 SCCOL nCols = func.getCols();
3179 SCROW nRows = func.getRows();
3181 // Determine whether this is column-major or row-major.
3182 bool bColumn = true;
3183 if ((eOrigin == chart2::data::LabelOrigin_SHORT_SIDE) ||
3184 (eOrigin == chart2::data::LabelOrigin_LONG_SIDE))
3186 if (nRows > nCols)
3188 bColumn = eOrigin == chart2::data::LabelOrigin_SHORT_SIDE;
3190 else if (nCols > nRows)
3192 bColumn = eOrigin != chart2::data::LabelOrigin_SHORT_SIDE;
3194 else
3195 return Sequence<OUString>();
3198 // Generate label strings based on the info so far.
3199 sal_Int32 nCount = bColumn ? nCols : nRows;
3200 GenerateLabelStrings genLabels(m_pDocument, nCount, eOrigin, bColumn);
3201 genLabels = ::std::for_each(m_aTokens.begin(), m_aTokens.end(), genLabels);
3202 Sequence<OUString> aSeq = genLabels.getLabels();
3204 return aSeq;
3207 namespace {
3209 sal_uInt32 getDisplayNumberFormat(const ScDocument* pDoc, const ScAddress& rPos)
3211 sal_uInt32 nFormat = pDoc->GetNumberFormat(ScRange(rPos)); // original format from cell.
3212 return nFormat;
3217 ::sal_Int32 SAL_CALL ScChart2DataSequence::getNumberFormatKeyByIndex( ::sal_Int32 nIndex )
3219 SolarMutexGuard aGuard;
3220 BuildDataCache();
3222 if (nIndex == -1)
3224 // return format of first non-empty cell
3225 // TODO: use nicer heuristic
3226 for (const Item& rItem : *m_xDataArray)
3228 ScRefCellValue aCell(*m_pDocument, rItem.mAddress);
3229 if (!aCell.isEmpty() && aCell.hasNumeric())
3231 return static_cast<sal_Int32>(getDisplayNumberFormat(m_pDocument, rItem.mAddress));
3235 // we could not find a non-empty cell
3236 return 0;
3239 if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= m_xDataArray->size())
3241 SAL_WARN("sc.ui", "Passed invalid index to getNumberFormatKeyByIndex(). Will return default value '0'.");
3242 return 0;
3245 return static_cast<sal_Int32>(getDisplayNumberFormat(m_pDocument, m_xDataArray->at(nIndex).mAddress));
3248 // XCloneable ================================================================
3250 uno::Reference< util::XCloneable > SAL_CALL ScChart2DataSequence::createClone()
3252 SolarMutexGuard aGuard;
3254 rtl::Reference<ScChart2DataSequence> p(new ScChart2DataSequence(m_pDocument, *this));
3255 return p;
3258 // XModifyBroadcaster ========================================================
3260 void SAL_CALL ScChart2DataSequence::addModifyListener( const uno::Reference< util::XModifyListener >& aListener )
3262 // like ScCellRangesBase::addModifyListener
3263 SolarMutexGuard aGuard;
3264 if (m_aTokens.empty())
3265 return;
3267 ScRangeList aRanges;
3268 ScRefTokenHelper::getRangeListFromTokens(m_pDocument, aRanges, m_aTokens, ScAddress());
3269 m_aValueListeners.emplace_back( aListener );
3271 if ( m_aValueListeners.size() != 1 )
3272 return;
3274 if (!m_pValueListener)
3275 m_pValueListener.reset(new ScLinkListener( LINK( this, ScChart2DataSequence, ValueListenerHdl ) ));
3277 if (!m_pHiddenListener)
3278 m_pHiddenListener.reset(new HiddenRangeListener(*this));
3280 if( m_pDocument )
3282 ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection();
3283 for (const auto& rxToken : m_aTokens)
3285 ScRange aRange;
3286 if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, rxToken, ScAddress()))
3287 continue;
3289 m_pDocument->StartListeningArea( aRange, false, m_pValueListener.get() );
3290 if (pCLC)
3291 pCLC->StartListeningHiddenRange(aRange, m_pHiddenListener.get());
3295 acquire(); // don't lose this object (one ref for all listeners)
3298 void SAL_CALL ScChart2DataSequence::removeModifyListener( const uno::Reference< util::XModifyListener >& aListener )
3300 // like ScCellRangesBase::removeModifyListener
3302 SolarMutexGuard aGuard;
3303 if (m_aTokens.empty())
3304 return;
3306 rtl::Reference<ScChart2DataSequence> xSelfHold(this); // in case the listeners have the last ref
3308 sal_uInt16 nCount = m_aValueListeners.size();
3309 for ( sal_uInt16 n=nCount; n--; )
3311 uno::Reference<util::XModifyListener>& rObj = m_aValueListeners[n];
3312 if ( rObj == aListener )
3314 m_aValueListeners.erase( m_aValueListeners.begin() + n );
3316 if ( m_aValueListeners.empty() )
3318 if (m_pValueListener)
3319 m_pValueListener->EndListeningAll();
3321 if (m_pHiddenListener && m_pDocument)
3323 ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection();
3324 if (pCLC)
3325 pCLC->EndListeningHiddenRange(m_pHiddenListener.get());
3328 release(); // release the ref for the listeners
3331 break;
3336 // DataSequence XPropertySet -------------------------------------------------
3338 uno::Reference< beans::XPropertySetInfo> SAL_CALL
3339 ScChart2DataSequence::getPropertySetInfo()
3341 SolarMutexGuard aGuard;
3342 static uno::Reference<beans::XPropertySetInfo> aRef =
3343 new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() );
3344 return aRef;
3347 void SAL_CALL ScChart2DataSequence::setPropertyValue(
3348 const OUString& rPropertyName, const uno::Any& rValue)
3350 if ( rPropertyName == SC_UNONAME_ROLE )
3352 if ( !(rValue >>= m_aRole))
3353 throw lang::IllegalArgumentException();
3355 else if ( rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS )
3357 bool bOldValue = m_bIncludeHiddenCells;
3358 if ( !(rValue >>= m_bIncludeHiddenCells))
3359 throw lang::IllegalArgumentException();
3360 if( bOldValue != m_bIncludeHiddenCells )
3361 m_xDataArray.reset(new std::vector<Item>);//data array is dirty now
3363 else if( rPropertyName == "TimeBased" )
3365 bool bTimeBased = mbTimeBased;
3366 rValue>>= bTimeBased;
3367 mbTimeBased = bTimeBased;
3369 else
3370 throw beans::UnknownPropertyException(rPropertyName);
3371 // TODO: support optional properties
3374 uno::Any SAL_CALL ScChart2DataSequence::getPropertyValue(const OUString& rPropertyName)
3376 uno::Any aRet;
3377 if ( rPropertyName == SC_UNONAME_ROLE )
3378 aRet <<= m_aRole;
3379 else if ( rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS )
3380 aRet <<= m_bIncludeHiddenCells;
3381 else if ( rPropertyName == SC_UNONAME_HIDDENVALUES )
3383 // This property is read-only thus cannot be set externally via
3384 // setPropertyValue(...).
3385 BuildDataCache();
3386 aRet <<= m_aHiddenValues;
3388 else if (rPropertyName == SC_UNONAME_TIME_BASED)
3390 aRet <<= mbTimeBased;
3392 else if (rPropertyName == SC_UNONAME_HAS_STRING_LABEL)
3394 // Read-only property. It returns whether or not the label value is a
3395 // direct user input, rather than an indirect reference.
3396 bool bHasStringLabel = false;
3397 if (m_aTokens.size() == 1)
3399 const formula::FormulaToken& rToken = *m_aTokens[0];
3400 bHasStringLabel = rToken.GetType() == formula::svString;
3402 aRet <<= bHasStringLabel;
3404 else
3405 throw beans::UnknownPropertyException(rPropertyName);
3406 // TODO: support optional properties
3407 return aRet;
3410 void SAL_CALL ScChart2DataSequence::addPropertyChangeListener(
3411 const OUString& /*rPropertyName*/,
3412 const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/)
3414 // FIXME: real implementation
3415 OSL_FAIL( "Not yet implemented" );
3418 void SAL_CALL ScChart2DataSequence::removePropertyChangeListener(
3419 const OUString& /*rPropertyName*/,
3420 const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/)
3422 // FIXME: real implementation
3423 OSL_FAIL( "Not yet implemented" );
3426 void SAL_CALL ScChart2DataSequence::addVetoableChangeListener(
3427 const OUString& /*rPropertyName*/,
3428 const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/)
3430 // FIXME: real implementation
3431 OSL_FAIL( "Not yet implemented" );
3434 void SAL_CALL ScChart2DataSequence::removeVetoableChangeListener(
3435 const OUString& /*rPropertyName*/,
3436 const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/)
3438 // FIXME: real implementation
3439 OSL_FAIL( "Not yet implemented" );
3442 void ScChart2DataSequence::setDataChangedHint(bool b)
3444 m_bGotDataChangedHint = b;
3447 sal_Bool ScChart2DataSequence::switchToNext(sal_Bool bWrap)
3449 if(!mbTimeBased)
3450 return true;
3452 if(mnCurrentTab >= mnTimeBasedEnd)
3454 if(bWrap)
3455 setToPointInTime(0);
3456 return false;
3459 for(const auto& rxToken : m_aTokens)
3461 if (rxToken->GetType() != svDoubleRef)
3462 continue;
3464 ScComplexRefData& rData = *rxToken->GetDoubleRef();
3465 ScSingleRefData& s = rData.Ref1;
3466 ScSingleRefData& e = rData.Ref2;
3468 s.IncTab(1);
3469 e.IncTab(1);
3472 ++mnCurrentTab;
3474 RebuildDataCache();
3476 return true;
3479 void ScChart2DataSequence::setRange(sal_Int32 nStart, sal_Int32 nEnd)
3481 mnTimeBasedStart = nStart;
3482 mnTimeBasedEnd = nEnd;
3483 mnCurrentTab = mnTimeBasedStart;
3486 sal_Bool ScChart2DataSequence::setToPointInTime(sal_Int32 nPoint)
3488 if(nPoint > mnTimeBasedEnd - mnTimeBasedStart)
3489 return false;
3491 SCTAB nTab = mnTimeBasedStart + nPoint;
3492 for(const auto& rxToken : m_aTokens)
3494 if (rxToken->GetType() != svDoubleRef)
3495 continue;
3497 ScComplexRefData& rData = *rxToken->GetDoubleRef();
3498 ScSingleRefData& s = rData.Ref1;
3499 ScSingleRefData& e = rData.Ref2;
3501 s.SetAbsTab(nTab);
3502 e.SetAbsTab(nTab);
3505 mnCurrentTab = nTab;
3507 RebuildDataCache();
3509 return true;
3512 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */