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
10 #include <memory>
11 #include <sal/config.h>
12
13 #include <PivotTableDataProvider.hxx>
14 #include <PivotTableDataSource.hxx>
15 #include <PivotTableDataSequence.hxx>
16
17 #include <miscuno.hxx>
18 #include <document.hxx>
19 #include <unonames.hxx>
20 #include <scresid.hxx>
21 #include <globstr.hrc>
22 #include <strings.hrc>
23 #include <dpobject.hxx>
24 #include <hints.hxx>
25
26 #include <vcl/svapp.hxx>
27 #include <sfx2/objsh.hxx>
28 #include <comphelper/propertysequence.hxx>
29 #include <comphelper/sequence.hxx>
30 #include <comphelper/processfactory.hxx>
31
32 #include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
33 #include <com/sun/star/chart/ChartDataRowSource.hpp>
34 #include <com/sun/star/frame/XModel.hpp>
35
36 #include <com/sun/star/sheet/XDataPilotResults.hpp>
37 #include <com/sun/star/sheet/DataResultFlags.hpp>
38
39 #include <com/sun/star/sheet/XDimensionsSupplier.hpp>
40 #include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
41 #include <com/sun/star/sheet/XLevelsSupplier.hpp>
42 #include <com/sun/star/sheet/XDataPilotMemberResults.hpp>
43 #include <com/sun/star/sheet/MemberResultFlags.hpp>
44 #include <com/sun/star/sheet/XMembersSupplier.hpp>
45
46 #include <com/sun/star/chart/ChartDataChangeEvent.hpp>
47 #include <com/sun/star/container/XNamed.hpp>
48
49 #include <unordered_map>
50
51 using namespace css;
52
53 namespace sc
54 {
55 namespace
56 {
57 const OUString constIdCategories("categories");
58 const OUString constIdLabel("label");
59 const OUString constIdData("data");
60
lcl_GetDataProviderPropertyMap()61 const SfxItemPropertyMapEntry* lcl_GetDataProviderPropertyMap()
62 {
63 static const SfxItemPropertyMapEntry aDataProviderPropertyMap_Impl[] =
64 {
65 { OUString(SC_UNONAME_INCLUDEHIDDENCELLS), 0, cppu::UnoType<bool>::get(), 0, 0 },
66 { OUString(SC_UNONAME_USE_INTERNAL_DATA_PROVIDER), 0, cppu::UnoType<bool>::get(), 0, 0 },
67 { OUString(), 0, css::uno::Type(), 0, 0 }
68 };
69 return aDataProviderPropertyMap_Impl;
70 }
71
lcl_GetXModel(const ScDocument * pDoc)72 uno::Reference<frame::XModel> lcl_GetXModel(const ScDocument * pDoc)
73 {
74 uno::Reference<frame::XModel> xModel;
75 SfxObjectShell* pObjSh(pDoc ? pDoc->GetDocumentShell() : nullptr);
76 if (pObjSh)
77 xModel.set(pObjSh->GetModel());
78 return xModel;
79 }
80
lcl_identifierForData(sal_Int32 index)81 OUString lcl_identifierForData(sal_Int32 index)
82 {
83 return "PT@" + constIdData + " " + OUString::number(index);
84 }
85
lcl_identifierForLabel(sal_Int32 index)86 OUString lcl_identifierForLabel(sal_Int32 index)
87 {
88 return "PT@" + constIdLabel + " " + OUString::number(index);
89 }
90
lcl_identifierForCategories()91 OUString lcl_identifierForCategories()
92 {
93 return "PT@" + constIdCategories;
94 }
95
lcl_getVisiblePageMembers(const uno::Reference<uno::XInterface> & xLevel)96 std::vector<OUString> lcl_getVisiblePageMembers(const uno::Reference<uno::XInterface> & xLevel)
97 {
98 std::vector<OUString> aResult;
99 if (!xLevel.is())
100 return aResult;
101
102 uno::Reference<sheet::XMembersSupplier> xMembersSupplier(xLevel, uno::UNO_QUERY);
103 if (!xMembersSupplier.is())
104 return aResult;
105
106 uno::Reference<sheet::XMembersAccess> xMembersAccess = xMembersSupplier->getMembers();
107 if (!xMembersAccess.is())
108 return aResult;
109
110 const css::uno::Sequence<OUString> aMembersNames = xMembersAccess->getElementNames();
111 for (OUString const & rMemberNames : aMembersNames)
112 {
113 uno::Reference<beans::XPropertySet> xProperties(xMembersAccess->getByName(rMemberNames), uno::UNO_QUERY);
114 if (!xProperties.is())
115 continue;
116
117 OUString aCaption = ScUnoHelpFunctions::GetStringProperty(xProperties, SC_UNO_DP_LAYOUTNAME, OUString());
118 if (aCaption.isEmpty())
119 aCaption = rMemberNames;
120
121 bool bVisible = ScUnoHelpFunctions::GetBoolProperty(xProperties, SC_UNO_DP_ISVISIBLE);
122
123 if (bVisible)
124 aResult.push_back(aCaption);
125 }
126
127 return aResult;
128 }
129
130 } // end anonymous namespace
131
132 SC_SIMPLE_SERVICE_INFO(PivotTableDataProvider, "PivotTableDataProvider", SC_SERVICENAME_CHART_PIVOTTABLE_DATAPROVIDER)
133
134 // DataProvider ==============================================================
135
PivotTableDataProvider(ScDocument * pDoc)136 PivotTableDataProvider::PivotTableDataProvider(ScDocument* pDoc)
137 : m_pDocument(pDoc)
138 , m_aPropSet(lcl_GetDataProviderPropertyMap())
139 , m_bIncludeHiddenCells(true)
140 , m_bNeedsUpdate(true)
141 , m_xContext(comphelper::getProcessComponentContext())
142 {
143 if (m_pDocument)
144 m_pDocument->AddUnoObject(*this);
145 }
146
~PivotTableDataProvider()147 PivotTableDataProvider::~PivotTableDataProvider()
148 {
149 SolarMutexGuard g;
150
151 if (m_pDocument)
152 m_pDocument->RemoveUnoObject( *this);
153 }
154
Notify(SfxBroadcaster &,const SfxHint & rHint)155 void PivotTableDataProvider::Notify(SfxBroadcaster& /*rBroadcaster*/, const SfxHint& rHint)
156 {
157 if (rHint.GetId() == SfxHintId::Dying)
158 {
159 m_pDocument = nullptr;
160 }
161 else if (dynamic_cast<const ScDataPilotModifiedHint*>(&rHint))
162 {
163 if (m_pDocument)
164 {
165 OUString sPivotTableName = static_cast<const ScDataPilotModifiedHint&>(rHint).GetName();
166 if (sPivotTableName == m_sPivotTableName)
167 {
168 m_bNeedsUpdate = true;
169 for (uno::Reference<util::XModifyListener> const & xListener : m_aValueListeners)
170 {
171 css::chart::ChartDataChangeEvent aEvent(static_cast<cppu::OWeakObject*>(this),
172 css::chart::ChartDataChangeType_ALL,
173 0, 0, 0, 0);
174 xListener->modified(aEvent);
175 }
176 }
177 }
178 }
179 }
180
createDataSourcePossible(const uno::Sequence<beans::PropertyValue> &)181 sal_Bool SAL_CALL PivotTableDataProvider::createDataSourcePossible(const uno::Sequence<beans::PropertyValue>& /*aArguments*/)
182 {
183 SolarMutexGuard aGuard;
184 if (!m_pDocument)
185 return false;
186
187 if (m_sPivotTableName.isEmpty())
188 return false;
189
190 ScDPCollection* pDPCollection = m_pDocument->GetDPCollection();
191 return bool(pDPCollection->GetByName(m_sPivotTableName));
192 }
193
194 uno::Reference<chart2::data::XDataSource> SAL_CALL
createDataSource(const uno::Sequence<beans::PropertyValue> & aArguments)195 PivotTableDataProvider::createDataSource(const uno::Sequence<beans::PropertyValue>& aArguments)
196 {
197 SolarMutexGuard aGuard;
198
199 if (!m_pDocument)
200 throw uno::RuntimeException();
201
202 bool bOrientCol = true;
203 OUString aRangeRepresentation;
204
205 for (beans::PropertyValue const & rProperty : aArguments)
206 {
207 if (rProperty.Name == "DataRowSource")
208 {
209 chart::ChartDataRowSource eSource = chart::ChartDataRowSource_COLUMNS;
210 if (!(rProperty.Value >>= eSource))
211 {
212 sal_Int32 nSource(0);
213 if (rProperty.Value >>= nSource)
214 eSource = chart::ChartDataRowSource(nSource);
215 }
216 bOrientCol = (eSource == chart::ChartDataRowSource_COLUMNS);
217 }
218 else if (rProperty.Name == "CellRangeRepresentation")
219 rProperty.Value >>= aRangeRepresentation;
220 }
221
222 uno::Reference<chart2::data::XDataSource> xResult;
223
224 if (aRangeRepresentation == lcl_identifierForCategories())
225 xResult = createCategoriesDataSource(bOrientCol);
226 else
227 xResult = createValuesDataSource();
228
229 return xResult;
230 }
231
232 uno::Reference<chart2::data::XLabeledDataSequence>
newLabeledDataSequence()233 PivotTableDataProvider::newLabeledDataSequence()
234 {
235 uno::Reference<chart2::data::XLabeledDataSequence> xResult;
236 if (!m_xContext.is())
237 return xResult;
238 xResult.set(chart2::data::LabeledDataSequence::create(m_xContext), uno::UNO_QUERY_THROW);
239 return xResult;
240 }
241
242 uno::Reference<chart2::data::XDataSource>
createCategoriesDataSource(bool bOrientationIsColumn)243 PivotTableDataProvider::createCategoriesDataSource(bool bOrientationIsColumn)
244 {
245 if (m_bNeedsUpdate)
246 collectPivotTableData();
247
248 uno::Reference<chart2::data::XDataSource> xDataSource;
249 std::vector<uno::Reference<chart2::data::XLabeledDataSequence>> aLabeledSequences;
250
251 std::vector<std::vector<ValueAndFormat>> const & rCategoriesVector = bOrientationIsColumn ? m_aCategoriesColumnOrientation
252 : m_aCategoriesRowOrientation;
253
254 for (std::vector<ValueAndFormat> const & rCategories : rCategoriesVector)
255 {
256 uno::Reference<chart2::data::XLabeledDataSequence> xResult = newLabeledDataSequence();
257 std::unique_ptr<PivotTableDataSequence> pSequence;
258 pSequence.reset(new PivotTableDataSequence(m_pDocument, m_sPivotTableName,
259 lcl_identifierForCategories(), rCategories));
260 pSequence->setRole("categories");
261 xResult->setValues(uno::Reference<chart2::data::XDataSequence>(pSequence.release()));
262
263 aLabeledSequences.push_back(xResult);
264 }
265
266 xDataSource.set(new PivotTableDataSource(aLabeledSequences));
267 return xDataSource;
268 }
269
collectPivotTableData()270 void PivotTableDataProvider::collectPivotTableData()
271 {
272 ScDPCollection* pDPCollection = m_pDocument->GetDPCollection();
273 ScDPObject* pDPObject = pDPCollection->GetByName(m_sPivotTableName);
274 if (!pDPObject)
275 return;
276
277 m_aCategoriesColumnOrientation.clear();
278 m_aCategoriesRowOrientation.clear();
279 m_aLabels.clear();
280 m_aDataRowVector.clear();
281 m_aColumnFields.clear();
282 m_aRowFields.clear();
283 m_aPageFields.clear();
284 m_aDataFields.clear();
285 m_aFieldOutputDescriptionMap.clear();
286
287 uno::Reference<sheet::XDataPilotResults> xDPResults(pDPObject->GetSource(), uno::UNO_QUERY);
288 if (!xDPResults.is())
289 return;
290 const uno::Sequence<uno::Sequence<sheet::DataResult>> xDataResultsSequence = xDPResults->getResults();
291
292 double fNan;
293 rtl::math::setNan(&fNan);
294
295 std::unordered_set<size_t> aValidRowIndex;
296
297 size_t nRowIndex = 0;
298 for (uno::Sequence<sheet::DataResult> const & xDataResults : xDataResultsSequence)
299 {
300 std::vector<ValueAndFormat> aRow;
301 bool bRowEmpty = true;
302 // First pass - collect a row of valid data and track if the row is empty
303 for (sheet::DataResult const & rDataResult : xDataResults)
304 {
305 if (rDataResult.Flags & css::sheet::DataResultFlags::SUBTOTAL)
306 continue;
307 if (rDataResult.Flags == 0 || rDataResult.Flags & css::sheet::DataResultFlags::HASDATA)
308 {
309 aRow.emplace_back(rDataResult.Flags ? rDataResult.Value : fNan, 0);
310 if (rDataResult.Flags != 0) // set as valid only if we have data
311 {
312 bRowEmpty = false;
313 // We need to remember all valid (non-empty) row indices
314 aValidRowIndex.insert(nRowIndex);
315 }
316 }
317 }
318 // Second pass: add to collection only non-empty rows
319 if (!bRowEmpty)
320 {
321 size_t nColumnIndex = 0;
322 for (ValueAndFormat const & aValue : aRow)
323 {
324 if (nColumnIndex >= m_aDataRowVector.size())
325 m_aDataRowVector.resize(nColumnIndex + 1);
326 m_aDataRowVector[nColumnIndex].push_back(aValue);
327 nColumnIndex++;
328 }
329 }
330 nRowIndex++;
331 }
332
333 uno::Reference<sheet::XDimensionsSupplier> xDimensionsSupplier(pDPObject->GetSource());
334 uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess(xDimensionsSupplier->getDimensions());
335
336 std::unordered_map<OUString, sal_Int32> aDataFieldNumberFormatMap;
337 std::vector<OUString> aDataFieldNamesVectors;
338
339 std::unordered_map<OUString, OUString> aDataFieldCaptionNames;
340
341 sheet::DataPilotFieldOrientation eDataFieldOrientation = sheet::DataPilotFieldOrientation_HIDDEN;
342
343 for (sal_Int32 nDim = 0; nDim < xDims->getCount(); nDim++)
344 {
345 uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
346 uno::Reference<beans::XPropertySet> xDimProp(xDim, uno::UNO_QUERY);
347 uno::Reference<sheet::XHierarchiesSupplier> xDimSupp(xDim, uno::UNO_QUERY);
348
349 if (!xDimProp.is() || !xDimSupp.is())
350 continue;
351
352 sheet::DataPilotFieldOrientation eDimOrient =
353 ScUnoHelpFunctions::GetEnumProperty(xDimProp, SC_UNO_DP_ORIENTATION,
354 sheet::DataPilotFieldOrientation_HIDDEN);
355
356 if (eDimOrient == sheet::DataPilotFieldOrientation_HIDDEN)
357 continue;
358
359 uno::Reference<container::XIndexAccess> xHierarchies = new ScNameToIndexAccess(xDimSupp->getHierarchies());
360 sal_Int32 nHierarchy = ScUnoHelpFunctions::GetLongProperty(xDimProp, SC_UNO_DP_USEDHIERARCHY);
361 if (nHierarchy >= xHierarchies->getCount())
362 nHierarchy = 0;
363
364 uno::Reference<sheet::XLevelsSupplier> xLevelsSupplier(xHierarchies->getByIndex(nHierarchy),
365 uno::UNO_QUERY);
366
367 if (!xLevelsSupplier.is())
368 continue;
369
370 uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess(xLevelsSupplier->getLevels());
371
372 for (long nLevel = 0; nLevel < xLevels->getCount(); nLevel++)
373 {
374 uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLevel), uno::UNO_QUERY);
375 uno::Reference<container::XNamed> xLevelName(xLevel, uno::UNO_QUERY);
376 uno::Reference<sheet::XDataPilotMemberResults> xLevelResult(xLevel, uno::UNO_QUERY );
377
378 if (xLevelName.is() && xLevelResult.is())
379 {
380 bool bIsDataLayout = ScUnoHelpFunctions::GetBoolProperty(xDimProp, SC_UNO_DP_ISDATALAYOUT);
381 sal_Int32 nDimPos = ScUnoHelpFunctions::GetLongProperty(xDimProp, SC_UNO_DP_POSITION);
382 sal_Int32 nNumberFormat = ScUnoHelpFunctions::GetLongProperty(xDimProp, SC_UNO_DP_NUMBERFO);
383 bool bHasHiddenMember = ScUnoHelpFunctions::GetBoolProperty(xDimProp, SC_UNO_DP_HAS_HIDDEN_MEMBER);
384
385 switch (eDimOrient)
386 {
387 case sheet::DataPilotFieldOrientation_COLUMN:
388 {
389 m_aColumnFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember);
390
391 const uno::Sequence<sheet::MemberResult> aSequence = xLevelResult->getResults();
392 size_t i = 0;
393 OUString sCaption;
394 OUString sName;
395 for (sheet::MemberResult const & rMember : aSequence)
396 {
397 // Skip grandtotals and subtotals
398 if (rMember.Flags & sheet::MemberResultFlags::SUBTOTAL ||
399 rMember.Flags & sheet::MemberResultFlags::GRANDTOTAL)
400 continue;
401 if (rMember.Flags & sheet::MemberResultFlags::HASMEMBER ||
402 rMember.Flags & sheet::MemberResultFlags::CONTINUE)
403 {
404 if (!(rMember.Flags & sheet::MemberResultFlags::CONTINUE))
405 {
406 sCaption = rMember.Caption;
407 sName = rMember.Name;
408 }
409
410 if (i >= m_aLabels.size())
411 m_aLabels.resize(i + 1);
412
413 if (size_t(nDimPos) >= m_aLabels[i].size())
414 m_aLabels[i].resize(nDimPos + 1);
415 m_aLabels[i][nDimPos] = ValueAndFormat(sCaption);
416
417 if (bIsDataLayout)
418 {
419 // Remember data fields to determine the number format of data
420 aDataFieldNamesVectors.push_back(sName);
421 eDataFieldOrientation = sheet::DataPilotFieldOrientation_COLUMN;
422 // Remember the caption name
423 aDataFieldCaptionNames[rMember.Name] = rMember.Caption;
424 }
425 i++;
426 }
427 }
428 }
429 break;
430
431 case sheet::DataPilotFieldOrientation_ROW:
432 {
433 m_aRowFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember);
434
435 const uno::Sequence<sheet::MemberResult> aSequence = xLevelResult->getResults();
436
437 size_t i = 0;
438 size_t nEachIndex = 0;
439 std::unique_ptr<ValueAndFormat> pItem;
440
441 for (sheet::MemberResult const & rMember : aSequence)
442 {
443 bool bFound = aValidRowIndex.find(nEachIndex) != aValidRowIndex.end();
444
445 nEachIndex++;
446
447 bool bHasContinueFlag = rMember.Flags & sheet::MemberResultFlags::CONTINUE;
448
449 if (rMember.Flags & sheet::MemberResultFlags::HASMEMBER || bHasContinueFlag)
450 {
451 if (!bHasContinueFlag)
452 {
453 // Chart2 does not use number format for labels, so use the display string.
454 pItem.reset(new ValueAndFormat(rMember.Caption));
455 }
456
457 if (bFound)
458 {
459 if (i >= m_aCategoriesRowOrientation.size())
460 m_aCategoriesRowOrientation.resize(i + 1);
461
462 if (size_t(nDimPos) >= m_aCategoriesColumnOrientation.size())
463 m_aCategoriesColumnOrientation.resize(nDimPos + 1);
464 m_aCategoriesColumnOrientation[nDimPos].push_back(*pItem);
465
466 if (size_t(nDimPos) >= m_aCategoriesRowOrientation[i].size())
467 m_aCategoriesRowOrientation[i].resize(nDimPos + 1);
468 m_aCategoriesRowOrientation[i][nDimPos] = *pItem;
469
470 if (bIsDataLayout)
471 {
472 // Remember data fields to determine the number format of data
473 aDataFieldNamesVectors.push_back(rMember.Name);
474 eDataFieldOrientation = sheet::DataPilotFieldOrientation_ROW;
475
476 // Remember the caption name
477 aDataFieldCaptionNames[rMember.Name] = rMember.Caption;
478 }
479
480 // Set to empty so the sub categories are set to empty when they continue
481 pItem.reset(new ValueAndFormat);
482 i++;
483 }
484 }
485 }
486 }
487 break;
488
489 case sheet::DataPilotFieldOrientation_PAGE:
490 {
491 m_aPageFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember);
492
493 // Resolve filtering
494 OUString aFieldOutputDescription;
495 if (bHasHiddenMember)
496 {
497 std::vector<OUString> aMembers = lcl_getVisiblePageMembers(xLevel);
498
499 if (aMembers.size() == 1)
500 aFieldOutputDescription = aMembers[0];
501 else
502 aFieldOutputDescription = ScResId(SCSTR_MULTIPLE);
503 }
504 else
505 {
506 aFieldOutputDescription = ScResId(SCSTR_ALL);
507 }
508 m_aFieldOutputDescriptionMap[nDim] = aFieldOutputDescription;
509 }
510 break;
511
512 case sheet::DataPilotFieldOrientation_DATA:
513 {
514 aDataFieldNumberFormatMap[xLevelName->getName()] = nNumberFormat;
515 m_aDataFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember);
516 }
517 break;
518
519 default:
520 break;
521 }
522 }
523 }
524 }
525
526 // Transform the name of data fields
527 for (chart2::data::PivotTableFieldEntry& rDataFields : m_aDataFields)
528 {
529 rDataFields.Name = aDataFieldCaptionNames[rDataFields.Name];
530 }
531
532 // Apply number format to the data
533 if (eDataFieldOrientation == sheet::DataPilotFieldOrientation_ROW)
534 {
535 for (std::vector<ValueAndFormat> & rDataRow : m_aDataRowVector)
536 {
537 size_t i = 0;
538 for (ValueAndFormat & rItem : rDataRow)
539 {
540 OUString sName = aDataFieldNamesVectors[i];
541 sal_Int32 nNumberFormat = aDataFieldNumberFormatMap[sName];
542 rItem.m_nNumberFormat = nNumberFormat;
543 i++;
544 }
545 }
546 }
547 else if (eDataFieldOrientation == sheet::DataPilotFieldOrientation_COLUMN)
548 {
549 size_t i = 0;
550 for (std::vector<ValueAndFormat> & rDataRow : m_aDataRowVector)
551 {
552 OUString sName = aDataFieldNamesVectors[i];
553 sal_Int32 nNumberFormat = aDataFieldNumberFormatMap[sName];
554 for (ValueAndFormat & rItem : rDataRow)
555 {
556 rItem.m_nNumberFormat = nNumberFormat;
557 }
558 i++;
559 }
560 }
561
562 // Sort fields so it respects the order of how it is represented in the pivot table
563
564 auto funcDimensionPositionSortCompare = [] (chart2::data::PivotTableFieldEntry const & entry1,
565 chart2::data::PivotTableFieldEntry const & entry2)
566 {
567 return entry1.DimensionPositionIndex < entry2.DimensionPositionIndex;
568 };
569
570 std::sort(m_aColumnFields.begin(), m_aColumnFields.end(), funcDimensionPositionSortCompare);
571 std::sort(m_aRowFields.begin(), m_aRowFields.end(), funcDimensionPositionSortCompare);
572 std::sort(m_aPageFields.begin(), m_aPageFields.end(), funcDimensionPositionSortCompare);
573 std::sort(m_aDataFields.begin(), m_aDataFields.end(), funcDimensionPositionSortCompare);
574
575 // Mark that we updated the data
576 m_bNeedsUpdate = false;
577 }
578
579 uno::Reference<chart2::data::XDataSequence>
assignValuesToDataSequence(size_t nIndex)580 PivotTableDataProvider::assignValuesToDataSequence(size_t nIndex)
581 {
582 uno::Reference<chart2::data::XDataSequence> xDataSequence;
583 if (nIndex >= m_aDataRowVector.size())
584 return xDataSequence;
585
586 OUString sDataID = lcl_identifierForData(nIndex);
587
588 std::vector<ValueAndFormat> const & rRowOfData = m_aDataRowVector[nIndex];
589 std::unique_ptr<PivotTableDataSequence> pSequence;
590 pSequence.reset(new PivotTableDataSequence(m_pDocument, m_sPivotTableName, sDataID, rRowOfData));
591 pSequence->setRole("values-y");
592 xDataSequence.set(pSequence.release());
593 return xDataSequence;
594 }
595
596 uno::Reference<chart2::data::XDataSequence>
assignLabelsToDataSequence(size_t nIndex)597 PivotTableDataProvider::assignLabelsToDataSequence(size_t nIndex)
598 {
599 uno::Reference<chart2::data::XDataSequence> xDataSequence;
600
601 OUString sLabelID = lcl_identifierForLabel(nIndex);
602
603 OUStringBuffer aLabel;
604 bool bFirst = true;
605
606 if (m_aLabels.empty())
607 {
608 aLabel = ScResId(STR_PIVOT_TOTAL);
609 }
610 else if (nIndex < m_aLabels.size())
611 {
612 for (ValueAndFormat const & rItem : m_aLabels[nIndex])
613 {
614 if (bFirst)
615 {
616 aLabel.append(rItem.m_aString);
617 bFirst = false;
618 }
619 else
620 {
621 aLabel.append(" - ").append(rItem.m_aString);
622 }
623 }
624 }
625
626 std::vector<ValueAndFormat> aLabelVector { ValueAndFormat(aLabel.makeStringAndClear()) };
627
628 std::unique_ptr<PivotTableDataSequence> pSequence;
629 pSequence.reset(new PivotTableDataSequence(m_pDocument, m_sPivotTableName,
630 sLabelID, aLabelVector));
631 pSequence->setRole("values-y");
632 xDataSequence.set(pSequence.release());
633 return xDataSequence;
634 }
635
636 css::uno::Reference<css::chart2::data::XDataSequence>
assignFirstCategoriesToDataSequence()637 PivotTableDataProvider::assignFirstCategoriesToDataSequence()
638 {
639 uno::Reference<chart2::data::XDataSequence> xDataSequence;
640
641 if (m_aCategoriesColumnOrientation.empty())
642 return xDataSequence;
643
644 std::vector<ValueAndFormat> const & rCategories = m_aCategoriesColumnOrientation.back();
645
646 std::unique_ptr<PivotTableDataSequence> pSequence;
647 pSequence.reset(new PivotTableDataSequence(m_pDocument, m_sPivotTableName,
648 lcl_identifierForCategories(), rCategories));
649 pSequence->setRole("categories");
650 xDataSequence.set(uno::Reference<chart2::data::XDataSequence>(pSequence.release()));
651
652 return xDataSequence;
653 }
654
655 uno::Reference<chart2::data::XDataSource>
createValuesDataSource()656 PivotTableDataProvider::createValuesDataSource()
657 {
658 if (m_bNeedsUpdate)
659 collectPivotTableData();
660
661 uno::Reference<chart2::data::XDataSource> xDataSource;
662 std::vector<uno::Reference<chart2::data::XLabeledDataSequence>> aLabeledSequences;
663
664 // Fill first sequence of categories
665 {
666 uno::Reference<chart2::data::XLabeledDataSequence> xResult = newLabeledDataSequence();
667 xResult->setValues(assignFirstCategoriesToDataSequence());
668 aLabeledSequences.push_back(xResult);
669 }
670
671 // Fill values and labels
672 {
673 for (size_t i = 0; i < m_aDataRowVector.size(); ++i)
674 {
675 uno::Reference<chart2::data::XLabeledDataSequence> xResult = newLabeledDataSequence();
676 xResult->setValues(assignValuesToDataSequence(i));
677 xResult->setLabel(assignLabelsToDataSequence(i));
678 aLabeledSequences.push_back(xResult);
679 }
680 }
681
682 xDataSource.set(new PivotTableDataSource(aLabeledSequences));
683 return xDataSource;
684 }
685
686
detectArguments(const uno::Reference<chart2::data::XDataSource> & xDataSource)687 uno::Sequence<beans::PropertyValue> SAL_CALL PivotTableDataProvider::detectArguments(
688 const uno::Reference<chart2::data::XDataSource> & xDataSource)
689 {
690 if (!m_pDocument ||!xDataSource.is())
691 return uno::Sequence<beans::PropertyValue>();
692
693 return comphelper::InitPropertySequence({
694 { "CellRangeRepresentation", uno::Any(OUString("PivotChart")) },
695 { "DataRowSource", uno::Any(chart::ChartDataRowSource_COLUMNS) },
696 { "FirstCellAsLabel", uno::Any(false) },
697 { "HasCategories", uno::Any(true) }
698 });
699 }
700
createDataSequenceByRangeRepresentationPossible(const OUString &)701 sal_Bool SAL_CALL PivotTableDataProvider::createDataSequenceByRangeRepresentationPossible(const OUString& /*aRangeRepresentation*/)
702 {
703 return false;
704 }
705
706 uno::Reference<chart2::data::XDataSequence> SAL_CALL
createDataSequenceByRangeRepresentation(const OUString &)707 PivotTableDataProvider::createDataSequenceByRangeRepresentation(const OUString& /*rRangeRepresentation*/)
708 {
709 uno::Reference<chart2::data::XDataSequence> xDataSequence;
710 return xDataSequence;
711 }
712
713 uno::Reference<chart2::data::XDataSequence> SAL_CALL
createDataSequenceByValueArray(const OUString &,const OUString &)714 PivotTableDataProvider::createDataSequenceByValueArray(const OUString& /*aRole*/,
715 const OUString& /*aRangeRepresentation*/)
716 {
717 return uno::Reference<chart2::data::XDataSequence>();
718 }
719
getRangeSelection()720 uno::Reference<sheet::XRangeSelection> SAL_CALL PivotTableDataProvider::getRangeSelection()
721 {
722 uno::Reference<sheet::XRangeSelection> xResult;
723
724 uno::Reference<frame::XModel> xModel(lcl_GetXModel(m_pDocument));
725 if (xModel.is())
726 xResult.set(xModel->getCurrentController(), uno::UNO_QUERY);
727
728 return xResult;
729 }
730
731 // XPivotTableDataProvider ========================================================
732
getColumnFields()733 uno::Sequence<chart2::data::PivotTableFieldEntry> PivotTableDataProvider::getColumnFields()
734 {
735 return comphelper::containerToSequence(m_aColumnFields);
736 }
737
getRowFields()738 uno::Sequence<chart2::data::PivotTableFieldEntry> PivotTableDataProvider::getRowFields()
739 {
740 return comphelper::containerToSequence(m_aRowFields);
741 }
742
getPageFields()743 uno::Sequence<chart2::data::PivotTableFieldEntry> PivotTableDataProvider::getPageFields()
744 {
745 return comphelper::containerToSequence(m_aPageFields);
746 }
747
getDataFields()748 uno::Sequence<chart2::data::PivotTableFieldEntry> PivotTableDataProvider::getDataFields()
749 {
750 return comphelper::containerToSequence(m_aDataFields);
751 }
752
getPivotTableName()753 OUString PivotTableDataProvider::getPivotTableName()
754 {
755 return m_sPivotTableName;
756 }
757
setPivotTableName(const OUString & sPivotTableName)758 void PivotTableDataProvider::setPivotTableName(const OUString& sPivotTableName)
759 {
760 ScDPCollection* pDPCollection = m_pDocument->GetDPCollection();
761 ScDPObject* pDPObject = pDPCollection->GetByName(sPivotTableName);
762 if (pDPObject)
763 m_sPivotTableName = sPivotTableName;
764 }
765
hasPivotTable()766 sal_Bool PivotTableDataProvider::hasPivotTable()
767 {
768 if (m_sPivotTableName.isEmpty())
769 return false;
770
771 ScDPCollection* pDPCollection = m_pDocument->GetDPCollection();
772 ScDPObject* pDPObject = pDPCollection->GetByName(m_sPivotTableName);
773
774 if (pDPObject)
775 return true;
776
777 return false;
778 }
779
780 uno::Reference<chart2::data::XDataSequence>
createDataSequenceOfValuesByIndex(sal_Int32 nIndex)781 PivotTableDataProvider::createDataSequenceOfValuesByIndex(sal_Int32 nIndex)
782 {
783 SolarMutexGuard aGuard;
784
785 if (m_bNeedsUpdate)
786 collectPivotTableData();
787
788 return assignValuesToDataSequence(size_t(nIndex));
789 }
790
791 uno::Reference<css::chart2::data::XDataSequence>
createDataSequenceOfLabelsByIndex(sal_Int32 nIndex)792 PivotTableDataProvider::createDataSequenceOfLabelsByIndex(sal_Int32 nIndex)
793 {
794 SolarMutexGuard aGuard;
795
796 if (m_bNeedsUpdate)
797 collectPivotTableData();
798
799 return assignLabelsToDataSequence(size_t(nIndex));
800 }
801
802 uno::Reference<css::chart2::data::XDataSequence>
createDataSequenceOfCategories()803 PivotTableDataProvider::createDataSequenceOfCategories()
804 {
805 SolarMutexGuard aGuard;
806
807 if (m_bNeedsUpdate)
808 collectPivotTableData();
809
810 return assignFirstCategoriesToDataSequence();
811 }
812
getFieldOutputDescription(sal_Int32 nDimensionIndex)813 OUString PivotTableDataProvider::getFieldOutputDescription(sal_Int32 nDimensionIndex)
814 {
815 if (nDimensionIndex < 0)
816 return OUString();
817 return m_aFieldOutputDescriptionMap[size_t(nDimensionIndex)];
818 }
819
820 // XModifyBroadcaster ========================================================
821
addModifyListener(const uno::Reference<util::XModifyListener> & aListener)822 void SAL_CALL PivotTableDataProvider::addModifyListener(const uno::Reference< util::XModifyListener>& aListener)
823 {
824 SolarMutexGuard aGuard;
825
826 m_aValueListeners.emplace_back(aListener);
827 }
828
removeModifyListener(const uno::Reference<util::XModifyListener> & aListener)829 void SAL_CALL PivotTableDataProvider::removeModifyListener(const uno::Reference<util::XModifyListener>& aListener )
830 {
831 SolarMutexGuard aGuard;
832
833 sal_uInt16 nCount = m_aValueListeners.size();
834 for (sal_uInt16 n = nCount; n--;)
835 {
836 uno::Reference<util::XModifyListener>& rObject = m_aValueListeners[n];
837 if (rObject == aListener)
838 {
839 m_aValueListeners.erase(m_aValueListeners.begin() + n);
840 }
841 }
842 }
843
844 // DataProvider XPropertySet ========================================================
845
846 uno::Reference< beans::XPropertySetInfo> SAL_CALL
getPropertySetInfo()847 PivotTableDataProvider::getPropertySetInfo()
848 {
849 SolarMutexGuard aGuard;
850 static uno::Reference<beans::XPropertySetInfo> aRef =
851 new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() );
852 return aRef;
853 }
854
setPropertyValue(const OUString & rPropertyName,const uno::Any & rValue)855 void SAL_CALL PivotTableDataProvider::setPropertyValue(const OUString& rPropertyName, const uno::Any& rValue)
856 {
857 if (rPropertyName != SC_UNONAME_INCLUDEHIDDENCELLS)
858 throw beans::UnknownPropertyException(rPropertyName);
859
860 if (!(rValue >>= m_bIncludeHiddenCells))
861 throw lang::IllegalArgumentException();
862 }
863
getPropertyValue(const OUString & rPropertyName)864 uno::Any SAL_CALL PivotTableDataProvider::getPropertyValue(const OUString& rPropertyName)
865 {
866 uno::Any aRet;
867 if (rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS)
868 aRet <<= m_bIncludeHiddenCells;
869 else if (rPropertyName == SC_UNONAME_USE_INTERNAL_DATA_PROVIDER)
870 {
871 // This is a read-only property.
872 aRet <<= m_pDocument->PastingDrawFromOtherDoc();
873 }
874 else
875 throw beans::UnknownPropertyException(rPropertyName);
876 return aRet;
877 }
878
addPropertyChangeListener(const OUString &,const uno::Reference<beans::XPropertyChangeListener> &)879 void SAL_CALL PivotTableDataProvider::addPropertyChangeListener(
880 const OUString& /*rPropertyName*/,
881 const uno::Reference<beans::XPropertyChangeListener>& /*xListener*/)
882 {
883 OSL_FAIL("Not yet implemented");
884 }
885
removePropertyChangeListener(const OUString &,const uno::Reference<beans::XPropertyChangeListener> &)886 void SAL_CALL PivotTableDataProvider::removePropertyChangeListener(
887 const OUString& /*rPropertyName*/,
888 const uno::Reference<beans::XPropertyChangeListener>& /*rListener*/)
889 {
890 OSL_FAIL("Not yet implemented");
891 }
892
addVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)893 void SAL_CALL PivotTableDataProvider::addVetoableChangeListener(
894 const OUString& /*rPropertyName*/,
895 const uno::Reference<beans::XVetoableChangeListener>& /*rListener*/)
896 {
897 OSL_FAIL("Not yet implemented");
898 }
899
removeVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)900 void SAL_CALL PivotTableDataProvider::removeVetoableChangeListener(
901 const OUString& /*rPropertyName*/,
902 const uno::Reference<beans::XVetoableChangeListener>& /*rListener*/ )
903 {
904 OSL_FAIL("Not yet implemented");
905 }
906
907 } // end sc namespace
908
909 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
910