Coverage Report

Created: 2026-07-10 11:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */