/src/libreoffice/sc/source/ui/StatisticsDialogs/TableFillingAndNavigationTools.cxx
Line | Count | Source |
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 | | |
11 | | #include <memory> |
12 | | |
13 | | #include <editeng/editobj.hxx> |
14 | | #include <editeng/wghtitem.hxx> |
15 | | #include <editeng/eeitem.hxx> |
16 | | #include <editeng/boxitem.hxx> |
17 | | #include <editeng/borderline.hxx> |
18 | | #include <editeng/justifyitem.hxx> |
19 | | |
20 | | #include <editutil.hxx> |
21 | | |
22 | | #include <TableFillingAndNavigationTools.hxx> |
23 | | #include <formulacell.hxx> |
24 | | #include <docfunc.hxx> |
25 | | #include <docsh.hxx> |
26 | | |
27 | | using namespace ::editeng; |
28 | | |
29 | | FormulaTemplate::FormulaTemplate(ScDocument* pDoc) |
30 | 0 | : mpDoc(pDoc) |
31 | 0 | , mbUse3D(true) |
32 | 0 | {} |
33 | | |
34 | | void FormulaTemplate::setTemplate(const OUString& aTemplate) |
35 | 0 | { |
36 | 0 | mTemplate = aTemplate; |
37 | 0 | } |
38 | | |
39 | | void FormulaTemplate::setTemplate(const char* aTemplate) |
40 | 0 | { |
41 | 0 | mTemplate = OUString::createFromAscii(aTemplate); |
42 | 0 | } |
43 | | |
44 | | const OUString& FormulaTemplate::getTemplate() |
45 | 0 | { |
46 | 0 | for (const auto& [rVariable, rRange] : mRangeReplacementMap) |
47 | 0 | { |
48 | 0 | applyRange(rVariable, rRange, mbUse3D); |
49 | 0 | } |
50 | 0 | for (const auto& [rVariable, rAddress] : mAddressReplacementMap) |
51 | 0 | { |
52 | 0 | applyAddress(rVariable, rAddress, mbUse3D); |
53 | 0 | } |
54 | 0 | return mTemplate; |
55 | 0 | } |
56 | | |
57 | | void FormulaTemplate::autoReplaceRange(const OUString& aVariable, const ScRange& rRange) |
58 | 0 | { |
59 | 0 | mRangeReplacementMap[aVariable] = rRange; |
60 | 0 | } |
61 | | |
62 | | void FormulaTemplate::autoReplaceAddress(const OUString& aVariable, ScAddress const & aAddress) |
63 | 0 | { |
64 | |
|
65 | 0 | mAddressReplacementMap[aVariable] = aAddress; |
66 | 0 | } |
67 | | |
68 | | void FormulaTemplate::applyRange(std::u16string_view aVariable, const ScRange& aRange, bool b3D) |
69 | 0 | { |
70 | 0 | ScRefFlags nFlag = b3D ? ScRefFlags::RANGE_ABS_3D : ScRefFlags::RANGE_ABS; |
71 | 0 | OUString aString = aRange.Format(*mpDoc, nFlag, mpDoc->GetAddressConvention()); |
72 | 0 | mTemplate = mTemplate.replaceAll(aVariable, aString); |
73 | 0 | } |
74 | | |
75 | | void FormulaTemplate::applyRangeList(std::u16string_view aVariable, const ScRangeList& aRangeList, sal_Unicode cDelimiter) |
76 | 0 | { |
77 | 0 | OUString aString; |
78 | 0 | aRangeList.Format(aString, ScRefFlags::RANGE_ABS_3D, *mpDoc, mpDoc->GetAddressConvention(), cDelimiter); |
79 | 0 | mTemplate = mTemplate.replaceAll(aVariable, aString); |
80 | 0 | } |
81 | | |
82 | | void FormulaTemplate::applyAddress(std::u16string_view aVariable, const ScAddress& aAddress, bool b3D) |
83 | 0 | { |
84 | 0 | ScRefFlags nFlag = b3D ? ScRefFlags::ADDR_ABS_3D : ScRefFlags::ADDR_ABS; |
85 | 0 | OUString aString = aAddress.Format(nFlag, mpDoc, mpDoc->GetAddressConvention()); |
86 | 0 | mTemplate = mTemplate.replaceAll(aVariable, aString); |
87 | 0 | } |
88 | | |
89 | | void FormulaTemplate::applyString(std::u16string_view aVariable, std::u16string_view aValue) |
90 | 0 | { |
91 | 0 | mTemplate = mTemplate.replaceAll(aVariable, aValue); |
92 | 0 | } |
93 | | |
94 | | void FormulaTemplate::applyNumber(std::u16string_view aVariable, sal_Int32 aValue) |
95 | 0 | { |
96 | 0 | mTemplate = mTemplate.replaceAll(aVariable, OUString::number(aValue)); |
97 | 0 | } |
98 | | |
99 | | AddressWalker::AddressWalker(const ScAddress& aInitialAddress) : |
100 | 0 | mCurrentAddress(aInitialAddress), |
101 | 0 | mMinimumAddress(aInitialAddress), |
102 | 0 | mMaximumAddress(aInitialAddress) |
103 | 0 | { |
104 | 0 | mAddressStack.push_back(mCurrentAddress); |
105 | 0 | } |
106 | | |
107 | | void AddressWalker::resetColumn() |
108 | 0 | { |
109 | 0 | mCurrentAddress.SetCol(mAddressStack.back().Col()); |
110 | 0 | } |
111 | | |
112 | | void AddressWalker::resetRow() |
113 | 0 | { |
114 | 0 | mCurrentAddress.SetRow(mAddressStack.back().Row()); |
115 | 0 | } |
116 | | |
117 | | void AddressWalker::reset() |
118 | 0 | { |
119 | 0 | mCurrentAddress = mAddressStack.back(); |
120 | 0 | } |
121 | | |
122 | | void AddressWalker::newLine() |
123 | 0 | { |
124 | 0 | resetColumn(); |
125 | 0 | nextRow(); |
126 | 0 | } |
127 | | |
128 | | ScAddress AddressWalker::current(SCCOL aRelCol, SCROW aRelRow, SCTAB aRelTab) |
129 | 0 | { |
130 | 0 | return ScAddress( |
131 | 0 | mCurrentAddress.Col() + aRelCol, |
132 | 0 | mCurrentAddress.Row() + aRelRow, |
133 | 0 | mCurrentAddress.Tab() + aRelTab); |
134 | 0 | } |
135 | | |
136 | | void AddressWalker::nextColumn() |
137 | 0 | { |
138 | 0 | mCurrentAddress.IncCol(); |
139 | |
|
140 | 0 | if(mMaximumAddress.Col() < mCurrentAddress.Col()) |
141 | 0 | mMaximumAddress.SetCol(mCurrentAddress.Col()); |
142 | 0 | } |
143 | | |
144 | | void AddressWalker::nextRow() |
145 | 0 | { |
146 | 0 | mCurrentAddress.IncRow(); |
147 | 0 | if(mMaximumAddress.Row() < mCurrentAddress.Row()) |
148 | 0 | mMaximumAddress.SetRow(mCurrentAddress.Row()); |
149 | 0 | } |
150 | | |
151 | | void AddressWalker::push(SCCOL aRelativeCol, SCROW aRelativeRow, SCTAB aRelativeTab) |
152 | 0 | { |
153 | 0 | mCurrentAddress = current(aRelativeCol, aRelativeRow, aRelativeTab); |
154 | 0 | mAddressStack.push_back(mCurrentAddress); |
155 | 0 | } |
156 | | |
157 | | AddressWalkerWriter::AddressWalkerWriter(const ScAddress& aInitialAddress, ScDocShell* pDocShell, ScDocument& rDocument, |
158 | | formula::FormulaGrammar::Grammar eGrammar ) : |
159 | 0 | AddressWalker(aInitialAddress), |
160 | 0 | mpDocShell(pDocShell), |
161 | 0 | mrDocument(rDocument), |
162 | 0 | meGrammar(eGrammar) |
163 | 0 | {} |
164 | | |
165 | | void AddressWalkerWriter::writeFormula(const OUString& aFormula) |
166 | 0 | { |
167 | 0 | mpDocShell->GetDocFunc().SetFormulaCell(mCurrentAddress, |
168 | 0 | new ScFormulaCell(mrDocument, mCurrentAddress, aFormula, meGrammar), true); |
169 | 0 | } |
170 | | |
171 | | void AddressWalkerWriter::writeFormulas(const std::vector<OUString>& rFormulas) |
172 | 0 | { |
173 | 0 | size_t nLength = rFormulas.size(); |
174 | 0 | if (!nLength) |
175 | 0 | return; |
176 | | |
177 | 0 | const size_t nMaxLen = mpDocShell->GetDocument().MaxRow() - mCurrentAddress.Row() + 1; |
178 | | // If not done already, trim the length to fit. |
179 | 0 | if (nLength > nMaxLen) |
180 | 0 | nLength = nMaxLen; |
181 | |
|
182 | 0 | std::vector<ScFormulaCell*> aFormulaCells(nLength); |
183 | 0 | ScAddress aAddr(mCurrentAddress); |
184 | 0 | for (size_t nIdx = 0; nIdx < nLength; ++nIdx) |
185 | 0 | { |
186 | 0 | aFormulaCells[nIdx] = new ScFormulaCell(mrDocument, aAddr, rFormulas[nIdx], meGrammar); |
187 | 0 | aAddr.IncRow(1); |
188 | 0 | } |
189 | |
|
190 | 0 | mpDocShell->GetDocFunc().SetFormulaCells(mCurrentAddress, aFormulaCells, true); |
191 | 0 | } |
192 | | |
193 | | void AddressWalkerWriter::writeMatrixFormula(const OUString& aFormula, SCCOL nCols, SCROW nRows) |
194 | 0 | { |
195 | 0 | ScRange aRange; |
196 | 0 | aRange.aStart = mCurrentAddress; |
197 | 0 | aRange.aEnd = mCurrentAddress; |
198 | 0 | if (nCols > 1) |
199 | 0 | aRange.aEnd.IncCol(nCols - 1); |
200 | 0 | if (nRows > 1) |
201 | 0 | aRange.aEnd.IncRow(nRows - 1); |
202 | 0 | mpDocShell->GetDocFunc().EnterMatrix(aRange, nullptr, nullptr, aFormula, false, false, OUString(), meGrammar ); |
203 | 0 | } |
204 | | |
205 | | void AddressWalkerWriter::writeString(const OUString& aString) |
206 | 0 | { |
207 | 0 | mpDocShell->GetDocFunc().SetStringCell(mCurrentAddress, aString, true); |
208 | 0 | } |
209 | | |
210 | | void AddressWalkerWriter::writeString(const char* aCharArray) |
211 | 0 | { |
212 | 0 | writeString(OUString::createFromAscii(aCharArray)); |
213 | 0 | } |
214 | | |
215 | | void AddressWalkerWriter::writeBoldString(const OUString& aString) |
216 | 0 | { |
217 | 0 | ScFieldEditEngine& rEngine = mrDocument.GetEditEngine(); |
218 | 0 | rEngine.SetTextCurrentDefaults(aString); |
219 | 0 | SfxItemSet aItemSet = rEngine.GetEmptyItemSet(); |
220 | 0 | SvxWeightItem aWeight(WEIGHT_BOLD, EE_CHAR_WEIGHT); |
221 | 0 | SvxHorJustifyItem aJustify(SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY); |
222 | 0 | aItemSet.Put(aWeight); |
223 | 0 | aItemSet.Put(aJustify); |
224 | 0 | rEngine.QuickSetAttribs(aItemSet, ESelection(0, 0, 0, aString.getLength()) ); |
225 | 0 | std::unique_ptr<EditTextObject> pEditText(rEngine.CreateTextObject()); |
226 | 0 | mpDocShell->GetDocFunc().SetEditCell(mCurrentAddress, *pEditText, true); |
227 | 0 | } |
228 | | |
229 | | void AddressWalkerWriter::writeValue(double aValue) |
230 | 0 | { |
231 | 0 | mpDocShell->GetDocFunc().SetValueCell(mCurrentAddress, aValue, true); |
232 | 0 | } |
233 | | |
234 | | // Applies a column header format to the current cell and subsequent (nCols - 1) columns |
235 | | // Header format = bold font, horizontally centered, text wrap and top/bottom borders |
236 | | void AddressWalkerWriter::formatAsColumnHeader(SCCOL nCols) |
237 | 0 | { |
238 | 0 | ScPatternAttr aPattern(mrDocument.getCellAttributeHelper()); |
239 | 0 | SvxHorJustifyItem aHJustify(SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY); |
240 | 0 | SvxVerJustifyItem aVJustify(SvxCellVerJustify::Center, ATTR_VER_JUSTIFY); |
241 | 0 | SvxWeightItem aWeight(WEIGHT_BOLD, ATTR_FONT_WEIGHT); |
242 | 0 | ScLineBreakCell aWrap(true); |
243 | 0 | SvxBoxItem aBorderOuter(ATTR_BORDER); |
244 | 0 | SvxBorderLine aLine; |
245 | 0 | aLine.GuessLinesWidths(aLine.GetBorderLineStyle(), SvxBorderLineWidth::Thin); |
246 | 0 | aBorderOuter.SetLine(&aLine, SvxBoxItemLine::TOP); |
247 | 0 | aBorderOuter.SetLine(&aLine, SvxBoxItemLine::BOTTOM); |
248 | |
|
249 | 0 | aPattern.ItemSetPut(aHJustify); |
250 | 0 | aPattern.ItemSetPut(aVJustify); |
251 | 0 | aPattern.ItemSetPut(aWeight); |
252 | 0 | aPattern.ItemSetPut(aWrap); |
253 | 0 | aPattern.ItemSetPut(aBorderOuter); |
254 | |
|
255 | 0 | mrDocument.ApplyPatternAreaTab(mCurrentAddress.Col(), mCurrentAddress.Row(), |
256 | 0 | mCurrentAddress.Col() + nCols - 1, mCurrentAddress.Row(), |
257 | 0 | mCurrentAddress.Tab(), aPattern); |
258 | 0 | } |
259 | | |
260 | | // Formats as the bottom end of a table with a bottom line |
261 | | // Starts in the current cell and formats nCols in total |
262 | | void AddressWalkerWriter::formatTableBottom(SCCOL nCols) |
263 | 0 | { |
264 | 0 | ScPatternAttr aPattern(mrDocument.getCellAttributeHelper()); |
265 | 0 | SvxBoxItem aBorderOuter(ATTR_BORDER); |
266 | 0 | SvxBorderLine aLine; |
267 | 0 | aLine.GuessLinesWidths(aLine.GetBorderLineStyle(), SvxBorderLineWidth::Thin); |
268 | 0 | aBorderOuter.SetLine(&aLine, SvxBoxItemLine::BOTTOM); |
269 | 0 | aPattern.ItemSetPut(aBorderOuter); |
270 | 0 | mrDocument.ApplyPatternAreaTab(mCurrentAddress.Col(), mCurrentAddress.Row(), |
271 | 0 | mCurrentAddress.Col() + nCols - 1, mCurrentAddress.Row(), |
272 | 0 | mCurrentAddress.Tab(), aPattern); |
273 | 0 | } |
274 | | |
275 | | // DataCellIterator |
276 | | |
277 | | DataCellIterator::DataCellIterator(const ScRange& aInputRange, bool aByColumn) |
278 | 0 | : mInputRange(aInputRange) |
279 | 0 | , mByColumn(aByColumn) |
280 | 0 | , mCol(0) |
281 | 0 | , mRow(0) |
282 | 0 | { |
283 | 0 | if(aByColumn) |
284 | 0 | mCol = aInputRange.aStart.Col(); |
285 | 0 | else |
286 | 0 | mRow = aInputRange.aStart.Row(); |
287 | 0 | } |
288 | | |
289 | | bool DataCellIterator::hasNext() const |
290 | 0 | { |
291 | 0 | if(mByColumn) |
292 | 0 | return mCol <= mInputRange.aEnd.Col(); |
293 | 0 | else |
294 | 0 | return mRow <= mInputRange.aEnd.Row(); |
295 | 0 | } |
296 | | |
297 | | void DataCellIterator::next() |
298 | 0 | { |
299 | 0 | if(mByColumn) |
300 | 0 | mCol++; |
301 | 0 | else |
302 | 0 | mRow++; |
303 | 0 | } |
304 | | |
305 | | ScAddress DataCellIterator::get() |
306 | 0 | { |
307 | 0 | return getRelative(0); |
308 | 0 | } |
309 | | |
310 | | ScAddress DataCellIterator::getRelative(int aDelta) |
311 | 0 | { |
312 | 0 | if(mByColumn) |
313 | 0 | { |
314 | 0 | SCCOL aNewColumn = mCol + aDelta; |
315 | 0 | if(aNewColumn < mInputRange.aStart.Col() || aNewColumn > mInputRange.aEnd.Col()) |
316 | 0 | { |
317 | 0 | ScAddress aResult; |
318 | 0 | aResult.SetInvalid(); |
319 | 0 | return aResult; |
320 | 0 | } |
321 | 0 | return ScAddress(aNewColumn, mInputRange.aStart.Row(), mInputRange.aStart.Tab()); |
322 | 0 | } |
323 | 0 | else |
324 | 0 | { |
325 | 0 | SCROW aNewRow = mRow + aDelta; |
326 | 0 | if(aNewRow < mInputRange.aStart.Row() || aNewRow > mInputRange.aEnd.Row()) |
327 | 0 | { |
328 | 0 | ScAddress aResult; |
329 | 0 | aResult.SetInvalid(); |
330 | 0 | return aResult; |
331 | 0 | } |
332 | 0 | return ScAddress(mInputRange.aStart.Col(), aNewRow, mInputRange.aStart.Tab()); |
333 | 0 | } |
334 | 0 | } |
335 | | |
336 | | // DataRangeIterator |
337 | | |
338 | | DataRangeIterator::DataRangeIterator(const ScRange& aInputRange) : |
339 | 0 | mInputRange(aInputRange), |
340 | 0 | mIndex(0) |
341 | 0 | {} |
342 | | |
343 | | DataRangeIterator::~DataRangeIterator() |
344 | 0 | {} |
345 | | |
346 | | sal_Int32 DataRangeIterator::index() |
347 | 0 | { |
348 | 0 | return mIndex; |
349 | 0 | } |
350 | | |
351 | | // DataRangeByColumnIterator |
352 | | |
353 | | DataRangeByColumnIterator::DataRangeByColumnIterator(const ScRange& aInputRange) |
354 | 0 | : DataRangeIterator(aInputRange) |
355 | 0 | , mCol(aInputRange.aStart.Col()) |
356 | 0 | {} |
357 | | |
358 | | bool DataRangeByColumnIterator::hasNext() |
359 | 0 | { |
360 | 0 | return mCol <= mInputRange.aEnd.Col(); |
361 | 0 | } |
362 | | |
363 | | void DataRangeByColumnIterator::next() |
364 | 0 | { |
365 | 0 | mCol++; |
366 | 0 | mIndex++; |
367 | 0 | } |
368 | | |
369 | | ScRange DataRangeByColumnIterator::get() |
370 | 0 | { |
371 | 0 | return ScRange( |
372 | 0 | ScAddress(mCol, mInputRange.aStart.Row(), mInputRange.aStart.Tab()), |
373 | 0 | ScAddress(mCol, mInputRange.aEnd.Row(), mInputRange.aEnd.Tab()) |
374 | 0 | ); |
375 | 0 | } |
376 | | |
377 | | size_t DataRangeByColumnIterator::size() |
378 | 0 | { |
379 | 0 | return mInputRange.aEnd.Row() - mInputRange.aStart.Row() + 1; |
380 | 0 | } |
381 | | |
382 | | void DataRangeByColumnIterator::reset() |
383 | 0 | { |
384 | 0 | mCol = mInputRange.aStart.Col(); |
385 | 0 | } |
386 | | |
387 | | DataCellIterator DataRangeByColumnIterator::iterateCells() |
388 | 0 | { |
389 | 0 | return DataCellIterator(get(), false); |
390 | 0 | } |
391 | | |
392 | | // DataRangeByRowIterator |
393 | | |
394 | | DataRangeByRowIterator::DataRangeByRowIterator(const ScRange& aInputRange) |
395 | 0 | : DataRangeIterator(aInputRange) |
396 | 0 | , mRow(aInputRange.aStart.Row()) |
397 | 0 | {} |
398 | | |
399 | | bool DataRangeByRowIterator::hasNext() |
400 | 0 | { |
401 | 0 | return mRow <= mInputRange.aEnd.Row(); |
402 | 0 | } |
403 | | |
404 | | void DataRangeByRowIterator::next() |
405 | 0 | { |
406 | 0 | mRow++; |
407 | 0 | mIndex++; |
408 | 0 | } |
409 | | |
410 | | ScRange DataRangeByRowIterator::get() |
411 | 0 | { |
412 | 0 | return ScRange( |
413 | 0 | ScAddress(mInputRange.aStart.Col(), mRow, mInputRange.aStart.Tab()), |
414 | 0 | ScAddress(mInputRange.aEnd.Col(), mRow, mInputRange.aEnd.Tab()) |
415 | 0 | ); |
416 | 0 | } |
417 | | |
418 | | size_t DataRangeByRowIterator::size() |
419 | 0 | { |
420 | 0 | return mInputRange.aEnd.Col() - mInputRange.aStart.Col() + 1; |
421 | 0 | } |
422 | | |
423 | | void DataRangeByRowIterator::reset() |
424 | 0 | { |
425 | 0 | mRow = mInputRange.aStart.Row(); |
426 | 0 | } |
427 | | |
428 | | DataCellIterator DataRangeByRowIterator::iterateCells() |
429 | 0 | { |
430 | 0 | return DataCellIterator(get(), true); |
431 | 0 | } |
432 | | |
433 | | |
434 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |