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