Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/docshell/docsh5.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
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <memory>
21
#include <sal/config.h>
22
23
#include <cassert>
24
25
#include <osl/diagnose.h>
26
#include <vcl/svapp.hxx>
27
#include <vcl/vclenum.hxx>
28
#include <vcl/weld/MessageDialog.hxx>
29
#include <vcl/weld/WaitObject.hxx>
30
#include <sfx2/app.hxx>
31
#include <sfx2/bindings.hxx>
32
#include <unotools/charclass.hxx>
33
34
#include <com/sun/star/script/vba/XVBACompatibility.hpp>
35
36
#include <dociter.hxx>
37
#include <docsh.hxx>
38
#include <global.hxx>
39
#include <globstr.hrc>
40
#include <scresid.hxx>
41
#include <globalnames.hxx>
42
#include <undodat.hxx>
43
#include <undotab.hxx>
44
#include <undoblk.hxx>
45
#include <undo/UndoSpillPivotTable.hxx>
46
#include <formulacell.hxx>
47
#include <scmatrix.hxx>
48
#include <dpobject.hxx>
49
#include <dpshttab.hxx>
50
#include <dbdocfun.hxx>
51
#include <consoli.hxx>
52
#include <dbdata.hxx>
53
#include <progress.hxx>
54
#include <olinetab.hxx>
55
#include <patattr.hxx>
56
#include <attrib.hxx>
57
#include <docpool.hxx>
58
#include <uiitems.hxx>
59
#include <sc.hrc>
60
#include <sizedev.hxx>
61
#include <clipparam.hxx>
62
#include <rowheightcontext.hxx>
63
#include <refupdatecontext.hxx>
64
65
using com::sun::star::script::XLibraryContainer;
66
using com::sun::star::script::vba::XVBACompatibility;
67
using com::sun::star::container::XNameContainer;
68
using com::sun::star::uno::Reference;
69
using com::sun::star::uno::UNO_QUERY;
70
71
//  former viewfunc/dbfunc methods
72
73
void ScDocShell::ErrorMessage(TranslateId pGlobStrId)
74
0
{
75
    //! StopMarking at the (active) view?
76
77
0
    weld::Window* pParent = GetActiveDialogParent();
78
0
    weld::WaitObject aWaitOff( pParent );
79
0
    bool bFocus = pParent && pParent->has_focus();
80
81
0
    if (pGlobStrId && pGlobStrId == STR_PROTECTIONERR)
82
0
    {
83
0
        if (IsReadOnly())
84
0
        {
85
0
            pGlobStrId = STR_READONLYERR;
86
0
        }
87
0
    }
88
89
0
    std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent,
90
0
                                                  VclMessageType::Info, VclButtonsType::Ok,
91
0
                                                  ScResId(pGlobStrId)));
92
0
    xInfoBox->run();
93
94
0
    if (bFocus)
95
0
        pParent->grab_focus();
96
0
}
97
98
bool ScDocShell::IsEditable() const
99
12.3k
{
100
    // import into read-only document is possible - must be extended if other filters use api
101
    // #i108547# MSOOXML filter uses "IsChangeReadOnlyEnabled" property
102
103
12.3k
    return !IsReadOnly() || m_pDocument->IsImportingXML() || m_pDocument->IsChangeReadOnlyEnabled();
104
12.3k
}
105
106
void ScDocShell::DBAreaDeleted( SCTAB nTab, SCCOL nX1, SCROW nY1, SCCOL nX2 )
107
0
{
108
0
    ScDocShellModificator aModificator( *this );
109
    // the auto-filter is in the first row of the area
110
0
    m_pDocument->RemoveFlagsTab( nX1, nY1, nX2, nY1, nTab, ScMF::Auto );
111
0
    PostPaint( nX1, nY1, nTab, nX2, nY1, nTab, PaintPartFlags::Grid );
112
    // No SetDocumentModified, as the unnamed database range might have to be restored later.
113
    // The UNO hint is broadcast directly instead, to keep UNO objects in valid state.
114
0
    m_pDocument->BroadcastUno( SfxHint( SfxHintId::DataChanged ) );
115
0
}
116
117
ScDBData* ScDocShell::GetDBData( const ScRange& rMarked, ScGetDBMode eMode, ScGetDBSelection eSel )
118
0
{
119
0
    SCCOL nCol = rMarked.aStart.Col();
120
0
    SCROW nRow = rMarked.aStart.Row();
121
0
    SCTAB nTab = rMarked.aStart.Tab();
122
123
0
    SCCOL nStartCol = nCol;
124
0
    SCROW nStartRow = nRow;
125
0
    SCTAB nStartTab = nTab;
126
0
    SCCOL nEndCol = rMarked.aEnd.Col();
127
0
    SCROW nEndRow = rMarked.aEnd.Row();
128
    //  Not simply GetDBAtCursor: The continuous data range for "unnamed" (GetDataArea) may be
129
    //  located next to the cursor; so a named DB range needs to be searched for there as well.
130
0
    ScDBCollection* pColl = m_pDocument->GetDBCollection();
131
0
    ScDBData* pData = m_pDocument->GetDBAtArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
132
0
    if (!pData)
133
0
        pData = pColl->GetDBNearCursor(nCol, nRow, nTab );
134
135
0
    bool bSelected = ( eSel == ScGetDBSelection::ForceMark ||
136
0
            (rMarked.aStart != rMarked.aEnd && eSel != ScGetDBSelection::RowDown) );
137
0
    bool bOnlyDown = (!bSelected && eSel == ScGetDBSelection::RowDown && rMarked.aStart.Row() == rMarked.aEnd.Row());
138
139
0
    bool bUseThis = false;
140
0
    if (pData)
141
0
    {
142
        //      take range, if nothing else is marked
143
144
0
        SCTAB nDummy;
145
0
        SCCOL nOldCol1;
146
0
        SCROW nOldRow1;
147
0
        SCCOL nOldCol2;
148
0
        SCROW nOldRow2;
149
0
        pData->GetArea( nDummy, nOldCol1,nOldRow1, nOldCol2,nOldRow2 );
150
0
        bool bIsNoName = ( pData->GetName() == STR_DB_LOCAL_NONAME );
151
152
0
        if (!bSelected)
153
0
        {
154
0
            bUseThis = true;
155
0
            if ( bIsNoName && (eMode == SC_DB_MAKE || eMode == SC_DB_AUTOFILTER) )
156
0
            {
157
                // If nothing marked or only one row marked, adapt
158
                // "unnamed" to contiguous area.
159
0
                nStartCol = nCol;
160
0
                nStartRow = nRow;
161
0
                if (bOnlyDown)
162
0
                {
163
0
                    nEndCol = rMarked.aEnd.Col();
164
0
                    nEndRow = rMarked.aEnd.Row();
165
0
                }
166
0
                else
167
0
                {
168
0
                    nEndCol = nStartCol;
169
0
                    nEndRow = nStartRow;
170
0
                }
171
0
                m_pDocument->GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow, false, bOnlyDown );
172
0
                if ( nOldCol1 != nStartCol || nOldCol2 != nEndCol || nOldRow1 != nStartRow )
173
0
                    bUseThis = false;               // doesn't fit at all
174
0
                else if ( nOldRow2 != nEndRow )
175
0
                {
176
                    //  extend range to new end row
177
0
                    pData->SetArea( nTab, nOldCol1,nOldRow1, nOldCol2,nEndRow );
178
0
                }
179
0
            }
180
0
        }
181
0
        else
182
0
        {
183
0
            if ( nOldCol1 == nStartCol && nOldRow1 == nStartRow &&
184
0
                 nOldCol2 == nEndCol && nOldRow2 == nEndRow )               // marked precisely?
185
0
                bUseThis = true;
186
0
            else
187
0
                bUseThis = false;           // always take marking (Bug 11964)
188
0
        }
189
190
        //      never take "unnamed" for import
191
192
0
        if ( bUseThis && eMode == SC_DB_IMPORT && bIsNoName )
193
0
            bUseThis = false;
194
0
    }
195
196
0
    if ( bUseThis )
197
0
    {
198
0
        pData->GetArea( nStartTab, nStartCol,nStartRow, nEndCol,nEndRow );
199
0
    }
200
    // tdf#168478 - use the previously found range rather than returning an empty one
201
0
    else if ( eMode != SC_DB_OLD )
202
0
    {
203
0
        if ( !bSelected )
204
0
        {                                       // continuous range
205
0
            nStartCol = nCol;
206
0
            nStartRow = nRow;
207
0
            if (bOnlyDown)
208
0
            {
209
0
                nEndCol = rMarked.aEnd.Col();
210
0
                nEndRow = rMarked.aEnd.Row();
211
0
            }
212
0
            else
213
0
            {
214
0
                nEndCol = nStartCol;
215
0
                nEndRow = nStartRow;
216
0
            }
217
0
            m_pDocument->GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow, false, bOnlyDown );
218
0
        }
219
220
0
        bool bHasHeader = m_pDocument->HasColHeader( nStartCol,nStartRow, nEndCol,nEndRow, nTab );
221
222
0
        ScDBData* pNoNameData = m_pDocument->GetAnonymousDBData(nTab);
223
0
        if ( eMode != SC_DB_IMPORT && pNoNameData)
224
0
        {
225
            // Do not reset AutoFilter range during temporary operations on
226
            // other ranges, use the document global temporary anonymous range
227
            // instead. But, if AutoFilter is to be toggled then do use the
228
            // sheet-local DB range.
229
0
            bool bSheetLocal = true;
230
0
            if (eMode != SC_DB_AUTOFILTER && pNoNameData->HasAutoFilter())
231
0
            {
232
0
                bSheetLocal = false;
233
0
                pNoNameData = m_pDocument->GetAnonymousDBData();
234
0
                if (!pNoNameData)
235
0
                {
236
0
                    m_pDocument->SetAnonymousDBData( std::unique_ptr<ScDBData>(new ScDBData( STR_DB_LOCAL_NONAME,
237
0
                            nTab, nStartCol, nStartRow, nEndCol, nEndRow, true, bHasHeader) ) );
238
0
                    pNoNameData = m_pDocument->GetAnonymousDBData();
239
0
                }
240
                // ScDocShell::CancelAutoDBRange() would restore the
241
                // sheet-local anonymous DBData from pOldAutoDBRange, unset so
242
                // that won't happen with data of a previous sheet-local
243
                // DBData.
244
0
                m_pOldAutoDBRange.reset();
245
0
            }
246
0
            else if (!m_pOldAutoDBRange)
247
0
            {
248
                // store the old unnamed database range with its settings for undo
249
                // (store at the first change, get the state before all changes)
250
0
                m_pOldAutoDBRange.reset( new ScDBData( *pNoNameData ) );
251
0
            }
252
0
            else if (m_pOldAutoDBRange->GetTab() != pNoNameData->GetTab())
253
0
            {
254
                // Different sheet-local unnamed DB range than the previous one.
255
0
                *m_pOldAutoDBRange = *pNoNameData;
256
0
            }
257
258
0
            SCCOL nOldX1;                                   // take old range away cleanly
259
0
            SCROW nOldY1;                                   //! (UNDO ???)
260
0
            SCCOL nOldX2;
261
0
            SCROW nOldY2;
262
0
            SCTAB nOldTab;
263
0
            pNoNameData->GetArea( nOldTab, nOldX1, nOldY1, nOldX2, nOldY2 );
264
265
            // If previously bHasHeader was set and the new range starts on the
266
            // same row and intersects the old column range, then don't reset
267
            // bHasHeader but assume that the new range still has headers, just
268
            // some are empty or numeric.
269
0
            if (!bHasHeader && pNoNameData->HasHeader() && nTab == nOldTab && nStartRow == nOldY1 &&
270
0
                    nStartCol <= nOldY2 && nOldY1 <= nEndCol)
271
0
                bHasHeader = true;
272
273
            // Remove AutoFilter button flags only for sheet-local DB range,
274
            // not if a temporary is used.
275
0
            if (bSheetLocal)
276
0
                DBAreaDeleted( nOldTab, nOldX1, nOldY1, nOldX2 );
277
278
0
            pNoNameData->SetSortParam( ScSortParam() );             // reset parameter
279
0
            pNoNameData->SetQueryParam( ScQueryParam() );
280
0
            pNoNameData->SetSubTotalParam( ScSubTotalParam() );
281
282
0
            pNoNameData->SetArea( nTab, nStartCol,nStartRow, nEndCol,nEndRow );     // set anew
283
0
            pNoNameData->SetByRow( true );
284
0
            pNoNameData->SetHeader( bHasHeader );
285
0
            pNoNameData->SetAutoFilter( false );
286
0
        }
287
0
        else
288
0
        {
289
0
            std::unique_ptr<ScDBCollection> pUndoColl;
290
291
0
            if (eMode==SC_DB_IMPORT)
292
0
            {
293
0
                m_pDocument->PreprocessDBDataUpdate();
294
0
                pUndoColl.reset( new ScDBCollection( *pColl ) );   // Undo for import range
295
296
0
                OUString aImport = ScResId( STR_DBNAME_IMPORT );
297
0
                tools::Long nCount = 0;
298
0
                const ScDBData* pDummy = nullptr;
299
0
                ScDBCollection::NamedDBs& rDBs = pColl->getNamedDBs();
300
0
                OUString aNewName;
301
0
                do
302
0
                {
303
0
                    ++nCount;
304
0
                    aNewName = aImport + OUString::number( nCount );
305
0
                    pDummy = rDBs.findByUpperName(ScGlobal::getCharClass().uppercase(aNewName));
306
0
                }
307
0
                while (pDummy);
308
0
                pNoNameData = new ScDBData( aNewName, nTab,
309
0
                                nStartCol,nStartRow, nEndCol,nEndRow,
310
0
                                true, bHasHeader );
311
0
                bool ins = rDBs.insert(std::unique_ptr<ScDBData>(pNoNameData));
312
0
                assert(ins); (void)ins;
313
0
            }
314
0
            else
315
0
            {
316
0
                pNoNameData = new ScDBData(STR_DB_LOCAL_NONAME, nTab,
317
0
                                nStartCol,nStartRow, nEndCol,nEndRow,
318
0
                                true, bHasHeader );
319
0
                m_pDocument->SetAnonymousDBData(nTab, std::unique_ptr<ScDBData>(pNoNameData));
320
0
            }
321
322
0
            if ( pUndoColl )
323
0
            {
324
0
                m_pDocument->CompileHybridFormula();
325
326
0
                GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDBData>( *this, u""_ustr,
327
0
                        std::move(pUndoColl), pNoNameData ? pNoNameData->GetName() : u""_ustr,
328
0
                        std::make_unique<ScDBCollection>( *pColl ) ) );
329
0
            }
330
331
            //  no longer needed to register new range at the Sba
332
333
            //  announce "Import1", etc., at the Navigator
334
0
            if (eMode==SC_DB_IMPORT)
335
0
                SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
336
0
        }
337
0
        pData = pNoNameData;
338
0
    }
339
340
0
    return pData;
341
0
}
342
343
ScDBData* ScDocShell::GetAnonymousDBData(const ScRange& rRange)
344
0
{
345
0
    ScDBCollection* pColl = m_pDocument->GetDBCollection();
346
0
    if (!pColl)
347
0
        return nullptr;
348
349
0
    ScDBData* pData = pColl->getAnonDBs().getByRange(rRange);
350
0
    if (!pData)
351
0
        return nullptr;
352
353
0
    if (!pData->HasHeader())
354
0
    {
355
0
        bool bHasHeader = m_pDocument->HasColHeader(
356
0
                rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aStart.Tab());
357
0
        pData->SetHeader(bHasHeader);
358
0
    }
359
360
0
    return pData;
361
0
}
362
363
std::unique_ptr<ScDBData> ScDocShell::GetOldAutoDBRange()
364
0
{
365
0
    return std::move(m_pOldAutoDBRange);
366
0
}
367
368
void ScDocShell::CancelAutoDBRange()
369
0
{
370
    // called when dialog is cancelled
371
//moggi:TODO
372
0
    if ( !m_pOldAutoDBRange )
373
0
        return;
374
375
0
    SCTAB nTab = GetCurTab();
376
0
    ScDBData* pDBData = m_pDocument->GetAnonymousDBData(nTab);
377
0
    if ( pDBData )
378
0
    {
379
0
        SCCOL nRangeX1;
380
0
        SCROW nRangeY1;
381
0
        SCCOL nRangeX2;
382
0
        SCROW nRangeY2;
383
0
        SCTAB nRangeTab;
384
0
        pDBData->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
385
0
        DBAreaDeleted( nRangeTab, nRangeX1, nRangeY1, nRangeX2 );
386
387
0
        *pDBData = *m_pOldAutoDBRange;    // restore old settings
388
389
0
        if ( m_pOldAutoDBRange->HasAutoFilter() )
390
0
        {
391
            // restore AutoFilter buttons
392
0
            m_pOldAutoDBRange->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
393
0
            m_pDocument->ApplyFlagsTab( nRangeX1, nRangeY1, nRangeX2, nRangeY1, nRangeTab, ScMF::Auto );
394
0
            PostPaint( nRangeX1, nRangeY1, nRangeTab, nRangeX2, nRangeY1, nRangeTab, PaintPartFlags::Grid );
395
0
        }
396
0
    }
397
398
0
    m_pOldAutoDBRange.reset();
399
0
}
400
401
        //  adjust height
402
        //! merge with docfunc
403
404
bool ScDocShell::AdjustRowHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab )
405
0
{
406
0
    ScSizeDeviceProvider aProv(*this);
407
0
    double fZoom(1.0);
408
0
    sc::RowHeightContext aCxt(m_pDocument->MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), fZoom, fZoom, aProv.GetDevice());
409
0
    bool bChange = m_pDocument->SetOptimalHeight(aCxt, nStartRow,nEndRow, nTab, true);
410
411
0
    if (bChange)
412
0
    {
413
        // tdf#76183: recalculate objects' positions
414
0
        m_pDocument->SetDrawPageSize(nTab);
415
416
0
        PostPaint( 0,nStartRow,nTab, m_pDocument->MaxCol(),m_pDocument->MaxRow(),nTab, PaintPartFlags::Grid|PaintPartFlags::Left );
417
0
    }
418
419
0
    return bChange;
420
0
}
421
422
void ScDocShell::UpdateAllRowHeights( const ScMarkData* pTabMark )
423
0
{
424
    // update automatic row heights
425
426
0
    ScSizeDeviceProvider aProv(*this);
427
0
    double fZoom(1.0);
428
0
    sc::RowHeightContext aCxt(m_pDocument->MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), fZoom, fZoom, aProv.GetDevice());
429
0
    m_pDocument->UpdateAllRowHeights(aCxt, pTabMark);
430
0
}
431
432
void ScDocShell::UpdateAllRowHeights(const bool bOnlyUsedRows)
433
4.11k
{
434
    // update automatic row heights on all sheets using the newer ScDocRowHeightUpdater
435
4.11k
    ScSizeDeviceProvider aProv(*this);
436
4.11k
    ScDocRowHeightUpdater aUpdater(*m_pDocument, aProv.GetDevice(), aProv.GetPPTX(),
437
4.11k
                                   aProv.GetPPTY(), nullptr);
438
4.11k
    aUpdater.update(bOnlyUsedRows);
439
4.11k
}
440
441
void ScDocShell::UpdatePendingRowHeights( SCTAB nUpdateTab, bool bBefore )
442
30.0k
{
443
30.0k
    bool bIsUndoEnabled = m_pDocument->IsUndoEnabled();
444
30.0k
    m_pDocument->EnableUndo( false );
445
30.0k
    m_pDocument->LockStreamValid( true );      // ignore draw page size (but not formula results)
446
30.0k
    if ( bBefore )          // check all sheets up to nUpdateTab
447
0
    {
448
0
        SCTAB nTabCount = m_pDocument->GetTableCount();
449
0
        if ( nUpdateTab >= nTabCount )
450
0
            nUpdateTab = nTabCount-1;     // nUpdateTab is inclusive
451
452
0
        ScMarkData aUpdateSheets(m_pDocument->GetSheetLimits());
453
0
        SCTAB nTab;
454
0
        for (nTab=0; nTab<=nUpdateTab; ++nTab)
455
0
            if ( m_pDocument->IsPendingRowHeights( nTab ) )
456
0
                aUpdateSheets.SelectTable( nTab, true );
457
458
0
        if (aUpdateSheets.GetSelectCount())
459
0
            UpdateAllRowHeights(&aUpdateSheets);        // update with a single progress bar
460
461
0
        for (nTab=0; nTab<=nUpdateTab; ++nTab)
462
0
            if ( aUpdateSheets.GetTableSelect( nTab ) )
463
0
            {
464
0
                m_pDocument->UpdatePageBreaks( nTab );
465
0
                m_pDocument->SetPendingRowHeights( nTab, false );
466
0
            }
467
0
    }
468
30.0k
    else                    // only nUpdateTab
469
30.0k
    {
470
30.0k
        if ( m_pDocument->IsPendingRowHeights( nUpdateTab ) )
471
0
        {
472
0
            AdjustRowHeight( 0, m_pDocument->MaxRow(), nUpdateTab );
473
0
            m_pDocument->UpdatePageBreaks( nUpdateTab );
474
0
            m_pDocument->SetPendingRowHeights( nUpdateTab, false );
475
0
        }
476
30.0k
    }
477
30.0k
    m_pDocument->LockStreamValid( false );
478
30.0k
    m_pDocument->EnableUndo( bIsUndoEnabled );
479
30.0k
}
480
481
void ScDocShell::RefreshPivotTables( const ScRange& rSource )
482
0
{
483
0
    ScDPCollection* pColl = m_pDocument->GetDPCollection();
484
0
    if (!pColl)
485
0
        return;
486
487
0
    ScDBDocFunc aFunc(*this);
488
0
    for (size_t i = 0, n = pColl->GetCount(); i < n; ++i)
489
0
    {
490
0
        ScDPObject& rOld = (*pColl)[i];
491
492
0
        const ScSheetSourceDesc* pSheetDesc = rOld.GetSheetDesc();
493
0
        if (pSheetDesc && pSheetDesc->GetSourceRange().Intersects(rSource))
494
0
            aFunc.UpdatePivotTable(rOld, true, false);
495
0
    }
496
0
}
497
498
// Re-check outputs that were previously blocked by #SPILL! (array formula
499
// cells and pivot tables). Called after cell-change operations that might
500
// have freed blocking cells.
501
void ScDocShell::ResolveSpilledOutputs()
502
375
{
503
375
    bool bAnyResolved = false;
504
505
    // Array formula cells currently in #SPILL! state: re-interpretation will
506
    // clear the error if the blocker is gone and then expand to result dimensions.
507
375
    const std::unordered_set<ScAddress> aSpilledCells = m_pDocument->GetSpilledFormulaCells();
508
375
    for (const ScAddress& rPosition : aSpilledCells)
509
0
    {
510
0
        ScFormulaCell* pFormulaCell = m_pDocument->GetFormulaCell(rPosition);
511
0
        if (pFormulaCell && pFormulaCell->GetErrCode() == FormulaError::Spill)
512
0
        {
513
0
            pFormulaCell->SetDirty();
514
0
            pFormulaCell->Interpret();
515
0
            if (pFormulaCell->GetErrCode() != FormulaError::Spill)
516
0
            {
517
0
                bAnyResolved = true;
518
                // Expand the matrix to the result dimensions.
519
0
                const ScMatrix* pMatrix = pFormulaCell->GetMatrix();
520
0
                if (pMatrix)
521
0
                {
522
0
                    SCSIZE nResCols = 0;
523
0
                    SCSIZE nResRows = 0;
524
0
                    pMatrix->GetDimensions(nResCols, nResRows);
525
0
                    m_pDocument->ResizeMatrixFormula(rPosition,
526
0
                                                     SCCOL(nResCols),
527
0
                                                     SCROW(nResRows));
528
0
                }
529
0
            }
530
0
        }
531
0
    }
532
533
    // Inverse direction: previously expanded dynamic array masters whose
534
    // declared range now contains a foreign cell. The matrix master doesn't
535
    // listen to its declared range, so we explicitly check. When a blocker
536
    // is found, collapse the matrix back to 1x1 (preserving the blocker) and
537
    // re-interpret.
538
375
    const std::unordered_set<ScAddress> aExpandedMatrices = m_pDocument->GetExpandedDynamicArrays();
539
375
    for (const ScAddress& rPosition : aExpandedMatrices)
540
0
    {
541
0
        ScFormulaCell* pFormulaCell = m_pDocument->GetFormulaCell(rPosition);
542
0
        if (!pFormulaCell || pFormulaCell->GetMatrixFlag() != ScMatrixMode::Formula)
543
0
        {
544
0
            m_pDocument->UnmarkExpandedDynamicArray(rPosition);
545
0
            continue;
546
0
        }
547
0
        SCCOL nDeclCols = 0;
548
0
        SCROW nDeclRows = 0;
549
0
        pFormulaCell->GetMatColsRows(nDeclCols, nDeclRows);
550
0
        if (nDeclCols <= 1 && nDeclRows <= 1)
551
0
        {
552
0
            m_pDocument->UnmarkExpandedDynamicArray(rPosition);
553
0
            continue;
554
0
        }
555
0
        ScRange aRange(rPosition.Col(), rPosition.Row(), rPosition.Tab(),
556
0
                       rPosition.Col() + nDeclCols - 1, rPosition.Row() + nDeclRows - 1,
557
0
                       rPosition.Tab());
558
0
        if (!m_pDocument->HasMatrixBlocker(aRange))
559
0
            continue;
560
561
        // Collapse: drop our own reference cells from the declared range.
562
        // Then SetMatColsRows on the master and re-interpret - the runtime
563
        // spill check now sees res > declared and sets #SPILL!.
564
0
        for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol)
565
0
        {
566
0
            for (SCROW nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow)
567
0
            {
568
0
                if (nCol == rPosition.Col() && nRow == rPosition.Row())
569
0
                    continue;
570
0
                ScRefCellValue aCell(*m_pDocument, ScAddress(nCol, nRow, rPosition.Tab()));
571
0
                if (aCell.getType() == CELLTYPE_FORMULA
572
0
                    && aCell.getFormula()->GetMatrixFlag() == ScMatrixMode::Reference)
573
0
                {
574
0
                    m_pDocument->DeleteAreaTab(nCol, nRow, nCol, nRow, rPosition.Tab(),
575
0
                                               InsertDeleteFlags::ALL);
576
0
                }
577
0
            }
578
0
        }
579
0
        pFormulaCell->SetMatColsRows(1, 1);
580
0
        m_pDocument->UnmarkExpandedDynamicArray(rPosition);
581
        // Conventional matrix formulas don't normally opt into the runtime
582
        // spill check. Pre-marking the cell flips that bit so the next
583
        // interpret recognises this as a spill-blocked master.
584
0
        m_pDocument->MarkFormulaSpilled(rPosition);
585
0
        pFormulaCell->SetDirty();
586
0
        pFormulaCell->Interpret();
587
0
        bAnyResolved = true;
588
0
    }
589
590
    // Re-check spilled pivot tables.
591
375
    ScDPCollection* pCollection = m_pDocument->GetDPCollection();
592
375
    if (pCollection)
593
375
    {
594
375
        bool bUndoEnabled = m_pDocument->IsUndoEnabled();
595
375
        bool bEnteredList = false;
596
375
        size_t nCount = pCollection->GetCount();
597
479
        for (size_t i = 0; i < nCount; ++i)
598
104
        {
599
104
            ScDPObject& rDPObject = (*pCollection)[i];
600
104
            if (!rDPObject.HasSpillError())
601
104
                continue;
602
603
            // Try to re-output with spill check. If the blocking cells have been
604
            // cleared, Output() will succeed and the pivot table will be rendered.
605
0
            ScAddress aPosition = rDPObject.GetOutRange().aStart;
606
0
            rDPObject.InvalidateData();
607
0
            if (rDPObject.Output(aPosition, true))
608
0
            {
609
0
                bAnyResolved = true;
610
611
0
                if (bUndoEnabled)
612
0
                {
613
0
                    if (!bEnteredList)
614
0
                    {
615
0
                        GetUndoManager()->EnterListAction(u""_ustr, u""_ustr, 0, ViewShellId(-1));
616
0
                        bEnteredList = true;
617
0
                    }
618
0
                    GetUndoManager()->AddUndoAction(
619
0
                        std::make_unique<sc::UndoSpillPivotTable>(
620
0
                            *this, rDPObject.GetName(), rDPObject.GetOutRange()));
621
0
                }
622
0
            }
623
0
        }
624
625
375
        if (bEnteredList)
626
0
            GetUndoManager()->LeaveAndMergeListAction();
627
375
    }
628
629
375
    if (bAnyResolved)
630
0
        PostPaintGridAll();
631
375
}
632
633
static OUString lcl_GetAreaName( ScDocument* pDoc, const ScArea* pArea )
634
0
{
635
0
    ScDBData* pData = pDoc->GetDBAtArea( pArea->nTab, pArea->nColStart, pArea->nRowStart,
636
0
                                                        pArea->nColEnd, pArea->nRowEnd );
637
0
    if (pData)
638
0
        return pData->GetName();
639
640
0
    OUString aName;
641
0
    pDoc->GetName( pArea->nTab, aName );
642
0
    return aName;
643
0
}
644
645
static ScDBData* getUndoData(const ScDBData* pDestData)
646
0
{
647
0
    if (!pDestData)
648
0
        return nullptr;
649
0
    return new ScDBData(*pDestData);
650
0
}
651
652
void ScDocShell::DoConsolidate( const ScConsolidateParam& rParam, bool bRecord )
653
0
{
654
0
    ScConsData aData;
655
656
0
    sal_uInt16 nPos;
657
0
    SCCOL nColSize = 0;
658
0
    SCROW nRowSize = 0;
659
0
    bool bErr = false;
660
0
    for (nPos=0; nPos<rParam.nDataAreaCount; nPos++)
661
0
    {
662
0
        ScArea const & rArea = rParam.pDataAreas[nPos];
663
0
        nColSize = std::max( nColSize, SCCOL( rArea.nColEnd - rArea.nColStart + 1 ) );
664
0
        nRowSize = std::max( nRowSize, SCROW( rArea.nRowEnd - rArea.nRowStart + 1 ) );
665
666
                                        // test if source data were moved
667
0
        if (rParam.bReferenceData)
668
0
            if (rArea.nTab == rParam.nTab && rArea.nRowEnd >= rParam.nRow)
669
0
                bErr = true;
670
0
    }
671
672
0
    if (bErr)
673
0
    {
674
0
        std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetActiveDialogParent(),
675
0
                                                      VclMessageType::Info, VclButtonsType::Ok,
676
0
                                                      ScResId(STR_CONSOLIDATE_ERR1)));
677
0
        xInfoBox->run();
678
0
        return;
679
0
    }
680
681
    //      execute
682
683
0
    weld::WaitObject aWait( GetActiveDialogParent() );
684
0
    ScDocShellModificator aModificator( *this );
685
686
0
    ScRange aOldDest;
687
0
    ScDBData* pDestData = m_pDocument->GetDBAtCursor( rParam.nCol, rParam.nRow, rParam.nTab, ScDBDataPortion::TOP_LEFT );
688
0
    if (pDestData)
689
0
        pDestData->GetArea(aOldDest);
690
691
0
    aData.SetSize( nColSize, nRowSize );
692
0
    aData.SetFlags( rParam.eFunction, rParam.bByCol, rParam.bByRow, rParam.bReferenceData );
693
0
    if ( rParam.bByCol || rParam.bByRow )
694
0
        for (nPos=0; nPos<rParam.nDataAreaCount; nPos++)
695
0
        {
696
0
            ScArea const & rArea = rParam.pDataAreas[nPos];
697
0
            aData.AddFields( m_pDocument.get(), rArea.nTab, rArea.nColStart, rArea.nRowStart,
698
0
                                                       rArea.nColEnd, rArea.nRowEnd );
699
0
        }
700
0
    aData.DoneFields();
701
0
    for (nPos=0; nPos<rParam.nDataAreaCount; nPos++)
702
0
    {
703
0
        ScArea const & rArea = rParam.pDataAreas[nPos];
704
0
        aData.AddData( m_pDocument.get(), rArea.nTab, rArea.nColStart, rArea.nRowStart,
705
0
                                                 rArea.nColEnd, rArea.nRowEnd );
706
0
        aData.AddName( lcl_GetAreaName(m_pDocument.get(), &rArea) );
707
0
    }
708
709
0
    aData.GetSize( nColSize, nRowSize );
710
0
    if (bRecord && nColSize > 0 && nRowSize > 0)
711
0
    {
712
0
        std::unique_ptr<ScDBData> pUndoData(getUndoData(pDestData));
713
714
0
        SCTAB nDestTab = rParam.nTab;
715
0
        ScArea aDestArea( rParam.nTab, rParam.nCol, rParam.nRow,
716
0
                            rParam.nCol+nColSize-1, rParam.nRow+nRowSize-1 );
717
0
        if (rParam.bByCol) ++aDestArea.nColEnd;
718
0
        if (rParam.bByRow) ++aDestArea.nRowEnd;
719
720
0
        if (rParam.bReferenceData)
721
0
        {
722
0
            SCTAB nTabCount = m_pDocument->GetTableCount();
723
0
            SCROW nInsertCount = aData.GetInsertCount();
724
725
            // old outlines
726
0
            ScOutlineTable* pTable = m_pDocument->GetOutlineTable( nDestTab );
727
0
            std::unique_ptr<ScOutlineTable> pUndoTab(pTable ? new ScOutlineTable( *pTable ) : nullptr);
728
729
0
            ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
730
0
            pUndoDoc->InitUndo( *m_pDocument, 0, nTabCount-1, false, true );
731
732
            // row state
733
0
            m_pDocument->CopyToDocument(0, 0, nDestTab, m_pDocument->MaxCol(), m_pDocument->MaxRow(), nDestTab,
734
0
                                     InsertDeleteFlags::NONE, false, *pUndoDoc);
735
736
            // all formulas
737
0
            m_pDocument->CopyToDocument(0, 0, 0, m_pDocument->MaxCol(), m_pDocument->MaxRow(), nTabCount-1,
738
0
                                     InsertDeleteFlags::FORMULA, false, *pUndoDoc);
739
740
            // complete output rows
741
0
            m_pDocument->CopyToDocument(0, aDestArea.nRowStart, nDestTab,
742
0
                                     m_pDocument->MaxCol(),aDestArea.nRowEnd, nDestTab,
743
0
                                     InsertDeleteFlags::ALL, false, *pUndoDoc);
744
745
            // old output range
746
0
            if (pDestData)
747
0
                m_pDocument->CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, *pUndoDoc);
748
749
0
            GetUndoManager()->AddUndoAction(
750
0
                    std::make_unique<ScUndoConsolidate>( *this, aDestArea, rParam, std::move(pUndoDoc),
751
0
                                            true, nInsertCount, std::move(pUndoTab), std::move(pUndoData) ) );
752
0
        }
753
0
        else
754
0
        {
755
0
            ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
756
0
            pUndoDoc->InitUndo( *m_pDocument, aDestArea.nTab, aDestArea.nTab );
757
758
0
            m_pDocument->CopyToDocument(aDestArea.nColStart, aDestArea.nRowStart, aDestArea.nTab,
759
0
                                     aDestArea.nColEnd, aDestArea.nRowEnd, aDestArea.nTab,
760
0
                                     InsertDeleteFlags::ALL, false, *pUndoDoc);
761
762
            // old output range
763
0
            if (pDestData)
764
0
                m_pDocument->CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, *pUndoDoc);
765
766
0
            GetUndoManager()->AddUndoAction(
767
0
                    std::make_unique<ScUndoConsolidate>( *this, aDestArea, rParam, std::move(pUndoDoc),
768
0
                                            false, 0, nullptr, std::move(pUndoData) ) );
769
0
        }
770
0
    }
771
772
0
    if (pDestData)                                      // delete / adjust destination range
773
0
    {
774
0
        m_pDocument->DeleteAreaTab(aOldDest, InsertDeleteFlags::CONTENTS);
775
0
        pDestData->SetArea( rParam.nTab, rParam.nCol, rParam.nRow,
776
0
                            rParam.nCol + nColSize - 1, rParam.nRow + nRowSize - 1 );
777
0
        pDestData->SetHeader( rParam.bByRow );
778
0
    }
779
780
0
    aData.OutputToDocument( *m_pDocument, rParam.nCol, rParam.nRow, rParam.nTab );
781
782
0
    SCCOL nPaintStartCol = rParam.nCol;
783
0
    SCROW nPaintStartRow = rParam.nRow;
784
0
    SCCOL nPaintEndCol = nPaintStartCol + nColSize - 1;
785
0
    SCROW nPaintEndRow = nPaintStartRow + nRowSize - 1;
786
0
    PaintPartFlags nPaintFlags = PaintPartFlags::Grid;
787
0
    if (rParam.bByCol)
788
0
        ++nPaintEndRow;
789
0
    if (rParam.bByRow)
790
0
        ++nPaintEndCol;
791
0
    if (rParam.bReferenceData)
792
0
    {
793
0
        nPaintStartCol = 0;
794
0
        nPaintEndCol = m_pDocument->MaxCol();
795
0
        nPaintEndRow = m_pDocument->MaxRow();
796
0
        nPaintFlags |= PaintPartFlags::Left | PaintPartFlags::Size;
797
0
    }
798
0
    if (pDestData)
799
0
    {
800
0
        if ( aOldDest.aEnd.Col() > nPaintEndCol )
801
0
            nPaintEndCol = aOldDest.aEnd.Col();
802
0
        if ( aOldDest.aEnd.Row() > nPaintEndRow )
803
0
            nPaintEndRow = aOldDest.aEnd.Row();
804
0
    }
805
0
    PostPaint( nPaintStartCol, nPaintStartRow, rParam.nTab,
806
0
                nPaintEndCol, nPaintEndRow, rParam.nTab, nPaintFlags );
807
0
    aModificator.SetDocumentModified();
808
0
}
809
810
void ScDocShell::UseScenario( SCTAB nTab, const OUString& rName, bool bRecord )
811
0
{
812
0
    if (!m_pDocument->IsScenario(nTab))
813
0
    {
814
0
        SCTAB   nTabCount = m_pDocument->GetTableCount();
815
0
        SCTAB   nSrcTab = SCTAB_MAX;
816
0
        SCTAB   nEndTab = nTab;
817
0
        OUString aCompare;
818
0
        while ( nEndTab+1 < nTabCount && m_pDocument->IsScenario(nEndTab+1) )
819
0
        {
820
0
            ++nEndTab;
821
0
            if (nSrcTab > MAXTAB)           // still searching for the scenario?
822
0
            {
823
0
                m_pDocument->GetName( nEndTab, aCompare );
824
0
                if (aCompare == rName)
825
0
                    nSrcTab = nEndTab;      // found
826
0
            }
827
0
        }
828
0
        if (ValidTab(nSrcTab))
829
0
        {
830
0
            if ( m_pDocument->TestCopyScenario( nSrcTab, nTab ) )          // test cell protection
831
0
            {
832
0
                ScDocShellModificator aModificator( *this );
833
0
                ScMarkData aScenMark(m_pDocument->GetSheetLimits());
834
0
                m_pDocument->MarkScenario( nSrcTab, nTab, aScenMark );
835
0
                const ScRange& aMultiRange = aScenMark.GetMultiMarkArea();
836
0
                SCCOL nStartCol = aMultiRange.aStart.Col();
837
0
                SCROW nStartRow = aMultiRange.aStart.Row();
838
0
                SCCOL nEndCol = aMultiRange.aEnd.Col();
839
0
                SCROW nEndRow = aMultiRange.aEnd.Row();
840
841
0
                if (bRecord)
842
0
                {
843
0
                    ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
844
0
                    pUndoDoc->InitUndo( *m_pDocument, nTab,nEndTab );             // also all scenarios
845
                    //  shown table:
846
0
                    m_pDocument->CopyToDocument(nStartCol, nStartRow, nTab,
847
0
                                             nEndCol, nEndRow, nTab, InsertDeleteFlags::ALL,
848
0
                                             true, *pUndoDoc, &aScenMark);
849
                    //  scenarios
850
0
                    for (SCTAB i=nTab+1; i<=nEndTab; i++)
851
0
                    {
852
0
                        pUndoDoc->SetScenario( i, true );
853
0
                        OUString aComment;
854
0
                        Color  aColor;
855
0
                        ScScenarioFlags nScenFlags;
856
0
                        m_pDocument->GetScenarioData( i, aComment, aColor, nScenFlags );
857
0
                        pUndoDoc->SetScenarioData( i, aComment, aColor, nScenFlags );
858
0
                        bool bActive = m_pDocument->IsActiveScenario( i );
859
0
                        pUndoDoc->SetActiveScenario( i, bActive );
860
                        //  At copy-back scenarios also contents
861
0
                        if ( nScenFlags & ScScenarioFlags::TwoWay )
862
0
                            m_pDocument->CopyToDocument(0, 0, i, m_pDocument->MaxCol(), m_pDocument->MaxRow(), i,
863
0
                                                     InsertDeleteFlags::ALL, false, *pUndoDoc );
864
0
                    }
865
866
0
                    GetUndoManager()->AddUndoAction(
867
0
                        std::make_unique<ScUndoUseScenario>( *this, aScenMark,
868
0
                                        ScArea( nTab,nStartCol,nStartRow,nEndCol,nEndRow ),
869
0
                                        std::move(pUndoDoc), rName ) );
870
0
                }
871
872
0
                m_pDocument->CopyScenario( nSrcTab, nTab );
873
874
0
                sc::SetFormulaDirtyContext aCxt;
875
0
                m_pDocument->SetAllFormulasDirty(aCxt);
876
877
                //  paint all, because the active scenario may be modified in other ranges;
878
                //! only if there are visible frames?
879
0
                PostPaint( 0,0,nTab, m_pDocument->MaxCol(),m_pDocument->MaxRow(),nTab, PaintPartFlags::Grid );
880
0
                aModificator.SetDocumentModified();
881
0
            }
882
0
            else
883
0
            {
884
0
                std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetActiveDialogParent(),
885
0
                                                              VclMessageType::Info, VclButtonsType::Ok,
886
0
                                                              ScResId(STR_PROTECTIONERR)));
887
0
                xInfoBox->run();
888
0
            }
889
0
        }
890
0
        else
891
0
        {
892
0
            std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetActiveDialogParent(),
893
0
                                                          VclMessageType::Info, VclButtonsType::Ok,
894
0
                                                          ScResId(STR_SCENARIO_NOTFOUND)));
895
0
            xInfoBox->run();
896
0
        }
897
0
    }
898
0
    else
899
0
    {
900
0
        OSL_FAIL( "UseScenario on Scenario-Sheet" );
901
0
    }
902
0
}
903
904
void ScDocShell::ModifyScenario( SCTAB nTab, const OUString& rName, const OUString& rComment,
905
                                    const Color& rColor, ScScenarioFlags nFlags )
906
88
{
907
    //  Undo
908
88
    OUString aOldName;
909
88
    m_pDocument->GetName( nTab, aOldName );
910
88
    OUString aOldComment;
911
88
    Color aOldColor;
912
88
    ScScenarioFlags nOldFlags;
913
88
    m_pDocument->GetScenarioData( nTab, aOldComment, aOldColor, nOldFlags );
914
88
    GetUndoManager()->AddUndoAction(
915
88
        std::make_unique<ScUndoScenarioFlags>(*this, nTab,
916
88
                aOldName, rName, aOldComment, rComment,
917
88
                aOldColor, rColor, nOldFlags, nFlags) );
918
919
    //  execute
920
88
    ScDocShellModificator aModificator( *this );
921
88
    m_pDocument->RenameTab( nTab, rName );
922
88
    m_pDocument->SetScenarioData( nTab, rComment, rColor, nFlags );
923
88
    PostPaintGridAll();
924
88
    aModificator.SetDocumentModified();
925
926
88
    if (aOldName != rName)
927
0
        SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
928
929
88
    SfxBindings* pBindings = GetViewBindings();
930
88
    if (pBindings)
931
0
        pBindings->Invalidate( SID_SELECT_SCENARIO );
932
88
}
933
934
SCTAB ScDocShell::MakeScenario( SCTAB nTab, const OUString& rName, const OUString& rComment,
935
                                    const Color& rColor, ScScenarioFlags nFlags,
936
                                    ScMarkData& rMark, bool bRecord )
937
22
{
938
22
    rMark.MarkToMulti();
939
22
    if (rMark.IsMultiMarked())
940
22
    {
941
22
        SCTAB nNewTab = nTab + 1;
942
33
        while (m_pDocument->IsScenario(nNewTab))
943
11
            ++nNewTab;
944
945
22
        bool bCopyAll = ( (nFlags & ScScenarioFlags::CopyAll) != ScScenarioFlags::NONE );
946
22
        const ScMarkData* pCopyMark = nullptr;
947
22
        if (!bCopyAll)
948
22
            pCopyMark = &rMark;
949
950
22
        ScDocShellModificator aModificator( *this );
951
952
22
        if (bRecord)
953
22
            m_pDocument->BeginDrawUndo();      // drawing layer must do its own undo actions
954
955
22
        if (m_pDocument->CopyTab( nTab, nNewTab, pCopyMark ))
956
22
        {
957
22
            if (bRecord)
958
22
            {
959
22
                GetUndoManager()->AddUndoAction(
960
22
                        std::make_unique<ScUndoMakeScenario>( *this, nTab, nNewTab,
961
22
                                                rName, rComment, rColor, nFlags, rMark ));
962
22
            }
963
964
22
            m_pDocument->RenameTab( nNewTab, rName);
965
22
            m_pDocument->SetScenario( nNewTab, true );
966
22
            m_pDocument->SetScenarioData( nNewTab, rComment, rColor, nFlags );
967
968
22
            ScMarkData aDestMark = rMark;
969
22
            aDestMark.SelectOneTable( nNewTab );
970
971
            //!     test for filter / buttons / merging
972
973
22
            ScPatternAttr aProtPattern(m_pDocument->getCellAttributeHelper());
974
22
            aProtPattern.ItemSetPut(ScProtectionAttr(true));
975
22
            m_pDocument->ApplyPatternAreaTab( 0,0, m_pDocument->MaxCol(),m_pDocument->MaxRow(), nNewTab, aProtPattern );
976
977
22
            ScPatternAttr aPattern(m_pDocument->getCellAttributeHelper());
978
22
            aPattern.ItemSetPut(ScMergeFlagAttr(ScMF::Scenario));
979
22
            aPattern.ItemSetPut(ScProtectionAttr(true));
980
22
            m_pDocument->ApplySelectionPattern( aPattern, aDestMark );
981
982
22
            if (!bCopyAll)
983
22
                m_pDocument->SetVisible( nNewTab, false );
984
985
            //  this is the active scenario, then
986
22
            m_pDocument->CopyScenario( nNewTab, nTab, true );  // sal_True - don't copy anything from scenario
987
988
22
            if (nFlags & ScScenarioFlags::ShowFrame)
989
22
                PostPaint( 0,0,nTab, m_pDocument->MaxCol(),m_pDocument->MaxRow(),nTab, PaintPartFlags::Grid );  // paint frames
990
22
            PostPaintExtras();                                          // table tab
991
22
            aModificator.SetDocumentModified();
992
993
            // A scenario tab is like a hidden sheet, broadcasting also
994
            // notifies ScTabViewShell to add an ScViewData::maTabData entry.
995
22
            Broadcast( ScTablesHint( SC_TAB_INSERTED, nNewTab ));
996
22
            SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
997
998
22
            return nNewTab;
999
22
        }
1000
22
    }
1001
0
    return nTab;
1002
22
}
1003
1004
bool ScDocShell::TransferTab( ScDocShell& rSrcDocShell, SCTAB nSrcPos,
1005
                                SCTAB nDestPos, bool bInsertNew,
1006
                                bool bNotifyAndPaint )
1007
0
{
1008
0
    ScDocument& rSrcDoc = rSrcDocShell.GetDocument();
1009
1010
    // set the transferred area to the copyparam to make adjusting formulas possible
1011
0
    ScClipParam aParam;
1012
0
    ScRange aRange(0, 0, nSrcPos, m_pDocument->MaxCol(), m_pDocument->MaxRow(), nSrcPos);
1013
0
    aParam.maRanges.push_back(aRange);
1014
0
    rSrcDoc.SetClipParam(aParam);
1015
1016
0
    bool bValid =  m_pDocument->TransferTab( rSrcDoc, nSrcPos, nDestPos,
1017
0
                    bInsertNew );       // no insert
1018
1019
    // TransferTab doesn't copy drawing objects with bInsertNew=FALSE
1020
0
    if ( bValid && !bInsertNew)
1021
0
        m_pDocument->TransferDrawPage( rSrcDoc, nSrcPos, nDestPos );
1022
1023
0
    if(bValid && rSrcDoc.IsScenario( nSrcPos ))
1024
0
    {
1025
0
        OUString aComment;
1026
0
        Color  aColor;
1027
0
        ScScenarioFlags nFlags;
1028
1029
0
        rSrcDoc.GetScenarioData( nSrcPos, aComment,aColor, nFlags);
1030
0
        m_pDocument->SetScenario(nDestPos,true);
1031
0
        m_pDocument->SetScenarioData(nDestPos,aComment,aColor,nFlags);
1032
0
        bool bActive = rSrcDoc.IsActiveScenario(nSrcPos);
1033
0
        m_pDocument->SetActiveScenario(nDestPos, bActive );
1034
1035
0
        bool bVisible = rSrcDoc.IsVisible(nSrcPos);
1036
0
        m_pDocument->SetVisible(nDestPos,bVisible );
1037
1038
0
    }
1039
1040
0
    if ( bValid && rSrcDoc.IsTabProtected( nSrcPos ) )
1041
0
        m_pDocument->SetTabProtection(nDestPos, rSrcDoc.GetTabProtection(nSrcPos));
1042
0
    if ( bNotifyAndPaint )
1043
0
    {
1044
0
            Broadcast( ScTablesHint( SC_TAB_INSERTED, nDestPos ) );
1045
0
            PostPaintExtras();
1046
0
            PostPaintGridAll();
1047
0
    }
1048
0
    return bValid;
1049
0
}
1050
1051
bool ScDocShell::MoveTable( SCTAB nSrcTab, SCTAB nDestTab, bool bCopy, bool bRecord )
1052
0
{
1053
0
    ScDocShellModificator aModificator( *this );
1054
1055
    // #i92477# be consistent with ScDocFunc::InsertTable: any index past the last sheet means "append"
1056
    // #i101139# nDestTab must be the target position, not APPEND (for CopyTabProtection etc.)
1057
0
    if ( nDestTab >= m_pDocument->GetTableCount() )
1058
0
        nDestTab = m_pDocument->GetTableCount();
1059
1060
0
    if (bCopy)
1061
0
    {
1062
0
        if (bRecord)
1063
0
            m_pDocument->BeginDrawUndo();          // drawing layer must do its own undo actions
1064
1065
0
        OUString sSrcCodeName;
1066
0
        m_pDocument->GetCodeName( nSrcTab, sSrcCodeName );
1067
0
        if (!m_pDocument->CopyTab( nSrcTab, nDestTab ))
1068
0
        {
1069
            //! EndDrawUndo?
1070
0
            return false;
1071
0
        }
1072
0
        else
1073
0
        {
1074
0
            SCTAB nAdjSource = nSrcTab;
1075
0
            if ( nDestTab <= nSrcTab )
1076
0
                ++nAdjSource;               // new position of source table after CopyTab
1077
1078
0
            if ( m_pDocument->IsTabProtected( nAdjSource ) )
1079
0
                m_pDocument->CopyTabProtection(nAdjSource, nDestTab);
1080
1081
0
            if (bRecord)
1082
0
            {
1083
0
                std::unique_ptr<std::vector<SCTAB>> pSrcList(new std::vector<SCTAB>(1, nSrcTab));
1084
0
                std::unique_ptr<std::vector<SCTAB>> pDestList(new std::vector<SCTAB>(1, nDestTab));
1085
0
                GetUndoManager()->AddUndoAction(
1086
0
                        std::make_unique<ScUndoCopyTab>(*this, std::move(pSrcList), std::move(pDestList)));
1087
0
            }
1088
1089
0
            bool bVbaEnabled = m_pDocument->IsInVBAMode();
1090
0
            if ( bVbaEnabled )
1091
0
            {
1092
0
                OUString aLibName( u"Standard"_ustr );
1093
0
                Reference< XLibraryContainer > xLibContainer = GetBasicContainer();
1094
0
                Reference< XVBACompatibility > xVBACompat( xLibContainer, UNO_QUERY );
1095
1096
0
                if ( xVBACompat.is() )
1097
0
                {
1098
0
                    aLibName = xVBACompat->getProjectName();
1099
0
                }
1100
1101
0
                SCTAB nTabToUse = nDestTab;
1102
0
                if ( nDestTab == SC_TAB_APPEND )
1103
0
                    nTabToUse = m_pDocument->GetMaxTableNumber() - 1;
1104
0
                OUString sSource;
1105
0
                try
1106
0
                {
1107
0
                    Reference< XNameContainer > xLib;
1108
0
                    if( xLibContainer.is() )
1109
0
                    {
1110
0
                        css::uno::Any aLibAny = xLibContainer->getByName( aLibName );
1111
0
                        aLibAny >>= xLib;
1112
0
                    }
1113
0
                    if( xLib.is() )
1114
0
                    {
1115
0
                        xLib->getByName( sSrcCodeName ) >>= sSource;
1116
0
                    }
1117
0
                }
1118
0
                catch ( const css::uno::Exception& )
1119
0
                {
1120
0
                }
1121
0
                VBA_InsertModule( *m_pDocument, nTabToUse, sSource );
1122
0
            }
1123
0
        }
1124
0
        Broadcast( ScTablesHint( SC_TAB_COPIED, nSrcTab, nDestTab ) );
1125
0
    }
1126
0
    else
1127
0
    {
1128
0
        if ( m_pDocument->GetChangeTrack() )
1129
0
            return false;
1130
1131
0
        if ( nSrcTab == nDestTab )
1132
0
        {
1133
            //! allow only for api calls?
1134
0
            return true;    // nothing to do, but valid
1135
0
        }
1136
1137
0
        std::optional<ScProgress> pProgress(std::in_place, this, ScResId(STR_UNDO_MOVE_TAB),
1138
0
                                                m_pDocument->GetCodeCount(), true);
1139
0
        bool bDone = m_pDocument->MoveTab( nSrcTab, nDestTab, &*pProgress );
1140
0
        pProgress.reset();
1141
0
        if (!bDone)
1142
0
        {
1143
0
            return false;
1144
0
        }
1145
0
        else if (bRecord)
1146
0
        {
1147
0
            std::unique_ptr<std::vector<SCTAB>> pSrcList(new std::vector<SCTAB>(1, nSrcTab));
1148
0
            std::unique_ptr<std::vector<SCTAB>> pDestList(new std::vector<SCTAB>(1, nDestTab));
1149
0
            GetUndoManager()->AddUndoAction(
1150
0
                    std::make_unique<ScUndoMoveTab>(*this, std::move(pSrcList), std::move(pDestList)));
1151
0
        }
1152
1153
0
        Broadcast( ScTablesHint( SC_TAB_MOVED, nSrcTab, nDestTab ) );
1154
0
    }
1155
1156
0
    PostPaintGridAll();
1157
0
    PostPaintExtras();
1158
0
    aModificator.SetDocumentModified();
1159
0
    SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
1160
1161
0
    return true;
1162
0
}
1163
1164
IMPL_LINK( ScDocShell, RefreshDBDataHdl, Timer*, pRefreshTimer, void )
1165
0
{
1166
0
    ScDBDocFunc aFunc(*this);
1167
1168
0
    ScDBData* pDBData = static_cast<ScDBData*>(pRefreshTimer);
1169
0
    ScImportParam aImportParam;
1170
0
    pDBData->GetImportParam( aImportParam );
1171
0
    if (aImportParam.bImport && !pDBData->HasImportSelection())
1172
0
    {
1173
0
        ScRange aRange;
1174
0
        pDBData->GetArea( aRange );
1175
0
        bool bContinue = aFunc.DoImport( aRange.aStart.Tab(), aImportParam, nullptr ); //! Api-Flag as parameter
1176
        // internal operations (sort, query, subtotal) only if no error
1177
0
        if (bContinue)
1178
0
        {
1179
0
            aFunc.RepeatDB( pDBData->GetName(), true, true );
1180
0
            RefreshPivotTables(aRange);
1181
0
        }
1182
0
    }
1183
0
}
1184
1185
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */