1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
21 #include <sal/config.h>
22 #include <sal/log.hxx>
27 #include <chart2uno.hxx>
28 #include <miscuno.hxx>
29 #include <document.hxx>
31 #include <formulacell.hxx>
32 #include <unonames.hxx>
33 #include <globstr.hrc>
34 #include <scresid.hxx>
35 #include <rangeutl.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>
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>
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
;
77 using ::std::distance
;
78 using ::std::shared_ptr
;
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
) :
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( ' ' );
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 );
133 xModel
.set( pObjSh
->GetModel());
141 vector
<std::unique_ptr
<FormulaToken
>> maTokens
;
144 TokenTable(const TokenTable
&) = delete;
145 const TokenTable
& operator=(const TokenTable
&) = delete;
153 void init( SCCOL nColCount
, SCROW nRowCount
)
155 mnColCount
= nColCount
;
156 mnRowCount
= nRowCount
;
157 maTokens
.reserve(mnColCount
*mnRowCount
);
161 for (auto & rToken
: maTokens
)
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" );
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
>();
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();
200 ScTokenRef
pCopy(p
->Clone());
201 ScRefTokenHelper::join(pDoc
, aTokens
, pCopy
, ScAddress());
206 vector
<ScTokenRef
> TokenTable::getRowRanges(const ScDocument
* pDoc
, SCROW nRow
) const
208 if (nRow
>= mnRowCount
)
209 return vector
<ScTokenRef
>();
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();
221 ScTokenRef
p2(p
->Clone());
222 ScRefTokenHelper::join(pDoc
, aTokens
, p2
, ScAddress());
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();
237 ScTokenRef
p2(p
->Clone());
238 ScRefTokenHelper::join(pDoc
, aTokens
, p2
, ScAddress());
243 typedef std::map
<SCROW
, std::unique_ptr
<FormulaToken
>> FormulaTokenMap
;
244 typedef std::map
<sal_uInt32
, FormulaTokenMap
> FormulaTokenMapMap
;
246 class Chart2PositionMap
249 Chart2PositionMap(SCCOL nColCount
, SCROW nRowCount
,
250 bool bFillRowHeader
, bool bFillColumnHeader
, FormulaTokenMapMap
& rCols
,
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;
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
)
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
;
295 for (auto it
= rCols
.begin(); it
!= rCols
.end(); ++it
, ++nCol
)
297 // Skip header columns
298 if (nCol
< nHeaderColCount
)
301 const auto& rCol
= *it
;
303 bool bFoundValuesInCol
= false;
304 bool bFoundAnythingInCol
= false;
306 for (auto it2
= rCol
.second
.begin(); it2
!= rCol
.second
.end(); ++it2
, ++nRow
)
308 const auto& rCell
= *it2
;
311 if (nRow
< nHeaderRowCount
|| !rCell
.second
)
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
);
332 if ( pDoc
->HasData( nCol1
, nRow1
, nTab1
) )
334 // Found some other data (non-numeric)
335 bFoundAnythingInCol
= true;
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
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
);
382 if( nCol
< nHeaderColCount
)
384 if( nRow
< nHeaderRowCount
)
385 maLeftUpperCorner
.push_back(std::move(pToken
));
387 maRowHeaders
.push_back(std::move(pToken
));
389 else if( nRow
< nHeaderRowCount
)
390 maColHeaders
.push_back(std::move(pToken
));
392 maData
.push_back(std::move(pToken
));
399 Chart2PositionMap::~Chart2PositionMap()
401 maLeftUpperCorner
.clear();
402 maColHeaders
.clear();
403 maRowHeaders
.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
454 Chart2Positioner(const Chart2Positioner
&) = delete;
455 const Chart2Positioner
& operator=(const Chart2Positioner
&) = delete;
457 Chart2Positioner(ScDocument
* pDoc
, const vector
<ScTokenRef
>& rRefTokens
) :
458 mrRefTokens(rRefTokens
),
465 mbDummyUpperLeft(false)
469 void setHeaders(bool bColHeaders
, bool bRowHeaders
)
471 mbColHeaders
= bColHeaders
;
472 mbRowHeaders
= bRowHeaders
;
475 Chart2PositionMap
* getPositionMap()
478 return mpPositionMap
.get();
482 void invalidateGlue();
484 void calcGlueState(SCCOL nCols
, SCROW nRows
);
485 void createPositionMap();
488 const vector
<ScTokenRef
>& mrRefTokens
;
489 std::unique_ptr
<Chart2PositionMap
> mpPositionMap
;
496 bool mbDummyUpperLeft
:1;
499 void Chart2Positioner::invalidateGlue()
501 meGlue
= GLUETYPE_NA
;
502 mpPositionMap
.reset();
505 void Chart2Positioner::glueState()
507 if (meGlue
!= GLUETYPE_NA
)
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
;
521 meGlue
= GLUETYPE_COLS
;
522 mnStartCol
= aData
.Ref1
.Col();
523 mnStartRow
= aData
.Ref1
.Row();
534 ScComplexRefData aData
;
535 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData
, mrRefTokens
.front()))
537 SAL_WARN("sc", "Chart2Positioner::glueState getDoubleRefDataFromToken failed");
543 mnStartCol
= aData
.Ref1
.Col();
544 mnStartRow
= aData
.Ref1
.Row();
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();
558 mnStartCol
= static_cast<SCCOL
>(n1
);
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();
570 mnStartRow
= static_cast<SCROW
>(n1
);
572 nEndRow
= static_cast<SCROW
>(n2
);
575 if (mnStartCol
== nEndCol
)
577 // All source data is in a single column.
578 meGlue
= GLUETYPE_ROWS
;
582 if (mnStartRow
== nEndRow
)
584 // All source data is in a single row.
585 meGlue
= GLUETYPE_COLS
;
590 SCCOL nC
= nEndCol
- mnStartCol
+ 1;
593 SCROW nR
= nEndRow
- mnStartRow
+ 1;
595 // #i103540# prevent invalid vector size
596 if ((nC
<= 0) || (nR
<= 0))
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,
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)
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
;
668 bool bGlueRows
= false;
669 for (SCROW nRow
= 0; bGlue
&& nRow
< nRowSize
; ++nRow
)
672 for (SCCOL nCol
= 0; bGlue
&& nCol
< nColSize
; ++nCol
, i
+= nRowSize
)
674 if (aCellStates
[i
] == Occupied
)
676 if (nCol
== 0 || nRow
== 0)
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
;
694 for (sal_uInt32 n
= 1; bGlue
&& n
< nCR
; ++n
, ++i
)
695 if (aCellStates
[i
] == Hole
)
700 if (bGlueCols
&& bGlueRows
)
701 meGlue
= GLUETYPE_BOTH
;
703 meGlue
= GLUETYPE_ROWS
;
705 meGlue
= GLUETYPE_COLS
;
706 if (aCellStates
.front() != Occupied
)
707 mbDummyUpperLeft
= true;
710 meGlue
= GLUETYPE_NONE
;
713 void Chart2Positioner::createPositionMap()
715 if (meGlue
== GLUETYPE_NA
&& mpPositionMap
)
716 mpPositionMap
.reset();
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();
732 aTabName
= pToken
->GetString();
734 ScComplexRefData aData
;
735 if( !ScRefTokenHelper::getDoubleRefDataFromToken(aData
, pToken
) )
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
)
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
];
773 rCell
.reset(new ScExternalSingleRefToken(nFileId
, aTabName
, aCellData
));
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;
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 )
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 );
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
824 Tokens2RangeString(ScDocument
& rDoc
, FormulaGrammar::Grammar eGram
, sal_Unicode cRangeSep
) :
825 mpRangeStr(std::make_shared
<OUStringBuffer
>()),
828 mcRangeSep(cRangeSep
),
833 void operator() (const ScTokenRef
& rToken
)
835 ScCompiler
aCompiler(*mpDoc
, ScAddress(0,0,0), meGrammar
);
837 aCompiler
.CreateStringFromToken(aStr
, rToken
.get());
841 mpRangeStr
->append(mcRangeSep
);
842 mpRangeStr
->append(aStr
);
845 void getString(OUString
& rStr
)
847 rStr
= mpRangeStr
->makeStringAndClear();
851 shared_ptr
<OUStringBuffer
> mpRangeStr
;
853 FormulaGrammar::Grammar meGrammar
;
854 sal_Unicode mcRangeSep
;
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
869 explicit Tokens2RangeStringXML(ScDocument
& rDoc
) :
870 mpRangeStr(std::make_shared
<OUStringBuffer
>()),
876 void operator() (const ScTokenRef
& rToken
)
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");
900 ScCompiler
aCompiler(*mpDoc
, ScAddress(0,0,0), FormulaGrammar::GRAM_ENGLISH
);
903 aCompiler
.CreateStringFromToken(aStr
, aStart
.get());
904 mpRangeStr
->append(aStr
);
906 mpRangeStr
->append(mcAddrSep
);
909 aCompiler
.CreateStringFromToken(aStr
, aEnd
.get());
910 mpRangeStr
->append(aStr
);
914 void getString(OUString
& rStr
)
916 rStr
= mpRangeStr
->makeStringAndClear();
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");
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);
940 rStart
.reset(new ScExternalSingleRefToken(nFileId
, aTabName
, aData
.Ref1
));
942 rStart
.reset(new ScSingleRefToken(rDoc
.GetSheetLimits(), aData
.Ref1
));
945 rEnd
.reset(new ScExternalSingleRefToken(nFileId
, aTabName
, aData
.Ref2
));
947 rEnd
.reset(new ScSingleRefToken(rDoc
.GetSheetLimits(), aData
.Ref2
));
951 static void setRelative(ScSingleRefData
& rData
)
953 rData
.SetColRel(true);
954 rData
.SetRowRel(true);
955 rData
.SetTabRel(true);
959 shared_ptr
<OUStringBuffer
> mpRangeStr
;
961 static const sal_Unicode mcRangeSep
= ' ';
962 static const sal_Unicode mcAddrSep
= ':';
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
)
981 , m_aPropSet(lcl_GetDataProviderPropertyMap())
982 , m_bIncludeHiddenCells( true)
985 m_pDocument
->AddUnoObject( *this);
988 ScChart2DataProvider::~ScChart2DataProvider()
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
;
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();
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
);
1047 uno::Reference
< chart2::data::XDataSequence
> xSeq( new ScChart2DataSequence( pDoc
, std::move(aValueTokens
), bIncludeHiddenCells
) );
1048 xResult
->setValues( xSeq
);
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
& )
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
)
1079 if (rRefTokens
.empty())
1082 SCCOL nMinCol
= pDoc
->GetSheetLimits().GetMaxColCount();
1083 SCROW nMinRow
= pDoc
->GetSheetLimits().GetMaxRowCount();
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())
1100 const ScSingleRefData
& rData
= *pToken
->GetSingleRef();
1101 nMinCol
= rData
.Col();
1102 nMinRow
= rData
.Row();
1103 nMaxCol
= rData
.Col();
1104 nMaxRow
= rData
.Row();
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();
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();
1126 nFileId
= pToken
->GetIndex();
1127 aExtTabName
= pToken
->GetString();
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();
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
)
1154 switch (pToken
->GetType())
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
)
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
)
1186 case svExternalSingleRef
:
1191 if (nFileId
!= pToken
->GetIndex() || aExtTabName
!= pToken
->GetString())
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());
1202 case svExternalDoubleRef
:
1207 if (nFileId
!= pToken
->GetIndex() || aExtTabName
!= pToken
->GetString())
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());
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.
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())
1248 case svExternalSingleRef
:
1250 const ScSingleRefData
& rData
= *rxToken
->GetSingleRef();
1251 if (rData
.Col() == nMinCol
&& rData
.Row() == nMinRow
)
1252 // The corner cell is contained.
1255 if (rData
.Col() == nMinCol
+nCornerColumnCount
&& rData
.Row() == nMinRow
)
1258 if (rData
.Col() == nMinCol
&& rData
.Row() == nMinRow
+nCornerRowCount
)
1261 if (rData
.Col() == nMinCol
+nCornerColumnCount
&& rData
.Row() == nMinRow
+nCornerRowCount
)
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.
1276 if (r1
.Col() <= nMinCol
+nCornerColumnCount
&& nMinCol
+nCornerColumnCount
<= r2
.Col() &&
1277 r1
.Row() <= nMinRow
&& nMinRow
<= r2
.Row())
1280 if (r1
.Col() <= nMinCol
&& nMinCol
<= r2
.Col() &&
1281 r1
.Row() <= nMinRow
+nCornerRowCount
&& nMinRow
+nCornerRowCount
<= r2
.Row())
1284 if (r1
.Col() <= nMinCol
+nCornerColumnCount
&& nMinCol
+nCornerColumnCount
<= r2
.Col() &&
1285 r1
.Row() <= nMinRow
+nCornerRowCount
&& nMinRow
+nCornerRowCount
<= r2
.Row())
1294 if (!bRight
|| !bBottom
|| !bDiagonal
)
1295 // Not all the adjacent cells are included. Bail out.
1298 ScSingleRefData aData
;
1300 aData
.SetFlag3D(true);
1301 aData
.SetAbsCol(nMinCol
);
1302 aData
.SetAbsRow(nMinRow
);
1303 aData
.SetAbsTab(nTab
);
1305 if( nCornerRowCount
==1 && nCornerColumnCount
==1 )
1310 new ScExternalSingleRefToken(nFileId
, std::move(aExtTabName
), aData
));
1311 ScRefTokenHelper::join(pDoc
, rRefTokens
, pCorner
, ScAddress());
1315 ScTokenRef
pCorner(new ScSingleRefToken(pDoc
->GetSheetLimits(), aData
));
1316 ScRefTokenHelper::join(pDoc
, rRefTokens
, pCorner
, ScAddress());
1321 ScSingleRefData
aDataEnd(aData
);
1322 aDataEnd
.IncCol(nCornerColumnCount
-1);
1323 aDataEnd
.IncRow(nCornerRowCount
-1);
1330 new ScExternalDoubleRefToken(nFileId
, std::move(aExtTabName
), r
));
1331 ScRefTokenHelper::join(pDoc
, rRefTokens
, pCorner
, ScAddress());
1335 ScTokenRef
pCorner(new ScDoubleRefToken(pDoc
->GetSheetLimits(), r
));
1336 ScRefTokenHelper::join(pDoc
, rRefTokens
, pCorner
, ScAddress());
1343 #define SHRINK_RANGE_THRESHOLD 10000
1345 class ShrinkRefTokenToDataRange
1349 explicit ShrinkRefTokenToDataRange(ScDocument
* pDoc
) : mpDoc(pDoc
) {}
1350 void operator() (const ScTokenRef
& rRef
)
1352 if (ScRefTokenHelper::isExternalRef(rRef
))
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
)
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
)
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
;
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;
1464 // limit to first sheet
1465 for(const auto& rxToken
: aRefTokens
)
1467 if (rxToken
->GetType() != svDoubleRef
)
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());
1483 shrinkToDataRange(m_pDocument
, aRefTokens
);
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();
1496 // No chart position map instance. Bail out.
1499 rtl::Reference
<ScChart2DataSource
> pDS
;
1500 ::std::vector
< uno::Reference
< chart2::data::XLabeledDataSequence
> > aSeqs
;
1505 vector
<ScTokenRef
> aValueTokens
;
1507 aValueTokens
= pChartMap
->getAllRowHeaderRanges();
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
;
1530 aValueTokens
= pChartMap
->getDataColRanges(static_cast<SCCOL
>(i
));
1531 aLabelTokens
= pChartMap
->getColHeaderRanges(static_cast<SCCOL
>(i
));
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
)
1571 pDS
->AddLabeledSequence( xSeq
);
1583 * Function object to create a list of table numbers from a token list.
1585 class InsertTabNumber
1589 mpTabNumVector(std::make_shared
<vector
<SCTAB
>>())
1593 void operator() (const ScTokenRef
& pToken
) const
1595 if (!ScRefTokenHelper::isRef(pToken
))
1598 const ScSingleRefData
& r
= *pToken
->GetSingleRef();
1599 mpTabNumVector
->push_back(r
.Tab());
1602 void getVector(vector
<SCTAB
>& rVector
)
1604 mpTabNumVector
->swap(rVector
);
1607 shared_ptr
< vector
<SCTAB
> > mpTabNumVector
;
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
; }
1626 SCCOL mnColumnCount
;
1628 SCCOL mnStartColumn
;
1632 RangeAnalyzer::RangeAnalyzer()
1634 , mbAmbiguous(false)
1642 void RangeAnalyzer::initRangeAnalyzer( const ScDocument
* pDoc
, const vector
<ScTokenRef
>& rTokens
)
1649 if( rTokens
.empty() )
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();
1673 if (mnStartColumn
!= r
.Ref1
.Col() && mnStartRow
!= r
.Ref1
.Row())
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();
1692 if (mnStartColumn
!= r
.Col() && mnStartRow
!= r
.Row())
1696 else if (eVar
== svIndex
&& aRefToken
->GetOpCode() == ocName
)
1698 ScRangeData
* pNameRange
= pDoc
->FindRangeNameBySheetAndIndex(aRefToken
->GetSheet(), aRefToken
->GetIndex());
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();
1711 if (mnStartColumn
!= aRange
.aStart
.Col() && mnStartRow
!= aRange
.aStart
.Row())
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 )
1731 else if( mnColumnCount
==1 && mnRowCount
>1 )
1733 else if( mnRowCount
>1 && mnColumnCount
>1 )
1734 rbRowSourceAmbiguous
= true;
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();
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;
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
)
1800 bool bThisIsCategories
= false;
1803 uno::Reference
< beans::XPropertySet
> xSeqProp( xLS
->getValues(), uno::UNO_QUERY
);
1805 if( xSeqProp
.is() && (xSeqProp
->getPropertyValue(u
"Role"_ustr
) >>= aRole
) &&
1806 aRole
== "categories" )
1807 bThisIsCategories
= bHasCategories
= true;
1810 RangeAnalyzer aLabel
,aValues
;
1812 uno::Reference
< chart2::data::XDataSequence
> xLabel( xLS
->getLabel());
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());
1832 ScRefTokenHelper::join(m_pDocument
, aAllTokens
, rxToken
, ScAddress());
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;
1843 uno::Reference
< chart2::data::XDataSequence
> xValues( xLS
->getValues());
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());
1862 ScRefTokenHelper::join(m_pDocument
, aAllTokens
, rxToken
, ScAddress());
1865 ScRefTokenHelper::join(m_pDocument
, aAllTokens
, rxToken
, ScAddress());
1866 if(bThisIsCategories
)
1867 ScRefTokenHelper::join(m_pDocument
, aAllCategoriesValuesTokens
, rxToken
, ScAddress());
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
) )
1883 else if( aValues
.inSameSingleRow( aLabel
) )
1887 //#i86188# also detect a single column split into rows correctly
1888 if( aValues
.inSameSingleColumn( aPrevValues
) )
1890 else if( aValues
.inSameSingleRow( aPrevValues
) )
1892 else if( aLabel
.inSameSingleColumn( aPrevLabel
) )
1894 else if( aLabel
.inSameSingleRow( aPrevLabel
) )
1900 aPrevValues
=aValues
;
1905 if (!bRowSourceAmbiguous
)
1907 bRowSourceDetected
= true;
1908 eRowSource
= ( nDataInCols
> 0
1909 ? chart::ChartDataRowSource_COLUMNS
1910 : chart::ChartDataRowSource_ROWS
);
1914 // set DataRowSource to the better of the two ambiguities
1915 eRowSource
= ( nDataInRows
> nDataInCols
1916 ? chart::ChartDataRowSource_ROWS
1917 : chart::ChartDataRowSource_COLUMNS
);
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
);
1939 aResult
.emplace_back( "HasCategories", -1,
1940 uno::Any( bHasCategories
), beans::PropertyState_DIRECT_VALUE
);
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
);
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
);
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
] );
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
] );
2013 std::pair
<OUString
, OUString
> aKey
= constructKey(xNew
);
2014 if (aOldEntryToIndex
.find(aKey
) == aOldEntryToIndex
.end())
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
;
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())
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())
2067 shrinkToDataRange(m_pDocument
, aRefTokens
);
2069 xResult
.set(new ScChart2DataSequence(m_pDocument
, std::move(aRefTokens
), m_bIncludeHiddenCells
));
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
));
2088 xResult
.set( xModel
->getCurrentController(), uno::UNO_QUERY
);
2093 sal_Bool SAL_CALL
ScChart2DataProvider::createDataSequenceByFormulaTokensPossible(
2094 const Sequence
<sheet::FormulaToken
>& aTokens
)
2096 if (!aTokens
.hasElements())
2099 ScTokenArray
aCode(*m_pDocument
);
2100 if (!ScTokenConversion::ConvertToTokenArray(*m_pDocument
, aCode
, aTokens
))
2103 sal_uInt16 n
= aCode
.GetLen();
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())
2116 switch (p
->GetOpCode())
2119 // separators are allowed.
2123 // open paran is allowed only as the first token.
2128 // close paren is allowed only as the last token.
2138 case svExternalSingleRef
:
2139 case svExternalDoubleRef
:
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())
2157 ScTokenArray
aCode(*m_pDocument
);
2158 if (!ScTokenConversion::ConvertToTokenArray(*m_pDocument
, aCode
, aTokens
))
2161 sal_uInt16 n
= aCode
.GetLen();
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())
2175 switch (p
->GetOpCode())
2178 // separators are allowed.
2182 // open paran is allowed only as the first token.
2183 throw lang::IllegalArgumentException();
2187 // close paren is allowed only as the last token.
2188 throw lang::IllegalArgumentException();
2191 throw lang::IllegalArgumentException();
2199 case svExternalSingleRef
:
2200 case svExternalDoubleRef
:
2202 ScTokenRef
pNew(p
->Clone());
2203 aRefTokens
.push_back(pNew
);
2207 throw lang::IllegalArgumentException();
2211 if (aRefTokens
.empty())
2214 shrinkToDataRange(m_pDocument
, aRefTokens
);
2216 xResult
.set(new ScChart2DataSequence(m_pDocument
, std::move(aRefTokens
), m_bIncludeHiddenCells
));
2220 // XRangeXMLConversion ---------------------------------------------------
2222 OUString SAL_CALL
ScChart2DataProvider::convertRangeToXML( const OUString
& sRangeRepresentation
)
2228 if (sRangeRepresentation
.isEmpty())
2229 // Empty data range is allowed.
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
);
2249 OUString SAL_CALL
ScChart2DataProvider::convertRangeFromXML( const OUString
& sXMLRange
)
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 )
2261 ScRangeStringConverter::GetTokenByOffset( sToken
, sXMLRange
, nOffset
);
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() )
2278 sRet
.append( aUIString
);
2282 return sRet
.makeStringAndClear();
2286 ScRangeStringConverter::GetStringFromXMLRangeString(aRet
, sXMLRange
, *m_pDocument
);
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() );
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
)
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();
2324 throw beans::UnknownPropertyException(rPropertyName
);
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
)
2362 m_pDocument
->AddUnoObject( *this);
2365 ScChart2DataSource::~ScChart2DataSource()
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())
2401 ScChart2DataSequence::HiddenRangeListener::HiddenRangeListener(ScChart2DataSequence
& 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
)
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)
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
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
)
2456 , m_pDocument( pDoc
)
2457 , m_aPropSet(lcl_GetDataSequencePropertyMap())
2458 , m_bGotDataChangedHint(false)
2459 , m_bExtDataRebuildQueued(false)
2460 , mbTimeBased(false)
2461 , mnTimeBasedStart(0)
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
)
2484 // Re-register all external files that the old instance was
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()
2503 m_pDocument
->RemoveUnoObject( *this);
2504 if (m_pHiddenListener
)
2506 ScChartListenerCollection
* pCLC
= m_pDocument
->GetChartListenerCollection();
2508 pCLC
->EndListeningHiddenRange(m_pHiddenListener
.get());
2510 StopListeningToAllExternalRefs();
2513 m_pValueListener
.reset();
2516 void ScChart2DataSequence::RefChanged()
2518 if( !m_pValueListener
|| m_aValueListeners
.empty() )
2521 m_pValueListener
->EndListeningAll();
2526 ScChartListenerCollection
* pCLC
= nullptr;
2527 if (m_pHiddenListener
)
2529 pCLC
= m_pDocument
->GetChartListenerCollection();
2531 pCLC
->EndListeningHiddenRange(m_pHiddenListener
.get());
2534 for (const auto& rxToken
: m_aTokens
)
2537 if (!ScRefTokenHelper::getRangeFromToken(m_pDocument
, aRange
, rxToken
, ScAddress()))
2540 m_pDocument
->StartListeningArea(aRange
, false, m_pValueListener
.get());
2542 pCLC
->StartListeningHiddenRange(aRange
, m_pHiddenListener
.get());
2546 void ScChart2DataSequence::BuildDataCache()
2548 m_bExtDataRebuildQueued
= false;
2550 if (!m_xDataArray
->empty())
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
);
2567 if (!ScRefTokenHelper::getRangeFromToken(m_pDocument
, aRange
, rxToken
, ScAddress()))
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(
2587 ScDBDataPortion::AREA
2589 if (pData
&& pData
->HasTotals())
2592 pData
->GetArea(aTempRange
);
2593 if (aTempRange
.aEnd
.Row() == nRow
)
2595 // Current row is totals row, skip
2600 bool bColHidden
= m_pDocument
->ColHidden(nCol
, nTab
, nullptr, &nLastCol
);
2601 bool bRowHidden
= m_pDocument
->RowHidden(nRow
, nTab
, nullptr, &nLastRow
);
2603 if (bColHidden
|| bRowHidden
)
2606 aHiddenValues
.push_back(nDataCount
-1);
2608 if( !m_bIncludeHiddenCells
)
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;
2624 case CELLTYPE_FORMULA
:
2626 ScFormulaCell
* pFCell
= aCell
.getFormula();
2627 FormulaError nErr
= pFCell
->GetErrCode();
2628 if (nErr
!= FormulaError::NONE
)
2631 if (pFCell
->IsValue())
2633 aItem
.mfValue
= pFCell
->GetValue();
2634 aItem
.mbIsValue
= true;
2640 case CELLTYPE_STRING
:
2645 aItem
.mAddress
= ScAddress(nCol
, nRow
, nTab
);
2647 m_xDataArray
->push_back(std::move(aItem
));
2655 // convert the hidden cell list to sequence.
2656 m_aHiddenValues
.realloc(aHiddenValues
.size());
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();
2679 if (!ScRefTokenHelper::getRangeFromToken(m_pDocument
, aRange
, pToken
, ScAddress(), true))
2682 sal_uInt16 nFileId
= pToken
->GetIndex();
2683 OUString aTabName
= pToken
->GetString().getString();
2684 ScExternalRefCache::TokenArrayRef pArray
= pRefMgr
->getDoubleRefTokens(nFileId
, aTabName
, aRange
, nullptr);
2686 // no external data exists for this range.
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
2705 if (p
->GetType() != svMatrix
)
2707 OSL_FAIL("Cached array is not a matrix token.");
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
))
2722 aItem
.mbIsValue
= true;
2723 aItem
.mfValue
= pMat
->GetDouble(nC
, nR
);
2725 SvNumberFormatter
* pFormatter
= m_pDocument
->GetFormatTable();
2728 const double fVal
= aItem
.mfValue
;
2729 const Color
* pColor
= nullptr;
2730 sal_uInt32 nFmt
= 0;
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
));
2744 else if (pMat
->IsStringOrEmpty(nC
, nR
))
2748 aItem
.mbIsValue
= false;
2749 aItem
.maString
= pMat
->GetString(nC
, nR
).getString();
2751 m_xDataArray
->push_back(std::move(aItem
));
2760 void ScChart2DataSequence::UpdateTokensFromRanges(const ScRangeList
& rRanges
)
2762 if (!m_oRangeIndices
)
2765 for ( size_t i
= 0, nCount
= rRanges
.size(); i
< nCount
; ++i
)
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
);
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
)
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
))
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());
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
);
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
)
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");
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.");
2876 UpdateTokensFromRanges(rRanges
);
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();
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
) :
2941 ScChart2DataSequence::ExternalRefListener::~ExternalRefListener()
2943 if (!mpDoc
|| mpDoc
->IsInDtorClear())
2944 // The document is being destroyed. Do nothing.
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
)
2955 case ScExternalRefManager::LINK_MODIFIED
:
2957 if (maFileIds
.count(nFileId
))
2958 // We are listening to this external document.
2959 mrParent
.RebuildDataCache();
2962 case ScExternalRefManager::LINK_BROKEN
:
2963 maFileIds
.erase(nFileId
);
2965 case ScExternalRefManager::OH_NO_WE_ARE_GOING_TO_DIE
:
2971 void ScChart2DataSequence::ExternalRefListener::addFileId(sal_uInt16 nFileId
)
2973 maFileIds
.insert(nFileId
);
2976 uno::Sequence
< uno::Any
> SAL_CALL
ScChart2DataSequence::getData()
2978 SolarMutexGuard aGuard
;
2980 throw uno::RuntimeException();
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())
3001 *pArr
<<= rItem
.maString
;
3004 *pArr
<<= rItem
.maString
;
3008 return m_aMixedDataCache
;
3011 // XNumericalDataSequence --------------------------------------------------
3013 uno::Sequence
< double > SAL_CALL
ScChart2DataSequence::getNumericalData()
3015 SolarMutexGuard aGuard
;
3017 throw uno::RuntimeException();
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();
3033 // XTextualDataSequence --------------------------------------------------
3035 uno::Sequence
< OUString
> SAL_CALL
ScChart2DataSequence::getTextualData()
3037 SolarMutexGuard aGuard
;
3038 uno::Sequence
<OUString
> aSeq
;
3040 throw uno::RuntimeException();
3044 sal_Int32 nCount
= m_xDataArray
->size();
3047 aSeq
= uno::Sequence
<OUString
>(nCount
);
3048 OUString
* pArr
= aSeq
.getArray();
3049 for (const Item
& rItem
: *m_xDataArray
)
3051 *pArr
= rItem
.maString
;
3055 else if ( m_aTokens
.front() )
3057 if( m_aTokens
.front()->GetType() == svString
)
3059 aSeq
= uno::Sequence
<OUString
> { m_aTokens
.front()->GetString().getString() };
3066 OUString SAL_CALL
ScChart2DataSequence::getSourceRangeRepresentation()
3068 SolarMutexGuard aGuard
;
3070 OSL_ENSURE( m_pDocument
, "No Document -> no SourceRangeRepresentation" );
3072 lcl_convertTokensToString(aStr
, m_aTokens
, *m_pDocument
);
3080 * This function object is used to accumulatively count the numbers of
3081 * columns and rows in all reference tokens.
3083 class AccumulateRangeSize
3086 AccumulateRangeSize(const ScDocument
* pDoc
) :
3087 mpDoc(pDoc
), mnCols(0), mnRows(0) {}
3089 void operator() (const ScTokenRef
& pToken
)
3092 bool bExternal
= ScRefTokenHelper::isExternalRef(pToken
);
3093 ScRefTokenHelper::getRangeFromToken(mpDoc
, r
, pToken
, ScAddress(), bExternal
);
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
; }
3102 const ScDocument
* mpDoc
;
3108 * This function object is used to generate label strings from a list of
3111 class GenerateLabelStrings
3114 GenerateLabelStrings(const ScDocument
* pDoc
, sal_Int32 nSize
, chart2::data::LabelOrigin eOrigin
, bool bColumn
) :
3116 mpLabels(std::make_shared
<Sequence
<OUString
>>(nSize
)),
3119 mbColumn(bColumn
) {}
3121 void operator() (const ScTokenRef
& pToken
)
3123 bool bExternal
= ScRefTokenHelper::isExternalRef(pToken
);
3125 ScRefTokenHelper::getRangeFromToken(mpDoc
, aRange
, pToken
, ScAddress(), bExternal
);
3126 OUString
* pArr
= mpLabels
->getArray();
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
));
3137 pArr
[mnCount
] = aString
;
3139 else //only indices for categories
3140 pArr
[mnCount
] = OUString::number( mnCount
+1 );
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 );
3157 const Sequence
<OUString
>& getLabels() const { return *mpLabels
; }
3160 const ScDocument
* mpDoc
;
3161 shared_ptr
< Sequence
<OUString
> > mpLabels
;
3162 chart2::data::LabelOrigin meOrigin
;
3169 uno::Sequence
< OUString
> SAL_CALL
ScChart2DataSequence::generateLabel(chart2::data::LabelOrigin eOrigin
)
3171 SolarMutexGuard aGuard
;
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
))
3188 bColumn
= eOrigin
== chart2::data::LabelOrigin_SHORT_SIDE
;
3190 else if (nCols
> nRows
)
3192 bColumn
= eOrigin
!= chart2::data::LabelOrigin_SHORT_SIDE
;
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();
3209 sal_uInt32
getDisplayNumberFormat(const ScDocument
* pDoc
, const ScAddress
& rPos
)
3211 sal_uInt32 nFormat
= pDoc
->GetNumberFormat(ScRange(rPos
)); // original format from cell.
3217 ::sal_Int32 SAL_CALL
ScChart2DataSequence::getNumberFormatKeyByIndex( ::sal_Int32 nIndex
)
3219 SolarMutexGuard aGuard
;
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
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'.");
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));
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())
3267 ScRangeList aRanges
;
3268 ScRefTokenHelper::getRangeListFromTokens(m_pDocument
, aRanges
, m_aTokens
, ScAddress());
3269 m_aValueListeners
.emplace_back( aListener
);
3271 if ( m_aValueListeners
.size() != 1 )
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));
3282 ScChartListenerCollection
* pCLC
= m_pDocument
->GetChartListenerCollection();
3283 for (const auto& rxToken
: m_aTokens
)
3286 if (!ScRefTokenHelper::getRangeFromToken(m_pDocument
, aRange
, rxToken
, ScAddress()))
3289 m_pDocument
->StartListeningArea( aRange
, false, m_pValueListener
.get() );
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())
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();
3325 pCLC
->EndListeningHiddenRange(m_pHiddenListener
.get());
3328 release(); // release the ref for the listeners
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() );
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
;
3370 throw beans::UnknownPropertyException(rPropertyName
);
3371 // TODO: support optional properties
3374 uno::Any SAL_CALL
ScChart2DataSequence::getPropertyValue(const OUString
& rPropertyName
)
3377 if ( rPropertyName
== SC_UNONAME_ROLE
)
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(...).
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
;
3405 throw beans::UnknownPropertyException(rPropertyName
);
3406 // TODO: support optional properties
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
)
3452 if(mnCurrentTab
>= mnTimeBasedEnd
)
3455 setToPointInTime(0);
3459 for(const auto& rxToken
: m_aTokens
)
3461 if (rxToken
->GetType() != svDoubleRef
)
3464 ScComplexRefData
& rData
= *rxToken
->GetDoubleRef();
3465 ScSingleRefData
& s
= rData
.Ref1
;
3466 ScSingleRefData
& e
= rData
.Ref2
;
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
)
3491 SCTAB nTab
= mnTimeBasedStart
+ nPoint
;
3492 for(const auto& rxToken
: m_aTokens
)
3494 if (rxToken
->GetType() != svDoubleRef
)
3497 ScComplexRefData
& rData
= *rxToken
->GetDoubleRef();
3498 ScSingleRefData
& s
= rData
.Ref1
;
3499 ScSingleRefData
& e
= rData
.Ref2
;
3505 mnCurrentTab
= nTab
;
3512 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */