1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <memory>
21 #include <sal/config.h>
22 #include <sal/log.hxx>
23
24 #include <algorithm>
25 #include <utility>
26
27 #include <chart2uno.hxx>
28 #include <miscuno.hxx>
29 #include <document.hxx>
30 #include <formulacell.hxx>
31 #include <unonames.hxx>
32 #include <globstr.hrc>
33 #include <scresid.hxx>
34 #include <rangeutl.hxx>
35 #include <hints.hxx>
36 #include <unoreflist.hxx>
37 #include <compiler.hxx>
38 #include <reftokenhelper.hxx>
39 #include <chartlis.hxx>
40 #include <tokenuno.hxx>
41 #include <cellvalue.hxx>
42 #include <tokenarray.hxx>
43 #include <scmatrix.hxx>
44 #include <brdcst.hxx>
45 #include <mtvelements.hxx>
46
47 #include <formula/opcode.hxx>
48 #include <svl/sharedstring.hxx>
49
50 #include <sfx2/objsh.hxx>
51 #include <vcl/svapp.hxx>
52
53 #include <com/sun/star/beans/UnknownPropertyException.hpp>
54 #include <com/sun/star/chart/ChartDataRowSource.hpp>
55 #include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
56 #include <com/sun/star/frame/XModel.hpp>
57 #include <comphelper/extract.hxx>
58 #include <comphelper/processfactory.hxx>
59 #include <comphelper/sequence.hxx>
60
61 #include <rtl/math.hxx>
62
63 SC_SIMPLE_SERVICE_INFO( ScChart2DataProvider, "ScChart2DataProvider",
64 "com.sun.star.chart2.data.DataProvider")
65 SC_SIMPLE_SERVICE_INFO( ScChart2DataSource, "ScChart2DataSource",
66 "com.sun.star.chart2.data.DataSource")
67 SC_SIMPLE_SERVICE_INFO( ScChart2DataSequence, "ScChart2DataSequence",
68 "com.sun.star.chart2.data.DataSequence")
69
70 using namespace ::com::sun::star;
71 using namespace ::formula;
72 using ::com::sun::star::uno::Sequence;
73 using ::std::unique_ptr;
74 using ::std::vector;
75 using ::std::distance;
76 using ::std::shared_ptr;
77
78 namespace
79 {
lcl_GetDataProviderPropertyMap()80 const SfxItemPropertyMapEntry* lcl_GetDataProviderPropertyMap()
81 {
82 static const SfxItemPropertyMapEntry aDataProviderPropertyMap_Impl[] =
83 {
84 { OUString(SC_UNONAME_INCLUDEHIDDENCELLS), 0, cppu::UnoType<bool>::get(), 0, 0 },
85 { OUString(SC_UNONAME_USE_INTERNAL_DATA_PROVIDER), 0, cppu::UnoType<bool>::get(), 0, 0 },
86 { OUString(), 0, css::uno::Type(), 0, 0 }
87 };
88 return aDataProviderPropertyMap_Impl;
89 }
90
lcl_GetDataSequencePropertyMap()91 const SfxItemPropertyMapEntry* lcl_GetDataSequencePropertyMap()
92 {
93 static const SfxItemPropertyMapEntry aDataSequencePropertyMap_Impl[] =
94 {
95 {OUString(SC_UNONAME_HIDDENVALUES), 0, cppu::UnoType<uno::Sequence<sal_Int32>>::get(), 0, 0 },
96 {OUString(SC_UNONAME_ROLE), 0, cppu::UnoType<css::chart2::data::DataSequenceRole>::get(), 0, 0 },
97 {OUString(SC_UNONAME_INCLUDEHIDDENCELLS), 0, cppu::UnoType<bool>::get(), 0, 0 },
98 { OUString(), 0, css::uno::Type(), 0, 0 }
99 };
100 return aDataSequencePropertyMap_Impl;
101 }
102
103 struct lcl_appendTableNumber
104 {
lcl_appendTableNumber__anon85c1922a0111::lcl_appendTableNumber105 explicit lcl_appendTableNumber( OUStringBuffer & rBuffer ) :
106 m_rBuffer( rBuffer )
107 {}
operator ()__anon85c1922a0111::lcl_appendTableNumber108 void operator() ( SCTAB nTab )
109 {
110 // there is no append with SCTAB or sal_Int16
111 m_rBuffer.append( static_cast< sal_Int32 >( nTab ));
112 m_rBuffer.append( ' ' );
113 }
114 private:
115 OUStringBuffer & m_rBuffer;
116 };
117
lcl_createTableNumberList(const::std::vector<SCTAB> & rTableVector)118 OUString lcl_createTableNumberList( const ::std::vector< SCTAB > & rTableVector )
119 {
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();
126 }
127
lcl_GetXModel(const ScDocument * pDoc)128 uno::Reference< frame::XModel > lcl_GetXModel( const ScDocument * pDoc )
129 {
130 uno::Reference< frame::XModel > xModel;
131 SfxObjectShell * pObjSh( pDoc ? pDoc->GetDocumentShell() : nullptr );
132 if( pObjSh )
133 xModel.set( pObjSh->GetModel());
134 return xModel;
135 }
136
137 struct TokenTable
138 {
139 SCROW mnRowCount;
140 SCCOL mnColCount;
141 vector<std::unique_ptr<FormulaToken>> maTokens;
142
143 // noncopyable
144 TokenTable(const TokenTable&) = delete;
145 const TokenTable& operator=(const TokenTable&) = delete;
146
TokenTable__anon85c1922a0111::TokenTable147 TokenTable()
148 : mnRowCount(0)
149 , mnColCount(0)
150 {
151 }
152
init__anon85c1922a0111::TokenTable153 void init( SCCOL nColCount, SCROW nRowCount )
154 {
155 mnColCount = nColCount;
156 mnRowCount = nRowCount;
157 maTokens.reserve(mnColCount*mnRowCount);
158 }
clear__anon85c1922a0111::TokenTable159 void clear()
160 {
161 for (auto & rToken : maTokens)
162 rToken.reset();
163 }
164
push_back__anon85c1922a0111::TokenTable165 void push_back( std::unique_ptr<FormulaToken> pToken )
166 {
167 maTokens.push_back( std::move(pToken) );
168 OSL_ENSURE( maTokens.size()<= static_cast<sal_uInt32>( mnColCount*mnRowCount ), "too many tokens" );
169 }
170
getIndex__anon85c1922a0111::TokenTable171 sal_uInt32 getIndex(SCCOL nCol, SCROW nRow) const
172 {
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()>= static_cast<sal_uInt32>( mnColCount*mnRowCount ), "too few tokens" );
177 return nRet;
178 }
179
180 vector<ScTokenRef> getColRanges(SCCOL nCol) const;
181 vector<ScTokenRef> getRowRanges(SCROW nRow) const;
182 vector<ScTokenRef> getAllRanges() const;
183 };
184
getColRanges(SCCOL nCol) const185 vector<ScTokenRef> TokenTable::getColRanges(SCCOL nCol) const
186 {
187 if (nCol >= mnColCount)
188 return vector<ScTokenRef>();
189 if( mnRowCount<=0 )
190 return vector<ScTokenRef>();
191
192 vector<ScTokenRef> aTokens;
193 sal_uInt32 nLast = getIndex(nCol, mnRowCount-1);
194 for (sal_uInt32 i = getIndex(nCol, 0); i <= nLast; ++i)
195 {
196 FormulaToken* p = maTokens[i].get();
197 if (!p)
198 continue;
199
200 ScTokenRef pCopy(p->Clone());
201 ScRefTokenHelper::join(aTokens, pCopy, ScAddress());
202 }
203 return aTokens;
204 }
205
getRowRanges(SCROW nRow) const206 vector<ScTokenRef> TokenTable::getRowRanges(SCROW nRow) const
207 {
208 if (nRow >= mnRowCount)
209 return vector<ScTokenRef>();
210 if( mnColCount<=0 )
211 return vector<ScTokenRef>();
212
213 vector<ScTokenRef> aTokens;
214 sal_uInt32 nLast = getIndex(mnColCount-1, nRow);
215 for (sal_uInt32 i = getIndex(0, nRow); i <= nLast; i += mnRowCount)
216 {
217 FormulaToken* p = maTokens[i].get();
218 if (!p)
219 continue;
220
221 ScTokenRef p2(p->Clone());
222 ScRefTokenHelper::join(aTokens, p2, ScAddress());
223 }
224 return aTokens;
225 }
226
getAllRanges() const227 vector<ScTokenRef> TokenTable::getAllRanges() const
228 {
229 vector<ScTokenRef> aTokens;
230 sal_uInt32 nStop = mnColCount*mnRowCount;
231 for (sal_uInt32 i = 0; i < nStop; i++)
232 {
233 FormulaToken* p = maTokens[i].get();
234 if (!p)
235 continue;
236
237 ScTokenRef p2(p->Clone());
238 ScRefTokenHelper::join(aTokens, p2, ScAddress());
239 }
240 return aTokens;
241 }
242
243 typedef std::map<SCROW, std::unique_ptr<FormulaToken>> FormulaTokenMap;
244 typedef std::map<sal_uInt32, FormulaTokenMap> FormulaTokenMapMap;
245
246 class Chart2PositionMap
247 {
248 public:
249 Chart2PositionMap(SCCOL nColCount, SCROW nRowCount,
250 bool bFillRowHeader, bool bFillColumnHeader, FormulaTokenMapMap& rCols,
251 ScDocument* pDoc );
252 ~Chart2PositionMap();
253
getDataColCount() const254 SCCOL getDataColCount() const { return mnDataColCount; }
getDataRowCount() const255 SCROW getDataRowCount() const { return mnDataRowCount; }
256
257 vector<ScTokenRef> getLeftUpperCornerRanges() const;
258 vector<ScTokenRef> getAllColHeaderRanges() const;
259 vector<ScTokenRef> getAllRowHeaderRanges() const;
260
261 vector<ScTokenRef> getColHeaderRanges(SCCOL nChartCol) const;
262 vector<ScTokenRef> getRowHeaderRanges(SCROW nChartRow) const;
263
264 vector<ScTokenRef> getDataColRanges(SCCOL nCol) const;
265 vector<ScTokenRef> getDataRowRanges(SCROW nRow) const;
266
267 private:
268 SCCOL mnDataColCount;
269 SCROW mnDataRowCount;
270
271 TokenTable maLeftUpperCorner; //nHeaderColCount*nHeaderRowCount
272 TokenTable maColHeaders; //mnDataColCount*nHeaderRowCount
273 TokenTable maRowHeaders; //nHeaderColCount*mnDataRowCount
274 TokenTable maData;//mnDataColCount*mnDataRowCount
275 };
276
Chart2PositionMap(SCCOL nAllColCount,SCROW nAllRowCount,bool bFillRowHeader,bool bFillColumnHeader,FormulaTokenMapMap & rCols,ScDocument * pDoc)277 Chart2PositionMap::Chart2PositionMap(SCCOL nAllColCount, SCROW nAllRowCount,
278 bool bFillRowHeader, bool bFillColumnHeader, FormulaTokenMapMap& rCols, ScDocument* pDoc)
279 {
280 // if bFillRowHeader is true, at least the first column serves as a row header.
281 // If more than one column is pure text all the first pure text columns are used as header.
282 // Likewise, if bFillColumnHeader is true, at least the first row serves as a column header.
283 // If more than one row is pure text all the first pure text rows are used as header.
284
285 SCROW nHeaderRowCount = (bFillColumnHeader && nAllColCount && nAllRowCount) ? 1 : 0;
286 SCCOL nHeaderColCount = (bFillRowHeader && nAllColCount && nAllRowCount) ? 1 : 0;
287
288 if( pDoc && (nHeaderColCount || nHeaderRowCount ) )
289 {
290 //check whether there is more than one text column or row that should be added to the headers
291 SCROW nMaxHeaderRow = nAllRowCount;
292 SCCOL nCol = 0;
293 for (auto it = rCols.begin(); it != rCols.end(); ++it, ++nCol)
294 {
295 // Skip header columns
296 if (nCol < nHeaderColCount)
297 continue;
298
299 const auto& rCol = *it;
300
301 bool bFoundValuesInCol = false;
302 bool bFoundAnythingInCol = false;
303 SCROW nRow = 0;
304 for (auto it2 = rCol.second.begin(); it2 != rCol.second.end(); ++it2, ++nRow)
305 {
306 const auto& rCell = *it2;
307
308 // Skip header rows
309 if (nRow < nHeaderRowCount || !rCell.second)
310 continue;
311
312 ScRange aRange;
313 bool bExternal = false;
314 StackVar eType = rCell.second->GetType();
315 if( eType==svExternal || eType==svExternalSingleRef || eType==svExternalDoubleRef || eType==svExternalName )
316 bExternal = true;//lllll todo correct?
317 ScTokenRef pSharedToken(rCell.second->Clone());
318 ScRefTokenHelper::getRangeFromToken(aRange, pSharedToken, ScAddress(), bExternal);
319 SCCOL nCol1=0, nCol2=0;
320 SCROW nRow1=0, nRow2=0;
321 SCTAB nTab1=0, nTab2=0;
322 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
323 if ( pDoc->HasValueData( nCol1, nRow1, nTab1 ) )
324 {
325 // Found some numeric data
326 bFoundValuesInCol = true;
327 nMaxHeaderRow = std::min(nMaxHeaderRow, nRow);
328 break;
329 }
330 if ( pDoc->HasData( nCol1, nRow1, nTab1 ) )
331 {
332 // Found some other data (non-numeric)
333 bFoundAnythingInCol = true;
334 }
335 else
336 {
337 // If cell is empty, it belongs to data
338 nMaxHeaderRow = std::min(nMaxHeaderRow, nRow);
339 }
340 }
341
342 if (nHeaderColCount && !bFoundValuesInCol && bFoundAnythingInCol && nCol == nHeaderColCount)
343 {
344 // There is no values in row, but some data. And this column is next to header
345 // So lets put it to header
346 nHeaderColCount++;
347 }
348 }
349
350 if (nHeaderRowCount)
351 {
352 nHeaderRowCount = nMaxHeaderRow;
353 }
354 }
355
356 mnDataColCount = nAllColCount - nHeaderColCount;
357 mnDataRowCount = nAllRowCount - nHeaderRowCount;
358
359 maLeftUpperCorner.init(nHeaderColCount,nHeaderRowCount);
360 maColHeaders.init(mnDataColCount,nHeaderRowCount);
361 maRowHeaders.init(nHeaderColCount,mnDataRowCount);
362 maData.init(mnDataColCount,mnDataRowCount);
363
364 FormulaTokenMapMap::iterator it1 = rCols.begin();
365 for (SCCOL nCol = 0; nCol < nAllColCount; ++nCol)
366 {
367 if (it1 != rCols.end())
368 {
369 FormulaTokenMap& rCol = it1->second;
370 FormulaTokenMap::iterator it2 = rCol.begin();
371 for (SCROW nRow = 0; nRow < nAllRowCount; ++nRow)
372 {
373 std::unique_ptr<FormulaToken> pToken;
374 if (it2 != rCol.end())
375 {
376 pToken = std::move(it2->second);
377 ++it2;
378 }
379
380 if( nCol < nHeaderColCount )
381 {
382 if( nRow < nHeaderRowCount )
383 maLeftUpperCorner.push_back(std::move(pToken));
384 else
385 maRowHeaders.push_back(std::move(pToken));
386 }
387 else if( nRow < nHeaderRowCount )
388 maColHeaders.push_back(std::move(pToken));
389 else
390 maData.push_back(std::move(pToken));
391 }
392 ++it1;
393 }
394 }
395 }
396
~Chart2PositionMap()397 Chart2PositionMap::~Chart2PositionMap()
398 {
399 maLeftUpperCorner.clear();
400 maColHeaders.clear();
401 maRowHeaders.clear();
402 maData.clear();
403 }
404
getLeftUpperCornerRanges() const405 vector<ScTokenRef> Chart2PositionMap::getLeftUpperCornerRanges() const
406 {
407 return maLeftUpperCorner.getAllRanges();
408 }
getAllColHeaderRanges() const409 vector<ScTokenRef> Chart2PositionMap::getAllColHeaderRanges() const
410 {
411 return maColHeaders.getAllRanges();
412 }
getAllRowHeaderRanges() const413 vector<ScTokenRef> Chart2PositionMap::getAllRowHeaderRanges() const
414 {
415 return maRowHeaders.getAllRanges();
416 }
getColHeaderRanges(SCCOL nCol) const417 vector<ScTokenRef> Chart2PositionMap::getColHeaderRanges(SCCOL nCol) const
418 {
419 return maColHeaders.getColRanges( nCol);
420 }
getRowHeaderRanges(SCROW nRow) const421 vector<ScTokenRef> Chart2PositionMap::getRowHeaderRanges(SCROW nRow) const
422 {
423 return maRowHeaders.getRowRanges( nRow);
424 }
425
getDataColRanges(SCCOL nCol) const426 vector<ScTokenRef> Chart2PositionMap::getDataColRanges(SCCOL nCol) const
427 {
428 return maData.getColRanges( nCol);
429 }
430
getDataRowRanges(SCROW nRow) const431 vector<ScTokenRef> Chart2PositionMap::getDataRowRanges(SCROW nRow) const
432 {
433 return maData.getRowRanges( nRow);
434 }
435
436 /**
437 * Designed to be a drop-in replacement for ScChartPositioner, in order to
438 * handle external references.
439 */
440 class Chart2Positioner
441 {
442 enum GlueType
443 {
444 GLUETYPE_NA,
445 GLUETYPE_NONE,
446 GLUETYPE_COLS,
447 GLUETYPE_ROWS,
448 GLUETYPE_BOTH
449 };
450
451 public:
452 Chart2Positioner(const Chart2Positioner&) = delete;
453 const Chart2Positioner& operator=(const Chart2Positioner&) = delete;
454
Chart2Positioner(ScDocument * pDoc,const vector<ScTokenRef> & rRefTokens)455 Chart2Positioner(ScDocument* pDoc, const vector<ScTokenRef>& rRefTokens) :
456 mrRefTokens(rRefTokens),
457 meGlue(GLUETYPE_NA),
458 mnStartCol(0),
459 mnStartRow(0),
460 mpDoc(pDoc),
461 mbColHeaders(false),
462 mbRowHeaders(false),
463 mbDummyUpperLeft(false)
464 {
465 }
466
setHeaders(bool bColHeaders,bool bRowHeaders)467 void setHeaders(bool bColHeaders, bool bRowHeaders)
468 {
469 mbColHeaders = bColHeaders;
470 mbRowHeaders = bRowHeaders;
471 }
472
getPositionMap()473 Chart2PositionMap* getPositionMap()
474 {
475 createPositionMap();
476 return mpPositionMap.get();
477 }
478
479 private:
480 void invalidateGlue();
481 void glueState();
482 void calcGlueState(SCCOL nCols, SCROW nRows);
483 void createPositionMap();
484
485 private:
486 const vector<ScTokenRef>& mrRefTokens;
487 std::unique_ptr<Chart2PositionMap> mpPositionMap;
488 GlueType meGlue;
489 SCCOL mnStartCol;
490 SCROW mnStartRow;
491 ScDocument* const mpDoc;
492 bool mbColHeaders:1;
493 bool mbRowHeaders:1;
494 bool mbDummyUpperLeft:1;
495 };
496
invalidateGlue()497 void Chart2Positioner::invalidateGlue()
498 {
499 meGlue = GLUETYPE_NA;
500 mpPositionMap.reset();
501 }
502
glueState()503 void Chart2Positioner::glueState()
504 {
505 if (meGlue != GLUETYPE_NA)
506 return;
507
508 mbDummyUpperLeft = false;
509 if (mrRefTokens.size() <= 1)
510 {
511 // Source data consists of only one data range.
512 const ScTokenRef& p = mrRefTokens.front();
513 ScComplexRefData aData;
514 if (ScRefTokenHelper::getDoubleRefDataFromToken(aData, p))
515 {
516 if (aData.Ref1.Tab() == aData.Ref2.Tab())
517 meGlue = GLUETYPE_NONE;
518 else
519 meGlue = GLUETYPE_COLS;
520 mnStartCol = aData.Ref1.Col();
521 mnStartRow = aData.Ref1.Row();
522 }
523 else
524 {
525 invalidateGlue();
526 mnStartCol = 0;
527 mnStartRow = 0;
528 }
529 return;
530 }
531
532 ScComplexRefData aData;
533 ScRefTokenHelper::getDoubleRefDataFromToken(aData, mrRefTokens.front());
534 mnStartCol = aData.Ref1.Col();
535 mnStartRow = aData.Ref1.Row();
536
537 SCCOL nEndCol = 0;
538 SCROW nEndRow = 0;
539 for (const auto& rxToken : mrRefTokens)
540 {
541 ScRefTokenHelper::getDoubleRefDataFromToken(aData, rxToken);
542 SCCOLROW n1 = aData.Ref1.Col();
543 SCCOLROW n2 = aData.Ref2.Col();
544 if (n1 > mpDoc->MaxCol())
545 n1 = mpDoc->MaxCol();
546 if (n2 > mpDoc->MaxCol())
547 n2 = mpDoc->MaxCol();
548 if (n1 < mnStartCol)
549 mnStartCol = static_cast<SCCOL>(n1);
550 if (n2 > nEndCol)
551 nEndCol = static_cast<SCCOL>(n2);
552
553 n1 = aData.Ref1.Row();
554 n2 = aData.Ref2.Row();
555 if (n1 > mpDoc->MaxRow())
556 n1 = mpDoc->MaxRow();
557 if (n2 > mpDoc->MaxRow())
558 n2 = mpDoc->MaxRow();
559
560 if (n1 < mnStartRow)
561 mnStartRow = static_cast<SCROW>(n1);
562 if (n2 > nEndRow)
563 nEndRow = static_cast<SCROW>(n2);
564 }
565
566 if (mnStartCol == nEndCol)
567 {
568 // All source data is in a single column.
569 meGlue = GLUETYPE_ROWS;
570 return;
571 }
572
573 if (mnStartRow == nEndRow)
574 {
575 // All source data is in a single row.
576 meGlue = GLUETYPE_COLS;
577 return;
578 }
579
580 // total column size
581 SCCOL nC = nEndCol - mnStartCol + 1;
582
583 // total row size
584 SCROW nR = nEndRow - mnStartRow + 1;
585
586 // #i103540# prevent invalid vector size
587 if ((nC <= 0) || (nR <= 0))
588 {
589 invalidateGlue();
590 mnStartCol = 0;
591 mnStartRow = 0;
592 return;
593 }
594
595 calcGlueState(nC, nR);
596 }
597
598 enum State { Hole = 0, Occupied = 1, Free = 2, Glue = 3 };
599
calcGlueState(SCCOL nColSize,SCROW nRowSize)600 void Chart2Positioner::calcGlueState(SCCOL nColSize, SCROW nRowSize)
601 {
602 // TODO: This code can use some space optimization. Using an array to
603 // store individual cell's states is terribly inefficient esp for large
604 // data ranges; let's use flat_segment_tree to reduce memory usage here.
605
606 sal_uInt32 nCR = static_cast<sal_uInt32>(nColSize*nRowSize);
607
608 vector<State> aCellStates(nCR, Hole);
609
610 // Mark all referenced cells "occupied".
611 for (const auto& rxToken : mrRefTokens)
612 {
613 ScComplexRefData aData;
614 ScRefTokenHelper::getDoubleRefDataFromToken(aData, rxToken);
615 SCCOL nCol1 = aData.Ref1.Col() - mnStartCol;
616 SCCOL nCol2 = aData.Ref2.Col() - mnStartCol;
617 SCROW nRow1 = aData.Ref1.Row() - mnStartRow;
618 SCROW nRow2 = aData.Ref2.Row() - mnStartRow;
619 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
620 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
621 {
622 size_t i = nCol*nRowSize + nRow;
623 aCellStates[i] = Occupied;
624 }
625 }
626
627 // If at least one cell in either the first column or first row is empty,
628 // we don't glue at all unless the whole column or row is empty; we expect
629 // all cells in the first column / row to be fully populated. If we have
630 // empty column or row, then we do glue by the column or row,
631 // respectively.
632
633 bool bGlue = true;
634 bool bGlueCols = false;
635 for (SCCOL nCol = 0; bGlue && nCol < nColSize; ++nCol)
636 {
637 for (SCROW nRow = 0; bGlue && nRow < nRowSize; ++nRow)
638 {
639 size_t i = nCol*nRowSize + nRow;
640 if (aCellStates[i] == Occupied)
641 {
642 if (nCol == 0 || nRow == 0)
643 break;
644
645 bGlue = false;
646 }
647 else
648 aCellStates[i] = Free;
649 }
650 size_t nLast = (nCol+1)*nRowSize - 1; // index for the last cell in the column.
651 if (bGlue && aCellStates[nLast] == Free)
652 {
653 // Whole column is empty.
654 aCellStates[nLast] = Glue;
655 bGlueCols = true;
656 }
657 }
658
659 bool bGlueRows = false;
660 for (SCROW nRow = 0; bGlue && nRow < nRowSize; ++nRow)
661 {
662 size_t i = nRow;
663 for (SCCOL nCol = 0; bGlue && nCol < nColSize; ++nCol, i += nRowSize)
664 {
665 if (aCellStates[i] == Occupied)
666 {
667 if (nCol == 0 || nRow == 0)
668 break;
669
670 bGlue = false;
671 }
672 else
673 aCellStates[i] = Free;
674 }
675 i = (nColSize-1)*nRowSize + nRow; // index for the row position in the last column.
676 if (bGlue && aCellStates[i] == Free)
677 {
678 // Whole row is empty.
679 aCellStates[i] = Glue;
680 bGlueRows = true;
681 }
682 }
683
684 size_t i = 1;
685 for (sal_uInt32 n = 1; bGlue && n < nCR; ++n, ++i)
686 if (aCellStates[i] == Hole)
687 bGlue = false;
688
689 if (bGlue)
690 {
691 if (bGlueCols && bGlueRows)
692 meGlue = GLUETYPE_BOTH;
693 else if (bGlueRows)
694 meGlue = GLUETYPE_ROWS;
695 else
696 meGlue = GLUETYPE_COLS;
697 if (aCellStates.front() != Occupied)
698 mbDummyUpperLeft = true;
699 }
700 else
701 meGlue = GLUETYPE_NONE;
702 }
703
createPositionMap()704 void Chart2Positioner::createPositionMap()
705 {
706 if (meGlue == GLUETYPE_NA && mpPositionMap.get())
707 mpPositionMap.reset();
708
709 if (mpPositionMap)
710 return;
711
712 glueState();
713
714 bool bNoGlue = (meGlue == GLUETYPE_NONE);
715 FormulaTokenMapMap aCols;
716 SCROW nNoGlueRow = 0;
717 for (const ScTokenRef& pToken : mrRefTokens)
718 {
719 bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
720 sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
721 svl::SharedString aTabName = svl::SharedString::getEmptyString();
722 if (bExternal)
723 aTabName = pToken->GetString();
724
725 ScComplexRefData aData;
726 if( !ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken) )
727 break;
728 const ScSingleRefData& s = aData.Ref1;
729 const ScSingleRefData& e = aData.Ref2;
730 SCCOL nCol1 = s.Col(), nCol2 = e.Col();
731 SCROW nRow1 = s.Row(), nRow2 = e.Row();
732 SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
733
734 for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
735 {
736 // columns on secondary sheets are appended; we treat them as if
737 // all columns are on the same sheet. TODO: We can't assume that
738 // the column range is 16-bit; remove that restriction.
739 sal_uInt32 nInsCol = (static_cast<sal_uInt32>(nTab) << 16) |
740 (bNoGlue ? 0 : static_cast<sal_uInt32>(nCol1));
741
742 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol, ++nInsCol)
743 {
744 FormulaTokenMap& rCol = aCols[nInsCol];
745
746 auto nInsRow = bNoGlue ? nNoGlueRow : nRow1;
747 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow, ++nInsRow)
748 {
749 ScSingleRefData aCellData;
750 aCellData.InitFlags();
751 aCellData.SetFlag3D(true);
752 aCellData.SetColRel(false);
753 aCellData.SetRowRel(false);
754 aCellData.SetTabRel(false);
755 aCellData.SetAbsCol(nCol);
756 aCellData.SetAbsRow(nRow);
757 aCellData.SetAbsTab(nTab);
758
759 if (rCol.find(nInsRow) == rCol.end())
760 {
761 if (bExternal)
762 rCol[ nInsRow ].reset(new ScExternalSingleRefToken(nFileId, aTabName, aCellData));
763 else
764 rCol[ nInsRow ].reset(new ScSingleRefToken(aCellData));
765 }
766 }
767 }
768 }
769 nNoGlueRow += nRow2 - nRow1 + 1;
770 }
771
772 bool bFillRowHeader = mbRowHeaders;
773 bool bFillColumnHeader = mbColHeaders;
774
775 SCSIZE nAllColCount = static_cast<SCSIZE>(aCols.size());
776 SCSIZE nAllRowCount = 0;
777 if (!aCols.empty())
778 {
779 FormulaTokenMap& rCol = aCols.begin()->second;
780 if (mbDummyUpperLeft)
781 if (rCol.find(0) == rCol.end())
782 rCol[ 0 ] = nullptr; // dummy for labeling
783 nAllRowCount = static_cast<SCSIZE>(rCol.size());
784 }
785
786 if( nAllColCount!=0 && nAllRowCount!=0 )
787 {
788 if (bNoGlue)
789 {
790 FormulaTokenMap& rFirstCol = aCols.begin()->second;
791 for (const auto& rFirstColEntry : rFirstCol)
792 {
793 SCROW nKey = rFirstColEntry.first;
794 for (auto& rEntry : aCols)
795 {
796 FormulaTokenMap& rCol = rEntry.second;
797 if (rCol.find(nKey) == rCol.end())
798 rCol[ nKey ] = nullptr;
799 }
800 }
801 }
802 }
803 mpPositionMap.reset(
804 new Chart2PositionMap(
805 static_cast<SCCOL>(nAllColCount), static_cast<SCROW>(nAllRowCount),
806 bFillRowHeader, bFillColumnHeader, aCols, mpDoc));
807 }
808
809 /**
810 * Function object to create a range string from a token list.
811 */
812 class Tokens2RangeString
813 {
814 public:
Tokens2RangeString(ScDocument * pDoc,FormulaGrammar::Grammar eGram,sal_Unicode cRangeSep)815 Tokens2RangeString(ScDocument* pDoc, FormulaGrammar::Grammar eGram, sal_Unicode cRangeSep) :
816 mpRangeStr(new OUStringBuffer),
817 mpDoc(pDoc),
818 meGrammar(eGram),
819 mcRangeSep(cRangeSep),
820 mbFirst(true)
821 {
822 }
823
operator ()(const ScTokenRef & rToken)824 void operator() (const ScTokenRef& rToken)
825 {
826 ScCompiler aCompiler(mpDoc, ScAddress(0,0,0), meGrammar);
827 OUString aStr;
828 aCompiler.CreateStringFromToken(aStr, rToken.get());
829 if (mbFirst)
830 mbFirst = false;
831 else
832 mpRangeStr->append(mcRangeSep);
833 mpRangeStr->append(aStr);
834 }
835
getString(OUString & rStr)836 void getString(OUString& rStr)
837 {
838 rStr = mpRangeStr->makeStringAndClear();
839 }
840
841 private:
842 shared_ptr<OUStringBuffer> mpRangeStr;
843 ScDocument* mpDoc;
844 FormulaGrammar::Grammar meGrammar;
845 sal_Unicode mcRangeSep;
846 bool mbFirst;
847 };
848
849 /**
850 * Function object to convert a list of tokens into a string form suitable
851 * for ODF export. In ODF, a range is expressed as
852 *
853 * (start cell address):(end cell address)
854 *
855 * and each address doesn't include any '$' symbols.
856 */
857 class Tokens2RangeStringXML
858 {
859 public:
Tokens2RangeStringXML(ScDocument * pDoc)860 explicit Tokens2RangeStringXML(ScDocument* pDoc) :
861 mpRangeStr(new OUStringBuffer),
862 mpDoc(pDoc),
863 mbFirst(true)
864 {
865 }
866
operator ()(const ScTokenRef & rToken)867 void operator() (const ScTokenRef& rToken)
868 {
869 if (mbFirst)
870 mbFirst = false;
871 else
872 mpRangeStr->append(mcRangeSep);
873
874 ScTokenRef aStart, aEnd;
875 bool bValidToken = splitRangeToken(rToken, aStart, aEnd);
876 OSL_ENSURE(bValidToken, "invalid token");
877 if (!bValidToken)
878 return;
879 ScCompiler aCompiler(mpDoc, ScAddress(0,0,0), FormulaGrammar::GRAM_ENGLISH);
880 {
881 OUString aStr;
882 aCompiler.CreateStringFromToken(aStr, aStart.get());
883 mpRangeStr->append(aStr);
884 }
885 mpRangeStr->append(mcAddrSep);
886 {
887 OUString aStr;
888 aCompiler.CreateStringFromToken(aStr, aEnd.get());
889 mpRangeStr->append(aStr);
890 }
891 }
892
getString(OUString & rStr)893 void getString(OUString& rStr)
894 {
895 rStr = mpRangeStr->makeStringAndClear();
896 }
897
898 private:
splitRangeToken(const ScTokenRef & pToken,ScTokenRef & rStart,ScTokenRef & rEnd)899 static bool splitRangeToken(const ScTokenRef& pToken, ScTokenRef& rStart, ScTokenRef& rEnd)
900 {
901 ScComplexRefData aData;
902 bool bIsRefToken = ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken);
903 OSL_ENSURE(bIsRefToken, "invalid token");
904 if (!bIsRefToken)
905 return false;
906 bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
907 sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
908 svl::SharedString aTabName = svl::SharedString::getEmptyString();
909 if (bExternal)
910 aTabName = pToken->GetString();
911
912 // In saving to XML, we don't prepend address with '$'.
913 setRelative(aData.Ref1);
914 setRelative(aData.Ref2);
915
916 // In XML, the range must explicitly specify sheet name.
917 aData.Ref1.SetFlag3D(true);
918 aData.Ref2.SetFlag3D(true);
919
920 if (bExternal)
921 rStart.reset(new ScExternalSingleRefToken(nFileId, aTabName, aData.Ref1));
922 else
923 rStart.reset(new ScSingleRefToken(aData.Ref1));
924
925 if (bExternal)
926 rEnd.reset(new ScExternalSingleRefToken(nFileId, aTabName, aData.Ref2));
927 else
928 rEnd.reset(new ScSingleRefToken(aData.Ref2));
929 return true;
930 }
931
setRelative(ScSingleRefData & rData)932 static void setRelative(ScSingleRefData& rData)
933 {
934 rData.SetColRel(true);
935 rData.SetRowRel(true);
936 rData.SetTabRel(true);
937 }
938
939 private:
940 shared_ptr<OUStringBuffer> mpRangeStr;
941 ScDocument* mpDoc;
942 static const sal_Unicode mcRangeSep = ' ';
943 static const sal_Unicode mcAddrSep = ':';
944 bool mbFirst;
945 };
946
lcl_convertTokensToString(OUString & rStr,const vector<ScTokenRef> & rTokens,ScDocument * pDoc)947 void lcl_convertTokensToString(OUString& rStr, const vector<ScTokenRef>& rTokens, ScDocument* pDoc)
948 {
949 const sal_Unicode cRangeSep = ScCompiler::GetNativeSymbolChar(ocSep);
950 FormulaGrammar::Grammar eGrammar = pDoc->GetGrammar();
951 Tokens2RangeString func(pDoc, eGrammar, cRangeSep);
952 func = ::std::for_each(rTokens.begin(), rTokens.end(), func);
953 func.getString(rStr);
954 }
955
956 } // anonymous namespace
957
958 // DataProvider ==============================================================
959
ScChart2DataProvider(ScDocument * pDoc)960 ScChart2DataProvider::ScChart2DataProvider( ScDocument* pDoc )
961 : m_pDocument( pDoc)
962 , m_aPropSet(lcl_GetDataProviderPropertyMap())
963 , m_bIncludeHiddenCells( true)
964 {
965 if ( m_pDocument )
966 m_pDocument->AddUnoObject( *this);
967 }
968
~ScChart2DataProvider()969 ScChart2DataProvider::~ScChart2DataProvider()
970 {
971 SolarMutexGuard g;
972
973 if ( m_pDocument )
974 m_pDocument->RemoveUnoObject( *this);
975 }
976
Notify(SfxBroadcaster &,const SfxHint & rHint)977 void ScChart2DataProvider::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
978 {
979 if ( rHint.GetId() == SfxHintId::Dying )
980 {
981 m_pDocument = nullptr;
982 }
983 }
984
createDataSourcePossible(const uno::Sequence<beans::PropertyValue> & aArguments)985 sal_Bool SAL_CALL ScChart2DataProvider::createDataSourcePossible( const uno::Sequence< beans::PropertyValue >& aArguments )
986 {
987 SolarMutexGuard aGuard;
988 if( ! m_pDocument )
989 return false;
990
991 OUString aRangeRepresentation;
992 for(const auto& rArgument : aArguments)
993 {
994 if ( rArgument.Name == "CellRangeRepresentation" )
995 {
996 rArgument.Value >>= aRangeRepresentation;
997 }
998 }
999
1000 vector<ScTokenRef> aTokens;
1001 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1002 ScRefTokenHelper::compileRangeRepresentation(
1003 aTokens, aRangeRepresentation, m_pDocument, cSep, m_pDocument->GetGrammar(), true);
1004 return !aTokens.empty();
1005 }
1006
1007 namespace
1008 {
1009
lcl_createLabeledDataSequenceFromTokens(vector<ScTokenRef> && aValueTokens,vector<ScTokenRef> && aLabelTokens,ScDocument * pDoc,const uno::Reference<chart2::data::XDataProvider> & xDP,bool bIncludeHiddenCells)1010 uno::Reference< chart2::data::XLabeledDataSequence > lcl_createLabeledDataSequenceFromTokens(
1011 vector< ScTokenRef > && aValueTokens, vector< ScTokenRef > && aLabelTokens,
1012 ScDocument* pDoc, const uno::Reference< chart2::data::XDataProvider >& xDP, bool bIncludeHiddenCells )
1013 {
1014 uno::Reference< chart2::data::XLabeledDataSequence > xResult;
1015 bool bHasValues = !aValueTokens.empty();
1016 bool bHasLabel = !aLabelTokens.empty();
1017 if( bHasValues || bHasLabel )
1018 {
1019 try
1020 {
1021 uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
1022 if ( xContext.is() )
1023 {
1024 xResult.set( chart2::data::LabeledDataSequence::create(xContext), uno::UNO_QUERY_THROW );
1025 }
1026 if ( bHasValues )
1027 {
1028 uno::Reference< chart2::data::XDataSequence > xSeq( new ScChart2DataSequence( pDoc, xDP, std::move(aValueTokens), bIncludeHiddenCells ) );
1029 xResult->setValues( xSeq );
1030 }
1031 if ( bHasLabel )
1032 {
1033 uno::Reference< chart2::data::XDataSequence > xLabelSeq( new ScChart2DataSequence( pDoc, xDP, std::move(aLabelTokens), bIncludeHiddenCells ) );
1034 xResult->setLabel( xLabelSeq );
1035 }
1036 }
1037 catch( const uno::Exception& )
1038 {
1039 }
1040 }
1041 return xResult;
1042 }
1043
1044 /**
1045 * Check the current list of reference tokens, and add the upper left
1046 * corner of the minimum range that encloses all ranges if certain
1047 * conditions are met.
1048 *
1049 * @param rRefTokens list of reference tokens
1050 *
1051 * @return true if the corner was added, false otherwise.
1052 */
lcl_addUpperLeftCornerIfMissing(vector<ScTokenRef> & rRefTokens,SCROW nCornerRowCount,SCCOL nCornerColumnCount)1053 bool lcl_addUpperLeftCornerIfMissing(vector<ScTokenRef>& rRefTokens,
1054 SCROW nCornerRowCount, SCCOL nCornerColumnCount)
1055 {
1056 using ::std::max;
1057 using ::std::min;
1058
1059 if (rRefTokens.empty())
1060 return false;
1061
1062 SCCOL nMinCol = MAXCOLCOUNT;
1063 SCROW nMinRow = MAXROWCOUNT;
1064 SCCOL nMaxCol = 0;
1065 SCROW nMaxRow = 0;
1066 SCTAB nTab = 0;
1067
1068 sal_uInt16 nFileId = 0;
1069 svl::SharedString aExtTabName;
1070 bool bExternal = false;
1071
1072 vector<ScTokenRef>::const_iterator itr = rRefTokens.begin(), itrEnd = rRefTokens.end();
1073
1074 // Get the first ref token.
1075 ScTokenRef pToken = *itr;
1076 switch (pToken->GetType())
1077 {
1078 case svSingleRef:
1079 {
1080 const ScSingleRefData& rData = *pToken->GetSingleRef();
1081 nMinCol = rData.Col();
1082 nMinRow = rData.Row();
1083 nMaxCol = rData.Col();
1084 nMaxRow = rData.Row();
1085 nTab = rData.Tab();
1086 }
1087 break;
1088 case svDoubleRef:
1089 {
1090 const ScComplexRefData& rData = *pToken->GetDoubleRef();
1091 nMinCol = min(rData.Ref1.Col(), rData.Ref2.Col());
1092 nMinRow = min(rData.Ref1.Row(), rData.Ref2.Row());
1093 nMaxCol = max(rData.Ref1.Col(), rData.Ref2.Col());
1094 nMaxRow = max(rData.Ref1.Row(), rData.Ref2.Row());
1095 nTab = rData.Ref1.Tab();
1096 }
1097 break;
1098 case svExternalSingleRef:
1099 {
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();
1106 nFileId = pToken->GetIndex();
1107 aExtTabName = pToken->GetString();
1108 bExternal = true;
1109 }
1110 break;
1111 case svExternalDoubleRef:
1112 {
1113 const ScComplexRefData& rData = *pToken->GetDoubleRef();
1114 nMinCol = min(rData.Ref1.Col(), rData.Ref2.Col());
1115 nMinRow = min(rData.Ref1.Row(), rData.Ref2.Row());
1116 nMaxCol = max(rData.Ref1.Col(), rData.Ref2.Col());
1117 nMaxRow = max(rData.Ref1.Row(), rData.Ref2.Row());
1118 nTab = rData.Ref1.Tab();
1119 nFileId = pToken->GetIndex();
1120 aExtTabName = pToken->GetString();
1121 bExternal = true;
1122 }
1123 break;
1124 default:
1125 ;
1126 }
1127
1128 // Determine the minimum range enclosing all data ranges. Also make sure
1129 // that they are all on the same table.
1130
1131 for (++itr; itr != itrEnd; ++itr)
1132 {
1133 pToken = *itr;
1134 switch (pToken->GetType())
1135 {
1136 case svSingleRef:
1137 {
1138 const ScSingleRefData& rData = *pToken->GetSingleRef();
1139
1140 nMinCol = min(nMinCol, rData.Col());
1141 nMinRow = min(nMinRow, rData.Row());
1142 nMaxCol = max(nMaxCol, rData.Col());
1143 nMaxRow = max(nMaxRow, rData.Row());
1144 if (nTab != rData.Tab() || bExternal)
1145 return false;
1146 }
1147 break;
1148 case svDoubleRef:
1149 {
1150 const ScComplexRefData& rData = *pToken->GetDoubleRef();
1151
1152 nMinCol = min(nMinCol, rData.Ref1.Col());
1153 nMinCol = min(nMinCol, rData.Ref2.Col());
1154 nMinRow = min(nMinRow, rData.Ref1.Row());
1155 nMinRow = min(nMinRow, rData.Ref2.Row());
1156
1157 nMaxCol = max(nMaxCol, rData.Ref1.Col());
1158 nMaxCol = max(nMaxCol, rData.Ref2.Col());
1159 nMaxRow = max(nMaxRow, rData.Ref1.Row());
1160 nMaxRow = max(nMaxRow, rData.Ref2.Row());
1161
1162 if (nTab != rData.Ref1.Tab() || bExternal)
1163 return false;
1164 }
1165 break;
1166 case svExternalSingleRef:
1167 {
1168 if (!bExternal)
1169 return false;
1170
1171 if (nFileId != pToken->GetIndex() || aExtTabName != pToken->GetString())
1172 return false;
1173
1174 const ScSingleRefData& rData = *pToken->GetSingleRef();
1175
1176 nMinCol = min(nMinCol, rData.Col());
1177 nMinRow = min(nMinRow, rData.Row());
1178 nMaxCol = max(nMaxCol, rData.Col());
1179 nMaxRow = max(nMaxRow, rData.Row());
1180 }
1181 break;
1182 case svExternalDoubleRef:
1183 {
1184 if (!bExternal)
1185 return false;
1186
1187 if (nFileId != pToken->GetIndex() || aExtTabName != pToken->GetString())
1188 return false;
1189
1190 const ScComplexRefData& rData = *pToken->GetDoubleRef();
1191
1192 nMinCol = min(nMinCol, rData.Ref1.Col());
1193 nMinCol = min(nMinCol, rData.Ref2.Col());
1194 nMinRow = min(nMinRow, rData.Ref1.Row());
1195 nMinRow = min(nMinRow, rData.Ref2.Row());
1196
1197 nMaxCol = max(nMaxCol, rData.Ref1.Col());
1198 nMaxCol = max(nMaxCol, rData.Ref2.Col());
1199 nMaxRow = max(nMaxRow, rData.Ref1.Row());
1200 nMaxRow = max(nMaxRow, rData.Ref2.Row());
1201 }
1202 break;
1203 default:
1204 ;
1205 }
1206 }
1207
1208 if (nMinRow >= nMaxRow || nMinCol >= nMaxCol ||
1209 nMinRow >= MAXROWCOUNT || nMinCol >= MAXCOLCOUNT ||
1210 nMaxRow >= MAXROWCOUNT || nMaxCol >= MAXCOLCOUNT)
1211 {
1212 // Invalid range. Bail out.
1213 return false;
1214 }
1215
1216 // Check if the following conditions are met:
1217
1218 // 1) The upper-left corner cell is not included.
1219 // 2) The three adjacent cells of that corner cell are included.
1220
1221 bool bRight = false, bBottom = false, bDiagonal = false;
1222 for (const auto& rxToken : rRefTokens)
1223 {
1224 switch (rxToken->GetType())
1225 {
1226 case svSingleRef:
1227 case svExternalSingleRef:
1228 {
1229 const ScSingleRefData& rData = *rxToken->GetSingleRef();
1230 if (rData.Col() == nMinCol && rData.Row() == nMinRow)
1231 // The corner cell is contained.
1232 return false;
1233
1234 if (rData.Col() == nMinCol+nCornerColumnCount && rData.Row() == nMinRow)
1235 bRight = true;
1236
1237 if (rData.Col() == nMinCol && rData.Row() == nMinRow+nCornerRowCount)
1238 bBottom = true;
1239
1240 if (rData.Col() == nMinCol+nCornerColumnCount && rData.Row() == nMinRow+nCornerRowCount)
1241 bDiagonal = true;
1242 }
1243 break;
1244 case svDoubleRef:
1245 case svExternalDoubleRef:
1246 {
1247 const ScComplexRefData& rData = *rxToken->GetDoubleRef();
1248 const ScSingleRefData& r1 = rData.Ref1;
1249 const ScSingleRefData& r2 = rData.Ref2;
1250 if (r1.Col() <= nMinCol && nMinCol <= r2.Col() &&
1251 r1.Row() <= nMinRow && nMinRow <= r2.Row())
1252 // The corner cell is contained.
1253 return false;
1254
1255 if (r1.Col() <= nMinCol+nCornerColumnCount && nMinCol+nCornerColumnCount <= r2.Col() &&
1256 r1.Row() <= nMinRow && nMinRow <= r2.Row())
1257 bRight = true;
1258
1259 if (r1.Col() <= nMinCol && nMinCol <= r2.Col() &&
1260 r1.Row() <= nMinRow+nCornerRowCount && nMinRow+nCornerRowCount <= r2.Row())
1261 bBottom = true;
1262
1263 if (r1.Col() <= nMinCol+nCornerColumnCount && nMinCol+nCornerColumnCount <= r2.Col() &&
1264 r1.Row() <= nMinRow+nCornerRowCount && nMinRow+nCornerRowCount <= r2.Row())
1265 bDiagonal = true;
1266 }
1267 break;
1268 default:
1269 ;
1270 }
1271 }
1272
1273 if (!bRight || !bBottom || !bDiagonal)
1274 // Not all the adjacent cells are included. Bail out.
1275 return false;
1276
1277 ScSingleRefData aData;
1278 aData.InitFlags();
1279 aData.SetFlag3D(true);
1280 aData.SetAbsCol(nMinCol);
1281 aData.SetAbsRow(nMinRow);
1282 aData.SetAbsTab(nTab);
1283
1284 if( nCornerRowCount==1 && nCornerColumnCount==1 )
1285 {
1286 if (bExternal)
1287 {
1288 ScTokenRef pCorner(
1289 new ScExternalSingleRefToken(nFileId, aExtTabName, aData));
1290 ScRefTokenHelper::join(rRefTokens, pCorner, ScAddress());
1291 }
1292 else
1293 {
1294 ScTokenRef pCorner(new ScSingleRefToken(aData));
1295 ScRefTokenHelper::join(rRefTokens, pCorner, ScAddress());
1296 }
1297 }
1298 else
1299 {
1300 ScSingleRefData aDataEnd(aData);
1301 aDataEnd.IncCol(nCornerColumnCount-1);
1302 aDataEnd.IncRow(nCornerRowCount-1);
1303 ScComplexRefData r;
1304 r.Ref1=aData;
1305 r.Ref2=aDataEnd;
1306 if (bExternal)
1307 {
1308 ScTokenRef pCorner(
1309 new ScExternalDoubleRefToken(nFileId, aExtTabName, r));
1310 ScRefTokenHelper::join(rRefTokens, pCorner, ScAddress());
1311 }
1312 else
1313 {
1314 ScTokenRef pCorner(new ScDoubleRefToken(r));
1315 ScRefTokenHelper::join(rRefTokens, pCorner, ScAddress());
1316 }
1317 }
1318
1319 return true;
1320 }
1321
1322 #define SHRINK_RANGE_THRESHOLD 10000
1323
1324 class ShrinkRefTokenToDataRange
1325 {
1326 ScDocument* const mpDoc;
1327 public:
ShrinkRefTokenToDataRange(ScDocument * pDoc)1328 explicit ShrinkRefTokenToDataRange(ScDocument* pDoc) : mpDoc(pDoc) {}
operator ()(const ScTokenRef & rRef)1329 void operator() (const ScTokenRef& rRef)
1330 {
1331 if (ScRefTokenHelper::isExternalRef(rRef))
1332 return;
1333
1334 // Don't assume an ScDoubleRefToken if it isn't. It can be at least an
1335 // ScSingleRefToken, then there isn't anything to shrink.
1336 if (rRef->GetType() != svDoubleRef)
1337 return;
1338
1339 ScComplexRefData& rData = *rRef->GetDoubleRef();
1340 ScSingleRefData& s = rData.Ref1;
1341 ScSingleRefData& e = rData.Ref2;
1342
1343 if(abs((e.Col()-s.Col())*(e.Row()-s.Row())) < SHRINK_RANGE_THRESHOLD)
1344 return;
1345
1346 SCCOL nMinCol = mpDoc->MaxCol(), nMaxCol = 0;
1347 SCROW nMinRow = mpDoc->MaxRow(), nMaxRow = 0;
1348
1349 // Determine the smallest range that encompasses the data ranges of all sheets.
1350 SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
1351 for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
1352 {
1353 SCCOL nCol1 = 0, nCol2 = mpDoc->MaxCol();
1354 SCROW nRow1 = 0, nRow2 = mpDoc->MaxRow();
1355 mpDoc->ShrinkToDataArea(nTab, nCol1, nRow1, nCol2, nRow2);
1356 nMinCol = std::min(nMinCol, nCol1);
1357 nMinRow = std::min(nMinRow, nRow1);
1358 nMaxCol = std::max(nMaxCol, nCol2);
1359 nMaxRow = std::max(nMaxRow, nRow2);
1360 }
1361
1362 // Shrink range to the data range if applicable.
1363 if (s.Col() < nMinCol)
1364 s.SetAbsCol(nMinCol);
1365 if (s.Row() < nMinRow)
1366 s.SetAbsRow(nMinRow);
1367 if (e.Col() > nMaxCol)
1368 e.SetAbsCol(nMaxCol);
1369 if (e.Row() > nMaxRow)
1370 e.SetAbsRow(nMaxRow);
1371 }
1372 };
1373
shrinkToDataRange(ScDocument * pDoc,vector<ScTokenRef> & rRefTokens)1374 void shrinkToDataRange(ScDocument* pDoc, vector<ScTokenRef>& rRefTokens)
1375 {
1376 std::for_each(rRefTokens.begin(), rRefTokens.end(), ShrinkRefTokenToDataRange(pDoc));
1377 }
1378
1379 }
1380
1381 uno::Reference< chart2::data::XDataSource> SAL_CALL
createDataSource(const uno::Sequence<beans::PropertyValue> & aArguments)1382 ScChart2DataProvider::createDataSource(
1383 const uno::Sequence< beans::PropertyValue >& aArguments )
1384 {
1385 SolarMutexGuard aGuard;
1386 if ( ! m_pDocument )
1387 throw uno::RuntimeException();
1388
1389 uno::Reference< chart2::data::XDataSource> xResult;
1390 bool bLabel = true;
1391 bool bCategories = false;
1392 bool bOrientCol = true;
1393 OUString aRangeRepresentation;
1394 uno::Sequence< sal_Int32 > aSequenceMapping;
1395 bool bTimeBased = false;
1396 for(const auto& rArgument : aArguments)
1397 {
1398 if ( rArgument.Name == "DataRowSource" )
1399 {
1400 chart::ChartDataRowSource eSource = chart::ChartDataRowSource_COLUMNS;
1401 if( ! (rArgument.Value >>= eSource))
1402 {
1403 sal_Int32 nSource(0);
1404 if( rArgument.Value >>= nSource )
1405 eSource = static_cast< chart::ChartDataRowSource >( nSource );
1406 }
1407 bOrientCol = (eSource == chart::ChartDataRowSource_COLUMNS);
1408 }
1409 else if ( rArgument.Name == "FirstCellAsLabel" )
1410 {
1411 bLabel = ::cppu::any2bool(rArgument.Value);
1412 }
1413 else if ( rArgument.Name == "HasCategories" )
1414 {
1415 bCategories = ::cppu::any2bool(rArgument.Value);
1416 }
1417 else if ( rArgument.Name == "CellRangeRepresentation" )
1418 {
1419 rArgument.Value >>= aRangeRepresentation;
1420 }
1421 else if ( rArgument.Name == "SequenceMapping" )
1422 {
1423 rArgument.Value >>= aSequenceMapping;
1424 }
1425 else if ( rArgument.Name == "TimeBased" )
1426 {
1427 rArgument.Value >>= bTimeBased;
1428 }
1429 }
1430
1431 vector<ScTokenRef> aRefTokens;
1432 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1433 ScRefTokenHelper::compileRangeRepresentation(
1434 aRefTokens, aRangeRepresentation, m_pDocument, cSep, m_pDocument->GetGrammar(), true);
1435 if (aRefTokens.empty())
1436 // Invalid range representation. Bail out.
1437 throw lang::IllegalArgumentException();
1438
1439 SCTAB nTimeBasedStart = MAXTAB;
1440 SCTAB nTimeBasedEnd = 0;
1441 if(bTimeBased)
1442 {
1443 // limit to first sheet
1444 for(const auto& rxToken : aRefTokens)
1445 {
1446 if (rxToken->GetType() != svDoubleRef)
1447 continue;
1448
1449 ScComplexRefData& rData = *rxToken->GetDoubleRef();
1450 ScSingleRefData& s = rData.Ref1;
1451 ScSingleRefData& e = rData.Ref2;
1452
1453 nTimeBasedStart = std::min(nTimeBasedStart, s.Tab());
1454 nTimeBasedEnd = std::min(nTimeBasedEnd, e.Tab());
1455
1456 if(s.Tab() != e.Tab())
1457 e.SetAbsTab(s.Tab());
1458 }
1459 }
1460
1461 if(!bTimeBased)
1462 shrinkToDataRange(m_pDocument, aRefTokens);
1463
1464 if (bLabel)
1465 lcl_addUpperLeftCornerIfMissing(aRefTokens, 1, 1); //#i90669#
1466
1467 bool bColHeaders = (bOrientCol ? bLabel : bCategories );
1468 bool bRowHeaders = (bOrientCol ? bCategories : bLabel );
1469
1470 Chart2Positioner aChPositioner(m_pDocument, aRefTokens);
1471 aChPositioner.setHeaders(bColHeaders, bRowHeaders);
1472
1473 const Chart2PositionMap* pChartMap = aChPositioner.getPositionMap();
1474 if (!pChartMap)
1475 // No chart position map instance. Bail out.
1476 return xResult;
1477
1478 ScChart2DataSource* pDS = nullptr;
1479 ::std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSeqs;
1480
1481 // Fill Categories
1482 if( bCategories )
1483 {
1484 vector<ScTokenRef> aValueTokens;
1485 if (bOrientCol)
1486 aValueTokens = pChartMap->getAllRowHeaderRanges();
1487 else
1488 aValueTokens = pChartMap->getAllColHeaderRanges();
1489
1490 vector<ScTokenRef> aLabelTokens(
1491 pChartMap->getLeftUpperCornerRanges());
1492
1493 uno::Reference< chart2::data::XLabeledDataSequence > xCategories = lcl_createLabeledDataSequenceFromTokens(
1494 std::move(aValueTokens), std::move(aLabelTokens), m_pDocument, this, m_bIncludeHiddenCells ); //ownership of pointers is transferred!
1495 if ( xCategories.is() )
1496 {
1497 aSeqs.push_back( xCategories );
1498 }
1499 }
1500
1501 // Fill Series (values and label)
1502 sal_Int32 nCount = bOrientCol ? pChartMap->getDataColCount() : pChartMap->getDataRowCount();
1503 for (sal_Int32 i = 0; i < nCount; ++i)
1504 {
1505 vector<ScTokenRef> aValueTokens;
1506 vector<ScTokenRef> aLabelTokens;
1507 if (bOrientCol)
1508 {
1509 aValueTokens = pChartMap->getDataColRanges(static_cast<SCCOL>(i));
1510 aLabelTokens = pChartMap->getColHeaderRanges(static_cast<SCCOL>(i));
1511 }
1512 else
1513 {
1514 aValueTokens = pChartMap->getDataRowRanges(static_cast<SCROW>(i));
1515 aLabelTokens = pChartMap->getRowHeaderRanges(static_cast<SCROW>(i));
1516 }
1517 uno::Reference< chart2::data::XLabeledDataSequence > xChartSeries = lcl_createLabeledDataSequenceFromTokens(
1518 std::move(aValueTokens), std::move(aLabelTokens), m_pDocument, this, m_bIncludeHiddenCells ); //ownership of pointers is transferred!
1519 if ( xChartSeries.is() && xChartSeries->getValues().is() && xChartSeries->getValues()->getData().hasElements() )
1520 {
1521 aSeqs.push_back( xChartSeries );
1522 }
1523 }
1524
1525 pDS = new ScChart2DataSource(m_pDocument);
1526
1527 //reorder labeled sequences according to aSequenceMapping
1528 ::std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSeqVector;
1529 aSeqVector.reserve(aSeqs.size());
1530 for (auto const& aSeq : aSeqs)
1531 {
1532 aSeqVector.push_back(aSeq);
1533 }
1534
1535 for( const sal_Int32 nNewIndex : aSequenceMapping )
1536 {
1537 // note: assuming that the values in the sequence mapping are always non-negative
1538 ::std::vector< uno::Reference< chart2::data::XLabeledDataSequence > >::size_type nOldIndex( static_cast< sal_uInt32 >( nNewIndex ) );
1539 if( nOldIndex < aSeqVector.size() )
1540 {
1541 pDS->AddLabeledSequence( aSeqVector[nOldIndex] );
1542 aSeqVector[nOldIndex] = nullptr;
1543 }
1544 }
1545
1546 for(const uno::Reference< chart2::data::XLabeledDataSequence >& xSeq : aSeqVector)
1547 {
1548 if ( xSeq.is() )
1549 {
1550 pDS->AddLabeledSequence( xSeq );
1551 }
1552 }
1553
1554 xResult.set( pDS );
1555 return xResult;
1556 }
1557
1558 namespace
1559 {
1560
1561 /**
1562 * Function object to create a list of table numbers from a token list.
1563 */
1564 class InsertTabNumber
1565 {
1566 public:
InsertTabNumber()1567 InsertTabNumber() :
1568 mpTabNumVector(new vector<SCTAB>)
1569 {
1570 }
1571
operator ()(const ScTokenRef & pToken) const1572 void operator() (const ScTokenRef& pToken) const
1573 {
1574 if (!ScRefTokenHelper::isRef(pToken))
1575 return;
1576
1577 const ScSingleRefData& r = *pToken->GetSingleRef();
1578 mpTabNumVector->push_back(r.Tab());
1579 }
1580
getVector(vector<SCTAB> & rVector)1581 void getVector(vector<SCTAB>& rVector)
1582 {
1583 mpTabNumVector->swap(rVector);
1584 }
1585 private:
1586 shared_ptr< vector<SCTAB> > mpTabNumVector;
1587 };
1588
1589 class RangeAnalyzer
1590 {
1591 public:
1592 RangeAnalyzer();
1593 void initRangeAnalyzer( const vector<ScTokenRef>& rTokens );
1594 void analyzeRange( sal_Int32& rnDataInRows, sal_Int32& rnDataInCols,
1595 bool& rbRowSourceAmbiguous ) const;
1596 bool inSameSingleRow( const RangeAnalyzer& rOther );
1597 bool inSameSingleColumn( const RangeAnalyzer& rOther );
getRowCount() const1598 SCROW getRowCount() const { return mnRowCount; }
getColumnCount() const1599 SCCOL getColumnCount() const { return mnColumnCount; }
1600
1601 private:
1602 bool mbEmpty;
1603 bool mbAmbiguous;
1604 SCROW mnRowCount;
1605 SCCOL mnColumnCount;
1606
1607 SCCOL mnStartColumn;
1608 SCROW mnStartRow;
1609 };
1610
RangeAnalyzer()1611 RangeAnalyzer::RangeAnalyzer()
1612 : mbEmpty(true)
1613 , mbAmbiguous(false)
1614 , mnRowCount(0)
1615 , mnColumnCount(0)
1616 , mnStartColumn(-1)
1617 , mnStartRow(-1)
1618 {
1619 }
1620
initRangeAnalyzer(const vector<ScTokenRef> & rTokens)1621 void RangeAnalyzer::initRangeAnalyzer( const vector<ScTokenRef>& rTokens )
1622 {
1623 mnRowCount=0;
1624 mnColumnCount=0;
1625 mnStartColumn = -1;
1626 mnStartRow = -1;
1627 mbAmbiguous=false;
1628 if( rTokens.empty() )
1629 {
1630 mbEmpty=true;
1631 return;
1632 }
1633 mbEmpty=false;
1634
1635 for (const ScTokenRef& aRefToken : rTokens)
1636 {
1637 StackVar eVar = aRefToken->GetType();
1638 if (eVar == svDoubleRef || eVar == svExternalDoubleRef)
1639 {
1640 const ScComplexRefData& r = *aRefToken->GetDoubleRef();
1641 if (r.Ref1.Tab() == r.Ref2.Tab())
1642 {
1643 mnColumnCount = std::max<SCCOL>(mnColumnCount, static_cast<SCCOL>(abs(r.Ref2.Col() - r.Ref1.Col())+1));
1644 mnRowCount = std::max<SCROW>(mnRowCount, static_cast<SCROW>(abs(r.Ref2.Row() - r.Ref1.Row())+1));
1645 if( mnStartColumn == -1 )
1646 {
1647 mnStartColumn = r.Ref1.Col();
1648 mnStartRow = r.Ref1.Row();
1649 }
1650 else
1651 {
1652 if (mnStartColumn != r.Ref1.Col() && mnStartRow != r.Ref1.Row())
1653 mbAmbiguous=true;
1654 }
1655 }
1656 else
1657 mbAmbiguous=true;
1658 }
1659 else if (eVar == svSingleRef || eVar == svExternalSingleRef)
1660 {
1661 const ScSingleRefData& r = *aRefToken->GetSingleRef();
1662 mnColumnCount = std::max<SCCOL>( mnColumnCount, 1);
1663 mnRowCount = std::max<SCROW>( mnRowCount, 1);
1664 if( mnStartColumn == -1 )
1665 {
1666 mnStartColumn = r.Col();
1667 mnStartRow = r.Row();
1668 }
1669 else
1670 {
1671 if (mnStartColumn != r.Col() && mnStartRow != r.Row())
1672 mbAmbiguous=true;
1673 }
1674 }
1675 else
1676 mbAmbiguous=true;
1677 }
1678 }
1679
analyzeRange(sal_Int32 & rnDataInRows,sal_Int32 & rnDataInCols,bool & rbRowSourceAmbiguous) const1680 void RangeAnalyzer::analyzeRange( sal_Int32& rnDataInRows,
1681 sal_Int32& rnDataInCols,
1682 bool& rbRowSourceAmbiguous ) const
1683 {
1684 if(!mbEmpty && !mbAmbiguous)
1685 {
1686 if( mnRowCount==1 && mnColumnCount>1 )
1687 ++rnDataInRows;
1688 else if( mnColumnCount==1 && mnRowCount>1 )
1689 ++rnDataInCols;
1690 else if( mnRowCount>1 && mnColumnCount>1 )
1691 rbRowSourceAmbiguous = true;
1692 }
1693 else if( !mbEmpty )
1694 rbRowSourceAmbiguous = true;
1695 }
1696
inSameSingleRow(const RangeAnalyzer & rOther)1697 bool RangeAnalyzer::inSameSingleRow( const RangeAnalyzer& rOther )
1698 {
1699 return mnStartRow==rOther.mnStartRow &&
1700 mnRowCount==1 && rOther.mnRowCount==1;
1701 }
1702
inSameSingleColumn(const RangeAnalyzer & rOther)1703 bool RangeAnalyzer::inSameSingleColumn( const RangeAnalyzer& rOther )
1704 {
1705 return mnStartColumn==rOther.mnStartColumn &&
1706 mnColumnCount==1 && rOther.mnColumnCount==1;
1707 }
1708
constructKey(const uno::Reference<chart2::data::XLabeledDataSequence> & xNew)1709 std::pair<OUString, OUString> constructKey(const uno::Reference< chart2::data::XLabeledDataSequence>& xNew)
1710 {
1711 std::pair<OUString, OUString> aKey;
1712 if( xNew->getLabel().is() )
1713 aKey.first = xNew->getLabel()->getSourceRangeRepresentation();
1714 if( xNew->getValues().is() )
1715 aKey.second = xNew->getValues()->getSourceRangeRepresentation();
1716 return aKey;
1717 }
1718
1719
1720 } //end anonymous namespace
1721
detectArguments(const uno::Reference<chart2::data::XDataSource> & xDataSource)1722 uno::Sequence< beans::PropertyValue > SAL_CALL ScChart2DataProvider::detectArguments(
1723 const uno::Reference< chart2::data::XDataSource >& xDataSource )
1724 {
1725 ::std::vector< beans::PropertyValue > aResult;
1726 bool bRowSourceDetected = false;
1727 bool bFirstCellAsLabel = false;
1728 bool bHasCategories = false;
1729 OUString sRangeRep;
1730
1731 bool bHasCategoriesLabels = false;
1732 vector<ScTokenRef> aAllCategoriesValuesTokens;
1733 vector<ScTokenRef> aAllSeriesLabelTokens;
1734
1735 chart::ChartDataRowSource eRowSource = chart::ChartDataRowSource_COLUMNS;
1736
1737 vector<ScTokenRef> aAllTokens;
1738
1739 // parse given data source and collect infos
1740 {
1741 SolarMutexGuard aGuard;
1742 OSL_ENSURE( m_pDocument, "No Document -> no detectArguments" );
1743 if(!m_pDocument ||!xDataSource.is())
1744 return comphelper::containerToSequence( aResult );
1745
1746 sal_Int32 nDataInRows = 0;
1747 sal_Int32 nDataInCols = 0;
1748 bool bRowSourceAmbiguous = false;
1749
1750 const Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aSequences( xDataSource->getDataSequences());
1751 const sal_Int32 nCount( aSequences.getLength());
1752 RangeAnalyzer aPrevLabel,aPrevValues;
1753 for( const uno::Reference< chart2::data::XLabeledDataSequence >& xLS : aSequences )
1754 {
1755 if( xLS.is() )
1756 {
1757 bool bThisIsCategories = false;
1758 if(!bHasCategories)
1759 {
1760 uno::Reference< beans::XPropertySet > xSeqProp( xLS->getValues(), uno::UNO_QUERY );
1761 OUString aRole;
1762 if( xSeqProp.is() && (xSeqProp->getPropertyValue("Role") >>= aRole) &&
1763 aRole == "categories" )
1764 bThisIsCategories = bHasCategories = true;
1765 }
1766
1767 RangeAnalyzer aLabel,aValues;
1768 // label
1769 uno::Reference< chart2::data::XDataSequence > xLabel( xLS->getLabel());
1770 if( xLabel.is())
1771 {
1772 bFirstCellAsLabel = true;
1773 vector<ScTokenRef> aTokens;
1774 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1775 ScRefTokenHelper::compileRangeRepresentation(
1776 aTokens, xLabel->getSourceRangeRepresentation(), m_pDocument, cSep, m_pDocument->GetGrammar(), true);
1777 aLabel.initRangeAnalyzer(aTokens);
1778 for (const auto& rxToken : aTokens)
1779 {
1780 ScRefTokenHelper::join(aAllTokens, rxToken, ScAddress());
1781 if(!bThisIsCategories)
1782 ScRefTokenHelper::join(aAllSeriesLabelTokens, rxToken, ScAddress());
1783 }
1784 if(bThisIsCategories)
1785 bHasCategoriesLabels=true;
1786 }
1787 // values
1788 uno::Reference< chart2::data::XDataSequence > xValues( xLS->getValues());
1789 if( xValues.is())
1790 {
1791 vector<ScTokenRef> aTokens;
1792 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1793 ScRefTokenHelper::compileRangeRepresentation(
1794 aTokens, xValues->getSourceRangeRepresentation(), m_pDocument, cSep, m_pDocument->GetGrammar(), true);
1795 aValues.initRangeAnalyzer(aTokens);
1796 for (const auto& rxToken : aTokens)
1797 {
1798 ScRefTokenHelper::join(aAllTokens, rxToken, ScAddress());
1799 if(bThisIsCategories)
1800 ScRefTokenHelper::join(aAllCategoriesValuesTokens, rxToken, ScAddress());
1801 }
1802 }
1803 //detect row source
1804 if(!bThisIsCategories || nCount==1) //categories might span multiple rows *and* columns, so they should be used for detection only if nothing else is available
1805 {
1806 if (!bRowSourceAmbiguous)
1807 {
1808 aValues.analyzeRange(nDataInRows,nDataInCols,bRowSourceAmbiguous);
1809 aLabel.analyzeRange(nDataInRows,nDataInCols,bRowSourceAmbiguous);
1810 if (nDataInRows > 1 && nDataInCols > 1)
1811 bRowSourceAmbiguous = true;
1812 else if( !bRowSourceAmbiguous && !nDataInRows && !nDataInCols )
1813 {
1814 if( aValues.inSameSingleColumn( aLabel ) )
1815 nDataInCols++;
1816 else if( aValues.inSameSingleRow( aLabel ) )
1817 nDataInRows++;
1818 else
1819 {
1820 //#i86188# also detect a single column split into rows correctly
1821 if( aValues.inSameSingleColumn( aPrevValues ) )
1822 nDataInRows++;
1823 else if( aValues.inSameSingleRow( aPrevValues ) )
1824 nDataInCols++;
1825 else if( aLabel.inSameSingleColumn( aPrevLabel ) )
1826 nDataInRows++;
1827 else if( aLabel.inSameSingleRow( aPrevLabel ) )
1828 nDataInCols++;
1829 }
1830 }
1831 }
1832 }
1833 aPrevValues=aValues;
1834 aPrevLabel=aLabel;
1835 }
1836 }
1837
1838 if (!bRowSourceAmbiguous)
1839 {
1840 bRowSourceDetected = true;
1841 eRowSource = ( nDataInCols > 0
1842 ? chart::ChartDataRowSource_COLUMNS
1843 : chart::ChartDataRowSource_ROWS );
1844 }
1845 else
1846 {
1847 // set DataRowSource to the better of the two ambiguities
1848 eRowSource = ( nDataInRows > nDataInCols
1849 ? chart::ChartDataRowSource_ROWS
1850 : chart::ChartDataRowSource_COLUMNS );
1851 }
1852
1853 }
1854
1855 // TableNumberList
1856 {
1857 vector<SCTAB> aTableNumVector;
1858 InsertTabNumber func;
1859 func = ::std::for_each(aAllTokens.begin(), aAllTokens.end(), func);
1860 func.getVector(aTableNumVector);
1861 aResult.emplace_back( "TableNumberList", -1,
1862 uno::makeAny( lcl_createTableNumberList( aTableNumVector ) ),
1863 beans::PropertyState_DIRECT_VALUE );
1864 }
1865
1866 if( bRowSourceDetected )
1867 {
1868 // DataRowSource (calculated before)
1869 aResult.emplace_back( "DataRowSource", -1,
1870 uno::makeAny( eRowSource ), beans::PropertyState_DIRECT_VALUE );
1871 // HasCategories
1872 aResult.emplace_back( "HasCategories", -1,
1873 uno::makeAny( bHasCategories ), beans::PropertyState_DIRECT_VALUE );
1874 // FirstCellAsLabel
1875 aResult.emplace_back( "FirstCellAsLabel", -1,
1876 uno::makeAny( bFirstCellAsLabel ), beans::PropertyState_DIRECT_VALUE );
1877 }
1878
1879 // Add the left upper corner to the range if it is missing.
1880 if (bRowSourceDetected && bFirstCellAsLabel && bHasCategories && !bHasCategoriesLabels )
1881 {
1882 RangeAnalyzer aTop,aLeft;
1883 if( eRowSource==chart::ChartDataRowSource_COLUMNS )
1884 {
1885 aTop.initRangeAnalyzer(aAllSeriesLabelTokens);
1886 aLeft.initRangeAnalyzer(aAllCategoriesValuesTokens);
1887 }
1888 else
1889 {
1890 aTop.initRangeAnalyzer(aAllCategoriesValuesTokens);
1891 aLeft.initRangeAnalyzer(aAllSeriesLabelTokens);
1892 }
1893 lcl_addUpperLeftCornerIfMissing(aAllTokens, aTop.getRowCount(), aLeft.getColumnCount());//e.g. #i91212#
1894 }
1895
1896 // Get range string.
1897 lcl_convertTokensToString(sRangeRep, aAllTokens, m_pDocument);
1898
1899 // add cell range property
1900 aResult.emplace_back( "CellRangeRepresentation", -1,
1901 uno::makeAny( sRangeRep ), beans::PropertyState_DIRECT_VALUE );
1902
1903 //Sequence Mapping
1904 bool const bSequencesReordered = true;//todo detect this above or detect this sequence mapping cheaper ...
1905 if( bSequencesReordered && bRowSourceDetected )
1906 {
1907 bool bDifferentIndexes = false;
1908
1909 std::vector< sal_Int32 > aSequenceMappingVector;
1910
1911 uno::Reference< chart2::data::XDataSource > xCompareDataSource;
1912 try
1913 {
1914 xCompareDataSource.set( createDataSource( comphelper::containerToSequence( aResult ) ) );
1915 }
1916 catch( const lang::IllegalArgumentException & )
1917 {
1918 // creation of data source to compare didn't work, so we cannot
1919 // create a sequence mapping
1920 }
1921
1922 if( xDataSource.is() && xCompareDataSource.is() )
1923 {
1924 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> >& aOldSequences =
1925 xCompareDataSource->getDataSequences();
1926 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> >& aNewSequences =
1927 xDataSource->getDataSequences();
1928
1929 std::map<std::pair<OUString, OUString>,sal_Int32> aOldEntryToIndex;
1930 for( sal_Int32 nIndex = 0, n = aOldSequences.getLength(); nIndex < n; nIndex++ )
1931 {
1932 const uno::Reference< chart2::data::XLabeledDataSequence>& xOld( aOldSequences[nIndex] );
1933 if( xOld.is() )
1934 {
1935 std::pair<OUString, OUString> aKey = constructKey(xOld);
1936 aOldEntryToIndex[aKey] = nIndex;
1937 }
1938 }
1939
1940 for( sal_Int32 nNewIndex = 0, n = aNewSequences.getLength(); nNewIndex < n; nNewIndex++ )
1941 {
1942 const uno::Reference< chart2::data::XLabeledDataSequence>& xNew( aNewSequences[nNewIndex] );
1943 if( !xNew.is() )
1944 continue;
1945
1946 std::pair<OUString, OUString> aKey = constructKey(xNew);
1947 if (aOldEntryToIndex.find(aKey) == aOldEntryToIndex.end())
1948 continue;
1949
1950 sal_Int32 nOldIndex = aOldEntryToIndex[aKey];
1951 if( nOldIndex != nNewIndex )
1952 bDifferentIndexes = true;
1953
1954 aSequenceMappingVector.push_back(nOldIndex);
1955 }
1956 }
1957
1958 if( bDifferentIndexes && !aSequenceMappingVector.empty() )
1959 {
1960 aResult.emplace_back( "SequenceMapping", -1,
1961 uno::makeAny( comphelper::containerToSequence(aSequenceMappingVector) )
1962 , beans::PropertyState_DIRECT_VALUE );
1963 }
1964 }
1965
1966 return comphelper::containerToSequence( aResult );
1967 }
1968
createDataSequenceByRangeRepresentationPossible(const OUString & aRangeRepresentation)1969 sal_Bool SAL_CALL ScChart2DataProvider::createDataSequenceByRangeRepresentationPossible( const OUString& aRangeRepresentation )
1970 {
1971 SolarMutexGuard aGuard;
1972 if( ! m_pDocument )
1973 return false;
1974
1975 vector<ScTokenRef> aTokens;
1976 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1977 ScRefTokenHelper::compileRangeRepresentation(
1978 aTokens, aRangeRepresentation, m_pDocument, cSep, m_pDocument->GetGrammar(), true);
1979 return !aTokens.empty();
1980 }
1981
1982 uno::Reference< chart2::data::XDataSequence > SAL_CALL
createDataSequenceByRangeRepresentation(const OUString & aRangeRepresentation)1983 ScChart2DataProvider::createDataSequenceByRangeRepresentation(
1984 const OUString& aRangeRepresentation )
1985 {
1986 SolarMutexGuard aGuard;
1987 uno::Reference< chart2::data::XDataSequence > xResult;
1988
1989 OSL_ENSURE( m_pDocument, "No Document -> no createDataSequenceByRangeRepresentation" );
1990 if(!m_pDocument || aRangeRepresentation.isEmpty())
1991 return xResult;
1992
1993 vector<ScTokenRef> aRefTokens;
1994 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1995 ScRefTokenHelper::compileRangeRepresentation(
1996 aRefTokens, aRangeRepresentation, m_pDocument, cSep, m_pDocument->GetGrammar(), true);
1997 if (aRefTokens.empty())
1998 return xResult;
1999
2000 shrinkToDataRange(m_pDocument, aRefTokens);
2001
2002 xResult.set(new ScChart2DataSequence(m_pDocument, this, std::move(aRefTokens), m_bIncludeHiddenCells));
2003
2004 return xResult;
2005 }
2006
2007 uno::Reference<chart2::data::XDataSequence> SAL_CALL
createDataSequenceByValueArray(const OUString &,const OUString &)2008 ScChart2DataProvider::createDataSequenceByValueArray(
2009 const OUString& /*aRole*/, const OUString& /*aRangeRepresentation*/ )
2010 {
2011 return uno::Reference<chart2::data::XDataSequence>();
2012 }
2013
getRangeSelection()2014 uno::Reference< sheet::XRangeSelection > SAL_CALL ScChart2DataProvider::getRangeSelection()
2015 {
2016 uno::Reference< sheet::XRangeSelection > xResult;
2017
2018 uno::Reference< frame::XModel > xModel( lcl_GetXModel( m_pDocument ));
2019 if( xModel.is())
2020 xResult.set( xModel->getCurrentController(), uno::UNO_QUERY );
2021
2022 return xResult;
2023 }
2024
createDataSequenceByFormulaTokensPossible(const Sequence<sheet::FormulaToken> & aTokens)2025 sal_Bool SAL_CALL ScChart2DataProvider::createDataSequenceByFormulaTokensPossible(
2026 const Sequence<sheet::FormulaToken>& aTokens )
2027 {
2028 if (!aTokens.hasElements())
2029 return false;
2030
2031 ScTokenArray aCode;
2032 if (!ScTokenConversion::ConvertToTokenArray(*m_pDocument, aCode, aTokens))
2033 return false;
2034
2035 sal_uInt16 n = aCode.GetLen();
2036 if (!n)
2037 return false;
2038
2039 formula::FormulaTokenArrayPlainIterator aIter(aCode);
2040 const formula::FormulaToken* pFirst = aIter.First();
2041 const formula::FormulaToken* pLast = aCode.GetArray()[n-1];
2042 for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
2043 {
2044 switch (p->GetType())
2045 {
2046 case svSep:
2047 {
2048 switch (p->GetOpCode())
2049 {
2050 case ocSep:
2051 // separators are allowed.
2052 break;
2053 case ocOpen:
2054 if (p != pFirst)
2055 // open paran is allowed only as the first token.
2056 return false;
2057 break;
2058 case ocClose:
2059 if (p != pLast)
2060 // close paren is allowed only as the last token.
2061 return false;
2062 break;
2063 default:
2064 return false;
2065 }
2066 }
2067 break;
2068 case svSingleRef:
2069 case svDoubleRef:
2070 case svExternalSingleRef:
2071 case svExternalDoubleRef:
2072 break;
2073 default:
2074 return false;
2075 }
2076 }
2077
2078 return true;
2079 }
2080
2081 uno::Reference<chart2::data::XDataSequence> SAL_CALL
createDataSequenceByFormulaTokens(const Sequence<sheet::FormulaToken> & aTokens)2082 ScChart2DataProvider::createDataSequenceByFormulaTokens(
2083 const Sequence<sheet::FormulaToken>& aTokens )
2084 {
2085 uno::Reference<chart2::data::XDataSequence> xResult;
2086 if (!aTokens.hasElements())
2087 return xResult;
2088
2089 ScTokenArray aCode;
2090 if (!ScTokenConversion::ConvertToTokenArray(*m_pDocument, aCode, aTokens))
2091 return xResult;
2092
2093 sal_uInt16 n = aCode.GetLen();
2094 if (!n)
2095 return xResult;
2096
2097 vector<ScTokenRef> aRefTokens;
2098 formula::FormulaTokenArrayPlainIterator aIter(aCode);
2099 const formula::FormulaToken* pFirst = aIter.First();
2100 const formula::FormulaToken* pLast = aCode.GetArray()[n-1];
2101 for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
2102 {
2103 switch (p->GetType())
2104 {
2105 case svSep:
2106 {
2107 switch (p->GetOpCode())
2108 {
2109 case ocSep:
2110 // separators are allowed.
2111 break;
2112 case ocOpen:
2113 if (p != pFirst)
2114 // open paran is allowed only as the first token.
2115 throw lang::IllegalArgumentException();
2116 break;
2117 case ocClose:
2118 if (p != pLast)
2119 // close paren is allowed only as the last token.
2120 throw lang::IllegalArgumentException();
2121 break;
2122 default:
2123 throw lang::IllegalArgumentException();
2124 }
2125 }
2126 break;
2127 case svString:
2128 case svSingleRef:
2129 case svDoubleRef:
2130 case svExternalSingleRef:
2131 case svExternalDoubleRef:
2132 {
2133 ScTokenRef pNew(p->Clone());
2134 aRefTokens.push_back(pNew);
2135 }
2136 break;
2137 default:
2138 throw lang::IllegalArgumentException();
2139 }
2140 }
2141
2142 if (aRefTokens.empty())
2143 return xResult;
2144
2145 shrinkToDataRange(m_pDocument, aRefTokens);
2146
2147 xResult.set(new ScChart2DataSequence(m_pDocument, this, std::move(aRefTokens), m_bIncludeHiddenCells));
2148 return xResult;
2149 }
2150
2151 // XRangeXMLConversion ---------------------------------------------------
2152
convertRangeToXML(const OUString & sRangeRepresentation)2153 OUString SAL_CALL ScChart2DataProvider::convertRangeToXML( const OUString& sRangeRepresentation )
2154 {
2155 OUString aRet;
2156 if (!m_pDocument)
2157 return aRet;
2158
2159 if (sRangeRepresentation.isEmpty())
2160 // Empty data range is allowed.
2161 return aRet;
2162
2163 vector<ScTokenRef> aRefTokens;
2164 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
2165 ScRefTokenHelper::compileRangeRepresentation(
2166 aRefTokens, sRangeRepresentation, m_pDocument, cSep, m_pDocument->GetGrammar(), true);
2167 if (aRefTokens.empty())
2168 throw lang::IllegalArgumentException();
2169
2170 Tokens2RangeStringXML converter(m_pDocument);
2171 converter = ::std::for_each(aRefTokens.begin(), aRefTokens.end(), converter);
2172 converter.getString(aRet);
2173
2174 return aRet;
2175 }
2176
convertRangeFromXML(const OUString & sXMLRange)2177 OUString SAL_CALL ScChart2DataProvider::convertRangeFromXML( const OUString& sXMLRange )
2178 {
2179 if (!m_pDocument)
2180 {
2181 // #i74062# When loading flat XML, this is called before the referenced sheets are in the document,
2182 // so the conversion has to take place directly with the strings, without looking up the sheets.
2183
2184 OUStringBuffer sRet;
2185 sal_Int32 nOffset = 0;
2186 while( nOffset >= 0 )
2187 {
2188 OUString sToken;
2189 ScRangeStringConverter::GetTokenByOffset( sToken, sXMLRange, nOffset );
2190 if( nOffset >= 0 )
2191 {
2192 // convert one address (remove dots)
2193
2194 OUString aUIString(sToken);
2195
2196 sal_Int32 nIndex = ScRangeStringConverter::IndexOf( sToken, ':', 0 );
2197 if ( nIndex >= 0 && nIndex < aUIString.getLength() - 1 &&
2198 aUIString[nIndex + 1] == '.' )
2199 aUIString = aUIString.replaceAt( nIndex + 1, 1, "" );
2200
2201 if ( aUIString[0] == '.' )
2202 aUIString = aUIString.copy( 1 );
2203
2204 if( !sRet.isEmpty() )
2205 sRet.append( ';' );
2206 sRet.append( aUIString );
2207 }
2208 }
2209
2210 return sRet.makeStringAndClear();
2211 }
2212
2213 OUString aRet;
2214 ScRangeStringConverter::GetStringFromXMLRangeString(aRet, sXMLRange, m_pDocument);
2215 return aRet;
2216 }
2217
2218 // DataProvider XPropertySet -------------------------------------------------
2219
2220 uno::Reference< beans::XPropertySetInfo> SAL_CALL
getPropertySetInfo()2221 ScChart2DataProvider::getPropertySetInfo()
2222 {
2223 SolarMutexGuard aGuard;
2224 static uno::Reference<beans::XPropertySetInfo> aRef =
2225 new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() );
2226 return aRef;
2227 }
2228
setPropertyValue(const OUString & rPropertyName,const uno::Any & rValue)2229 void SAL_CALL ScChart2DataProvider::setPropertyValue(
2230 const OUString& rPropertyName, const uno::Any& rValue)
2231 {
2232 if ( rPropertyName != SC_UNONAME_INCLUDEHIDDENCELLS )
2233 throw beans::UnknownPropertyException(rPropertyName);
2234
2235 if ( !(rValue >>= m_bIncludeHiddenCells))
2236 throw lang::IllegalArgumentException();
2237
2238 }
2239
getPropertyValue(const OUString & rPropertyName)2240 uno::Any SAL_CALL ScChart2DataProvider::getPropertyValue(
2241 const OUString& rPropertyName)
2242 {
2243 uno::Any aRet;
2244 if ( rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS )
2245 aRet <<= m_bIncludeHiddenCells;
2246 else if (rPropertyName == SC_UNONAME_USE_INTERNAL_DATA_PROVIDER)
2247 {
2248 // This is a read-only property.
2249 aRet <<= m_pDocument->PastingDrawFromOtherDoc();
2250 }
2251 else
2252 throw beans::UnknownPropertyException(rPropertyName);
2253 return aRet;
2254 }
2255
addPropertyChangeListener(const OUString &,const uno::Reference<beans::XPropertyChangeListener> &)2256 void SAL_CALL ScChart2DataProvider::addPropertyChangeListener(
2257 const OUString& /*rPropertyName*/,
2258 const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/)
2259 {
2260 OSL_FAIL( "Not yet implemented" );
2261 }
2262
removePropertyChangeListener(const OUString &,const uno::Reference<beans::XPropertyChangeListener> &)2263 void SAL_CALL ScChart2DataProvider::removePropertyChangeListener(
2264 const OUString& /*rPropertyName*/,
2265 const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/)
2266 {
2267 OSL_FAIL( "Not yet implemented" );
2268 }
2269
addVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)2270 void SAL_CALL ScChart2DataProvider::addVetoableChangeListener(
2271 const OUString& /*rPropertyName*/,
2272 const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/)
2273 {
2274 OSL_FAIL( "Not yet implemented" );
2275 }
2276
removeVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)2277 void SAL_CALL ScChart2DataProvider::removeVetoableChangeListener(
2278 const OUString& /*rPropertyName*/,
2279 const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/ )
2280 {
2281 OSL_FAIL( "Not yet implemented" );
2282 }
2283
2284 // DataSource ================================================================
2285
ScChart2DataSource(ScDocument * pDoc)2286 ScChart2DataSource::ScChart2DataSource( ScDocument* pDoc)
2287 : m_pDocument( pDoc)
2288 {
2289 if ( m_pDocument )
2290 m_pDocument->AddUnoObject( *this);
2291 }
2292
~ScChart2DataSource()2293 ScChart2DataSource::~ScChart2DataSource()
2294 {
2295 SolarMutexGuard g;
2296
2297 if ( m_pDocument )
2298 m_pDocument->RemoveUnoObject( *this);
2299 }
2300
Notify(SfxBroadcaster &,const SfxHint & rHint)2301 void ScChart2DataSource::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
2302 {
2303 if ( rHint.GetId() == SfxHintId::Dying )
2304 {
2305 m_pDocument = nullptr;
2306 }
2307 }
2308
2309 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> > SAL_CALL
getDataSequences()2310 ScChart2DataSource::getDataSequences()
2311 {
2312 SolarMutexGuard aGuard;
2313 return comphelper::containerToSequence(m_aLabeledSequences);
2314 }
2315
AddLabeledSequence(const uno::Reference<chart2::data::XLabeledDataSequence> & xNew)2316 void ScChart2DataSource::AddLabeledSequence(const uno::Reference < chart2::data::XLabeledDataSequence >& xNew)
2317 {
2318 m_aLabeledSequences.push_back(xNew);
2319 }
2320
2321 // DataSequence ==============================================================
2322
Item()2323 ScChart2DataSequence::Item::Item() :
2324 mfValue(0.0), mbIsValue(false)
2325 {
2326 ::rtl::math::setNan(&mfValue);
2327 }
2328
HiddenRangeListener(ScChart2DataSequence & rParent)2329 ScChart2DataSequence::HiddenRangeListener::HiddenRangeListener(ScChart2DataSequence& rParent) :
2330 mrParent(rParent)
2331 {
2332 }
2333
~HiddenRangeListener()2334 ScChart2DataSequence::HiddenRangeListener::~HiddenRangeListener()
2335 {
2336 }
2337
notify()2338 void ScChart2DataSequence::HiddenRangeListener::notify()
2339 {
2340 mrParent.setDataChangedHint(true);
2341 }
2342
ScChart2DataSequence(ScDocument * pDoc,const uno::Reference<chart2::data::XDataProvider> & xDP,vector<ScTokenRef> && rTokens,bool bIncludeHiddenCells)2343 ScChart2DataSequence::ScChart2DataSequence( ScDocument* pDoc,
2344 const uno::Reference < chart2::data::XDataProvider >& xDP,
2345 vector<ScTokenRef>&& rTokens,
2346 bool bIncludeHiddenCells )
2347 : m_bIncludeHiddenCells( bIncludeHiddenCells)
2348 , m_nObjectId( 0 )
2349 , m_pDocument( pDoc)
2350 , m_aTokens(std::move(rTokens))
2351 , m_xDataProvider( xDP)
2352 , m_aPropSet(lcl_GetDataSequencePropertyMap())
2353 , m_bGotDataChangedHint(false)
2354 , m_bExtDataRebuildQueued(false)
2355 , mbTimeBased(false)
2356 , mnTimeBasedStart(0)
2357 , mnTimeBasedEnd(0)
2358 , mnCurrentTab(0)
2359 {
2360 if ( m_pDocument )
2361 {
2362 m_pDocument->AddUnoObject( *this);
2363 m_nObjectId = m_pDocument->GetNewUnoId();
2364 }
2365 // FIXME: real implementation of identifier and it's mapping to ranges.
2366 // Reuse ScChartListener?
2367
2368 // BM: don't use names of named ranges but the UI range strings
2369 // String aStr;
2370 // rRangeList->Format( aStr, ScRefFlags::RANGE_ABS_3D, m_pDocument );
2371 // m_aIdentifier = aStr;
2372
2373 // m_aIdentifier = "ID_";
2374 // static sal_Int32 nID = 0;
2375 // m_aIdentifier += OUString::valueOf( ++nID);
2376 }
2377
~ScChart2DataSequence()2378 ScChart2DataSequence::~ScChart2DataSequence()
2379 {
2380 SolarMutexGuard g;
2381
2382 if ( m_pDocument )
2383 {
2384 m_pDocument->RemoveUnoObject( *this);
2385 if (m_pHiddenListener)
2386 {
2387 ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection();
2388 if (pCLC)
2389 pCLC->EndListeningHiddenRange(m_pHiddenListener.get());
2390 }
2391 StopListeningToAllExternalRefs();
2392 }
2393
2394 m_pValueListener.reset();
2395 }
2396
RefChanged()2397 void ScChart2DataSequence::RefChanged()
2398 {
2399 if( m_pValueListener && !m_aValueListeners.empty() )
2400 {
2401 m_pValueListener->EndListeningAll();
2402
2403 if( m_pDocument )
2404 {
2405 ScChartListenerCollection* pCLC = nullptr;
2406 if (m_pHiddenListener)
2407 {
2408 pCLC = m_pDocument->GetChartListenerCollection();
2409 if (pCLC)
2410 pCLC->EndListeningHiddenRange(m_pHiddenListener.get());
2411 }
2412
2413 for (const auto& rxToken : m_aTokens)
2414 {
2415 ScRange aRange;
2416 if (!ScRefTokenHelper::getRangeFromToken(aRange, rxToken, ScAddress()))
2417 continue;
2418
2419 m_pDocument->StartListeningArea(aRange, false, m_pValueListener.get());
2420 if (pCLC)
2421 pCLC->StartListeningHiddenRange(aRange, m_pHiddenListener.get());
2422 }
2423 }
2424 }
2425 }
2426
BuildDataCache()2427 void ScChart2DataSequence::BuildDataCache()
2428 {
2429 m_bExtDataRebuildQueued = false;
2430
2431 if (!m_aDataArray.empty())
2432 return;
2433
2434 StopListeningToAllExternalRefs();
2435
2436 ::std::vector<sal_Int32> aHiddenValues;
2437 sal_Int32 nDataCount = 0;
2438
2439 for (const auto& rxToken : m_aTokens)
2440 {
2441 if (ScRefTokenHelper::isExternalRef(rxToken))
2442 {
2443 nDataCount += FillCacheFromExternalRef(rxToken);
2444 }
2445 else
2446 {
2447 ScRange aRange;
2448 if (!ScRefTokenHelper::getRangeFromToken(aRange, rxToken, ScAddress()))
2449 continue;
2450
2451 SCCOL nLastCol = -1;
2452 SCROW nLastRow = -1;
2453 for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
2454 {
2455 for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol)
2456 {
2457 sc::ColumnBlockPosition hint;
2458 m_pDocument->InitColumnBlockPosition( hint, nTab, nCol );
2459 for (SCROW nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow)
2460 {
2461 bool bColHidden = m_pDocument->ColHidden(nCol, nTab, nullptr, &nLastCol);
2462 bool bRowHidden = m_pDocument->RowHidden(nRow, nTab, nullptr, &nLastRow);
2463
2464 if (bColHidden || bRowHidden)
2465 {
2466 // hidden cell
2467 aHiddenValues.push_back(nDataCount-1);
2468
2469 if( !m_bIncludeHiddenCells )
2470 continue;
2471 }
2472
2473 Item aItem;
2474
2475 ScAddress aAdr(nCol, nRow, nTab);
2476 aItem.maString = m_pDocument->GetString(aAdr);
2477
2478 ScRefCellValue aCell(*m_pDocument, aAdr, hint);
2479 switch (aCell.meType)
2480 {
2481 case CELLTYPE_VALUE:
2482 aItem.mfValue = aCell.getValue();
2483 aItem.mbIsValue = true;
2484 break;
2485 case CELLTYPE_FORMULA:
2486 {
2487 ScFormulaCell* pFCell = aCell.mpFormula;
2488 FormulaError nErr = pFCell->GetErrCode();
2489 if (nErr != FormulaError::NONE)
2490 break;
2491
2492 if (pFCell->IsValue())
2493 {
2494 aItem.mfValue = pFCell->GetValue();
2495 aItem.mbIsValue = true;
2496 }
2497 }
2498 break;
2499 case CELLTYPE_EDIT:
2500 case CELLTYPE_NONE:
2501 case CELLTYPE_STRING:
2502 default:
2503 ; // do nothing
2504 }
2505
2506 aItem.mAddress = ScAddress(nCol, nRow, nTab);
2507
2508 m_aDataArray.push_back(aItem);
2509 ++nDataCount;
2510 }
2511 }
2512 }
2513 }
2514 }
2515
2516 // convert the hidden cell list to sequence.
2517 m_aHiddenValues.realloc(aHiddenValues.size());
2518 std::copy(
2519 aHiddenValues.begin(), aHiddenValues.end(), m_aHiddenValues.begin());
2520
2521 // Clear the data series cache when the array is re-built.
2522 m_aMixedDataCache.realloc(0);
2523 }
2524
RebuildDataCache()2525 void ScChart2DataSequence::RebuildDataCache()
2526 {
2527 if (!m_bExtDataRebuildQueued)
2528 {
2529 m_aDataArray.clear();
2530 m_pDocument->BroadcastUno(ScHint(SfxHintId::ScDataChanged, ScAddress()));
2531 m_bExtDataRebuildQueued = true;
2532 m_bGotDataChangedHint = true;
2533 }
2534 }
2535
FillCacheFromExternalRef(const ScTokenRef & pToken)2536 sal_Int32 ScChart2DataSequence::FillCacheFromExternalRef(const ScTokenRef& pToken)
2537 {
2538 ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager();
2539 ScRange aRange;
2540 if (!ScRefTokenHelper::getRangeFromToken(aRange, pToken, ScAddress(), true))
2541 return 0;
2542
2543 sal_uInt16 nFileId = pToken->GetIndex();
2544 OUString aTabName = pToken->GetString().getString();
2545 ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens(nFileId, aTabName, aRange, nullptr);
2546 if (!pArray)
2547 // no external data exists for this range.
2548 return 0;
2549
2550 // Start listening for this external document.
2551 ExternalRefListener* pExtRefListener = GetExtRefListener();
2552 pRefMgr->addLinkListener(nFileId, pExtRefListener);
2553 pExtRefListener->addFileId(nFileId);
2554
2555 ScExternalRefCache::TableTypeRef pTable = pRefMgr->getCacheTable(nFileId, aTabName, false);
2556 sal_Int32 nDataCount = 0;
2557 FormulaTokenArrayPlainIterator aIter(*pArray);
2558 for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
2559 {
2560 // Cached external range is always represented as a single
2561 // matrix token, although that might change in the future when
2562 // we introduce a new token type to store multi-table range
2563 // data.
2564
2565 if (p->GetType() != svMatrix)
2566 {
2567 OSL_FAIL("Cached array is not a matrix token.");
2568 continue;
2569 }
2570
2571 const ScMatrix* pMat = p->GetMatrix();
2572 SCSIZE nCSize, nRSize;
2573 pMat->GetDimensions(nCSize, nRSize);
2574 for (SCSIZE nC = 0; nC < nCSize; ++nC)
2575 {
2576 for (SCSIZE nR = 0; nR < nRSize; ++nR)
2577 {
2578 if (pMat->IsValue(nC, nR) || pMat->IsBoolean(nC, nR))
2579 {
2580 Item aItem;
2581
2582 aItem.mbIsValue = true;
2583 aItem.mfValue = pMat->GetDouble(nC, nR);
2584
2585 SvNumberFormatter* pFormatter = m_pDocument->GetFormatTable();
2586 if (pFormatter)
2587 {
2588 const double fVal = aItem.mfValue;
2589 Color* pColor = nullptr;
2590 sal_uInt32 nFmt = 0;
2591 if (pTable)
2592 {
2593 // Get the correct format index from the cache.
2594 SCCOL nCol = aRange.aStart.Col() + static_cast<SCCOL>(nC);
2595 SCROW nRow = aRange.aStart.Row() + static_cast<SCROW>(nR);
2596 pTable->getCell(nCol, nRow, &nFmt);
2597 }
2598 pFormatter->GetOutputString(fVal, nFmt, aItem.maString, &pColor);
2599 }
2600
2601 m_aDataArray.push_back(aItem);
2602 ++nDataCount;
2603 }
2604 else if (pMat->IsStringOrEmpty(nC, nR))
2605 {
2606 Item aItem;
2607
2608 aItem.mbIsValue = false;
2609 aItem.maString = pMat->GetString(nC, nR).getString();
2610
2611 m_aDataArray.emplace_back(aItem);
2612 ++nDataCount;
2613 }
2614 }
2615 }
2616 }
2617 return nDataCount;
2618 }
2619
UpdateTokensFromRanges(const ScRangeList & rRanges)2620 void ScChart2DataSequence::UpdateTokensFromRanges(const ScRangeList& rRanges)
2621 {
2622 if (!m_pRangeIndices.get())
2623 return;
2624
2625 for ( size_t i = 0, nCount = rRanges.size(); i < nCount; ++i )
2626 {
2627 ScTokenRef pToken;
2628 const ScRange & rRange = rRanges[i];
2629
2630 ScRefTokenHelper::getTokenFromRange(pToken, rRange);
2631 sal_uInt32 nOrigPos = (*m_pRangeIndices)[i];
2632 m_aTokens[nOrigPos] = pToken;
2633 }
2634
2635 RefChanged();
2636
2637 // any change of the range address is broadcast to value (modify) listeners
2638 if ( !m_aValueListeners.empty() )
2639 m_bGotDataChangedHint = true;
2640 }
2641
GetExtRefListener()2642 ScChart2DataSequence::ExternalRefListener* ScChart2DataSequence::GetExtRefListener()
2643 {
2644 if (!m_pExtRefListener.get())
2645 m_pExtRefListener.reset(new ExternalRefListener(*this, m_pDocument));
2646
2647 return m_pExtRefListener.get();
2648 }
2649
StopListeningToAllExternalRefs()2650 void ScChart2DataSequence::StopListeningToAllExternalRefs()
2651 {
2652 if (!m_pExtRefListener.get())
2653 return;
2654
2655 const std::unordered_set<sal_uInt16>& rFileIds = m_pExtRefListener->getAllFileIds();
2656 ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager();
2657 for (const auto& rFileId : rFileIds)
2658 pRefMgr->removeLinkListener(rFileId, m_pExtRefListener.get());
2659
2660 m_pExtRefListener.reset();
2661 }
2662
CopyData(const ScChart2DataSequence & r)2663 void ScChart2DataSequence::CopyData(const ScChart2DataSequence& r)
2664 {
2665 if (!m_pDocument)
2666 {
2667 OSL_FAIL("document instance is nullptr!?");
2668 return;
2669 }
2670
2671 std::vector<Item> aDataArray(r.m_aDataArray);
2672 m_aDataArray.swap(aDataArray);
2673
2674 m_aHiddenValues = r.m_aHiddenValues;
2675 m_aRole = r.m_aRole;
2676
2677 if (r.m_pRangeIndices.get())
2678 m_pRangeIndices.reset(new vector<sal_uInt32>(*r.m_pRangeIndices));
2679
2680 if (r.m_pExtRefListener.get())
2681 {
2682 // Re-register all external files that the old instance was
2683 // listening to.
2684
2685 ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager();
2686 m_pExtRefListener.reset(new ExternalRefListener(*this, m_pDocument));
2687 const std::unordered_set<sal_uInt16>& rFileIds = r.m_pExtRefListener->getAllFileIds();
2688 for (const auto& rFileId : rFileIds)
2689 {
2690 pRefMgr->addLinkListener(rFileId, m_pExtRefListener.get());
2691 m_pExtRefListener->addFileId(rFileId);
2692 }
2693 }
2694 }
2695
Notify(SfxBroadcaster &,const SfxHint & rHint)2696 void ScChart2DataSequence::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
2697 {
2698 if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) )
2699 {
2700 // Create a range list from the token list, have the range list
2701 // updated, and bring the change back to the token list.
2702
2703 ScRangeList aRanges;
2704 m_pRangeIndices.reset(new vector<sal_uInt32>);
2705 vector<ScTokenRef>::const_iterator itrBeg = m_aTokens.begin(), itrEnd = m_aTokens.end();
2706 for (vector<ScTokenRef>::const_iterator itr = itrBeg ;itr != itrEnd; ++itr)
2707 {
2708 if (!ScRefTokenHelper::isExternalRef(*itr))
2709 {
2710 ScRange aRange;
2711 ScRefTokenHelper::getRangeFromToken(aRange, *itr, ScAddress());
2712 aRanges.push_back(aRange);
2713 sal_uInt32 nPos = distance(itrBeg, itr);
2714 m_pRangeIndices->push_back(nPos);
2715 }
2716 }
2717
2718 OSL_ENSURE(m_pRangeIndices->size() == aRanges.size(),
2719 "range list and range index list have different sizes.");
2720
2721 unique_ptr<ScRangeList> pUndoRanges;
2722 if ( m_pDocument->HasUnoRefUndo() )
2723 pUndoRanges.reset(new ScRangeList(aRanges));
2724
2725 const ScUpdateRefHint& rRef = static_cast<const ScUpdateRefHint&>(rHint);
2726 bool bChanged = aRanges.UpdateReference(
2727 rRef.GetMode(), m_pDocument, rRef.GetRange(), rRef.GetDx(), rRef.GetDy(), rRef.GetDz());
2728
2729 if (bChanged)
2730 {
2731 OSL_ENSURE(m_pRangeIndices->size() == aRanges.size(),
2732 "range list and range index list have different sizes after the reference update.");
2733
2734 // Bring the change back from the range list to the token list.
2735 UpdateTokensFromRanges(aRanges);
2736
2737 if (pUndoRanges)
2738 m_pDocument->AddUnoRefChange(m_nObjectId, *pUndoRanges);
2739 }
2740 }
2741 else if ( dynamic_cast<const ScUnoRefUndoHint*>(&rHint) )
2742 {
2743 const ScUnoRefUndoHint& rUndoHint = static_cast<const ScUnoRefUndoHint&>(rHint);
2744
2745 do
2746 {
2747 if (rUndoHint.GetObjectId() != m_nObjectId)
2748 break;
2749
2750 // The hint object provides the old ranges. Restore the old state
2751 // from these ranges.
2752
2753 if (!m_pRangeIndices.get() || m_pRangeIndices->empty())
2754 {
2755 OSL_FAIL(" faulty range indices");
2756 break;
2757 }
2758
2759 const ScRangeList& rRanges = rUndoHint.GetRanges();
2760
2761 size_t nCount = rRanges.size();
2762 if (nCount != m_pRangeIndices->size())
2763 {
2764 OSL_FAIL("range count and range index count differ.");
2765 break;
2766 }
2767
2768 UpdateTokensFromRanges(rRanges);
2769 }
2770 while (false);
2771 }
2772 else
2773 {
2774 const SfxHintId nId = rHint.GetId();
2775 if ( nId ==SfxHintId::Dying )
2776 {
2777 m_pDocument = nullptr;
2778 }
2779 else if ( nId == SfxHintId::DataChanged )
2780 {
2781 // delayed broadcast as in ScCellRangesBase
2782
2783 if ( m_bGotDataChangedHint && m_pDocument )
2784 {
2785 m_aDataArray.clear();
2786 lang::EventObject aEvent;
2787 aEvent.Source.set(static_cast<cppu::OWeakObject*>(this));
2788
2789 if( m_pDocument )
2790 {
2791 for (const uno::Reference<util::XModifyListener> & xListener: m_aValueListeners)
2792 m_pDocument->AddUnoListenerCall( xListener, aEvent );
2793 }
2794
2795 m_bGotDataChangedHint = false;
2796 }
2797 }
2798 else if ( nId == SfxHintId::ScCalcAll )
2799 {
2800 // broadcast from DoHardRecalc - set m_bGotDataChangedHint
2801 // (SfxHintId::DataChanged follows separately)
2802
2803 if ( !m_aValueListeners.empty() )
2804 m_bGotDataChangedHint = true;
2805 }
2806 else if (nId == SfxHintId::ScClearCache)
2807 {
2808 // necessary after import
2809 m_aDataArray.clear();
2810 }
2811 }
2812 }
2813
IMPL_LINK(ScChart2DataSequence,ValueListenerHdl,const SfxHint &,rHint,void)2814 IMPL_LINK( ScChart2DataSequence, ValueListenerHdl, const SfxHint&, rHint, void )
2815 {
2816 if ( m_pDocument && (rHint.GetId() == SfxHintId::ScDataChanged) )
2817 {
2818 // This may be called several times for a single change, if several formulas
2819 // in the range are notified. So only a flag is set that is checked when
2820 // SfxHintId::DataChanged is received.
2821
2822 setDataChangedHint(true);
2823 }
2824 }
2825
ExternalRefListener(ScChart2DataSequence & rParent,ScDocument * pDoc)2826 ScChart2DataSequence::ExternalRefListener::ExternalRefListener(
2827 ScChart2DataSequence& rParent, ScDocument* pDoc) :
2828 ScExternalRefManager::LinkListener(),
2829 mrParent(rParent),
2830 mpDoc(pDoc)
2831 {
2832 }
2833
~ExternalRefListener()2834 ScChart2DataSequence::ExternalRefListener::~ExternalRefListener()
2835 {
2836 if (!mpDoc || mpDoc->IsInDtorClear())
2837 // The document is being destroyed. Do nothing.
2838 return;
2839
2840 // Make sure to remove all pointers to this object.
2841 mpDoc->GetExternalRefManager()->removeLinkListener(this);
2842 }
2843
notify(sal_uInt16 nFileId,ScExternalRefManager::LinkUpdateType eType)2844 void ScChart2DataSequence::ExternalRefListener::notify(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType)
2845 {
2846 switch (eType)
2847 {
2848 case ScExternalRefManager::LINK_MODIFIED:
2849 {
2850 if (maFileIds.count(nFileId))
2851 // We are listening to this external document.
2852 mrParent.RebuildDataCache();
2853 }
2854 break;
2855 case ScExternalRefManager::LINK_BROKEN:
2856 maFileIds.erase(nFileId);
2857 break;
2858 }
2859 }
2860
addFileId(sal_uInt16 nFileId)2861 void ScChart2DataSequence::ExternalRefListener::addFileId(sal_uInt16 nFileId)
2862 {
2863 maFileIds.insert(nFileId);
2864 }
2865
getData()2866 uno::Sequence< uno::Any> SAL_CALL ScChart2DataSequence::getData()
2867 {
2868 SolarMutexGuard aGuard;
2869 if ( !m_pDocument)
2870 throw uno::RuntimeException();
2871
2872 BuildDataCache();
2873
2874 if (!m_aMixedDataCache.hasElements())
2875 {
2876 // Build a cache for the 1st time...
2877
2878 sal_Int32 nCount = m_aDataArray.size();
2879 m_aMixedDataCache.realloc(nCount);
2880 uno::Any* pArr = m_aMixedDataCache.getArray();
2881 for (const Item &rItem : m_aDataArray)
2882 {
2883 if (rItem.mbIsValue)
2884 *pArr <<= rItem.mfValue;
2885 else if (rItem.maString.isEmpty())
2886 {
2887 ScRefCellValue aCell(*m_pDocument, rItem.mAddress);
2888 if (aCell.isEmpty())
2889 *pArr = uno::Any();
2890 else
2891 *pArr <<= rItem.maString;
2892 }
2893 else
2894 *pArr <<= rItem.maString;
2895 ++pArr;
2896 }
2897 }
2898 return m_aMixedDataCache;
2899 }
2900
2901 // XNumericalDataSequence --------------------------------------------------
2902
getNumericalData()2903 uno::Sequence< double > SAL_CALL ScChart2DataSequence::getNumericalData()
2904 {
2905 SolarMutexGuard aGuard;
2906 if ( !m_pDocument)
2907 throw uno::RuntimeException();
2908
2909 BuildDataCache();
2910
2911 double fNAN;
2912 ::rtl::math::setNan(&fNAN);
2913
2914 sal_Int32 nCount = m_aDataArray.size();
2915 uno::Sequence<double> aSeq(nCount);
2916 double* pArr = aSeq.getArray();
2917 for (const Item& rItem : m_aDataArray)
2918 {
2919 *pArr = rItem.mbIsValue ? rItem.mfValue : fNAN;
2920 ++pArr;
2921 }
2922
2923 return aSeq;
2924 }
2925
2926 // XTextualDataSequence --------------------------------------------------
2927
getTextualData()2928 uno::Sequence< OUString > SAL_CALL ScChart2DataSequence::getTextualData()
2929 {
2930 SolarMutexGuard aGuard;
2931 uno::Sequence<OUString> aSeq;
2932 if ( !m_pDocument )
2933 throw uno::RuntimeException();
2934
2935 BuildDataCache();
2936
2937 sal_Int32 nCount = m_aDataArray.size();
2938 if ( nCount > 0 )
2939 {
2940 aSeq = uno::Sequence<OUString>(nCount);
2941 OUString* pArr = aSeq.getArray();
2942 for (const Item& rItem : m_aDataArray)
2943 {
2944 *pArr = rItem.maString;
2945 ++pArr;
2946 }
2947 }
2948 else if ( m_aTokens.front() )
2949 {
2950 if( m_aTokens.front()->GetType() == svString )
2951 {
2952 aSeq = uno::Sequence<OUString>(1);
2953 aSeq[0] = m_aTokens.front()->GetString().getString();
2954 }
2955 }
2956
2957 return aSeq;
2958 }
2959
getSourceRangeRepresentation()2960 OUString SAL_CALL ScChart2DataSequence::getSourceRangeRepresentation()
2961 {
2962 SolarMutexGuard aGuard;
2963 OUString aStr;
2964 OSL_ENSURE( m_pDocument, "No Document -> no SourceRangeRepresentation" );
2965 if (m_pDocument)
2966 lcl_convertTokensToString(aStr, m_aTokens, m_pDocument);
2967
2968 return aStr;
2969 }
2970
2971 namespace {
2972
2973 /**
2974 * This function object is used to accumulatively count the numbers of
2975 * columns and rows in all reference tokens.
2976 */
2977 class AccumulateRangeSize
2978 {
2979 public:
AccumulateRangeSize()2980 AccumulateRangeSize() :
2981 mnCols(0), mnRows(0) {}
2982
operator ()(const ScTokenRef & pToken)2983 void operator() (const ScTokenRef& pToken)
2984 {
2985 ScRange r;
2986 bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
2987 ScRefTokenHelper::getRangeFromToken(r, pToken, ScAddress(), bExternal);
2988 r.PutInOrder();
2989 mnCols += r.aEnd.Col() - r.aStart.Col() + 1;
2990 mnRows += r.aEnd.Row() - r.aStart.Row() + 1;
2991 }
2992
getCols() const2993 SCCOL getCols() const { return mnCols; }
getRows() const2994 SCROW getRows() const { return mnRows; }
2995 private:
2996 SCCOL mnCols;
2997 SCROW mnRows;
2998 };
2999
3000 /**
3001 * This function object is used to generate label strings from a list of
3002 * reference tokens.
3003 */
3004 class GenerateLabelStrings
3005 {
3006 public:
GenerateLabelStrings(sal_Int32 nSize,chart2::data::LabelOrigin eOrigin,bool bColumn)3007 GenerateLabelStrings(sal_Int32 nSize, chart2::data::LabelOrigin eOrigin, bool bColumn) :
3008 mpLabels(new Sequence<OUString>(nSize)),
3009 meOrigin(eOrigin),
3010 mnCount(0),
3011 mbColumn(bColumn) {}
3012
operator ()(const ScTokenRef & pToken)3013 void operator() (const ScTokenRef& pToken)
3014 {
3015 bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
3016 ScRange aRange;
3017 ScRefTokenHelper::getRangeFromToken(aRange, pToken, ScAddress(), bExternal);
3018 OUString* pArr = mpLabels->getArray();
3019 if (mbColumn)
3020 {
3021 for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol)
3022 {
3023 if ( meOrigin != chart2::data::LabelOrigin_LONG_SIDE)
3024 {
3025 OUString aString = ScResId(STR_COLUMN) + " ";
3026 ScAddress aPos( nCol, 0, 0 );
3027 OUString aColStr(aPos.Format(ScRefFlags::COL_VALID));
3028 aString += aColStr;
3029 pArr[mnCount] = aString;
3030 }
3031 else //only indices for categories
3032 pArr[mnCount] = OUString::number( mnCount+1 );
3033 ++mnCount;
3034 }
3035 }
3036 else
3037 {
3038 for (sal_Int32 nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow)
3039 {
3040 if (meOrigin != chart2::data::LabelOrigin_LONG_SIDE)
3041 {
3042 OUString aString = ScResId(STR_ROW) +
3043 " " + OUString::number( nRow+1 );
3044 pArr[mnCount] = aString;
3045 }
3046 else //only indices for categories
3047 pArr[mnCount] = OUString::number( mnCount+1 );
3048 ++mnCount;
3049 }
3050 }
3051 }
3052
getLabels() const3053 const Sequence<OUString>& getLabels() const { return *mpLabels; }
3054
3055 private:
3056 shared_ptr< Sequence<OUString> > mpLabels;
3057 chart2::data::LabelOrigin meOrigin;
3058 sal_Int32 mnCount;
3059 bool mbColumn;
3060 };
3061
3062 }
3063
generateLabel(chart2::data::LabelOrigin eOrigin)3064 uno::Sequence< OUString > SAL_CALL ScChart2DataSequence::generateLabel(chart2::data::LabelOrigin eOrigin)
3065 {
3066 SolarMutexGuard aGuard;
3067 if ( !m_pDocument)
3068 throw uno::RuntimeException();
3069
3070 // Determine the total size of all ranges.
3071 AccumulateRangeSize func;
3072 func = ::std::for_each(m_aTokens.begin(), m_aTokens.end(), func);
3073 SCCOL nCols = func.getCols();
3074 SCROW nRows = func.getRows();
3075
3076 // Determine whether this is column-major or row-major.
3077 bool bColumn = true;
3078 if ((eOrigin == chart2::data::LabelOrigin_SHORT_SIDE) ||
3079 (eOrigin == chart2::data::LabelOrigin_LONG_SIDE))
3080 {
3081 if (nRows > nCols)
3082 {
3083 bColumn = eOrigin == chart2::data::LabelOrigin_SHORT_SIDE;
3084 }
3085 else if (nCols > nRows)
3086 {
3087 bColumn = eOrigin != chart2::data::LabelOrigin_SHORT_SIDE;
3088 }
3089 else
3090 return Sequence<OUString>();
3091 }
3092
3093 // Generate label strings based on the info so far.
3094 sal_Int32 nCount = bColumn ? nCols : nRows;
3095 GenerateLabelStrings genLabels(nCount, eOrigin, bColumn);
3096 genLabels = ::std::for_each(m_aTokens.begin(), m_aTokens.end(), genLabels);
3097 Sequence<OUString> aSeq = genLabels.getLabels();
3098
3099 return aSeq;
3100 }
3101
3102 namespace {
3103
getDisplayNumberFormat(const ScDocument * pDoc,const ScAddress & rPos)3104 sal_uInt32 getDisplayNumberFormat(const ScDocument* pDoc, const ScAddress& rPos)
3105 {
3106 sal_uInt32 nFormat = pDoc->GetNumberFormat(rPos); // original format from cell.
3107 return nFormat;
3108 }
3109
3110 }
3111
getNumberFormatKeyByIndex(::sal_Int32 nIndex)3112 ::sal_Int32 SAL_CALL ScChart2DataSequence::getNumberFormatKeyByIndex( ::sal_Int32 nIndex )
3113 {
3114 SolarMutexGuard aGuard;
3115 BuildDataCache();
3116
3117 if (nIndex == -1)
3118 {
3119 // return format of first non-empty cell
3120 // TODO: use nicer heuristic
3121 for (const Item& rItem : m_aDataArray)
3122 {
3123 ScRefCellValue aCell(*m_pDocument, rItem.mAddress);
3124 if (!aCell.isEmpty() && aCell.hasNumeric())
3125 {
3126 return static_cast<sal_Int32>(getDisplayNumberFormat(m_pDocument, rItem.mAddress));
3127 }
3128 }
3129
3130 // we could not find a non-empty cell
3131 return 0;
3132 }
3133
3134 if (nIndex < 0 || nIndex >= static_cast<sal_Int32>(m_aDataArray.size()))
3135 {
3136 SAL_WARN("sc.ui", "Passed invalid index to getNumberFormatKeyByIndex(). Will return default value '0'.");
3137 return 0;
3138 }
3139
3140 return static_cast<sal_Int32>(getDisplayNumberFormat(m_pDocument, m_aDataArray.at(nIndex).mAddress));
3141 }
3142
3143 // XCloneable ================================================================
3144
createClone()3145 uno::Reference< util::XCloneable > SAL_CALL ScChart2DataSequence::createClone()
3146 {
3147 SolarMutexGuard aGuard;
3148
3149 // Clone tokens.
3150 vector<ScTokenRef> aTokensNew;
3151 aTokensNew.reserve(m_aTokens.size());
3152 for (const auto& rxToken : m_aTokens)
3153 {
3154 ScTokenRef p(rxToken->Clone());
3155 aTokensNew.push_back(p);
3156 }
3157
3158 rtl::Reference<ScChart2DataSequence> p(new ScChart2DataSequence(m_pDocument, m_xDataProvider, std::move(aTokensNew), m_bIncludeHiddenCells));
3159 p->CopyData(*this);
3160 uno::Reference< util::XCloneable > xClone(p.get());
3161
3162 return xClone;
3163 }
3164
3165 // XModifyBroadcaster ========================================================
3166
addModifyListener(const uno::Reference<util::XModifyListener> & aListener)3167 void SAL_CALL ScChart2DataSequence::addModifyListener( const uno::Reference< util::XModifyListener >& aListener )
3168 {
3169 // like ScCellRangesBase::addModifyListener
3170 SolarMutexGuard aGuard;
3171 if (m_aTokens.empty())
3172 return;
3173
3174 ScRangeList aRanges;
3175 ScRefTokenHelper::getRangeListFromTokens(aRanges, m_aTokens, ScAddress());
3176 m_aValueListeners.emplace_back( aListener );
3177
3178 if ( m_aValueListeners.size() == 1 )
3179 {
3180 if (!m_pValueListener)
3181 m_pValueListener.reset(new ScLinkListener( LINK( this, ScChart2DataSequence, ValueListenerHdl ) ));
3182
3183 if (!m_pHiddenListener)
3184 m_pHiddenListener.reset(new HiddenRangeListener(*this));
3185
3186 if( m_pDocument )
3187 {
3188 ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection();
3189 for (const auto& rxToken : m_aTokens)
3190 {
3191 ScRange aRange;
3192 if (!ScRefTokenHelper::getRangeFromToken(aRange, rxToken, ScAddress()))
3193 continue;
3194
3195 m_pDocument->StartListeningArea( aRange, false, m_pValueListener.get() );
3196 if (pCLC)
3197 pCLC->StartListeningHiddenRange(aRange, m_pHiddenListener.get());
3198 }
3199 }
3200
3201 acquire(); // don't lose this object (one ref for all listeners)
3202 }
3203 }
3204
removeModifyListener(const uno::Reference<util::XModifyListener> & aListener)3205 void SAL_CALL ScChart2DataSequence::removeModifyListener( const uno::Reference< util::XModifyListener >& aListener )
3206 {
3207 // like ScCellRangesBase::removeModifyListener
3208
3209 SolarMutexGuard aGuard;
3210 if (m_aTokens.empty())
3211 return;
3212
3213 rtl::Reference<ScChart2DataSequence> aSelfHold(this); // in case the listeners have the last ref
3214
3215 sal_uInt16 nCount = m_aValueListeners.size();
3216 for ( sal_uInt16 n=nCount; n--; )
3217 {
3218 uno::Reference<util::XModifyListener>& rObj = m_aValueListeners[n];
3219 if ( rObj == aListener )
3220 {
3221 m_aValueListeners.erase( m_aValueListeners.begin() + n );
3222
3223 if ( m_aValueListeners.empty() )
3224 {
3225 if (m_pValueListener)
3226 m_pValueListener->EndListeningAll();
3227
3228 if (m_pHiddenListener.get() && m_pDocument)
3229 {
3230 ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection();
3231 if (pCLC)
3232 pCLC->EndListeningHiddenRange(m_pHiddenListener.get());
3233 }
3234
3235 release(); // release the ref for the listeners
3236 }
3237
3238 break;
3239 }
3240 }
3241 }
3242
3243 // DataSequence XPropertySet -------------------------------------------------
3244
3245 uno::Reference< beans::XPropertySetInfo> SAL_CALL
getPropertySetInfo()3246 ScChart2DataSequence::getPropertySetInfo()
3247 {
3248 SolarMutexGuard aGuard;
3249 static uno::Reference<beans::XPropertySetInfo> aRef =
3250 new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() );
3251 return aRef;
3252 }
3253
setPropertyValue(const OUString & rPropertyName,const uno::Any & rValue)3254 void SAL_CALL ScChart2DataSequence::setPropertyValue(
3255 const OUString& rPropertyName, const uno::Any& rValue)
3256 {
3257 if ( rPropertyName == SC_UNONAME_ROLE )
3258 {
3259 if ( !(rValue >>= m_aRole))
3260 throw lang::IllegalArgumentException();
3261 }
3262 else if ( rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS )
3263 {
3264 bool bOldValue = m_bIncludeHiddenCells;
3265 if ( !(rValue >>= m_bIncludeHiddenCells))
3266 throw lang::IllegalArgumentException();
3267 if( bOldValue != m_bIncludeHiddenCells )
3268 m_aDataArray.clear();//data array is dirty now
3269 }
3270 else if( rPropertyName == "TimeBased" )
3271 {
3272 bool bTimeBased = mbTimeBased;
3273 rValue>>= bTimeBased;
3274 mbTimeBased = bTimeBased;
3275 }
3276 else
3277 throw beans::UnknownPropertyException(rPropertyName);
3278 // TODO: support optional properties
3279 }
3280
getPropertyValue(const OUString & rPropertyName)3281 uno::Any SAL_CALL ScChart2DataSequence::getPropertyValue(const OUString& rPropertyName)
3282 {
3283 uno::Any aRet;
3284 if ( rPropertyName == SC_UNONAME_ROLE )
3285 aRet <<= m_aRole;
3286 else if ( rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS )
3287 aRet <<= m_bIncludeHiddenCells;
3288 else if ( rPropertyName == SC_UNONAME_HIDDENVALUES )
3289 {
3290 // This property is read-only thus cannot be set externally via
3291 // setPropertyValue(...).
3292 BuildDataCache();
3293 aRet <<= m_aHiddenValues;
3294 }
3295 else if (rPropertyName == SC_UNONAME_TIME_BASED)
3296 {
3297 aRet <<= mbTimeBased;
3298 }
3299 else if (rPropertyName == SC_UNONAME_HAS_STRING_LABEL)
3300 {
3301 // Read-only property. It returns whether or not the label value is a
3302 // direct user input, rather than an indirect reference.
3303 bool bHasStringLabel = false;
3304 if (m_aTokens.size() == 1)
3305 {
3306 const formula::FormulaToken& rToken = *m_aTokens[0];
3307 bHasStringLabel = rToken.GetType() == formula::svString;
3308 }
3309 aRet <<= bHasStringLabel;
3310 }
3311 else
3312 throw beans::UnknownPropertyException(rPropertyName);
3313 // TODO: support optional properties
3314 return aRet;
3315 }
3316
addPropertyChangeListener(const OUString &,const uno::Reference<beans::XPropertyChangeListener> &)3317 void SAL_CALL ScChart2DataSequence::addPropertyChangeListener(
3318 const OUString& /*rPropertyName*/,
3319 const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/)
3320 {
3321 // FIXME: real implementation
3322 OSL_FAIL( "Not yet implemented" );
3323 }
3324
removePropertyChangeListener(const OUString &,const uno::Reference<beans::XPropertyChangeListener> &)3325 void SAL_CALL ScChart2DataSequence::removePropertyChangeListener(
3326 const OUString& /*rPropertyName*/,
3327 const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/)
3328 {
3329 // FIXME: real implementation
3330 OSL_FAIL( "Not yet implemented" );
3331 }
3332
addVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)3333 void SAL_CALL ScChart2DataSequence::addVetoableChangeListener(
3334 const OUString& /*rPropertyName*/,
3335 const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/)
3336 {
3337 // FIXME: real implementation
3338 OSL_FAIL( "Not yet implemented" );
3339 }
3340
removeVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)3341 void SAL_CALL ScChart2DataSequence::removeVetoableChangeListener(
3342 const OUString& /*rPropertyName*/,
3343 const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/)
3344 {
3345 // FIXME: real implementation
3346 OSL_FAIL( "Not yet implemented" );
3347 }
3348
setDataChangedHint(bool b)3349 void ScChart2DataSequence::setDataChangedHint(bool b)
3350 {
3351 m_bGotDataChangedHint = b;
3352 }
3353
switchToNext(sal_Bool bWrap)3354 sal_Bool ScChart2DataSequence::switchToNext(sal_Bool bWrap)
3355 {
3356 if(!mbTimeBased)
3357 return true;
3358
3359 if(mnCurrentTab >= mnTimeBasedEnd)
3360 {
3361 if(bWrap)
3362 setToPointInTime(0);
3363 return false;
3364 }
3365
3366 for(const auto& rxToken : m_aTokens)
3367 {
3368 if (rxToken->GetType() != svDoubleRef)
3369 continue;
3370
3371 ScComplexRefData& rData = *rxToken->GetDoubleRef();
3372 ScSingleRefData& s = rData.Ref1;
3373 ScSingleRefData& e = rData.Ref2;
3374
3375 s.IncTab(1);
3376 e.IncTab(1);
3377 }
3378
3379 ++mnCurrentTab;
3380
3381 RebuildDataCache();
3382
3383 return true;
3384 }
3385
setRange(sal_Int32 nStart,sal_Int32 nEnd)3386 void ScChart2DataSequence::setRange(sal_Int32 nStart, sal_Int32 nEnd)
3387 {
3388 mnTimeBasedStart = nStart;
3389 mnTimeBasedEnd = nEnd;
3390 mnCurrentTab = mnTimeBasedStart;
3391 }
3392
setToPointInTime(sal_Int32 nPoint)3393 sal_Bool ScChart2DataSequence::setToPointInTime(sal_Int32 nPoint)
3394 {
3395 if(nPoint > mnTimeBasedEnd - mnTimeBasedStart)
3396 return false;
3397
3398 SCTAB nTab = mnTimeBasedStart + nPoint;
3399 for(const auto& rxToken : m_aTokens)
3400 {
3401 if (rxToken->GetType() != svDoubleRef)
3402 continue;
3403
3404 ScComplexRefData& rData = *rxToken->GetDoubleRef();
3405 ScSingleRefData& s = rData.Ref1;
3406 ScSingleRefData& e = rData.Ref2;
3407
3408 s.SetAbsTab(nTab);
3409 e.SetAbsTab(nTab);
3410 }
3411
3412 mnCurrentTab = nTab;
3413
3414 RebuildDataCache();
3415
3416 return true;
3417 }
3418
3419 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3420