Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / sc / source / core / data / PivotTableFormatOutput.cxx
blobc9073d6be07b5a113a0be2d4dce3f2f6072d7335
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <pivot/PivotTableFormatOutput.hxx>
11 #include <pivot/DPOutLevelData.hxx>
13 #include <dpoutput.hxx>
14 #include <dpobject.hxx>
15 #include <dptabdat.hxx>
16 #include <dpcache.hxx>
17 #include <document.hxx>
19 #include <com/sun/star/sheet/MemberResultFlags.hpp>
21 namespace sc
23 namespace
25 class NameResolver
27 private:
28 ScDPTableData& mrTableData;
29 ScDPCache const& mrCache;
31 std::unordered_map<sal_Int32, std::vector<OUString>> maNameCache;
33 void fillNamesForDimension(std::vector<OUString>& rNames, sal_Int32 nDimension)
35 for (const auto& rItemData : mrCache.GetDimMemberValues(nDimension))
37 OUString sFormattedName;
38 if (rItemData.HasStringData() || rItemData.IsEmpty())
39 sFormattedName = rItemData.GetString();
40 else
41 sFormattedName = ScDPObject::GetFormattedString(&mrTableData, nDimension,
42 rItemData.GetValue());
43 rNames.push_back(sFormattedName);
47 public:
48 NameResolver(ScDPTableData& rTableData, ScDPCache const& rCache)
49 : mrTableData(rTableData)
50 , mrCache(rCache)
54 OUString getNameForIndex(sal_uInt32 nIndex, sal_Int32 nDimension)
56 auto iterator = maNameCache.find(nDimension);
57 if (iterator == maNameCache.end())
59 std::vector<OUString> aNames;
60 fillNamesForDimension(aNames, nDimension);
61 iterator = maNameCache.emplace(nDimension, aNames).first;
64 const std::vector<OUString>& rNames = iterator->second;
65 if (nIndex >= rNames.size())
66 return OUString();
67 return rNames[nIndex];
71 void initLines(std::vector<LineData>& rLines, std::vector<ScDPOutLevelData> const& rFields)
73 for (size_t i = 0; i < rFields.size(); i++)
75 size_t nFieldLength(rFields[i].maResult.getLength());
76 if (rLines.size() < nFieldLength)
77 rLines.resize(nFieldLength);
79 for (LineData& rLineData : rLines)
81 rLineData.maFields.resize(rFields.size());
86 Selection const* findSelection(PivotTableFormat const& rFormat, tools::Long nDimension)
88 for (Selection const& rSelection : rFormat.getSelections())
90 if (rSelection.nField == nDimension)
91 return &rSelection;
93 return nullptr;
96 void fillOutputFieldFromSelection(FormatOutputField& rOutputField, Selection const& rSelection,
97 size_t nSelectionIndex, NameResolver& rNameResolver)
99 if (rSelection.nIndices.empty())
101 rOutputField.bMatchesAll = true;
103 else
105 if (rSelection.nIndices.size() > 1 && rSelection.nIndices.size() > nSelectionIndex)
106 rOutputField.nIndex = rSelection.nIndices[nSelectionIndex];
107 else
108 rOutputField.nIndex = rSelection.nIndices[0];
110 if (rOutputField.nDimension == -2)
111 rOutputField.aName = "DATA";
112 else
113 rOutputField.aName
114 = rNameResolver.getNameForIndex(rOutputField.nIndex, rOutputField.nDimension);
116 rOutputField.bSet = true;
119 void initFormatOutputField(size_t nSelectionIndex, std::vector<FormatOutputField>& rOutputFields,
120 std::vector<ScDPOutLevelData> const& rFields,
121 PivotTableFormat const& rFormat, NameResolver& rNameResolver)
123 rOutputFields.resize(rFields.size());
124 for (size_t i = 0; i < rOutputFields.size(); i++)
126 FormatOutputField& rOutputField = rOutputFields[i];
127 if (!rFields[i].mbDataLayout)
128 rOutputField.nDimension = rFields[i].mnDim;
130 Selection const* pSelection = findSelection(rFormat, rOutputField.nDimension);
131 if (pSelection == nullptr)
132 continue;
134 fillOutputFieldFromSelection(rOutputField, *pSelection, nSelectionIndex, rNameResolver);
138 } // end anonymous namespace
140 void FormatOutput::prepare(SCTAB nTab, std::vector<ScDPOutLevelData> const& rColumnFields,
141 std::vector<ScDPOutLevelData> const& rRowFields,
142 bool bColumnFieldIsDataOnly)
144 if (!mpFormats)
145 return;
147 // initialize row and column lines, so the number of fields matches the pivot table output
148 initLines(maRowLines, rRowFields);
150 if (rColumnFields.size() == 0 && bColumnFieldIsDataOnly)
152 maColumnLines.resize(1);
153 maColumnLines[0].maFields.resize(1);
155 else
157 initLines(maColumnLines, rColumnFields);
160 // check the table data exists
161 auto* pTableData = mrObject.GetTableData();
162 if (!pTableData)
163 return;
165 ScDPFilteredCache const& rFilteredCache = pTableData->GetCacheTable();
166 ScDPCache const& rCache = rFilteredCache.getCache();
168 NameResolver aNameResolver(*pTableData, rCache);
170 // Initialize format output entries (FormatOutputEntry) and set the data already available from output fields
171 // (rColumnFields and rRowFields) and the pivot table format list (PivotTableFormat).
173 for (PivotTableFormat const& rFormat : mpFormats->getVector())
175 size_t nMaxNumberOfIndices = 1;
176 for (auto const& rSelection : rFormat.aSelections)
178 if (rSelection.nIndices.size() > 1)
179 nMaxNumberOfIndices = rSelection.nIndices.size();
182 for (size_t nSelectionIndex = 0; nSelectionIndex < nMaxNumberOfIndices; nSelectionIndex++)
184 sc::FormatOutputEntry aEntry;
185 aEntry.pPattern = rFormat.pPattern;
186 aEntry.onTab = nTab;
187 aEntry.eType = rFormat.eType;
189 initFormatOutputField(nSelectionIndex, aEntry.aRowOutputFields, rRowFields, rFormat,
190 aNameResolver);
192 // If column fields list is empty, but there is a data field in columns that is not part of column fields
193 if (rColumnFields.size() == 0 && bColumnFieldIsDataOnly)
195 // Initialize column output fields to have 1 data output field
196 aEntry.aColumnOutputFields.resize(1);
197 FormatOutputField& rOutputField = aEntry.aColumnOutputFields[0];
198 rOutputField.nDimension = -2;
199 Selection const* pSelection = findSelection(rFormat, -2);
200 if (pSelection)
201 fillOutputFieldFromSelection(rOutputField, *pSelection, nSelectionIndex,
202 aNameResolver);
204 else
206 initFormatOutputField(nSelectionIndex, aEntry.aColumnOutputFields, rColumnFields,
207 rFormat, aNameResolver);
210 maFormatOutputEntries.push_back(aEntry);
215 void FormatOutput::insertEmptyDataColumn(SCCOL nColPos, SCROW nRowPos)
217 if (!mpFormats)
218 return;
220 LineData& rLine = maColumnLines[0];
221 rLine.oLine = nColPos;
222 rLine.oPosition = nRowPos;
224 FieldData& rFieldData = rLine.maFields[0];
225 rFieldData.nIndex = 0;
226 rFieldData.bIsSet = true;
229 namespace
231 void fillLineAndFieldData(std::vector<LineData>& rLineDataVector, size_t nFieldIndex,
232 ScDPOutLevelData const& rField, tools::Long nMemberIndex,
233 sheet::MemberResult const& rMember, SCCOLROW nLine, SCCOLROW nPosition)
235 LineData& rLine = rLineDataVector[nMemberIndex];
236 rLine.oLine = nLine;
237 rLine.oPosition = nPosition;
239 FieldData& rFieldData = rLine.maFields[nFieldIndex];
240 if (!rField.mbDataLayout)
241 rFieldData.mnDimension = rField.mnDim;
242 rFieldData.aName = rMember.Name;
243 rFieldData.nIndex = nMemberIndex;
244 rFieldData.bIsSet = true;
245 rFieldData.bIsMember = rMember.Flags & sheet::MemberResultFlags::HASMEMBER;
246 rFieldData.bSubtotal = rMember.Flags & sheet::MemberResultFlags::SUBTOTAL;
247 rFieldData.bContinue = rMember.Flags & sheet::MemberResultFlags::CONTINUE;
249 // Search previous entries for the name / value
250 if (rFieldData.bContinue)
252 tools::Long nCurrent = nMemberIndex - 1;
253 while (nCurrent >= 0 && rLineDataVector[nCurrent].maFields[nFieldIndex].bContinue)
254 nCurrent--;
256 if (nCurrent >= 0)
258 FieldData& rCurrentFieldData = rLineDataVector[nCurrent].maFields[nFieldIndex];
259 rFieldData.aName = rCurrentFieldData.aName;
260 rFieldData.nIndex = rCurrentFieldData.nIndex;
264 } // end anonymous namespace
266 void FormatOutput::insertFieldMember(size_t nFieldIndex, ScDPOutLevelData const& rField,
267 tools::Long nMemberIndex, sheet::MemberResult const& rMember,
268 SCCOL nColPos, SCROW nRowPos,
269 sc::FormatResultDirection eResultDirection)
271 if (!mpFormats)
272 return;
274 if (eResultDirection == sc::FormatResultDirection::ROW)
275 fillLineAndFieldData(maRowLines, nFieldIndex, rField, nMemberIndex, rMember, nRowPos,
276 nColPos);
277 else if (eResultDirection == sc::FormatResultDirection::COLUMN)
278 fillLineAndFieldData(maColumnLines, nFieldIndex, rField, nMemberIndex, rMember, nColPos,
279 nRowPos);
281 namespace
283 void checkForMatchingLines(std::vector<LineData> const& rLines,
284 std::vector<FormatOutputField> const& rFormatOutputField,
285 FormatType eType,
286 std::vector<std::reference_wrapper<const LineData>>& rMatches,
287 std::vector<std::reference_wrapper<const LineData>>& rMaybeMatches)
289 for (LineData const& rLineData : rLines)
291 size_t nMatch = 0;
292 size_t nMaybeMatch = 0;
293 size_t nNoOfFields = rLineData.maFields.size();
295 for (size_t nIndex = 0; nIndex < nNoOfFields; nIndex++)
297 FieldData const& rFieldData = rLineData.maFields[nIndex];
298 FormatOutputField const& rFormatEntry = rFormatOutputField[nIndex];
299 bool bFieldMatch = false;
300 bool bFieldMaybeMatch = false;
302 tools::Long nDimension = rFieldData.mnDimension;
303 if (nDimension == rFormatEntry.nDimension)
305 if (rFormatEntry.bSet)
307 if (rFormatEntry.bMatchesAll && !rFieldData.bSubtotal)
308 bFieldMatch = true;
309 else if (nDimension == -2 && rFieldData.nIndex == rFormatEntry.nIndex)
310 bFieldMatch = true;
311 else if (nDimension != -2 && rFieldData.aName == rFormatEntry.aName)
312 bFieldMatch = true;
314 else if (!rFormatEntry.bSet && eType == FormatType::Data && !rFieldData.bIsMember
315 && !rFieldData.bContinue)
317 bFieldMatch = true;
319 else
321 bFieldMaybeMatch = true;
325 if (!bFieldMatch && !bFieldMaybeMatch)
326 break;
328 if (bFieldMatch)
329 nMatch++;
331 if (bFieldMaybeMatch)
332 nMaybeMatch++;
335 if (nMatch == nNoOfFields)
337 rMatches.push_back(std::cref(rLineData));
339 else if (nMatch + nMaybeMatch == nNoOfFields)
341 rMaybeMatches.push_back(std::cref(rLineData));
346 /** Check the lines in matches and maybe matches and output */
347 void evaluateMatches(ScDocument& rDocument,
348 std::vector<std::reference_wrapper<const LineData>> const& rMatches,
349 std::vector<std::reference_wrapper<const LineData>> const& rMaybeMatches,
350 std::vector<SCCOLROW>& aRows, std::vector<SCCOLROW>& aColumns,
351 FormatOutputEntry const& rOutputEntry, FormatResultDirection eResultDirection)
353 // We expect that tab and pattern to be set or this method shouldn't be called at all
354 assert(rOutputEntry.onTab);
355 assert(rOutputEntry.pPattern);
357 if (rMatches.empty() && rMaybeMatches.empty())
358 return;
360 bool bMaybeExists = rMatches.empty();
362 auto const& rLineDataVector = bMaybeExists ? rMaybeMatches : rMatches;
364 for (LineData const& rLineData : rLineDataVector)
366 // Can't continue if we don't have complete row/column data
367 if (!rLineData.oLine || !rLineData.oPosition)
368 continue;
370 if (rOutputEntry.eType == FormatType::Label && !bMaybeExists)
372 // Primary axis is set to column (line) then row (position)
373 SCCOLROW nColumn = *rLineData.oLine;
374 SCCOLROW nRow = *rLineData.oPosition;
376 // In row orientation, the primary axis is row, then column, so we need to swap
377 if (eResultDirection == FormatResultDirection::ROW)
378 std::swap(nRow, nColumn);
380 // Set the pattern to the sheet
381 rDocument.ApplyPattern(nColumn, nRow, *rOutputEntry.onTab, *rOutputEntry.pPattern);
383 else if (rOutputEntry.eType == FormatType::Data)
385 if (eResultDirection == FormatResultDirection::ROW)
386 aRows.push_back(*rLineData.oLine);
387 else if (eResultDirection == FormatResultDirection::COLUMN)
388 aColumns.push_back(*rLineData.oLine);
393 } // end anonymous namespace
395 void FormatOutput::apply(ScDocument& rDocument)
397 if (!mpFormats)
398 return;
400 for (auto const& rOutputEntry : maFormatOutputEntries)
402 if (!rOutputEntry.onTab || !rOutputEntry.pPattern)
403 continue;
405 std::vector<SCCOLROW> aRows;
406 std::vector<SCCOLROW> aColumns;
408 std::vector<std::reference_wrapper<const LineData>> rMatches;
409 std::vector<std::reference_wrapper<const LineData>> rMaybeMatches;
411 checkForMatchingLines(maRowLines, rOutputEntry.aRowOutputFields, rOutputEntry.eType,
412 rMatches, rMaybeMatches);
414 evaluateMatches(rDocument, rMatches, rMaybeMatches, aRows, aColumns, rOutputEntry,
415 FormatResultDirection::ROW);
419 std::vector<std::reference_wrapper<const LineData>> rMatches;
420 std::vector<std::reference_wrapper<const LineData>> rMaybeMatches;
422 checkForMatchingLines(maColumnLines, rOutputEntry.aColumnOutputFields,
423 rOutputEntry.eType, rMatches, rMaybeMatches);
425 evaluateMatches(rDocument, rMatches, rMaybeMatches, aRows, aColumns, rOutputEntry,
426 FormatResultDirection::COLUMN);
429 if (!aColumns.empty() && !aRows.empty() && rOutputEntry.eType == FormatType::Data)
431 for (SCCOLROW nRow : aRows)
432 for (SCCOLROW nColumn : aColumns)
433 rDocument.ApplyPattern(nColumn, nRow, *rOutputEntry.onTab,
434 *rOutputEntry.pPattern);
439 } // end sc
441 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */