Coverage Report

Created: 2026-09-28 10:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/operation/InsertCellsOperation.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
#include <operation/InsertCellsOperation.hxx>
11
12
#include <docfunc.hxx>
13
#include <docsh.hxx>
14
#include <markdata.hxx>
15
#include <editable.hxx>
16
#include <dpobject.hxx>
17
#include <attrib.hxx>
18
#include <dociter.hxx>
19
#include <patattr.hxx>
20
#include <cellmergeoption.hxx>
21
#include <tabvwsh.hxx>
22
#include <undoblk.hxx>
23
#include <refundo.hxx>
24
#include <globstr.hrc>
25
#include <scresid.hxx>
26
#include <strings.hrc>
27
28
#include <SheetViewManager.hxx>
29
#include <comphelper/lok.hxx>
30
#include <sfx2/app.hxx>
31
#include <vcl/weld/WaitObject.hxx>
32
#include <vcl/weld/Window.hxx>
33
#include <osl/diagnose.h>
34
35
namespace
36
{
37
/**
38
 * Check if this insertion attempt would end up cutting one or more pivot
39
 * tables in half, which is not desirable.
40
 *
41
 * @return true if this insertion can be done safely without shearing any
42
 *         existing pivot tables, false otherwise.
43
 */
44
bool canInsertCellsByPivot(const ScRange& rRange, const ScMarkData& rMarkData, InsCellCmd eCmd,
45
                           const ScDocument& rDoc)
46
0
{
47
0
    if (!rDoc.HasPivotTable())
48
        // This document has no pivot tables.
49
0
        return true;
50
51
0
    const ScDPCollection* pDPs = rDoc.GetDPCollection();
52
53
0
    ScRange aRange(rRange); // local copy
54
0
    switch (eCmd)
55
0
    {
56
0
        case INS_INSROWS_BEFORE:
57
0
        {
58
0
            aRange.aStart.SetCol(0);
59
0
            aRange.aEnd.SetCol(rDoc.MaxCol());
60
0
            [[fallthrough]];
61
0
        }
62
0
        case INS_CELLSDOWN:
63
0
        {
64
0
            auto bIntersects = std::ranges::any_of(rMarkData, [&pDPs, &aRange](const SCTAB& rTab) {
65
0
                return pDPs->IntersectsTableByColumns(aRange.aStart.Col(), aRange.aEnd.Col(),
66
0
                                                      aRange.aStart.Row(), rTab);
67
0
            });
68
0
            if (bIntersects)
69
                // This column range cuts through at least one pivot table.  Not good.
70
0
                return false;
71
72
            // Start row must be either at the top or above any pivot tables.
73
0
            if (aRange.aStart.Row() < 0)
74
                // I don't know how to handle this case.
75
0
                return false;
76
77
0
            if (aRange.aStart.Row() == 0)
78
                // First row is always allowed.
79
0
                return true;
80
81
0
            ScRange aTest(aRange);
82
0
            aTest.aStart.IncRow(-1); // Test one row up.
83
0
            aTest.aEnd.SetRow(aTest.aStart.Row());
84
0
            for (const auto& rTab : rMarkData)
85
0
            {
86
0
                aTest.aStart.SetTab(rTab);
87
0
                aTest.aEnd.SetTab(rTab);
88
0
                if (pDPs->HasTable(aTest))
89
0
                    return false;
90
0
            }
91
0
        }
92
0
        break;
93
0
        case INS_INSCOLS_BEFORE:
94
0
        {
95
0
            aRange.aStart.SetRow(0);
96
0
            aRange.aEnd.SetRow(rDoc.MaxRow());
97
0
            [[fallthrough]];
98
0
        }
99
0
        case INS_CELLSRIGHT:
100
0
        {
101
0
            auto bIntersects = std::ranges::any_of(rMarkData, [&pDPs, &aRange](const SCTAB& rTab) {
102
0
                return pDPs->IntersectsTableByRows(aRange.aStart.Col(), aRange.aStart.Row(),
103
0
                                                   aRange.aEnd.Row(), rTab);
104
0
            });
105
0
            if (bIntersects)
106
                // This column range cuts through at least one pivot table.  Not good.
107
0
                return false;
108
109
            // Start row must be either at the top or above any pivot tables.
110
0
            if (aRange.aStart.Col() < 0)
111
                // I don't know how to handle this case.
112
0
                return false;
113
114
0
            if (aRange.aStart.Col() == 0)
115
                // First row is always allowed.
116
0
                return true;
117
118
0
            ScRange aTest(aRange);
119
0
            aTest.aStart.IncCol(-1); // Test one column to the left.
120
0
            aTest.aEnd.SetCol(aTest.aStart.Col());
121
0
            for (const auto& rTab : rMarkData)
122
0
            {
123
0
                aTest.aStart.SetTab(rTab);
124
0
                aTest.aEnd.SetTab(rTab);
125
0
                if (pDPs->HasTable(aTest))
126
0
                    return false;
127
0
            }
128
0
        }
129
0
        break;
130
0
        default:;
131
0
    }
132
0
    return true;
133
0
}
134
135
} // anonymous namespace
136
137
namespace sc
138
{
139
InsertCellsOperation::InsertCellsOperation(ScDocShell& rDocShell, ScRange const& rRange,
140
                                           ScMarkData const* pTabMark, InsCellCmd eCmd,
141
                                           bool bRecord, bool bApi, bool bPartOfPaste,
142
                                           size_t nInsertCount)
143
0
    : Operation(toOperationType(eCmd), bRecord, bApi)
144
0
    , mrDocShell(rDocShell)
145
0
    , maRange(rRange)
146
0
    , mpTabMark(pTabMark)
147
0
    , meCmd(eCmd)
148
0
    , mbPartOfPaste(bPartOfPaste)
149
0
    , mnInsertCount(nInsertCount)
150
0
{
151
0
}
152
153
OperationType InsertCellsOperation::toOperationType(InsCellCmd eCmd)
154
0
{
155
0
    switch (eCmd)
156
0
    {
157
0
        case INS_INSCOLS_BEFORE:
158
0
            return OperationType::InsertColumnsBefore;
159
0
        case INS_INSCOLS_AFTER:
160
0
            return OperationType::InsertColumnsAfter;
161
0
        case INS_INSROWS_BEFORE:
162
0
            return OperationType::InsertRowsBefore;
163
0
        case INS_INSROWS_AFTER:
164
0
            return OperationType::InsertRowsAfter;
165
0
        case INS_CELLSDOWN:
166
0
            return OperationType::InsertCellsDown;
167
0
        case INS_CELLSRIGHT:
168
0
            return OperationType::InsertCellsRight;
169
0
        default:
170
0
            return OperationType::Unknown;
171
0
    }
172
0
}
173
174
bool InsertCellsOperation::canRunTheOperation() const
175
0
{
176
0
    return !isInputOnSheetViewAutoFilter(maRange);
177
0
}
178
179
bool InsertCellsOperation::runImplementation()
180
0
{
181
0
    ScDocShellModificator aModificator(mrDocShell);
182
0
    ScDocument& rDoc = mrDocShell.GetDocument();
183
184
0
    if (mrDocShell.GetDocument().GetChangeTrack()
185
0
        && ((meCmd == INS_CELLSDOWN
186
0
             && (maRange.aStart.Col() != 0 || maRange.aEnd.Col() != rDoc.MaxCol()))
187
0
            || (meCmd == INS_CELLSRIGHT
188
0
                && (maRange.aStart.Row() != 0 || maRange.aEnd.Row() != rDoc.MaxRow()))))
189
0
    {
190
        // We should not reach this via UI disabled slots.
191
0
        assert(mbApi);
192
0
        SAL_WARN("sc.ui", "ScDocFunc::InsertCells - no change-tracking of partial cell shift");
193
0
        return false;
194
0
    }
195
196
0
    ScRange aTargetRange(maRange);
197
198
    // If insertion is for full cols/rows and after the current
199
    // selection, then shift the range accordingly
200
0
    if (meCmd == INS_INSROWS_AFTER)
201
0
    {
202
0
        ScRange aErrorRange(ScAddress::UNINITIALIZED);
203
0
        if (!aTargetRange.Move(0, maRange.aEnd.Row() - maRange.aStart.Row() + 1, 0, aErrorRange,
204
0
                               rDoc))
205
0
        {
206
0
            return false;
207
0
        }
208
0
    }
209
0
    if (meCmd == INS_INSCOLS_AFTER)
210
0
    {
211
0
        ScRange aErrorRange(ScAddress::UNINITIALIZED);
212
0
        if (!aTargetRange.Move(maRange.aEnd.Col() - maRange.aStart.Col() + 1, 0, 0, aErrorRange,
213
0
                               rDoc))
214
0
        {
215
0
            return false;
216
0
        }
217
0
    }
218
219
0
    SCCOL nStartCol = aTargetRange.aStart.Col();
220
0
    SCROW nStartRow = aTargetRange.aStart.Row();
221
0
    SCTAB nStartTab = aTargetRange.aStart.Tab();
222
0
    SCCOL nEndCol = aTargetRange.aEnd.Col() + mnInsertCount;
223
0
    SCROW nEndRow = aTargetRange.aEnd.Row() + mnInsertCount;
224
0
    SCTAB nEndTab = aTargetRange.aEnd.Tab();
225
226
0
    if (!rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow))
227
0
    {
228
0
        OSL_FAIL("invalid row in InsertCells");
229
0
        return false;
230
0
    }
231
232
0
    SCTAB nTabCount = rDoc.GetTableCount();
233
0
    SCCOL nPaintStartCol = nStartCol;
234
0
    SCROW nPaintStartRow = nStartRow;
235
0
    SCCOL nPaintEndCol = nEndCol;
236
0
    SCROW nPaintEndRow = nEndRow;
237
0
    PaintPartFlags nPaintFlags = PaintPartFlags::Grid;
238
0
    bool bSuccess;
239
240
0
    ScTabViewShell* pViewSh = mrDocShell.GetBestViewShell(); //preserve current cursor position
241
0
    SCCOL nCursorCol = 0;
242
0
    SCROW nCursorRow = 0;
243
0
    if (pViewSh)
244
0
    {
245
0
        nCursorCol = pViewSh->GetViewData().GetCurX();
246
0
        nCursorRow = pViewSh->GetViewData().GetCurY();
247
0
    }
248
249
0
    if (mbRecord && !rDoc.IsUndoEnabled())
250
0
        mbRecord = false;
251
252
0
    ScMarkData aMark(rDoc.GetSheetLimits());
253
0
    if (mpTabMark)
254
0
        aMark = *mpTabMark;
255
0
    else
256
0
    {
257
0
        SCTAB nCount = 0;
258
0
        for (SCTAB i = 0; i < nTabCount; i++)
259
0
        {
260
0
            if (!rDoc.IsScenario(i))
261
0
            {
262
0
                nCount++;
263
0
                if (nCount == nEndTab + 1)
264
0
                {
265
0
                    aMark.SelectTable(i, true);
266
0
                    break;
267
0
                }
268
0
            }
269
0
        }
270
0
    }
271
272
0
    ScMarkData aFullMark(aMark); // including scenario sheets
273
0
    for (const auto& rTab : aMark)
274
0
    {
275
0
        if (rTab >= nTabCount)
276
0
            break;
277
278
0
        for (SCTAB j = rTab + 1; j < nTabCount && rDoc.IsScenario(j); j++)
279
0
            aFullMark.SelectTable(j, true);
280
0
    }
281
282
0
    SCTAB nSelCount = aMark.GetSelectCount();
283
284
    // Adjust also related scenarios
285
286
0
    SCCOL nMergeTestStartCol = nStartCol;
287
0
    SCROW nMergeTestStartRow = nStartRow;
288
0
    SCCOL nMergeTestEndCol = nEndCol;
289
0
    SCROW nMergeTestEndRow = nEndRow;
290
291
0
    ScRange aExtendMergeRange(aTargetRange);
292
293
0
    if (aTargetRange.aStart == aTargetRange.aEnd
294
0
        && rDoc.HasAttrib(aTargetRange, HasAttrFlags::Merged))
295
0
    {
296
0
        rDoc.ExtendMerge(aExtendMergeRange);
297
0
        rDoc.ExtendOverlapped(aExtendMergeRange);
298
0
        nMergeTestEndCol = aExtendMergeRange.aEnd.Col();
299
0
        nMergeTestEndRow = aExtendMergeRange.aEnd.Row();
300
0
        nPaintEndCol = nMergeTestEndCol;
301
0
        nPaintEndRow = nMergeTestEndRow;
302
0
    }
303
304
0
    if (meCmd == INS_INSROWS_BEFORE || meCmd == INS_INSROWS_AFTER)
305
0
    {
306
0
        nMergeTestStartCol = 0;
307
0
        nMergeTestEndCol = rDoc.MaxCol();
308
0
    }
309
0
    if (meCmd == INS_INSCOLS_BEFORE || meCmd == INS_INSCOLS_AFTER)
310
0
    {
311
0
        nMergeTestStartRow = 0;
312
0
        nMergeTestEndRow = rDoc.MaxRow();
313
0
    }
314
0
    if (meCmd == INS_CELLSDOWN)
315
0
        nMergeTestEndRow = rDoc.MaxRow();
316
0
    if (meCmd == INS_CELLSRIGHT)
317
0
        nMergeTestEndCol = rDoc.MaxCol();
318
319
0
    bool bNeedRefresh = false;
320
321
0
    SCCOL nEditTestEndCol = (meCmd == INS_INSCOLS_BEFORE || meCmd == INS_INSCOLS_AFTER)
322
0
                                ? rDoc.MaxCol()
323
0
                                : nMergeTestEndCol;
324
0
    SCROW nEditTestEndRow = (meCmd == INS_INSROWS_BEFORE || meCmd == INS_INSROWS_AFTER)
325
0
                                ? rDoc.MaxRow()
326
0
                                : nMergeTestEndRow;
327
328
0
    ScEditableTester aTester;
329
0
    switch (meCmd)
330
0
    {
331
0
        case INS_INSCOLS_BEFORE:
332
0
            aTester = ScEditableTester::CreateAndTestBlockForAction(
333
0
                rDoc, sc::EditAction::InsertColumnsBefore, nMergeTestStartCol, 0, nMergeTestEndCol,
334
0
                rDoc.MaxRow(), aMark);
335
0
            break;
336
0
        case INS_INSCOLS_AFTER:
337
0
            aTester = ScEditableTester::CreateAndTestBlockForAction(
338
0
                rDoc, sc::EditAction::InsertColumnsAfter, nMergeTestStartCol, 0, nMergeTestEndCol,
339
0
                rDoc.MaxRow(), aMark);
340
0
            break;
341
0
        case INS_INSROWS_BEFORE:
342
0
            aTester = ScEditableTester::CreateAndTestBlockForAction(
343
0
                rDoc, sc::EditAction::InsertRowsBefore, 0, nMergeTestStartRow, rDoc.MaxCol(),
344
0
                nMergeTestEndRow, aMark);
345
0
            break;
346
0
        case INS_INSROWS_AFTER:
347
0
            aTester = ScEditableTester::CreateAndTestBlockForAction(
348
0
                rDoc, sc::EditAction::InsertRowsAfter, 0, nMergeTestStartRow, rDoc.MaxCol(),
349
0
                nMergeTestEndRow, aMark);
350
0
            break;
351
0
        default:
352
0
            aTester = ScEditableTester::CreateAndTestSelectedBlock(
353
0
                rDoc, nMergeTestStartCol, nMergeTestStartRow, nEditTestEndCol, nEditTestEndRow,
354
0
                aMark);
355
0
            break;
356
0
    }
357
358
0
    if (!aTester.IsEditable())
359
0
    {
360
0
        if (!mbApi)
361
0
            mrDocShell.ErrorMessage(aTester.GetMessageId());
362
0
        return false;
363
0
    }
364
365
    // Check if this insertion is allowed with respect to pivot table.
366
0
    if (!canInsertCellsByPivot(aTargetRange, aMark, meCmd, rDoc))
367
0
    {
368
0
        if (!mbApi)
369
0
            mrDocShell.ErrorMessage(STR_NO_INSERT_DELETE_OVER_PIVOT_TABLE);
370
0
        return false;
371
0
    }
372
373
0
    weld::WaitObject aWait(
374
0
        ScDocShell::GetActiveDialogParent()); // important due to TrackFormulas at UpdateReference
375
376
0
    ScUndoInsertCells* pUndoInsertCells = nullptr;
377
0
    ScDocumentUniquePtr pRefUndoDoc;
378
0
    std::unique_ptr<ScRefUndoData> pUndoData;
379
0
    if (mbRecord)
380
0
    {
381
0
        pRefUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
382
0
        pRefUndoDoc->InitUndo(rDoc, 0, nTabCount - 1);
383
384
        // pRefUndoDoc is filled in InsertCol / InsertRow
385
386
0
        pUndoData.reset(new ScRefUndoData(rDoc));
387
388
0
        rDoc.BeginDrawUndo();
389
0
    }
390
391
    // #i8302 : we unmerge overwhelming ranges, before insertion all the actions are put in the same ListAction
392
    // the patch comes from mloiseleur and maoyg
393
0
    bool bInsertMerge = false;
394
0
    std::vector<ScRange> qIncreaseRange;
395
0
    OUString aUndo = ScResId(STR_UNDO_INSERTCELLS);
396
0
    if (mbRecord)
397
0
    {
398
0
        ViewShellId nViewShellId(-1);
399
0
        if (pViewSh)
400
0
            nViewShellId = pViewSh->GetViewShellId();
401
0
        mrDocShell.GetUndoManager()->EnterListAction(aUndo, aUndo, 0, nViewShellId);
402
0
    }
403
0
    std::unique_ptr<ScUndoRemoveMerge> pUndoRemoveMerge;
404
405
0
    for (const SCTAB i : aMark)
406
0
    {
407
0
        if (i >= nTabCount)
408
0
            break;
409
410
0
        if (rDoc.HasAttrib(nMergeTestStartCol, nMergeTestStartRow, i, nMergeTestEndCol,
411
0
                           nMergeTestEndRow, i, HasAttrFlags::Merged | HasAttrFlags::Overlapped))
412
0
        {
413
0
            if (meCmd == INS_CELLSRIGHT)
414
0
                bNeedRefresh = true;
415
416
0
            SCCOL nMergeStartCol = nMergeTestStartCol;
417
0
            SCROW nMergeStartRow = nMergeTestStartRow;
418
0
            SCCOL nMergeEndCol = nMergeTestEndCol;
419
0
            SCROW nMergeEndRow = nMergeTestEndRow;
420
421
0
            rDoc.ExtendMerge(nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i);
422
0
            rDoc.ExtendOverlapped(nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i);
423
424
0
            if ((meCmd == INS_CELLSDOWN
425
0
                 && (nMergeStartCol != nMergeTestStartCol || nMergeEndCol != nMergeTestEndCol))
426
0
                || (meCmd == INS_CELLSRIGHT
427
0
                    && (nMergeStartRow != nMergeTestStartRow || nMergeEndRow != nMergeTestEndRow)))
428
0
            {
429
0
                if (!mbApi)
430
0
                    mrDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0);
431
0
                mrDocShell.GetUndoManager()->LeaveListAction();
432
0
                return false;
433
0
            }
434
435
0
            SCCOL nTestCol = -1;
436
0
            SCROW nTestRow1 = -1;
437
0
            SCROW nTestRow2 = -1;
438
439
0
            ScDocAttrIterator aTestIter(rDoc, i, nMergeTestStartCol, nMergeTestStartRow,
440
0
                                        nMergeTestEndCol, nMergeTestEndRow);
441
0
            ScRange aExtendRange(nMergeTestStartCol, nMergeTestStartRow, i, nMergeTestEndCol,
442
0
                                 nMergeTestEndRow, i);
443
0
            const ScPatternAttr* pPattern = nullptr;
444
0
            while ((pPattern = aTestIter.GetNext(nTestCol, nTestRow1, nTestRow2)) != nullptr)
445
0
            {
446
0
                const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE);
447
0
                const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG);
448
0
                ScMF nNewFlags = rMergeFlagAttr.GetValue() & (ScMF::Hor | ScMF::Ver);
449
0
                if (rMergeFlag.IsMerged() || nNewFlags == ScMF::Hor || nNewFlags == ScMF::Ver)
450
0
                {
451
0
                    ScRange aRange(nTestCol, nTestRow1, i);
452
0
                    rDoc.ExtendOverlapped(aRange);
453
0
                    rDoc.ExtendMerge(aRange, true);
454
455
0
                    if (nTestRow1 < nTestRow2 && nNewFlags == ScMF::Hor)
456
0
                    {
457
0
                        for (SCROW nTestRow = nTestRow1; nTestRow <= nTestRow2; nTestRow++)
458
0
                        {
459
0
                            ScRange aTestRange(nTestCol, nTestRow, i);
460
0
                            rDoc.ExtendOverlapped(aTestRange);
461
0
                            rDoc.ExtendMerge(aTestRange, true);
462
0
                            ScRange aMergeRange(aTestRange.aStart.Col(), aTestRange.aStart.Row(),
463
0
                                                i);
464
0
                            if (!aExtendRange.Contains(aMergeRange))
465
0
                            {
466
0
                                qIncreaseRange.push_back(aTestRange);
467
0
                                bInsertMerge = true;
468
0
                            }
469
0
                        }
470
0
                    }
471
0
                    else
472
0
                    {
473
0
                        ScRange aMergeRange(aRange.aStart.Col(), aRange.aStart.Row(), i);
474
0
                        if (!aExtendRange.Contains(aMergeRange))
475
0
                        {
476
0
                            qIncreaseRange.push_back(aRange);
477
0
                        }
478
0
                        bInsertMerge = true;
479
0
                    }
480
0
                }
481
0
            }
482
483
0
            if (bInsertMerge)
484
0
            {
485
0
                if (meCmd == INS_INSROWS_BEFORE || meCmd == INS_INSROWS_AFTER
486
0
                    || meCmd == INS_CELLSDOWN)
487
0
                {
488
0
                    nStartRow = aExtendMergeRange.aStart.Row();
489
0
                    nEndRow = aExtendMergeRange.aEnd.Row();
490
491
0
                    if (meCmd == INS_CELLSDOWN)
492
0
                        nEndCol = nMergeTestEndCol;
493
0
                    else
494
0
                    {
495
0
                        nStartCol = 0;
496
0
                        nEndCol = rDoc.MaxCol();
497
0
                    }
498
0
                }
499
0
                else if (meCmd == INS_CELLSRIGHT || meCmd == INS_INSCOLS_BEFORE
500
0
                         || meCmd == INS_INSCOLS_AFTER)
501
0
                {
502
0
                    nStartCol = aExtendMergeRange.aStart.Col();
503
0
                    nEndCol = aExtendMergeRange.aEnd.Col();
504
0
                    if (meCmd == INS_CELLSRIGHT)
505
0
                    {
506
0
                        nEndRow = nMergeTestEndRow;
507
0
                    }
508
0
                    else
509
0
                    {
510
0
                        nStartRow = 0;
511
0
                        nEndRow = rDoc.MaxRow();
512
0
                    }
513
0
                }
514
515
0
                if (!qIncreaseRange.empty())
516
0
                {
517
0
                    if (mbRecord && !pUndoRemoveMerge)
518
0
                    {
519
0
                        ScDocumentUniquePtr pUndoDoc(new ScDocument(SCDOCMODE_UNDO));
520
0
                        pUndoDoc->InitUndo(rDoc, *aMark.begin(), *aMark.rbegin());
521
0
                        pUndoRemoveMerge.reset(
522
0
                            new ScUndoRemoveMerge(mrDocShell, maRange, std::move(pUndoDoc)));
523
0
                    }
524
525
0
                    for (const ScRange& aRange : qIncreaseRange)
526
0
                    {
527
0
                        if (rDoc.HasAttrib(aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged))
528
0
                        {
529
0
                            mrDocShell.GetDocFunc().UnmergeCells(aRange, mbRecord,
530
0
                                                                 pUndoRemoveMerge.get());
531
0
                        }
532
0
                    }
533
0
                }
534
0
            }
535
0
            else
536
0
            {
537
0
                if (!mbApi)
538
0
                    mrDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0);
539
0
                mrDocShell.GetUndoManager()->LeaveListAction();
540
0
                return false;
541
0
            }
542
0
        }
543
0
    }
544
545
0
    if (mbRecord && pUndoRemoveMerge)
546
0
    {
547
0
        mrDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoRemoveMerge));
548
0
    }
549
550
0
    switch (meCmd)
551
0
    {
552
0
        case INS_CELLSDOWN:
553
0
            bSuccess = rDoc.InsertRow(nStartCol, 0, nEndCol, MAXTAB, nStartRow,
554
0
                                      static_cast<SCSIZE>(nEndRow - nStartRow + 1),
555
0
                                      pRefUndoDoc.get(), &aFullMark);
556
0
            nPaintEndRow = rDoc.MaxRow();
557
0
            break;
558
0
        case INS_INSROWS_BEFORE:
559
0
        case INS_INSROWS_AFTER:
560
0
            bSuccess = rDoc.InsertRow(0, 0, rDoc.MaxCol(), MAXTAB, nStartRow,
561
0
                                      static_cast<SCSIZE>(nEndRow - nStartRow + 1),
562
0
                                      pRefUndoDoc.get(), &aFullMark);
563
0
            nPaintStartCol = 0;
564
0
            nPaintEndCol = rDoc.MaxCol();
565
0
            nPaintEndRow = rDoc.MaxRow();
566
0
            nPaintFlags |= PaintPartFlags::Left;
567
0
            break;
568
0
        case INS_CELLSRIGHT:
569
0
            bSuccess = rDoc.InsertCol(nStartRow, 0, nEndRow, MAXTAB, nStartCol,
570
0
                                      static_cast<SCSIZE>(nEndCol - nStartCol + 1),
571
0
                                      pRefUndoDoc.get(), &aFullMark);
572
0
            nPaintEndCol = rDoc.MaxCol();
573
0
            break;
574
0
        case INS_INSCOLS_BEFORE:
575
0
        case INS_INSCOLS_AFTER:
576
0
            bSuccess = rDoc.InsertCol(0, 0, rDoc.MaxRow(), MAXTAB, nStartCol,
577
0
                                      static_cast<SCSIZE>(nEndCol - nStartCol + 1),
578
0
                                      pRefUndoDoc.get(), &aFullMark);
579
0
            nPaintStartRow = 0;
580
0
            nPaintEndRow = rDoc.MaxRow();
581
0
            nPaintEndCol = rDoc.MaxCol();
582
0
            nPaintFlags |= PaintPartFlags::Top;
583
0
            break;
584
0
        default:
585
0
            OSL_FAIL("Wrong code at inserting");
586
0
            bSuccess = false;
587
0
            break;
588
0
    }
589
590
0
    if (bSuccess)
591
0
    {
592
0
        SCTAB nUndoPos = 0;
593
594
0
        if (mbRecord)
595
0
        {
596
0
            std::unique_ptr<SCTAB[]> pTabs(new SCTAB[nSelCount]);
597
0
            std::unique_ptr<SCTAB[]> pScenarios(new SCTAB[nSelCount]);
598
0
            nUndoPos = 0;
599
0
            for (const auto& rTab : aMark)
600
0
            {
601
0
                if (rTab >= nTabCount)
602
0
                    break;
603
604
0
                SCTAB nCount = 0;
605
0
                for (SCTAB j = rTab + 1; j < nTabCount && rDoc.IsScenario(j); j++)
606
0
                    nCount++;
607
608
0
                pScenarios[nUndoPos] = nCount;
609
0
                pTabs[nUndoPos] = rTab;
610
0
                nUndoPos++;
611
0
            }
612
613
0
            if (!bInsertMerge)
614
0
            {
615
0
                mrDocShell.GetUndoManager()->LeaveListAction();
616
0
            }
617
618
0
            auto pUndoAction = std::make_unique<ScUndoInsertCells>(
619
0
                mrDocShell, ScRange(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab),
620
0
                nUndoPos, std::move(pTabs), std::move(pScenarios), meCmd, std::move(pRefUndoDoc),
621
0
                std::move(pUndoData), mbPartOfPaste);
622
0
            pUndoInsertCells = pUndoAction.get();
623
0
            mrDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoAction));
624
0
        }
625
626
        // #i8302 : we remerge growing ranges, with the new part inserted
627
628
0
        while (!qIncreaseRange.empty())
629
0
        {
630
0
            ScRange aRange = qIncreaseRange.back();
631
0
            if (!rDoc.HasAttrib(aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged))
632
0
            {
633
0
                switch (meCmd)
634
0
                {
635
0
                    case INS_CELLSDOWN:
636
0
                    case INS_INSROWS_BEFORE:
637
0
                    case INS_INSROWS_AFTER:
638
0
                        aRange.aEnd.IncRow(static_cast<SCCOL>(nEndRow - nStartRow + 1));
639
0
                        break;
640
0
                    case INS_CELLSRIGHT:
641
0
                    case INS_INSCOLS_BEFORE:
642
0
                    case INS_INSCOLS_AFTER:
643
0
                        aRange.aEnd.IncCol(static_cast<SCCOL>(nEndCol - nStartCol + 1));
644
0
                        break;
645
0
                    default:
646
0
                        break;
647
0
                }
648
0
                ScCellMergeOption aMergeOption(aRange.aStart.Col(), aRange.aStart.Row(),
649
0
                                               aRange.aEnd.Col(), aRange.aEnd.Row());
650
0
                aMergeOption.maTabs.insert(aRange.aStart.Tab());
651
0
                mrDocShell.GetDocFunc().MergeCells(aMergeOption, false, true, true);
652
0
            }
653
0
            qIncreaseRange.pop_back();
654
0
        }
655
656
0
        if (bInsertMerge)
657
0
            mrDocShell.GetUndoManager()->LeaveListAction();
658
659
0
        for (const SCTAB i : aMark)
660
0
        {
661
0
            if (i >= nTabCount)
662
0
                break;
663
664
0
            rDoc.SetDrawPageSize(i);
665
666
0
            if (bNeedRefresh)
667
0
                rDoc.ExtendMerge(nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol,
668
0
                                 nMergeTestEndRow, i, true);
669
0
            else
670
0
                rDoc.RefreshAutoFilter(nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol,
671
0
                                       nMergeTestEndRow, i);
672
673
0
            if (meCmd == INS_INSROWS_BEFORE || meCmd == INS_INSCOLS_BEFORE
674
0
                || meCmd == INS_INSROWS_AFTER || meCmd == INS_INSCOLS_AFTER)
675
0
                rDoc.UpdatePageBreaks(i);
676
677
0
            sal_uInt16 nExtFlags = 0;
678
0
            mrDocShell.UpdatePaintExt(nExtFlags, nPaintStartCol, nPaintStartRow, i, nPaintEndCol,
679
0
                                      nPaintEndRow, i);
680
681
0
            SCTAB nScenarioCount = 0;
682
683
0
            for (SCTAB j = i + 1; j < nTabCount && rDoc.IsScenario(j); j++)
684
0
                nScenarioCount++;
685
686
0
            bool bAdjusted;
687
0
            switch (meCmd)
688
0
            {
689
0
                case INS_INSROWS_BEFORE:
690
0
                case INS_INSROWS_AFTER:
691
0
                    bAdjusted = mrDocShell.GetDocFunc().AdjustRowHeight(
692
0
                        ScRange(0, nStartRow, i, rDoc.MaxCol(), nEndRow, i + nScenarioCount), true,
693
0
                        mbApi);
694
0
                    break;
695
0
                case INS_INSCOLS_BEFORE:
696
0
                case INS_INSCOLS_AFTER:
697
0
                case INS_CELLSRIGHT:
698
                    // A horizontal insert, a whole column or cells shifted right, only adds empty
699
                    // cells and shifts existing cells sideways, leaving their content and column
700
                    // widths unchanged, so no row's optimal height can change. Nothing to
701
                    // recompute.
702
0
                    bAdjusted = false;
703
0
                    break;
704
0
                default:
705
0
                    bAdjusted = mrDocShell.GetDocFunc().AdjustRowHeight(
706
0
                        ScRange(0, nPaintStartRow, i, rDoc.MaxCol(), nPaintEndRow,
707
0
                                i + nScenarioCount),
708
0
                        true, mbApi);
709
0
                    break;
710
0
            }
711
0
            if (bAdjusted)
712
0
            {
713
                //  paint only what is not done by AdjustRowHeight
714
0
                if (nPaintFlags & PaintPartFlags::Top)
715
0
                    mrDocShell.PostPaint(nPaintStartCol, nPaintStartRow, i, nPaintEndCol,
716
0
                                         nPaintEndRow, i + nScenarioCount, PaintPartFlags::Top);
717
0
            }
718
0
            else
719
0
                mrDocShell.PostPaint(nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow,
720
0
                                     i + nScenarioCount, nPaintFlags, nExtFlags);
721
0
        }
722
0
    }
723
0
    else
724
0
    {
725
0
        if (bInsertMerge)
726
0
        {
727
0
            while (!qIncreaseRange.empty())
728
0
            {
729
0
                ScRange aRange = qIncreaseRange.back();
730
0
                ScCellMergeOption aMergeOption(aRange.aStart.Col(), aRange.aStart.Row(),
731
0
                                               aRange.aEnd.Col(), aRange.aEnd.Row());
732
0
                mrDocShell.GetDocFunc().MergeCells(aMergeOption, false, true, true);
733
0
                qIncreaseRange.pop_back();
734
0
            }
735
736
0
            if (pViewSh)
737
0
            {
738
0
                pViewSh->MarkRange(aTargetRange, false);
739
0
                pViewSh->SetCursor(nCursorCol, nCursorRow);
740
0
            }
741
0
        }
742
743
0
        mrDocShell.GetUndoManager()->LeaveListAction();
744
0
        mrDocShell.GetUndoManager()->RemoveLastUndoAction();
745
746
0
        pRefUndoDoc.reset();
747
0
        if (!mbApi)
748
0
            mrDocShell.ErrorMessage(STR_INSERT_FULL); // column/row full
749
0
    }
750
751
    // The cursor position needs to be modified earlier than updating
752
    // any enabled edit view which is triggered by SetDocumentModified below.
753
0
    if (bSuccess)
754
0
    {
755
0
        bool bInsertCols = (meCmd == INS_INSCOLS_BEFORE || meCmd == INS_INSCOLS_AFTER);
756
0
        bool bInsertRows = (meCmd == INS_INSROWS_BEFORE || meCmd == INS_INSROWS_AFTER);
757
758
0
        if (bInsertCols)
759
0
        {
760
0
            pViewSh->OnLOKInsertDeleteColumn(
761
0
                maRange.aStart.Col() - (meCmd == INS_INSCOLS_BEFORE ? 1 : 0), 1);
762
0
        }
763
764
0
        if (bInsertRows)
765
0
        {
766
0
            pViewSh->OnLOKInsertDeleteRow(
767
0
                maRange.aStart.Row() - (meCmd == INS_INSROWS_BEFORE ? 1 : 0), 1);
768
0
        }
769
770
        // Update sheet view sort ranges to account for inserted rows/columns
771
0
        if (bInsertRows || bInsertCols)
772
0
        {
773
0
            SCTAB nDefaultViewTab = rDoc.GetDefaultViewTableNumber(nStartTab);
774
0
            std::shared_ptr<sc::SheetViewManager> pManager
775
0
                = rDoc.GetSheetViewManager(nDefaultViewTab);
776
0
            if (pManager)
777
0
            {
778
0
                auto pSortDataBefore = pManager->captureSortData();
779
780
0
                if (bInsertRows)
781
0
                    pManager->insertedRows(nStartRow, nEndRow - nStartRow + 1);
782
0
                if (bInsertCols)
783
0
                    pManager->insertedColumns(nStartCol, nEndCol - nStartCol + 1);
784
785
0
                auto pSortDataAfter = pManager->captureSortData();
786
787
0
                if (mbRecord && pUndoInsertCells)
788
0
                {
789
0
                    pUndoInsertCells->setDefaultViewContext(
790
0
                        nDefaultViewTab, std::move(pSortDataBefore), std::move(pSortDataAfter));
791
0
                }
792
0
            }
793
0
        }
794
795
0
        syncSheetViews(pUndoInsertCells);
796
0
    }
797
798
0
    aModificator.SetDocumentModified();
799
800
0
    SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreaLinksChanged));
801
0
    return bSuccess;
802
0
}
803
804
} // end sc namespace
805
806
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */