Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/view/dbfunc3.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 <dbfunc.hxx>
21
#include <scitems.hxx>
22
#include <vcl/svapp.hxx>
23
#include <vcl/weld.hxx>
24
#include <svl/numformat.hxx>
25
#include <svl/zforlist.hxx>
26
#include <sfx2/app.hxx>
27
#include <unotools/collatorwrapper.hxx>
28
#include <com/sun/star/beans/XPropertySet.hpp>
29
#include <com/sun/star/container/XNameAccess.hpp>
30
#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
31
#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
32
#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
33
#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
34
#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
35
#include <com/sun/star/sheet/MemberResultFlags.hpp>
36
#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
37
#include <com/sun/star/sheet/XDrillDownDataSupplier.hpp>
38
39
#include <global.hxx>
40
#include <scresid.hxx>
41
#include <globstr.hrc>
42
#include <undotab.hxx>
43
#include <undodat.hxx>
44
#include <dbdata.hxx>
45
#include <rangenam.hxx>
46
#include <docsh.hxx>
47
#include <olinetab.hxx>
48
#include <olinefun.hxx>
49
#include <dpobject.hxx>
50
#include <dpsave.hxx>
51
#include <dpdimsave.hxx>
52
#include <dbdocfun.hxx>
53
#include <dpoutput.hxx>
54
#include <editable.hxx>
55
#include <patattr.hxx>
56
#include <unonames.hxx>
57
#include <userlist.hxx>
58
#include <queryentry.hxx>
59
#include <markdata.hxx>
60
#include <tabvwsh.hxx>
61
#include <generalfunction.hxx>
62
#include <sortparam.hxx>
63
64
#include <comphelper/lok.hxx>
65
#include <osl/diagnose.h>
66
67
#include <memory>
68
#include <string_view>
69
#include <unordered_set>
70
#include <unordered_map>
71
#include <vector>
72
#include <algorithm>
73
74
using namespace com::sun::star;
75
using ::com::sun::star::uno::Any;
76
using ::com::sun::star::uno::Sequence;
77
using ::com::sun::star::uno::Reference;
78
using ::com::sun::star::uno::UNO_QUERY;
79
using ::com::sun::star::beans::XPropertySet;
80
using ::com::sun::star::container::XNameAccess;
81
using ::com::sun::star::sheet::XDimensionsSupplier;
82
83
//          outliner
84
85
// create outline grouping
86
87
void ScDBFunc::MakeOutline( bool bColumns, bool bRecord )
88
0
{
89
0
    ScRange aRange;
90
0
    if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
91
0
    {
92
0
        ScDocShell* pDocSh = GetViewData().GetDocShell();
93
0
        ScOutlineDocFunc aFunc(*pDocSh);
94
0
        aFunc.MakeOutline( aRange, bColumns, bRecord, false );
95
96
0
        ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), bColumns ? COLUMN_HEADER : ROW_HEADER, GetViewData().GetTabNumber());
97
0
        ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
98
0
                                                            bColumns, !bColumns, false /* bSizes*/,
99
0
                                                            false /* bHidden */, false /* bFiltered */,
100
0
                                                            true /* bGroups */, GetViewData().GetTabNumber());
101
0
    }
102
0
    else
103
0
        ErrorMessage(STR_NOMULTISELECT);
104
0
}
105
106
// delete outline grouping
107
108
void ScDBFunc::RemoveOutline( bool bColumns, bool bRecord )
109
0
{
110
0
    ScRange aRange;
111
0
    if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
112
0
    {
113
0
        ScDocShell* pDocSh = GetViewData().GetDocShell();
114
0
        ScOutlineDocFunc aFunc(*pDocSh);
115
0
        aFunc.RemoveOutline( aRange, bColumns, bRecord, false );
116
117
0
        ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), bColumns ? COLUMN_HEADER : ROW_HEADER, GetViewData().GetTabNumber());
118
0
        ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
119
0
                                                            bColumns, !bColumns, false /* bSizes*/,
120
0
                                                            true /* bHidden */, true /* bFiltered */,
121
0
                                                            true /* bGroups */, GetViewData().GetTabNumber());
122
0
    }
123
0
    else
124
0
        ErrorMessage(STR_NOMULTISELECT);
125
0
}
126
127
//  menu status: delete outlines
128
129
void ScDBFunc::TestRemoveOutline( bool& rCol, bool& rRow )
130
0
{
131
0
    bool bColFound = false;
132
0
    bool bRowFound = false;
133
134
0
    SCCOL nStartCol, nEndCol;
135
0
    SCROW nStartRow, nEndRow;
136
0
    SCTAB nStartTab, nEndTab;
137
0
    if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
138
0
    {
139
0
        SCTAB nTab = nStartTab;
140
0
        ScDocument& rDoc = GetViewData().GetDocument();
141
0
        ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
142
0
        if (pTable)
143
0
        {
144
0
            ScOutlineEntry* pEntry;
145
0
            SCCOLROW nStart;
146
0
            SCCOLROW nEnd;
147
0
            bool bColMarked = ( nStartRow == 0 && nEndRow == rDoc.MaxRow() );
148
0
            bool bRowMarked = ( nStartCol == 0 && nEndCol == rDoc.MaxCol() );
149
150
            // columns
151
152
0
            if ( !bRowMarked || bColMarked )        // not when entire rows are marked
153
0
            {
154
0
                ScOutlineArray& rArray = pTable->GetColArray();
155
0
                ScSubOutlineIterator aColIter( &rArray );
156
0
                while (!bColFound)
157
0
                {
158
0
                    pEntry=aColIter.GetNext();
159
0
                    if (!pEntry)
160
0
                        break;
161
0
                    nStart = pEntry->GetStart();
162
0
                    nEnd   = pEntry->GetEnd();
163
0
                    if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) )
164
0
                        bColFound = true;
165
0
                }
166
0
            }
167
168
            // rows
169
170
0
            if ( !bColMarked || bRowMarked )        // not when entire columns are marked
171
0
            {
172
0
                ScOutlineArray& rArray = pTable->GetRowArray();
173
0
                ScSubOutlineIterator aRowIter( &rArray );
174
0
                while (!bRowFound)
175
0
                {
176
0
                    pEntry=aRowIter.GetNext();
177
0
                    if (!pEntry)
178
0
                        break;
179
0
                    nStart = pEntry->GetStart();
180
0
                    nEnd   = pEntry->GetEnd();
181
0
                    if ( nStartRow<=nEnd && nEndRow>=nStart )
182
0
                        bRowFound = true;
183
0
                }
184
0
            }
185
0
        }
186
0
    }
187
188
0
    rCol = bColFound;
189
0
    rRow = bRowFound;
190
0
}
191
192
void ScDBFunc::RemoveAllOutlines( bool bRecord )
193
0
{
194
0
    SCTAB nTab = GetViewData().CurrentTabForData();
195
0
    ScDocShell* pDocSh = GetViewData().GetDocShell();
196
0
    ScOutlineDocFunc aFunc(*pDocSh);
197
198
0
    bool bOk = aFunc.RemoveAllOutlines( nTab, bRecord );
199
200
0
    if (bOk)
201
0
    {
202
0
        ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
203
0
                                                            true /* bColumns */, true /* bRows */, false /* bSizes*/,
204
0
                                                            true /* bHidden */, true /* bFiltered */,
205
0
                                                            true /* bGroups */, GetViewData().GetTabNumber());
206
0
        UpdateScrollBars(BOTH_HEADERS);
207
0
    }
208
0
}
209
210
// auto outlines
211
212
void ScDBFunc::AutoOutline( )
213
0
{
214
0
    ScDocument& rDoc = GetViewData().GetDocument();
215
0
    SCTAB nTab = GetViewData().CurrentTabForData();
216
0
    ScRange aRange( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab );     // the complete sheet, if nothing is marked
217
0
    ScMarkData& rMark = GetViewData().GetMarkData();
218
0
    if ( rMark.IsMarked() || rMark.IsMultiMarked() )
219
0
    {
220
0
        rMark.MarkToMulti();
221
0
        aRange = rMark.GetMultiMarkArea();
222
0
    }
223
224
0
    ScDocShell* pDocSh = GetViewData().GetDocShell();
225
0
    ScOutlineDocFunc aFunc(*pDocSh);
226
0
    aFunc.AutoOutline( aRange, true );
227
0
}
228
229
// select outline level
230
231
void ScDBFunc::SelectLevel( bool bColumns, sal_uInt16 nLevel, bool bRecord )
232
0
{
233
0
    SCTAB nTab = GetViewData().CurrentTabForData();
234
0
    ScDocShell* pDocSh = GetViewData().GetDocShell();
235
0
    ScOutlineDocFunc aFunc(*pDocSh);
236
237
0
    bool bOk = aFunc.SelectLevel( nTab, bColumns, nLevel, bRecord, true/*bPaint*/ );
238
239
0
    if (bOk)
240
0
    {
241
0
        ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
242
0
                                                            bColumns, !bColumns, false /* bSizes*/,
243
0
                                                            true /* bHidden */, true /* bFiltered */,
244
0
                                                            true /* bGroups */, GetViewData().GetTabNumber());
245
0
        UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
246
0
    }
247
0
}
248
249
// show individual outline groups
250
251
void ScDBFunc::SetOutlineState( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bHidden)
252
0
{
253
0
    const sal_uInt16 nHeadEntry = static_cast< sal_uInt16 >( -1 );
254
0
    if ( nEntry ==  nHeadEntry)
255
0
        SelectLevel( bColumns, sal::static_int_cast<sal_uInt16>(nLevel) );
256
0
    else
257
0
    {
258
0
        if ( !bHidden )
259
0
            ShowOutline( bColumns, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
260
0
        else
261
0
            HideOutline( bColumns, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
262
0
    }
263
0
}
264
265
void ScDBFunc::ShowOutline( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bRecord, bool bPaint )
266
0
{
267
0
    SCTAB nTab = GetViewData().CurrentTabForData();
268
0
    ScDocShell* pDocSh = GetViewData().GetDocShell();
269
0
    ScOutlineDocFunc aFunc(*pDocSh);
270
271
0
    aFunc.ShowOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint );
272
273
0
    if ( bPaint )
274
0
    {
275
0
        ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
276
0
                                                            bColumns, !bColumns, false /* bSizes*/,
277
0
                                                            true /* bHidden */, true /* bFiltered */,
278
0
                                                            true /* bGroups */, GetViewData().GetTabNumber());
279
0
        UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
280
0
    }
281
0
}
282
283
// hide individual outline groups
284
285
void ScDBFunc::HideOutline( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bRecord, bool bPaint )
286
0
{
287
0
    SCTAB nTab = GetViewData().CurrentTabForData();
288
0
    ScDocShell* pDocSh = GetViewData().GetDocShell();
289
0
    ScOutlineDocFunc aFunc(*pDocSh);
290
291
0
    bool bOk = aFunc.HideOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint );
292
293
0
    if ( bOk && bPaint )
294
0
    {
295
0
        ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
296
0
                                                            bColumns, !bColumns, false /* bSizes*/,
297
0
                                                            true /* bHidden */, true /* bFiltered */,
298
0
                                                            true /* bGroups */, GetViewData().GetTabNumber());
299
0
        UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
300
0
    }
301
0
}
302
303
// menu status: show/hide marked range
304
305
bool ScDBFunc::OutlinePossible(bool bHide)
306
0
{
307
0
    bool bEnable = false;
308
309
0
    SCCOL nStartCol;
310
0
    SCROW nStartRow;
311
0
    SCTAB nStartTab;
312
0
    SCCOL nEndCol;
313
0
    SCROW nEndRow;
314
0
    SCTAB nEndTab;
315
316
0
    if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
317
0
    {
318
0
        ScDocument& rDoc = GetViewData().GetDocument();
319
0
        SCTAB nTab = GetViewData().CurrentTabForData();
320
0
        ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
321
0
        if (pTable)
322
0
        {
323
0
            SCCOLROW nStart;
324
0
            SCCOLROW nEnd;
325
326
            // columns
327
328
0
            ScOutlineArray& rColArray = pTable->GetColArray();
329
0
            ScSubOutlineIterator aColIter( &rColArray );
330
0
            while (!bEnable)
331
0
            {
332
0
                ScOutlineEntry* pEntry = aColIter.GetNext();
333
0
                if (!pEntry)
334
0
                    break;
335
0
                nStart = pEntry->GetStart();
336
0
                nEnd   = pEntry->GetEnd();
337
0
                if ( bHide )
338
0
                {
339
0
                    if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) )
340
0
                        if (!pEntry->IsHidden())
341
0
                            bEnable = true;
342
0
                }
343
0
                else
344
0
                {
345
0
                    if ( nStart>=nStartCol && nEnd<=nEndCol )
346
0
                        if (pEntry->IsHidden())
347
0
                            bEnable = true;
348
0
                }
349
0
            }
350
351
            // rows
352
353
0
            ScOutlineArray& rRowArray = pTable->GetRowArray();
354
0
            ScSubOutlineIterator aRowIter( &rRowArray );
355
0
            for (;;)
356
0
            {
357
0
                ScOutlineEntry* pEntry = aRowIter.GetNext();
358
0
                if (!pEntry)
359
0
                    break;
360
0
                nStart = pEntry->GetStart();
361
0
                nEnd   = pEntry->GetEnd();
362
0
                if ( bHide )
363
0
                {
364
0
                    if ( nStartRow<=nEnd && nEndRow>=nStart )
365
0
                        if (!pEntry->IsHidden())
366
0
                            bEnable = true;
367
0
                }
368
0
                else
369
0
                {
370
0
                    if ( nStart>=nStartRow && nEnd<=nEndRow )
371
0
                        if (pEntry->IsHidden())
372
0
                            bEnable = true;
373
0
                }
374
0
            }
375
0
        }
376
0
    }
377
378
0
    return bEnable;
379
0
}
380
381
// show marked range
382
383
void ScDBFunc::ShowMarkedOutlines( bool bRecord )
384
0
{
385
0
    ScRange aRange;
386
0
    if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
387
0
    {
388
0
        ScDocShell* pDocSh = GetViewData().GetDocShell();
389
0
        ScOutlineDocFunc aFunc(*pDocSh);
390
0
        bool bDone = aFunc.ShowMarkedOutlines( aRange, bRecord );
391
0
        if (bDone)
392
0
        {
393
0
            ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
394
0
                GetViewData().GetViewShell(), true, true,
395
0
                false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
396
0
                true /* bGroups */, GetViewData().GetTabNumber());
397
0
            UpdateScrollBars();
398
0
        }
399
0
    }
400
0
    else
401
0
        ErrorMessage(STR_NOMULTISELECT);
402
0
}
403
404
// hide marked range
405
406
void ScDBFunc::HideMarkedOutlines( bool bRecord )
407
0
{
408
0
    ScRange aRange;
409
0
    if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
410
0
    {
411
0
        ScDocShell* pDocSh = GetViewData().GetDocShell();
412
0
        ScOutlineDocFunc aFunc(*pDocSh);
413
0
        bool bDone = aFunc.HideMarkedOutlines( aRange, bRecord );
414
0
        if (bDone)
415
0
        {
416
0
            ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
417
0
                GetViewData().GetViewShell(), true, true,
418
0
                false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
419
0
                true /* bGroups */, GetViewData().GetTabNumber());
420
0
            UpdateScrollBars();
421
0
        }
422
0
    }
423
0
    else
424
0
        ErrorMessage(STR_NOMULTISELECT);
425
0
}
426
427
// sub totals
428
429
void ScDBFunc::DoSubTotals( const ScSubTotalParam& rParam, bool bRecord,
430
                            const ScSortParam* pForceNewSort )
431
0
{
432
0
    bool bDo = !rParam.bRemoveOnly;                         // sal_False = only delete
433
434
0
    ScDocShell* pDocSh = GetViewData().GetDocShell();
435
0
    ScDocument& rDoc = pDocSh->GetDocument();
436
0
    ScMarkData& rMark = GetViewData().GetMarkData();
437
0
    SCTAB nTab = GetViewData().CurrentTabForData();
438
0
    if (bRecord && !rDoc.IsUndoEnabled())
439
0
        bRecord = false;
440
441
0
    ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
442
0
                                                rParam.nCol2, rParam.nRow2 );
443
0
    if (!pDBData)
444
0
    {
445
0
        OSL_FAIL( "SubTotals: no DBData" );
446
0
        return;
447
0
    }
448
449
0
    ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, nTab, 0, rParam.nRow1 + 1, rDoc.MaxCol(), rDoc.MaxRow());
450
0
    if (!aTester.IsEditable())
451
0
    {
452
0
        ErrorMessage(aTester.GetMessageId());
453
0
        return;
454
0
    }
455
456
0
    if (rDoc.HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
457
0
                         rParam.nCol2, rParam.nRow2, nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
458
0
    {
459
0
        ErrorMessage(STR_MSSG_INSERTCELLS_0);   // do not insert into merged
460
0
        return;
461
0
    }
462
463
0
    weld::WaitObject aWait(GetViewData().GetDialogParent());
464
0
    bool bOk = true;
465
0
    if (rParam.bReplace)
466
0
    {
467
0
        if (rDoc.TestRemoveSubTotals( nTab, rParam ))
468
0
        {
469
0
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(),
470
0
                                                      VclMessageType::Question, VclButtonsType::YesNo,
471
0
                                                      ScResId(STR_MSSG_DOSUBTOTALS_1))); // "delete data?"
472
0
            xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0)); // "StarCalc"
473
0
            xBox->set_default_response(RET_YES);
474
0
            bOk = xBox->run() == RET_YES;
475
0
        }
476
0
    }
477
478
0
    if (!bOk)
479
0
        return;
480
481
0
    ScDocShellModificator aModificator( *pDocSh );
482
483
0
    ScSubTotalParam aNewParam( rParam );        // change end of range
484
0
    ScDocumentUniquePtr pUndoDoc;
485
0
    std::unique_ptr<ScOutlineTable> pUndoTab;
486
0
    std::unique_ptr<ScRangeName> pUndoRange;
487
0
    std::unique_ptr<ScDBCollection> pUndoDB;
488
489
0
    if (bRecord)                                        // record old data
490
0
    {
491
0
        bool bOldFilter = bDo && rParam.bDoSort;
492
0
        SCTAB nTabCount = rDoc.GetTableCount();
493
0
        pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
494
0
        ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
495
0
        if (pTable)
496
0
        {
497
0
            pUndoTab.reset(new ScOutlineTable( *pTable ));
498
499
0
            SCCOLROW nOutStartCol;                          // row/column status
500
0
            SCCOLROW nOutStartRow;
501
0
            SCCOLROW nOutEndCol;
502
0
            SCCOLROW nOutEndRow;
503
0
            pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
504
0
            pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
505
506
0
            pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
507
0
            rDoc.CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
508
0
            rDoc.CopyToDocument( 0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
509
0
        }
510
0
        else
511
0
            pUndoDoc->InitUndo( rDoc, nTab, nTab, false, bOldFilter );
512
513
        // record data range - including filter results
514
0
        rDoc.CopyToDocument( 0,rParam.nRow1+1,nTab, rDoc.MaxCol(),rParam.nRow2,nTab,
515
0
                                InsertDeleteFlags::ALL, false, *pUndoDoc );
516
517
        // all formulas for reference
518
0
        rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),nTabCount-1,
519
0
                                    InsertDeleteFlags::FORMULA, false, *pUndoDoc );
520
521
        // database and other ranges
522
0
        ScRangeName* pDocRange = rDoc.GetRangeName();
523
0
        if (!pDocRange->empty())
524
0
            pUndoRange.reset(new ScRangeName( *pDocRange ));
525
0
        ScDBCollection* pDocDB = rDoc.GetDBCollection();
526
0
        if (!pDocDB->empty())
527
0
            pUndoDB.reset(new ScDBCollection( *pDocDB ));
528
0
    }
529
530
0
    ScOutlineTable* pOut = rDoc.GetOutlineTable( nTab );
531
0
    if (pOut)
532
0
    {
533
        // Remove all existing outlines in the specified range.
534
0
        ScOutlineArray& rRowArray = pOut->GetRowArray();
535
0
        sal_uInt16 nDepth = rRowArray.GetDepth();
536
0
        for (sal_uInt16 i = 0; i < nDepth; ++i)
537
0
        {
538
0
            bool bSize;
539
0
            rRowArray.Remove(aNewParam.nRow1, aNewParam.nRow2, bSize);
540
0
        }
541
0
    }
542
543
0
    if (rParam.bReplace)
544
0
        rDoc.RemoveSubTotals( nTab, aNewParam );
545
0
    bool bSuccess = true;
546
0
    if (bDo)
547
0
    {
548
        // Sort
549
0
        if ( rParam.bDoSort || pForceNewSort )
550
0
        {
551
0
            pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
552
553
            // set subtotal fields before sorting
554
            // (duplicate values are dropped, so that they can be called again)
555
556
0
            ScSortParam aOldSort;
557
0
            pDBData->GetSortParam( aOldSort );
558
0
            ScSortParam aSortParam( aNewParam, pForceNewSort ? *pForceNewSort : aOldSort );
559
0
            Sort( aSortParam, false, false );
560
0
        }
561
562
0
        bSuccess = rDoc.DoSubTotals( nTab, aNewParam );
563
0
    }
564
0
    ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
565
0
        aNewParam.nCol2, aNewParam.nRow2, nTab );
566
0
    rDoc.SetDirty( aDirtyRange, true );
567
568
0
    if (bRecord)
569
0
    {
570
0
        pDocSh->GetUndoManager()->AddUndoAction(
571
0
            std::make_unique<ScUndoSubTotals>( *pDocSh, nTab,
572
0
                                    rParam, aNewParam.nRow2,
573
0
                                    std::move(pUndoDoc), std::move(pUndoTab), // pUndoDBData,
574
0
                                    std::move(pUndoRange), std::move(pUndoDB) ) );
575
0
    }
576
577
0
    if (!bSuccess)
578
0
    {
579
        // "Can not insert any rows"
580
0
        ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
581
0
    }
582
583
                                                // store
584
0
    pDBData->SetSubTotalParam( aNewParam );
585
0
    pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
586
0
    rDoc.CompileDBFormula();
587
588
0
    const ScRange aMarkRange( aNewParam.nCol1, aNewParam.nRow1, nTab, aNewParam.nCol2, aNewParam.nRow2, nTab);
589
0
    DoneBlockMode();
590
0
    InitOwnBlockMode( aMarkRange );
591
0
    rMark.SetMarkArea( aMarkRange );
592
0
    MarkDataChanged();
593
594
0
    pDocSh->PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
595
0
                      PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
596
597
0
    aModificator.SetDocumentModified();
598
599
0
    SelectionChanged();
600
0
}
601
602
// consolidate
603
604
void ScDBFunc::Consolidate( const ScConsolidateParam& rParam )
605
0
{
606
0
    ScDocShell* pDocShell = GetViewData().GetDocShell();
607
0
    pDocShell->DoConsolidate( rParam );
608
0
    SetTabNo( rParam.nTab, true );
609
0
}
610
611
// pivot
612
613
static OUString lcl_MakePivotTabName( std::u16string_view rPrefix, SCTAB nNumber )
614
0
{
615
0
    OUString aName = rPrefix + OUString::number( nNumber );
616
0
    return aName;
617
0
}
618
619
bool ScDBFunc::MakePivotTable(
620
    const ScDPSaveData& rData, const ScRange& rDest, bool bNewTable,
621
    const ScDPObject& rSource )
622
0
{
623
    //  error message if no fields are set
624
    //  this must be removed when drag&drop of fields from a toolbox is available
625
626
0
    if ( rData.IsEmpty() )
627
0
    {
628
0
        ErrorMessage(STR_PIVOT_NODATA);
629
0
        return false;
630
0
    }
631
632
0
    ScDocShell* pDocSh  = GetViewData().GetDocShell();
633
0
    ScDocument& rDoc    = GetViewData().GetDocument();
634
0
    bool bUndo = rDoc.IsUndoEnabled();
635
636
0
    ScRange aDestRange = rDest;
637
0
    if ( bNewTable )
638
0
    {
639
0
        SCTAB nSrcTab = GetViewData().CurrentTabForData();
640
641
0
        OUString aName( ScResId(STR_PIVOT_TABLE) );
642
0
        OUString aStr;
643
644
0
        rDoc.GetName( nSrcTab, aStr );
645
0
        aName += "_" + aStr + "_";
646
647
0
        SCTAB nNewTab = nSrcTab+1;
648
649
0
        SCTAB i=1;
650
0
        while ( !rDoc.InsertTab( nNewTab, lcl_MakePivotTabName( aName, i ) ) && i <= MAXTAB )
651
0
            i++;
652
653
0
        bool bAppend = ( nNewTab+1 == rDoc.GetTableCount() );
654
0
        if (bUndo)
655
0
        {
656
0
            pDocSh->GetUndoManager()->AddUndoAction(
657
0
                        std::make_unique<ScUndoInsertTab>( *pDocSh, nNewTab, bAppend, lcl_MakePivotTabName( aName, i ) ));
658
0
        }
659
660
0
        GetViewData().InsertTab( nNewTab );
661
0
        SetTabNo(nNewTab, true);
662
663
0
        aDestRange = ScRange( 0, 0, nNewTab );
664
0
    }
665
666
0
    ScDPObject* pDPObj = rDoc.GetDPAtCursor(
667
0
                            aDestRange.aStart.Col(), aDestRange.aStart.Row(), aDestRange.aStart.Tab() );
668
669
0
    ScDPObject aObj( rSource );
670
0
    aObj.SetOutRange( aDestRange );
671
0
    if ( pDPObj && !rData.GetExistingDimensionData() )
672
0
    {
673
        // copy dimension data from old object - lost in the dialog
674
        //! change the dialog to keep the dimension data
675
676
0
        ScDPSaveData aNewData( rData );
677
0
        const ScDPSaveData* pOldData = pDPObj->GetSaveData();
678
0
        if ( pOldData )
679
0
        {
680
0
            const ScDPDimensionSaveData* pDimSave = pOldData->GetExistingDimensionData();
681
0
            aNewData.SetDimensionData( pDimSave );
682
0
        }
683
0
        aObj.SetSaveData( aNewData );
684
0
    }
685
0
    else
686
0
        aObj.SetSaveData( rData );
687
688
0
    bool bAllowMove = (pDPObj != nullptr);   // allow re-positioning when editing existing table
689
690
0
    ScDBDocFunc aFunc( *pDocSh );
691
0
    bool bSuccess = aFunc.DataPilotUpdate(pDPObj, &aObj, true, false, bAllowMove);
692
693
0
    CursorPosChanged();     // shells may be switched
694
695
0
    if ( bNewTable )
696
0
    {
697
0
        pDocSh->PostPaintExtras();
698
0
        SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
699
0
    }
700
701
0
    return bSuccess;
702
0
}
703
704
void ScDBFunc::DeletePivotTable()
705
0
{
706
0
    ScDocShell* pDocSh    = GetViewData().GetDocShell();
707
0
    ScDocument& rDoc      = pDocSh->GetDocument();
708
0
    ScDPObject* pDPObj    = rDoc.GetDPAtCursor( GetViewData().GetCurX(),
709
0
                                                  GetViewData().GetCurY(),
710
0
                                                  GetViewData().CurrentTabForData() );
711
0
    if ( pDPObj )
712
0
    {
713
0
        ScDBDocFunc aFunc( *pDocSh );
714
0
        aFunc.RemovePivotTable(*pDPObj, true, false);
715
0
        CursorPosChanged();     // shells may be switched
716
0
    }
717
0
    else
718
0
        ErrorMessage(STR_PIVOT_NOTFOUND);
719
0
}
720
721
void ScDBFunc::RecalcPivotTable()
722
0
{
723
0
    ScDocShell* pDocSh  = GetViewData().GetDocShell();
724
0
    ScDocument& rDoc    = GetViewData().GetDocument();
725
726
0
    ScDPObject* pDPObj  = rDoc.GetDPAtCursor( GetViewData().GetCurX(),
727
0
                                              GetViewData().GetCurY(),
728
0
                                              GetViewData().CurrentTabForData() );
729
0
    if (pDPObj)
730
0
    {
731
        // Remove existing data cache for the data that this datapilot uses,
732
        // to force re-build data cache.
733
0
        ScDBDocFunc aFunc(*pDocSh);
734
0
        aFunc.RefreshPivotTables(pDPObj, false);
735
736
0
        CursorPosChanged();     // shells may be switched
737
0
    }
738
0
    else
739
0
        ErrorMessage(STR_PIVOT_NOTFOUND);
740
0
}
741
742
void ScDBFunc::GetSelectedMemberList(ScDPUniqueStringSet& rEntries, tools::Long& rDimension)
743
0
{
744
0
    ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
745
0
                                        GetViewData().GetCurY(), GetViewData().CurrentTabForData() );
746
0
    if ( !pDPObj )
747
0
        return;
748
749
0
    tools::Long nStartDimension = -1;
750
0
    tools::Long nStartHierarchy = -1;
751
0
    tools::Long nStartLevel     = -1;
752
753
0
    ScRangeListRef xRanges;
754
0
    GetViewData().GetMultiArea( xRanges );         // incl. cursor if nothing is selected
755
0
    size_t nRangeCount = xRanges->size();
756
0
    bool bContinue = true;
757
758
0
    for (size_t nRangePos=0; nRangePos < nRangeCount && bContinue; nRangePos++)
759
0
    {
760
0
        ScRange const & rRange = (*xRanges)[nRangePos];
761
0
        SCCOL nStartCol = rRange.aStart.Col();
762
0
        SCROW nStartRow = rRange.aStart.Row();
763
0
        SCCOL nEndCol = rRange.aEnd.Col();
764
0
        SCROW nEndRow = rRange.aEnd.Row();
765
0
        SCTAB nTab = rRange.aStart.Tab();
766
767
0
        for (SCROW nRow=nStartRow; nRow<=nEndRow && bContinue; nRow++)
768
0
            for (SCCOL nCol=nStartCol; nCol<=nEndCol && bContinue; nCol++)
769
0
            {
770
0
                sheet::DataPilotTableHeaderData aData;
771
0
                pDPObj->GetHeaderPositionData(ScAddress(nCol, nRow, nTab), aData);
772
0
                if ( aData.Dimension < 0 )
773
0
                    bContinue = false;              // not part of any dimension
774
0
                else
775
0
                {
776
0
                    if ( nStartDimension < 0 )      // first member?
777
0
                    {
778
0
                        nStartDimension = aData.Dimension;
779
0
                        nStartHierarchy = aData.Hierarchy;
780
0
                        nStartLevel     = aData.Level;
781
0
                    }
782
0
                    if ( aData.Dimension != nStartDimension ||
783
0
                         aData.Hierarchy != nStartHierarchy ||
784
0
                         aData.Level     != nStartLevel )
785
0
                    {
786
0
                        bContinue = false;          // cannot mix dimensions
787
0
                    }
788
0
                }
789
0
                if ( bContinue )
790
0
                {
791
                    // accept any part of a member description, also subtotals,
792
                    // but don't stop if empty parts are contained
793
0
                    if ( aData.Flags & sheet::MemberResultFlags::HASMEMBER )
794
0
                        rEntries.insert(aData.MemberName);
795
0
                }
796
0
            }
797
0
    }
798
799
0
    rDimension = nStartDimension;   // dimension from which the found members came
800
0
    if (!bContinue)
801
0
        rEntries.clear();         // remove all if not valid
802
0
}
803
804
bool ScDBFunc::HasSelectionForDateGroup( ScDPNumGroupInfo& rOldInfo, sal_Int32& rParts )
805
0
{
806
    // determine if the date group dialog has to be shown for the current selection
807
808
0
    bool bFound = false;
809
810
0
    SCCOL nCurX = GetViewData().GetCurX();
811
0
    SCROW nCurY = GetViewData().GetCurY();
812
0
    SCTAB nTab = GetViewData().CurrentTabForData();
813
0
    ScDocument& rDoc = GetViewData().GetDocument();
814
815
0
    ScDPObject* pDPObj = rDoc.GetDPAtCursor( nCurX, nCurY, nTab );
816
0
    if ( pDPObj )
817
0
    {
818
0
        ScDPUniqueStringSet aEntries;
819
0
        tools::Long nSelectDimension = -1;
820
0
        GetSelectedMemberList( aEntries, nSelectDimension );
821
822
0
        if (!aEntries.empty())
823
0
        {
824
0
            bool bIsDataLayout;
825
0
            OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
826
0
            OUString aBaseDimName( aDimName );
827
828
0
            bool bInGroupDim = false;
829
0
            bool bFoundParts = false;
830
831
0
            ScDPDimensionSaveData* pDimData =
832
0
                const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() );
833
0
            if ( pDimData )
834
0
            {
835
0
                const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
836
0
                const ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDim( aDimName );
837
0
                if ( pNumGroupDim )
838
0
                {
839
                    //  existing num group dimension
840
841
0
                    if ( pNumGroupDim->GetDatePart() != 0 )
842
0
                    {
843
                        //  dimension has date info -> edit settings of this dimension
844
                        //  (parts are collected below)
845
846
0
                        rOldInfo = pNumGroupDim->GetDateInfo();
847
0
                        bFound = true;
848
0
                    }
849
0
                    else if ( pNumGroupDim->GetInfo().mbDateValues )
850
0
                    {
851
                        //  Numerical grouping with DateValues flag is used for grouping
852
                        //  of days with a "Number of days" value.
853
854
0
                        rOldInfo = pNumGroupDim->GetInfo();
855
0
                        rParts = css::sheet::DataPilotFieldGroupBy::DAYS;               // not found in CollectDateParts
856
0
                        bFoundParts = true;
857
0
                        bFound = true;
858
0
                    }
859
0
                    bInGroupDim = true;
860
0
                }
861
0
                else if ( pGroupDim )
862
0
                {
863
                    //  existing additional group dimension
864
865
0
                    if ( pGroupDim->GetDatePart() != 0 )
866
0
                    {
867
                        //  dimension has date info -> edit settings of this dimension
868
                        //  (parts are collected below)
869
870
0
                        rOldInfo = pGroupDim->GetDateInfo();
871
0
                        aBaseDimName = pGroupDim->GetSourceDimName();
872
0
                        bFound = true;
873
0
                    }
874
0
                    bInGroupDim = true;
875
0
                }
876
0
            }
877
0
            if ( bFound && !bFoundParts )
878
0
            {
879
                // collect date parts from all group dimensions
880
0
                rParts = pDimData->CollectDateParts( aBaseDimName );
881
0
            }
882
0
            if ( !bFound && !bInGroupDim )
883
0
            {
884
                // create new date group dimensions if the selection is a single cell
885
                // in a normal dimension with date content
886
887
0
                ScRange aSelRange;
888
0
                if ( (GetViewData().GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) &&
889
0
                        aSelRange.aStart == aSelRange.aEnd )
890
0
                {
891
0
                    SCCOL nSelCol = aSelRange.aStart.Col();
892
0
                    SCROW nSelRow = aSelRange.aStart.Row();
893
0
                    SCTAB nSelTab = aSelRange.aStart.Tab();
894
0
                    if ( rDoc.HasValueData( nSelCol, nSelRow, nSelTab ) )
895
0
                    {
896
0
                        sal_uLong nIndex = rDoc.GetAttr(
897
0
                                        nSelCol, nSelRow, nSelTab, ATTR_VALUE_FORMAT).GetValue();
898
0
                        SvNumFormatType nType = rDoc.GetFormatTable()->GetType(nIndex);
899
0
                        if ( nType == SvNumFormatType::DATE || nType == SvNumFormatType::TIME || nType == SvNumFormatType::DATETIME )
900
0
                        {
901
0
                            bFound = true;
902
                            // use currently selected value for automatic limits
903
0
                            if( rOldInfo.mbAutoStart )
904
0
                                rOldInfo.mfStart = rDoc.GetValue( aSelRange.aStart );
905
0
                            if( rOldInfo.mbAutoEnd )
906
0
                                rOldInfo.mfEnd = rDoc.GetValue( aSelRange.aStart );
907
0
                        }
908
0
                    }
909
0
                }
910
0
            }
911
0
        }
912
0
    }
913
914
0
    return bFound;
915
0
}
916
917
bool ScDBFunc::HasSelectionForNumGroup( ScDPNumGroupInfo& rOldInfo )
918
0
{
919
    // determine if the numeric group dialog has to be shown for the current selection
920
921
0
    bool bFound = false;
922
923
0
    SCCOL nCurX = GetViewData().GetCurX();
924
0
    SCROW nCurY = GetViewData().GetCurY();
925
0
    SCTAB nTab = GetViewData().CurrentTabForData();
926
0
    ScDocument& rDoc = GetViewData().GetDocument();
927
928
0
    ScDPObject* pDPObj = rDoc.GetDPAtCursor( nCurX, nCurY, nTab );
929
0
    if ( pDPObj )
930
0
    {
931
0
        ScDPUniqueStringSet aEntries;
932
0
        tools::Long nSelectDimension = -1;
933
0
        GetSelectedMemberList( aEntries, nSelectDimension );
934
935
0
        if (!aEntries.empty())
936
0
        {
937
0
            bool bIsDataLayout;
938
0
            OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
939
940
0
            bool bInGroupDim = false;
941
942
0
            ScDPDimensionSaveData* pDimData =
943
0
                const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() );
944
0
            if ( pDimData )
945
0
            {
946
0
                const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
947
0
                if ( pNumGroupDim )
948
0
                {
949
                    //  existing num group dimension
950
                    //  -> edit settings of this dimension
951
952
0
                    rOldInfo = pNumGroupDim->GetInfo();
953
0
                    bFound = true;
954
0
                }
955
0
                else if ( pDimData->GetNamedGroupDim( aDimName ) )
956
0
                    bInGroupDim = true;                                    // in a group dimension
957
0
            }
958
0
            if ( !bFound && !bInGroupDim )
959
0
            {
960
                // create a new num group dimension if the selection is a single cell
961
                // in a normal dimension with numeric content
962
963
0
                ScRange aSelRange;
964
0
                if ( (GetViewData().GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) &&
965
0
                        aSelRange.aStart == aSelRange.aEnd )
966
0
                {
967
0
                    if ( rDoc.HasValueData( aSelRange.aStart.Col(), aSelRange.aStart.Row(),
968
0
                                             aSelRange.aStart.Tab() ) )
969
0
                    {
970
0
                        bFound = true;
971
                        // use currently selected value for automatic limits
972
0
                        if( rOldInfo.mbAutoStart )
973
0
                            rOldInfo.mfStart = rDoc.GetValue( aSelRange.aStart );
974
0
                        if( rOldInfo.mbAutoEnd )
975
0
                            rOldInfo.mfEnd = rDoc.GetValue( aSelRange.aStart );
976
0
                    }
977
0
                }
978
0
            }
979
0
        }
980
0
    }
981
982
0
    return bFound;
983
0
}
984
985
void ScDBFunc::DateGroupDataPilot( const ScDPNumGroupInfo& rInfo, sal_Int32 nParts )
986
0
{
987
0
    ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
988
0
                                        GetViewData().GetCurY(), GetViewData().CurrentTabForData() );
989
0
    if (!pDPObj)
990
0
        return;
991
992
0
    ScDPUniqueStringSet aEntries;
993
0
    tools::Long nSelectDimension = -1;
994
0
    GetSelectedMemberList( aEntries, nSelectDimension );
995
996
0
    if (aEntries.empty())
997
0
        return;
998
999
0
    std::vector<OUString> aDeletedNames;
1000
0
    bool bIsDataLayout;
1001
0
    OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1002
1003
0
    ScDPSaveData aData( *pDPObj->GetSaveData() );
1004
0
    ScDPDimensionSaveData* pDimData = aData.GetDimensionData();     // created if not there
1005
1006
    // find the source dimension name.
1007
0
    OUString aBaseDimName = aDimName;
1008
0
    if( const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName ) )
1009
0
        aBaseDimName = pBaseGroupDim->GetSourceDimName();
1010
1011
    // Remove all group dimensions associated with this source dimension. For
1012
    // date grouping, we need to remove all existing groups for the affected
1013
    // source dimension and build new one(s) from scratch.  Keep the deleted
1014
    // names so that they can be reused during re-construction.
1015
0
    aData.RemoveAllGroupDimensions(aBaseDimName, &aDeletedNames);
1016
1017
0
    if ( nParts )
1018
0
    {
1019
        // create date group dimensions
1020
1021
0
        bool bFirst = true;
1022
0
        sal_Int32 nMask = 1;
1023
0
        for (sal_uInt16 nBit=0; nBit<32; nBit++)
1024
0
        {
1025
0
            if ( nParts & nMask )
1026
0
            {
1027
0
                if ( bFirst )
1028
0
                {
1029
                    // innermost part: create NumGroupDimension (replacing original values)
1030
                    // Dimension name is left unchanged
1031
1032
0
                    if ( (nParts == sheet::DataPilotFieldGroupBy::DAYS) && (rInfo.mfStep >= 1.0) )
1033
0
                    {
1034
                        // only days, and a step value specified: use numerical grouping
1035
                        // with DateValues flag, not date grouping
1036
1037
0
                        ScDPNumGroupInfo aNumInfo( rInfo );
1038
0
                        aNumInfo.mbDateValues = true;
1039
1040
0
                        ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, aNumInfo );
1041
0
                        pDimData->AddNumGroupDimension( aNumGroupDim );
1042
0
                    }
1043
0
                    else
1044
0
                    {
1045
0
                        ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, rInfo, nMask );
1046
0
                        pDimData->AddNumGroupDimension( aNumGroupDim );
1047
0
                    }
1048
1049
0
                    bFirst = false;
1050
0
                }
1051
0
                else
1052
0
                {
1053
                    // additional parts: create GroupDimension (shown as additional dimensions)
1054
0
                    OUString aGroupDimName =
1055
0
                        pDimData->CreateDateGroupDimName(nMask, *pDPObj, true, &aDeletedNames);
1056
0
                    ScDPSaveGroupDimension aGroupDim( aBaseDimName, aGroupDimName );
1057
0
                    aGroupDim.SetDateInfo( rInfo, nMask );
1058
0
                    pDimData->AddGroupDimension( aGroupDim );
1059
1060
                    // set orientation
1061
0
                    ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName );
1062
0
                    if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN )
1063
0
                    {
1064
0
                        ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aBaseDimName );
1065
0
                        pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
1066
0
                        aData.SetPosition( pSaveDimension, 0 ); //! before (immediate) base
1067
0
                    }
1068
0
                }
1069
0
            }
1070
0
            nMask *= 2;
1071
0
        }
1072
0
    }
1073
1074
    // apply changes
1075
0
    ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1076
0
    pDPObj->SetSaveData( aData );
1077
0
    aFunc.RefreshPivotTableGroups(pDPObj);
1078
1079
    // unmark cell selection
1080
0
    Unmark();
1081
0
}
1082
1083
void ScDBFunc::NumGroupDataPilot( const ScDPNumGroupInfo& rInfo )
1084
0
{
1085
0
    ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1086
0
                                        GetViewData().GetCurY(), GetViewData().CurrentTabForData() );
1087
0
    if (!pDPObj)
1088
0
        return;
1089
1090
0
    ScDPUniqueStringSet aEntries;
1091
0
    tools::Long nSelectDimension = -1;
1092
0
    GetSelectedMemberList( aEntries, nSelectDimension );
1093
1094
0
    if (aEntries.empty())
1095
0
        return;
1096
1097
0
    bool bIsDataLayout;
1098
0
    OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1099
1100
0
    ScDPSaveData aData( *pDPObj->GetSaveData() );
1101
0
    ScDPDimensionSaveData* pDimData = aData.GetDimensionData();     // created if not there
1102
1103
0
    ScDPSaveNumGroupDimension* pExisting = pDimData->GetNumGroupDimAcc( aDimName );
1104
0
    if ( pExisting )
1105
0
    {
1106
        // modify existing group dimension
1107
0
        pExisting->SetGroupInfo( rInfo );
1108
0
    }
1109
0
    else
1110
0
    {
1111
        // create new group dimension
1112
0
        ScDPSaveNumGroupDimension aNumGroupDim( aDimName, rInfo );
1113
0
        pDimData->AddNumGroupDimension( aNumGroupDim );
1114
0
    }
1115
1116
    // apply changes
1117
0
    ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1118
0
    pDPObj->SetSaveData( aData );
1119
0
    aFunc.RefreshPivotTableGroups(pDPObj);
1120
1121
    // unmark cell selection
1122
0
    Unmark();
1123
0
}
1124
1125
void ScDBFunc::GroupDataPilot()
1126
0
{
1127
0
    ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1128
0
                                        GetViewData().GetCurY(), GetViewData().CurrentTabForData() );
1129
0
    if (!pDPObj)
1130
0
        return;
1131
1132
0
    ScDPUniqueStringSet aEntries;
1133
0
    tools::Long nSelectDimension = -1;
1134
0
    GetSelectedMemberList( aEntries, nSelectDimension );
1135
1136
0
    if (aEntries.empty())
1137
0
        return;
1138
1139
0
    bool bIsDataLayout;
1140
0
    OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1141
1142
0
    ScDPSaveData aData( *pDPObj->GetSaveData() );
1143
0
    ScDPDimensionSaveData* pDimData = aData.GetDimensionData();     // created if not there
1144
1145
    // find original base
1146
0
    OUString aBaseDimName = aDimName;
1147
0
    const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName );
1148
0
    if ( pBaseGroupDim )
1149
0
    {
1150
        // any entry's SourceDimName is the original base
1151
0
        aBaseDimName = pBaseGroupDim->GetSourceDimName();
1152
0
    }
1153
1154
    // find existing group dimension
1155
    // (using the selected dim, can be intermediate group dim)
1156
0
    ScDPSaveGroupDimension* pGroupDimension = pDimData->GetGroupDimAccForBase( aDimName );
1157
1158
    // remove the selected items from their groups
1159
    // (empty groups are removed, too)
1160
0
    if ( pGroupDimension )
1161
0
    {
1162
0
        for (const OUString& aEntryName : aEntries)
1163
0
        {
1164
0
            if ( pBaseGroupDim )
1165
0
            {
1166
                // for each selected (intermediate) group, remove all its items
1167
                // (same logic as for adding, below)
1168
0
                const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
1169
0
                if ( pBaseGroup )
1170
0
                    pBaseGroup->RemoveElementsFromGroups( *pGroupDimension );   // remove all elements
1171
0
                else
1172
0
                    pGroupDimension->RemoveFromGroups( aEntryName );
1173
0
            }
1174
0
            else
1175
0
                pGroupDimension->RemoveFromGroups( aEntryName );
1176
0
        }
1177
0
    }
1178
1179
0
    std::unique_ptr<ScDPSaveGroupDimension> pNewGroupDim;
1180
0
    if ( !pGroupDimension )
1181
0
    {
1182
        // create a new group dimension
1183
0
        OUString aGroupDimName =
1184
0
            pDimData->CreateGroupDimName(aBaseDimName, *pDPObj, false, nullptr);
1185
0
        pNewGroupDim.reset(new ScDPSaveGroupDimension( aBaseDimName, aGroupDimName ));
1186
1187
0
        pGroupDimension = pNewGroupDim.get();     // make changes to the new dim if none existed
1188
1189
0
        if ( pBaseGroupDim )
1190
0
        {
1191
            // If it's a higher-order group dimension, pre-allocate groups for all
1192
            // non-selected original groups, so the individual base members aren't
1193
            // used for automatic groups (this would make the original groups hard
1194
            // to find).
1195
            //! Also do this when removing groups?
1196
            //! Handle this case dynamically with automatic groups?
1197
1198
0
            tools::Long nGroupCount = pBaseGroupDim->GetGroupCount();
1199
0
            for ( tools::Long nGroup = 0; nGroup < nGroupCount; nGroup++ )
1200
0
            {
1201
0
                const ScDPSaveGroupItem& rBaseGroup = pBaseGroupDim->GetGroupByIndex( nGroup );
1202
1203
0
                if (!aEntries.count(rBaseGroup.GetGroupName()))
1204
0
                {
1205
                    // add an additional group for each item that is not in the selection
1206
0
                    ScDPSaveGroupItem aGroup( rBaseGroup.GetGroupName() );
1207
0
                    aGroup.AddElementsFromGroup( rBaseGroup );
1208
0
                    pGroupDimension->AddGroupItem( aGroup );
1209
0
                }
1210
0
            }
1211
0
        }
1212
0
    }
1213
0
    OUString aGroupDimName = pGroupDimension->GetGroupDimName();
1214
1215
0
    ScDPSaveGroupItem aGroup(pGroupDimension->CreateGroupName(ScResId(STR_PIVOT_GROUP)));
1216
0
    for (const OUString& aEntryName : aEntries)
1217
0
    {
1218
0
        if ( pBaseGroupDim )
1219
0
        {
1220
            // for each selected (intermediate) group, add all its items
1221
0
            const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
1222
0
            if ( pBaseGroup )
1223
0
                aGroup.AddElementsFromGroup( *pBaseGroup );
1224
0
            else
1225
0
                aGroup.AddElement( aEntryName );    // no group found -> automatic group, add the item itself
1226
0
        }
1227
0
        else
1228
0
            aGroup.AddElement( aEntryName );        // no group dimension, add all items directly
1229
0
    }
1230
1231
0
    pGroupDimension->AddGroupItem( aGroup );
1232
1233
0
    if ( pNewGroupDim )
1234
0
    {
1235
0
        pDimData->AddGroupDimension( *pNewGroupDim );
1236
0
        pNewGroupDim.reset();        // AddGroupDimension copies the object
1237
        // don't access pGroupDimension after here
1238
0
    }
1239
0
    pGroupDimension = nullptr;
1240
1241
    // set orientation
1242
0
    ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName );
1243
0
    if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN )
1244
0
    {
1245
0
        ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aDimName );
1246
0
        pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
1247
0
        aData.SetPosition( pSaveDimension, 0 ); //! before (immediate) base
1248
0
    }
1249
1250
    // apply changes
1251
0
    ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1252
0
    pDPObj->SetSaveData( aData );
1253
0
    aFunc.RefreshPivotTableGroups(pDPObj);
1254
1255
    // unmark cell selection
1256
0
    Unmark();
1257
0
}
1258
1259
void ScDBFunc::UngroupDataPilot()
1260
0
{
1261
0
    ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1262
0
                                        GetViewData().GetCurY(), GetViewData().CurrentTabForData() );
1263
0
    if (!pDPObj)
1264
0
        return;
1265
1266
0
    ScDPUniqueStringSet aEntries;
1267
0
    tools::Long nSelectDimension = -1;
1268
0
    GetSelectedMemberList( aEntries, nSelectDimension );
1269
1270
0
    if (aEntries.empty())
1271
0
        return;
1272
1273
0
    bool bIsDataLayout;
1274
0
    OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1275
1276
0
    ScDPSaveData aData( *pDPObj->GetSaveData() );
1277
0
    if (!aData.GetExistingDimensionData())
1278
        // There is nothing to ungroup.
1279
0
        return;
1280
1281
0
    ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
1282
1283
0
    ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
1284
0
    const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
1285
0
    if ( ( pGroupDim && pGroupDim->GetDatePart() != 0 ) ||
1286
0
         ( pNumGroupDim && pNumGroupDim->GetDatePart() != 0 ) )
1287
0
    {
1288
        // Date grouping: need to remove all affected group dimensions.
1289
        // This is done using DateGroupDataPilot with nParts=0.
1290
1291
0
        DateGroupDataPilot( ScDPNumGroupInfo(), 0 );
1292
0
        return;
1293
0
    }
1294
1295
0
    if ( pGroupDim )
1296
0
    {
1297
0
        for (const auto& rEntry : aEntries)
1298
0
            pGroupDim->RemoveGroup(rEntry);
1299
1300
        // remove group dimension if empty
1301
0
        bool bEmptyDim = pGroupDim->IsEmpty();
1302
0
        if ( !bEmptyDim )
1303
0
        {
1304
            // If all remaining groups in the dimension aren't shown, remove
1305
            // the dimension too, as if it was completely empty.
1306
0
            ScDPUniqueStringSet aVisibleEntries;
1307
0
            pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension );
1308
0
            bEmptyDim = pGroupDim->HasOnlyHidden( aVisibleEntries );
1309
0
        }
1310
0
        if ( bEmptyDim )
1311
0
        {
1312
0
            pDimData->RemoveGroupDimension( aDimName );     // pGroupDim is deleted
1313
1314
            // also remove SaveData settings for the dimension that no longer exists
1315
0
            aData.RemoveDimensionByName( aDimName );
1316
0
        }
1317
0
    }
1318
0
    else if ( pNumGroupDim )
1319
0
    {
1320
        // remove the numerical grouping
1321
0
        pDimData->RemoveNumGroupDimension( aDimName );
1322
        // SaveData settings can remain unchanged - the same dimension still exists
1323
0
    }
1324
1325
    // apply changes
1326
0
    ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1327
0
    pDPObj->SetSaveData( aData );
1328
0
    aFunc.RefreshPivotTableGroups(pDPObj);
1329
1330
    // unmark cell selection
1331
0
    Unmark();
1332
0
}
1333
1334
static OUString lcl_replaceMemberNameInSubtotal(const OUString& rSubtotal, std::u16string_view rMemberName)
1335
0
{
1336
0
    sal_Int32 n = rSubtotal.getLength();
1337
0
    const sal_Unicode* p = rSubtotal.getStr();
1338
0
    OUStringBuffer aBuf, aWordBuf;
1339
0
    for (sal_Int32 i = 0; i < n; ++i)
1340
0
    {
1341
0
        sal_Unicode c = p[i];
1342
0
        if (c == ' ')
1343
0
        {
1344
0
            OUString aWord = aWordBuf.makeStringAndClear();
1345
0
            if (aWord == rMemberName)
1346
0
                aBuf.append('?');
1347
0
            else
1348
0
                aBuf.append(aWord);
1349
0
            aBuf.append(c);
1350
0
        }
1351
0
        else if (c == '\\')
1352
0
        {
1353
            // Escape a backslash character.
1354
0
            aWordBuf.append(OUStringChar(c) + OUStringChar(c));
1355
0
        }
1356
0
        else if (c == '?')
1357
0
        {
1358
            // A literal '?' must be escaped with a backslash ('\');
1359
0
            aWordBuf.append("\\" + OUStringChar(c));
1360
0
        }
1361
0
        else
1362
0
            aWordBuf.append(c);
1363
0
    }
1364
1365
0
    if (!aWordBuf.isEmpty())
1366
0
    {
1367
0
        OUString aWord = aWordBuf.makeStringAndClear();
1368
0
        if (aWord == rMemberName)
1369
0
            aBuf.append('?');
1370
0
        else
1371
0
            aBuf.append(aWord);
1372
0
    }
1373
1374
0
    return aBuf.makeStringAndClear();
1375
0
}
1376
1377
void ScDBFunc::DataPilotInput( const ScAddress& rPos, const OUString& rString )
1378
0
{
1379
0
    using namespace ::com::sun::star::sheet;
1380
1381
0
    ScDocument& rDoc = GetViewData().GetDocument();
1382
0
    ScDPObject* pDPObj = rDoc.GetDPAtCursor( rPos.Col(), rPos.Row(), rPos.Tab() );
1383
0
    if (!pDPObj)
1384
0
        return;
1385
1386
0
    OUString aOldText = rDoc.GetString(rPos.Col(), rPos.Row(), rPos.Tab());
1387
1388
0
    if ( aOldText == rString )
1389
0
    {
1390
        // nothing to do: silently exit
1391
0
        return;
1392
0
    }
1393
1394
0
    TranslateId pErrorId;
1395
1396
0
    pDPObj->BuildAllDimensionMembers();
1397
0
    ScDPSaveData aData( *pDPObj->GetSaveData() );
1398
0
    bool bChange = false;
1399
0
    bool bNeedReloadGroups = false;
1400
1401
0
    DataPilotFieldOrientation nOrient = DataPilotFieldOrientation_HIDDEN;
1402
0
    tools::Long nField = pDPObj->GetHeaderDim( rPos, nOrient );
1403
0
    if ( nField >= 0 )
1404
0
    {
1405
        // changing a field title
1406
0
        if ( aData.GetExistingDimensionData() )
1407
0
        {
1408
            // only group dimensions can be renamed
1409
1410
0
            ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
1411
0
            ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aOldText );
1412
0
            if ( pGroupDim )
1413
0
            {
1414
                // valid name: not empty, no existing dimension (group or other)
1415
0
                if (!rString.isEmpty() && !pDPObj->IsDimNameInUse(rString))
1416
0
                {
1417
0
                    pGroupDim->Rename( rString );
1418
1419
                    // also rename in SaveData to preserve the field settings
1420
0
                    ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aOldText );
1421
0
                    pSaveDim->SetName( rString );
1422
1423
0
                    bChange = true;
1424
0
                }
1425
0
                else
1426
0
                    pErrorId = STR_INVALIDNAME;
1427
0
            }
1428
0
        }
1429
0
        else if (nOrient == DataPilotFieldOrientation_COLUMN || nOrient == DataPilotFieldOrientation_ROW)
1430
0
        {
1431
0
            bool bDataLayout = false;
1432
0
            OUString aDimName = pDPObj->GetDimName(nField, bDataLayout);
1433
0
            ScDPSaveDimension* pDim = bDataLayout ? aData.GetDataLayoutDimension() : aData.GetDimensionByName(aDimName);
1434
0
            if (pDim)
1435
0
            {
1436
0
                if (!rString.isEmpty())
1437
0
                {
1438
0
                    if (rString.equalsIgnoreAsciiCase(aDimName))
1439
0
                    {
1440
0
                        pDim->RemoveLayoutName();
1441
0
                        bChange = true;
1442
0
                    }
1443
0
                    else if (!pDPObj->IsDimNameInUse(rString))
1444
0
                    {
1445
0
                        pDim->SetLayoutName(rString);
1446
0
                        bChange = true;
1447
0
                    }
1448
0
                    else
1449
0
                        pErrorId = STR_INVALIDNAME;
1450
0
                }
1451
0
                else
1452
0
                    pErrorId = STR_INVALIDNAME;
1453
0
            }
1454
0
        }
1455
0
    }
1456
0
    else if (pDPObj->IsDataDescriptionCell(rPos))
1457
0
    {
1458
        // There is only one data dimension.
1459
0
        ScDPSaveDimension* pDim = aData.GetFirstDimension(sheet::DataPilotFieldOrientation_DATA);
1460
0
        if (pDim)
1461
0
        {
1462
0
            if (!rString.isEmpty())
1463
0
            {
1464
0
                if (pDim->GetName().equalsIgnoreAsciiCase(rString))
1465
0
                {
1466
0
                    pDim->RemoveLayoutName();
1467
0
                    bChange = true;
1468
0
                }
1469
0
                else if (!pDPObj->IsDimNameInUse(rString))
1470
0
                {
1471
0
                    pDim->SetLayoutName(rString);
1472
0
                    bChange = true;
1473
0
                }
1474
0
                else
1475
0
                    pErrorId = STR_INVALIDNAME;
1476
0
            }
1477
0
            else
1478
0
                pErrorId = STR_INVALIDNAME;
1479
0
        }
1480
0
    }
1481
0
    else
1482
0
    {
1483
        // This is not a field header.
1484
0
        sheet::DataPilotTableHeaderData aPosData;
1485
0
        pDPObj->GetHeaderPositionData(rPos, aPosData);
1486
1487
0
        if ((aPosData.Flags & MemberResultFlags::HASMEMBER) && !aOldText.isEmpty())
1488
0
        {
1489
0
            if ( aData.GetExistingDimensionData() && !(aPosData.Flags & MemberResultFlags::SUBTOTAL))
1490
0
            {
1491
0
                bool bIsDataLayout;
1492
0
                OUString aDimName = pDPObj->GetDimName( aPosData.Dimension, bIsDataLayout );
1493
1494
0
                ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
1495
0
                ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
1496
0
                if ( pGroupDim )
1497
0
                {
1498
                    // valid name: not empty, no existing group in this dimension
1499
                    //! ignore case?
1500
0
                    if (!rString.isEmpty() && !pGroupDim->GetNamedGroup(rString))
1501
0
                    {
1502
0
                        ScDPSaveGroupItem* pGroup = pGroupDim->GetNamedGroupAcc( aOldText );
1503
0
                        if ( pGroup )
1504
0
                            pGroup->Rename( rString );     // rename the existing group
1505
0
                        else
1506
0
                        {
1507
                            // create a new group to replace the automatic group
1508
0
                            ScDPSaveGroupItem aGroup( rString );
1509
0
                            aGroup.AddElement( aOldText );
1510
0
                            pGroupDim->AddGroupItem( aGroup );
1511
0
                        }
1512
1513
                        // in both cases also adjust savedata, to preserve member settings (show details)
1514
0
                        ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aDimName );
1515
0
                        ScDPSaveMember* pSaveMember = pSaveDim->GetExistingMemberByName( aOldText );
1516
0
                        if ( pSaveMember )
1517
0
                            pSaveMember->SetName( rString );
1518
1519
0
                        bChange = true;
1520
0
                        bNeedReloadGroups = true;
1521
0
                    }
1522
0
                    else
1523
0
                        pErrorId = STR_INVALIDNAME;
1524
0
                }
1525
0
            }
1526
0
            else if (aPosData.Flags & MemberResultFlags::GRANDTOTAL)
1527
0
            {
1528
0
                aData.SetGrandTotalName(rString);
1529
0
                bChange = true;
1530
0
            }
1531
0
            else if (aPosData.Dimension >= 0 && !aPosData.MemberName.isEmpty())
1532
0
            {
1533
0
                bool bDataLayout = false;
1534
0
                OUString aDimName = pDPObj->GetDimName(static_cast<tools::Long>(aPosData.Dimension), bDataLayout);
1535
0
                if (bDataLayout)
1536
0
                {
1537
                    // data dimension
1538
0
                    do
1539
0
                    {
1540
0
                        if (aPosData.Flags & MemberResultFlags::SUBTOTAL)
1541
0
                            break;
1542
1543
0
                        ScDPSaveDimension* pDim = aData.GetDimensionByName(aPosData.MemberName);
1544
0
                        if (!pDim)
1545
0
                            break;
1546
1547
0
                        if (rString.isEmpty())
1548
0
                        {
1549
0
                            pErrorId = STR_INVALIDNAME;
1550
0
                            break;
1551
0
                        }
1552
1553
0
                        if (aPosData.MemberName.equalsIgnoreAsciiCase(rString))
1554
0
                        {
1555
0
                            pDim->RemoveLayoutName();
1556
0
                            bChange = true;
1557
0
                        }
1558
0
                        else if (!pDPObj->IsDimNameInUse(rString))
1559
0
                        {
1560
0
                            pDim->SetLayoutName(rString);
1561
0
                            bChange = true;
1562
0
                        }
1563
0
                        else
1564
0
                            pErrorId = STR_INVALIDNAME;
1565
0
                    }
1566
0
                    while (false);
1567
0
                }
1568
0
                else
1569
0
                {
1570
                    // field member
1571
0
                    do
1572
0
                    {
1573
0
                        ScDPSaveDimension* pDim = aData.GetDimensionByName(aDimName);
1574
0
                        if (!pDim)
1575
0
                            break;
1576
1577
0
                        ScDPSaveMember* pMem = pDim->GetExistingMemberByName(aPosData.MemberName);
1578
0
                        if (!pMem)
1579
0
                            break;
1580
1581
0
                        if (aPosData.Flags & MemberResultFlags::SUBTOTAL)
1582
0
                        {
1583
                            // Change subtotal only when the table has one data dimension.
1584
0
                            if (aData.GetDataDimensionCount() > 1)
1585
0
                                break;
1586
1587
                            // display name for subtotal is allowed only if the subtotal type is 'Automatic'.
1588
0
                            if (pDim->GetSubTotalsCount() != 1)
1589
0
                                break;
1590
1591
0
                            if (pDim->GetSubTotalFunc(0) != ScGeneralFunction::AUTO)
1592
0
                                break;
1593
1594
0
                            const std::optional<OUString> & pLayoutName = pMem->GetLayoutName();
1595
0
                            OUString aMemberName;
1596
0
                            if (pLayoutName)
1597
0
                                aMemberName = *pLayoutName;
1598
0
                            else
1599
0
                                aMemberName = aPosData.MemberName;
1600
1601
0
                            OUString aNew = lcl_replaceMemberNameInSubtotal(rString, aMemberName);
1602
0
                            pDim->SetSubtotalName(aNew);
1603
0
                            bChange = true;
1604
0
                        }
1605
0
                        else
1606
0
                        {
1607
                            // Check to make sure the member name isn't
1608
                            // already used.
1609
0
                            if (!rString.isEmpty())
1610
0
                            {
1611
0
                                if (rString.equalsIgnoreAsciiCase(pMem->GetName()))
1612
0
                                {
1613
0
                                    pMem->RemoveLayoutName();
1614
0
                                    bChange = true;
1615
0
                                }
1616
0
                                else if (!pDim->IsMemberNameInUse(rString))
1617
0
                                {
1618
0
                                    pMem->SetLayoutName(rString);
1619
0
                                    bChange = true;
1620
0
                                }
1621
0
                                else
1622
0
                                    pErrorId = STR_INVALIDNAME;
1623
0
                            }
1624
0
                            else
1625
0
                                pErrorId = STR_INVALIDNAME;
1626
0
                        }
1627
0
                    }
1628
0
                    while (false);
1629
0
                }
1630
0
            }
1631
0
        }
1632
0
    }
1633
1634
0
    if ( bChange )
1635
0
    {
1636
        // apply changes
1637
0
        ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1638
0
        pDPObj->SetSaveData( aData );
1639
0
        if (bNeedReloadGroups)
1640
0
        {
1641
0
            ScDPCollection* pDPs = rDoc.GetDPCollection();
1642
0
            if (pDPs)
1643
0
            {
1644
0
                o3tl::sorted_vector<ScDPObject*> aRefs;
1645
                // tdf#111305: Reload groups in cache after modifications.
1646
0
                pDPs->ReloadGroupsInCache(pDPObj, aRefs);
1647
0
            } // pDPs
1648
0
        } // bNeedReloadGroups
1649
0
        aFunc.UpdatePivotTable(*pDPObj, true, false);
1650
0
    }
1651
0
    else
1652
0
    {
1653
0
        if (!pErrorId)
1654
0
            pErrorId = STR_ERR_DATAPILOT_INPUT;
1655
0
        ErrorMessage(pErrorId);
1656
0
    }
1657
0
}
1658
1659
static void lcl_MoveToEnd( ScDPSaveDimension& rDim, const OUString& rItemName )
1660
0
{
1661
0
    std::unique_ptr<ScDPSaveMember> pNewMember;
1662
0
    const ScDPSaveMember* pOldMember = rDim.GetExistingMemberByName( rItemName );
1663
0
    if ( pOldMember )
1664
0
        pNewMember.reset(new ScDPSaveMember( *pOldMember ));
1665
0
    else
1666
0
        pNewMember.reset(new ScDPSaveMember( rItemName ));
1667
0
    rDim.AddMember( std::move(pNewMember) );
1668
    // AddMember takes ownership of the new pointer,
1669
    // puts it to the end of the list even if it was in the list before.
1670
0
}
1671
1672
namespace {
1673
1674
struct ScOUStringCollate
1675
{
1676
    CollatorWrapper* mpCollator;
1677
1678
0
    explicit ScOUStringCollate(CollatorWrapper* pColl) : mpCollator(pColl) {}
1679
1680
    bool operator()(const OUString& rStr1, const OUString& rStr2) const
1681
0
    {
1682
0
        return ( mpCollator->compareString(rStr1, rStr2) < 0 );
1683
0
    }
1684
};
1685
1686
}
1687
1688
void ScDBFunc::DataPilotSort(ScDPObject* pDPObj, tools::Long nDimIndex, bool bAscending, const sal_uInt16* pUserListId)
1689
0
{
1690
0
    if (!pDPObj)
1691
0
        return;
1692
1693
    // We need to run this to get all members later.
1694
0
    if ( pUserListId )
1695
0
        pDPObj->BuildAllDimensionMembers();
1696
1697
0
    if (nDimIndex < 0)
1698
        // Invalid dimension index.  Bail out.
1699
0
        return;
1700
1701
0
    ScDPSaveData* pSaveData = pDPObj->GetSaveData();
1702
0
    if (!pSaveData)
1703
0
        return;
1704
1705
0
    ScDPSaveData aNewSaveData(*pSaveData);
1706
0
    bool bDataLayout;
1707
0
    OUString aDimName = pDPObj->GetDimName(nDimIndex, bDataLayout);
1708
0
    ScDPSaveDimension* pSaveDim = aNewSaveData.GetDimensionByName(aDimName);
1709
0
    if (!pSaveDim)
1710
0
        return;
1711
1712
    // manual evaluation of sort order is only needed if a user list id is given
1713
0
    if ( pUserListId )
1714
0
    {
1715
0
        typedef ScDPSaveDimension::MemberList MemList;
1716
0
        const MemList& rDimMembers = pSaveDim->GetMembers();
1717
0
        std::vector<OUString> aMembers;
1718
0
        std::unordered_set<OUString> aMemberSet;
1719
0
        size_t nMemberCount = 0;
1720
0
        for (ScDPSaveMember* pMem : rDimMembers)
1721
0
        {
1722
0
            aMembers.push_back(pMem->GetName());
1723
0
            aMemberSet.insert(pMem->GetName());
1724
0
            ++nMemberCount;
1725
0
        }
1726
1727
        // Sort the member list in ascending order.
1728
0
        ScOUStringCollate aCollate( &ScGlobal::GetCollator() );
1729
0
        std::stable_sort(aMembers.begin(), aMembers.end(), aCollate);
1730
1731
        // Collect and rank those custom sort strings that also exist in the member name list.
1732
1733
0
        typedef std::unordered_map<OUString, sal_uInt16> UserSortMap;
1734
0
        UserSortMap aSubStrs;
1735
0
        sal_uInt16 nSubCount = 0;
1736
0
        ScUserList& rUserList = ScGlobal::GetUserList();
1737
0
        size_t nUserListSize = rUserList.size();
1738
0
        if (!nUserListSize || *pUserListId >= static_cast<sal_uInt16>(nUserListSize))
1739
0
            return;
1740
1741
0
        const ScUserListData& rData = rUserList[*pUserListId];
1742
0
        sal_uInt16 n = rData.GetSubCount();
1743
0
        for (sal_uInt16 i = 0; i < n; ++i)
1744
0
        {
1745
0
            OUString aSub = rData.GetSubStr(i);
1746
0
            if (!aMemberSet.count(aSub))
1747
                // This string doesn't exist in the member name set.  Don't add this.
1748
0
                continue;
1749
1750
0
            aSubStrs.emplace(aSub, nSubCount++);
1751
0
        }
1752
1753
        // Rank all members.
1754
1755
0
        std::vector<OUString> aRankedNames(nMemberCount);
1756
0
        sal_uInt16 nCurStrId = 0;
1757
0
        for (auto const& aMemberName : aMembers)
1758
0
        {
1759
0
            sal_uInt16 nRank = 0;
1760
0
            UserSortMap::const_iterator itrSub = aSubStrs.find(aMemberName);
1761
0
            if (itrSub == aSubStrs.end())
1762
0
                nRank = nSubCount + nCurStrId++;
1763
0
            else
1764
0
                nRank = itrSub->second;
1765
1766
0
            if (!bAscending)
1767
0
                nRank = static_cast< sal_uInt16 >( nMemberCount - nRank - 1 );
1768
1769
0
            aRankedNames[nRank] = aMemberName;
1770
0
        }
1771
1772
        // Re-order ScDPSaveMember instances with the new ranks.
1773
0
        for (auto const& aRankedName : aRankedNames)
1774
0
        {
1775
0
            const ScDPSaveMember* pOldMem = pSaveDim->GetExistingMemberByName(aRankedName);
1776
0
            if (!pOldMem)
1777
                // All members are supposed to be present.
1778
0
                continue;
1779
1780
0
            pSaveDim->AddMember(std::unique_ptr<ScDPSaveMember>(new ScDPSaveMember(*pOldMem)));
1781
0
        }
1782
1783
        // Set the sorting mode to manual for now.  We may introduce a new sorting
1784
        // mode later on.
1785
1786
0
        sheet::DataPilotFieldSortInfo aSortInfo;
1787
0
        aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
1788
0
        pSaveDim->SetSortInfo(&aSortInfo);
1789
0
    }
1790
0
    else
1791
0
    {
1792
        // without user list id, just apply sorting mode
1793
1794
0
        sheet::DataPilotFieldSortInfo aSortInfo;
1795
0
        aSortInfo.Mode = sheet::DataPilotFieldSortMode::NAME;
1796
0
        aSortInfo.IsAscending = bAscending;
1797
0
        pSaveDim->SetSortInfo(&aSortInfo);
1798
0
    }
1799
1800
    // Update the datapilot with the newly sorted field members.
1801
1802
0
    std::unique_ptr<ScDPObject> pNewObj(new ScDPObject(*pDPObj));
1803
0
    pNewObj->SetSaveData(aNewSaveData);
1804
0
    ScDBDocFunc aFunc(*GetViewData().GetDocShell());
1805
1806
0
    aFunc.DataPilotUpdate(pDPObj, pNewObj.get(), true, false);
1807
0
}
1808
1809
bool ScDBFunc::DataPilotMove( const ScRange& rSource, const ScAddress& rDest )
1810
0
{
1811
0
    bool bRet = false;
1812
0
    ScDocument& rDoc = GetViewData().GetDocument();
1813
0
    ScDPObject* pDPObj = rDoc.GetDPAtCursor( rSource.aStart.Col(), rSource.aStart.Row(), rSource.aStart.Tab() );
1814
0
    if ( pDPObj && pDPObj == rDoc.GetDPAtCursor( rDest.Col(), rDest.Row(), rDest.Tab() ) )
1815
0
    {
1816
0
        sheet::DataPilotTableHeaderData aDestData;
1817
0
        pDPObj->GetHeaderPositionData( rDest, aDestData );
1818
0
        bool bValid = ( aDestData.Dimension >= 0 );        // dropping onto a field
1819
1820
        // look through the source range
1821
0
        std::unordered_set< OUString > aMembersSet;   // for lookup
1822
0
        std::vector< OUString > aMembersVector;  // members in original order, for inserting
1823
0
        aMembersVector.reserve( std::max( static_cast<SCSIZE>( rSource.aEnd.Col() - rSource.aStart.Col() + 1 ),
1824
0
                                          static_cast<SCSIZE>( rSource.aEnd.Row() - rSource.aStart.Row() + 1 ) ) );
1825
0
        for (SCROW nRow = rSource.aStart.Row(); bValid && nRow <= rSource.aEnd.Row(); ++nRow )
1826
0
            for (SCCOL nCol = rSource.aStart.Col(); bValid && nCol <= rSource.aEnd.Col(); ++nCol )
1827
0
            {
1828
0
                sheet::DataPilotTableHeaderData aSourceData;
1829
0
                pDPObj->GetHeaderPositionData( ScAddress( nCol, nRow, rSource.aStart.Tab() ), aSourceData );
1830
0
                if ( aSourceData.Dimension == aDestData.Dimension && !aSourceData.MemberName.isEmpty() )
1831
0
                {
1832
0
                    if ( aMembersSet.insert( aSourceData.MemberName ).second )
1833
0
                    {
1834
0
                        aMembersVector.push_back( aSourceData.MemberName );
1835
0
                    }
1836
                    // duplicates are ignored
1837
0
                }
1838
0
                else
1839
0
                    bValid = false;     // empty (subtotal) or different field
1840
0
            }
1841
1842
0
        if ( bValid )
1843
0
        {
1844
0
            bool bIsDataLayout;
1845
0
            OUString aDimName = pDPObj->GetDimName( aDestData.Dimension, bIsDataLayout );
1846
0
            if ( !bIsDataLayout )
1847
0
            {
1848
0
                ScDPSaveData aData( *pDPObj->GetSaveData() );
1849
0
                ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName );
1850
1851
                // get all member names in source order
1852
0
                uno::Sequence<OUString> aMemberNames;
1853
0
                pDPObj->GetMemberNames( aDestData.Dimension, aMemberNames );
1854
1855
0
                bool bInserted = false;
1856
1857
0
                for (const OUString& aMemberStr : aMemberNames)
1858
0
                {
1859
0
                    if ( !bInserted && aMemberStr == aDestData.MemberName )
1860
0
                    {
1861
                        // insert dragged items before this item
1862
0
                        for ( const auto& rMember : aMembersVector )
1863
0
                            lcl_MoveToEnd( *pDim, rMember );
1864
0
                        bInserted = true;
1865
0
                    }
1866
1867
0
                    if ( aMembersSet.find( aMemberStr ) == aMembersSet.end() )  // skip dragged items
1868
0
                        lcl_MoveToEnd( *pDim, aMemberStr );
1869
0
                }
1870
                // insert dragged item at end if dest wasn't found (for example, empty)
1871
0
                if ( !bInserted )
1872
0
                    for ( const auto& rMember : aMembersVector )
1873
0
                        lcl_MoveToEnd( *pDim, rMember );
1874
1875
                // Items that were in SaveData, but not in the source, end up at the start of the list.
1876
1877
                // set flag for manual sorting
1878
0
                sheet::DataPilotFieldSortInfo aSortInfo;
1879
0
                aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
1880
0
                pDim->SetSortInfo( &aSortInfo );
1881
1882
                // apply changes
1883
0
                ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1884
0
                std::unique_ptr<ScDPObject> pNewObj(new ScDPObject( *pDPObj ));
1885
0
                pNewObj->SetSaveData( aData );
1886
0
                aFunc.DataPilotUpdate( pDPObj, pNewObj.get(), true, false );      //! bApi for drag&drop?
1887
0
                pNewObj.reset();
1888
1889
0
                Unmark();       // entry was moved - no use in leaving the old cell selected
1890
1891
0
                bRet = true;
1892
0
            }
1893
0
        }
1894
0
    }
1895
1896
0
    return bRet;
1897
0
}
1898
1899
bool ScDBFunc::HasSelectionForDrillDown( css::sheet::DataPilotFieldOrientation& rOrientation )
1900
0
{
1901
0
    bool bRet = false;
1902
1903
0
    ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1904
0
                                        GetViewData().GetCurY(), GetViewData().CurrentTabForData() );
1905
0
    if ( pDPObj )
1906
0
    {
1907
0
        ScDPUniqueStringSet aEntries;
1908
0
        tools::Long nSelectDimension = -1;
1909
0
        GetSelectedMemberList( aEntries, nSelectDimension );
1910
1911
0
        if (!aEntries.empty())
1912
0
        {
1913
0
            bool bIsDataLayout;
1914
0
            OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1915
0
            if ( !bIsDataLayout )
1916
0
            {
1917
0
                ScDPSaveData* pSaveData = pDPObj->GetSaveData();
1918
0
                ScDPSaveDimension* pDim = pSaveData->GetExistingDimensionByName( aDimName );
1919
0
                if ( pDim )
1920
0
                {
1921
0
                    css::sheet::DataPilotFieldOrientation nDimOrient = pDim->GetOrientation();
1922
0
                    ScDPSaveDimension* pInner = pSaveData->GetInnermostDimension( nDimOrient );
1923
0
                    if ( pDim == pInner )
1924
0
                    {
1925
0
                        rOrientation = nDimOrient;
1926
0
                        bRet = true;
1927
0
                    }
1928
0
                }
1929
0
            }
1930
0
        }
1931
0
    }
1932
1933
0
    return bRet;
1934
0
}
1935
1936
void ScDBFunc::SetDataPilotDetails(bool bShow, const OUString* pNewDimensionName)
1937
0
{
1938
0
    ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1939
0
                                        GetViewData().GetCurY(), GetViewData().CurrentTabForData() );
1940
0
    if ( !pDPObj )
1941
0
        return;
1942
1943
0
    ScDPUniqueStringSet aEntries;
1944
0
    tools::Long nSelectDimension = -1;
1945
0
    GetSelectedMemberList( aEntries, nSelectDimension );
1946
1947
0
    if (aEntries.empty())
1948
0
        return;
1949
1950
0
    bool bIsDataLayout;
1951
0
    OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1952
0
    if ( bIsDataLayout )
1953
0
        return;
1954
1955
0
    ScDPSaveData aData( *pDPObj->GetSaveData() );
1956
0
    ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName );
1957
1958
0
    if ( bShow && pNewDimensionName )
1959
0
    {
1960
        //  add the new dimension with the same orientation, at the end
1961
1962
0
        ScDPSaveDimension* pNewDim = aData.GetDimensionByName( *pNewDimensionName );
1963
0
        ScDPSaveDimension* pDuplicated = nullptr;
1964
0
        if ( pNewDim->GetOrientation() == sheet::DataPilotFieldOrientation_DATA )
1965
0
        {
1966
            // Need to duplicate the dimension, create column/row in addition to data:
1967
            // The duplicated dimension inherits the existing settings, pNewDim is modified below.
1968
0
            pDuplicated = aData.DuplicateDimension( *pNewDimensionName );
1969
0
        }
1970
1971
0
        css::sheet::DataPilotFieldOrientation nOrientation = pDim->GetOrientation();
1972
0
        pNewDim->SetOrientation( nOrientation );
1973
1974
0
        tools::Long nPosition = LONG_MAX;
1975
0
        aData.SetPosition( pNewDim, nPosition );
1976
1977
0
        ScDPSaveDimension* pDataLayout = aData.GetDataLayoutDimension();
1978
0
        if ( pDataLayout->GetOrientation() == nOrientation &&
1979
0
             aData.GetDataDimensionCount() <= 1 )
1980
0
        {
1981
            // If there is only one data dimension, the data layout dimension
1982
            // must still be the last one in its orientation.
1983
0
            aData.SetPosition( pDataLayout, nPosition );
1984
0
        }
1985
1986
0
        if ( pDuplicated )
1987
0
        {
1988
            // The duplicated (data) dimension needs to be behind the original dimension
1989
0
            aData.SetPosition( pDuplicated, nPosition );
1990
0
        }
1991
1992
        //  Hide details for all visible members (selected are changed below).
1993
        //! Use all members from source level instead (including non-visible)?
1994
1995
0
        ScDPUniqueStringSet aVisibleEntries;
1996
0
        pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension );
1997
1998
0
        for (const OUString& aVisName : aVisibleEntries)
1999
0
        {
2000
0
            ScDPSaveMember* pMember = pDim->GetMemberByName( aVisName );
2001
0
            pMember->SetShowDetails( false );
2002
0
        }
2003
0
    }
2004
2005
0
    for (const auto& rEntry : aEntries)
2006
0
    {
2007
0
        ScDPSaveMember* pMember = pDim->GetMemberByName(rEntry);
2008
0
        pMember->SetShowDetails( bShow );
2009
0
    }
2010
2011
    // apply changes
2012
0
    ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
2013
0
    std::unique_ptr<ScDPObject> pNewObj(new ScDPObject( *pDPObj ));
2014
0
    pNewObj->SetSaveData( aData );
2015
0
    aFunc.DataPilotUpdate( pDPObj, pNewObj.get(), true, false );
2016
0
    pNewObj.reset();
2017
2018
    // unmark cell selection
2019
0
    Unmark();
2020
0
}
2021
2022
void ScDBFunc::ShowDataPilotSourceData( ScDPObject& rDPObj, const Sequence<sheet::DataPilotFieldFilter>& rFilters )
2023
0
{
2024
0
    ScDocument& rDoc = GetViewData().GetDocument();
2025
0
    if (rDoc.GetDocumentShell()->IsReadOnly())
2026
0
    {
2027
0
        ErrorMessage(STR_READONLYERR);
2028
0
        return;
2029
0
    }
2030
2031
0
    Reference<sheet::XDimensionsSupplier> xDimSupplier = rDPObj.GetSource();
2032
0
    Reference<container::XNameAccess> xDims = xDimSupplier->getDimensions();
2033
0
    Reference<sheet::XDrillDownDataSupplier> xDDSupplier(xDimSupplier, UNO_QUERY);
2034
0
    if (!xDDSupplier.is())
2035
0
        return;
2036
2037
0
    Sequence< Sequence<Any> > aTabData = xDDSupplier->getDrillDownData(rFilters);
2038
0
    sal_Int32 nRowSize = aTabData.getLength();
2039
0
    if (nRowSize <= 1)
2040
        // There is no data to show.  Bail out.
2041
0
        return;
2042
2043
0
    SCCOL nColSize = aTabData[0].getLength();
2044
2045
0
    SCTAB nNewTab = GetViewData().CurrentTabForData();
2046
2047
0
    ScDocumentUniquePtr pInsDoc(new ScDocument(SCDOCMODE_CLIP));
2048
0
    pInsDoc->ResetClip( &rDoc, nNewTab );
2049
0
    for (SCROW nRow = 0; nRow < nRowSize; ++nRow)
2050
0
    {
2051
0
        for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
2052
0
        {
2053
0
            const Any& rAny = aTabData[nRow][nCol];
2054
0
            OUString aStr;
2055
0
            double fVal;
2056
0
            if (rAny >>= aStr)
2057
0
            {
2058
0
                pInsDoc->SetString(ScAddress(nCol,nRow,nNewTab), aStr);
2059
0
            }
2060
0
            else if (rAny >>= fVal)
2061
0
                pInsDoc->SetValue(nCol, nRow, nNewTab, fVal);
2062
0
        }
2063
0
    }
2064
2065
    // set number format (important for dates)
2066
0
    for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
2067
0
    {
2068
0
        OUString aStr;
2069
0
        if (!(aTabData[0][nCol] >>= aStr))
2070
0
            continue;
2071
2072
0
        Reference<XPropertySet> xPropSet(xDims->getByName(aStr), UNO_QUERY);
2073
0
        if (!xPropSet.is())
2074
0
            continue;
2075
2076
0
        Any any = xPropSet->getPropertyValue( SC_UNO_DP_NUMBERFO );
2077
0
        sal_Int32 nNumFmt = 0;
2078
0
        if (!(any >>= nNumFmt))
2079
0
            continue;
2080
2081
0
        ScPatternAttr aPattern(pInsDoc->getCellAttributeHelper());
2082
0
        aPattern.ItemSetPut(SfxUInt32Item(ATTR_VALUE_FORMAT, static_cast<sal_uInt32>(nNumFmt)));
2083
0
        pInsDoc->ApplyPatternAreaTab(nCol, 1, nCol, nRowSize-1, nNewTab, aPattern);
2084
0
    }
2085
2086
0
    SCCOL nEndCol = 0;
2087
0
    SCROW nEndRow = 0;
2088
0
    pInsDoc->GetCellArea( nNewTab, nEndCol, nEndRow );
2089
0
    pInsDoc->SetClipArea( ScRange( 0, 0, nNewTab, nEndCol, nEndRow, nNewTab ) );
2090
2091
0
    SfxUndoManager* pMgr = GetViewData().GetDocShell()->GetUndoManager();
2092
0
    OUString aUndo = ScResId( STR_UNDO_DOOUTLINE );
2093
0
    pMgr->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
2094
2095
0
    OUString aNewTabName;
2096
0
    rDoc.CreateValidTabName(aNewTabName);
2097
0
    if ( InsertTable(aNewTabName, nNewTab) )
2098
0
        PasteFromClip( InsertDeleteFlags::ALL, pInsDoc.get() );
2099
2100
0
    pMgr->LeaveListAction();
2101
0
}
2102
2103
// repeat data base operations (sorting, filtering, subtotals)
2104
2105
void ScDBFunc::RepeatDB( bool bRecord )
2106
0
{
2107
0
    SCCOL nCurX = GetViewData().GetCurX();
2108
0
    SCROW nCurY = GetViewData().GetCurY();
2109
0
    SCTAB nTab = GetViewData().CurrentTabForData();
2110
0
    ScDocument& rDoc = GetViewData().GetDocument();
2111
0
    ScDBData* pDBData = GetDBData();
2112
0
    if (bRecord && !rDoc.IsUndoEnabled())
2113
0
        bRecord = false;
2114
2115
0
    ScQueryParam aQueryParam;
2116
0
    pDBData->GetQueryParam( aQueryParam );
2117
0
    bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
2118
2119
0
    ScSortParam aSortParam;
2120
0
    pDBData->GetSortParam( aSortParam );
2121
0
    bool bSort = aSortParam.maKeyState[0].bDoSort;
2122
2123
0
    ScSubTotalParam aSubTotalParam;
2124
0
    pDBData->GetSubTotalParam( aSubTotalParam );
2125
0
    bool bSubTotal = aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly;
2126
2127
0
    if ( bQuery || bSort || bSubTotal )
2128
0
    {
2129
0
        bool bQuerySize = false;
2130
0
        ScRange aOldQuery;
2131
0
        ScRange aNewQuery;
2132
0
        if (bQuery && !aQueryParam.bInplace)
2133
0
        {
2134
0
            ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
2135
0
                                                  aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
2136
0
            if (pDest && pDest->IsDoSize())
2137
0
            {
2138
0
                pDest->GetArea( aOldQuery );
2139
0
                bQuerySize = true;
2140
0
            }
2141
0
        }
2142
2143
0
        SCTAB nDummy;
2144
0
        SCCOL nStartCol;
2145
0
        SCROW nStartRow;
2146
0
        SCCOL nEndCol;
2147
0
        SCROW nEndRow;
2148
0
        pDBData->GetArea( nDummy, nStartCol, nStartRow, nEndCol, nEndRow );
2149
2150
        //! undo only needed data ?
2151
2152
0
        ScDocumentUniquePtr pUndoDoc;
2153
0
        std::unique_ptr<ScOutlineTable> pUndoTab;
2154
0
        std::unique_ptr<ScRangeName> pUndoRange;
2155
0
        std::unique_ptr<ScDBCollection> pUndoDB;
2156
2157
0
        if (bRecord)
2158
0
        {
2159
0
            SCTAB nTabCount = rDoc.GetTableCount();
2160
0
            pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2161
0
            ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
2162
0
            if (pTable)
2163
0
            {
2164
0
                pUndoTab.reset(new ScOutlineTable( *pTable ));
2165
2166
0
                SCCOLROW nOutStartCol;                          // row/column status
2167
0
                SCCOLROW nOutStartRow;
2168
0
                SCCOLROW nOutEndCol;
2169
0
                SCCOLROW nOutEndRow;
2170
0
                pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
2171
0
                pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
2172
2173
0
                pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
2174
0
                rDoc.CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
2175
0
                rDoc.CopyToDocument( 0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
2176
0
            }
2177
0
            else
2178
0
                pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
2179
2180
            // Record data range - including filter results
2181
0
            rDoc.CopyToDocument( 0,nStartRow,nTab, rDoc.MaxCol(),nEndRow,nTab, InsertDeleteFlags::ALL, false, *pUndoDoc );
2182
2183
            // all formulas for reference
2184
0
            rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),nTabCount-1, InsertDeleteFlags::FORMULA, false, *pUndoDoc );
2185
2186
            // data base and other ranges
2187
0
            ScRangeName* pDocRange = rDoc.GetRangeName();
2188
0
            if (!pDocRange->empty())
2189
0
                pUndoRange.reset(new ScRangeName( *pDocRange ));
2190
0
            ScDBCollection* pDocDB = rDoc.GetDBCollection();
2191
0
            if (!pDocDB->empty())
2192
0
                pUndoDB.reset(new ScDBCollection( *pDocDB ));
2193
0
        }
2194
2195
0
        if (bSort && bSubTotal)
2196
0
        {
2197
            // sort without subtotals
2198
2199
0
            aSubTotalParam.bRemoveOnly = true;      // is reset below
2200
0
            DoSubTotals( aSubTotalParam, false );
2201
0
        }
2202
2203
0
        if (bSort)
2204
0
        {
2205
0
            pDBData->GetSortParam( aSortParam );            // range may have changed
2206
0
            Sort( aSortParam, false, false);
2207
0
        }
2208
0
        if (bQuery)
2209
0
        {
2210
0
            pDBData->GetQueryParam( aQueryParam );          // range may have changed
2211
0
            ScRange aAdvSource;
2212
0
            if (pDBData->GetAdvancedQuerySource(aAdvSource))
2213
0
            {
2214
0
                rDoc.CreateQueryParam(aAdvSource, aQueryParam);
2215
0
                Query( aQueryParam, &aAdvSource, false );
2216
0
            }
2217
0
            else
2218
0
                Query( aQueryParam, nullptr, false );
2219
2220
            // if not inplace the sheet may have changed
2221
0
            if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
2222
0
                SetTabNo( nTab );
2223
0
        }
2224
0
        if (bSubTotal)
2225
0
        {
2226
0
            pDBData->GetSubTotalParam( aSubTotalParam );    // range may have changed
2227
0
            aSubTotalParam.bRemoveOnly = false;
2228
0
            DoSubTotals( aSubTotalParam, false );
2229
0
        }
2230
2231
0
        if (bRecord)
2232
0
        {
2233
0
            SCTAB nDummyTab;
2234
0
            SCCOL nDummyCol;
2235
0
            SCROW nDummyRow, nNewEndRow;
2236
0
            pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
2237
2238
0
            const ScRange* pOld = nullptr;
2239
0
            const ScRange* pNew = nullptr;
2240
0
            if (bQuerySize)
2241
0
            {
2242
0
                ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
2243
0
                                                      aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
2244
0
                if (pDest)
2245
0
                {
2246
0
                    pDest->GetArea( aNewQuery );
2247
0
                    pOld = &aOldQuery;
2248
0
                    pNew = &aNewQuery;
2249
0
                }
2250
0
            }
2251
2252
0
            GetViewData().GetDocShell()->GetUndoManager()->AddUndoAction(
2253
0
                std::make_unique<ScUndoRepeatDB>( *GetViewData().GetDocShell(), nTab,
2254
0
                                        nStartCol, nStartRow, nEndCol, nEndRow,
2255
0
                                        nNewEndRow,
2256
0
                                        nCurX, nCurY,
2257
0
                                        std::move(pUndoDoc), std::move(pUndoTab),
2258
0
                                        std::move(pUndoRange), std::move(pUndoDB),
2259
0
                                        pOld, pNew ) );
2260
0
        }
2261
2262
0
        GetViewData().GetDocShell()->PostPaint(
2263
0
            ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
2264
0
            PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
2265
0
    }
2266
0
    else        // "no not execute any operations"
2267
0
        ErrorMessage(STR_MSSG_REPEATDB_0);
2268
0
}
2269
2270
void ScDBFunc::OnLOKShowHideColRow(bool bColumns, SCCOLROW nStart)
2271
0
{
2272
0
    if (!comphelper::LibreOfficeKit::isActive())
2273
0
        return;
2274
2275
0
    SCTAB nCurrentTabIndex = GetViewData().CurrentTabForData();
2276
0
    SfxViewShell* pThisViewShell = GetViewData().GetViewShell();
2277
0
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
2278
0
    while (pViewShell)
2279
0
    {
2280
0
        ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
2281
0
        if (pTabViewShell && pTabViewShell->GetDocId() == pThisViewShell->GetDocId())
2282
0
        {
2283
0
            if (bColumns)
2284
0
            {
2285
0
                if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurrentTabIndex))
2286
0
                    pPosHelper->invalidateByIndex(nStart);
2287
0
            }
2288
0
            else
2289
0
            {
2290
0
                if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurrentTabIndex))
2291
0
                    pPosHelper->invalidateByIndex(nStart);
2292
0
            }
2293
2294
0
            if (pTabViewShell->getPart() == nCurrentTabIndex)
2295
0
            {
2296
0
                pTabViewShell->ShowCursor();
2297
0
                pTabViewShell->MarkDataChanged();
2298
0
            }
2299
0
        }
2300
0
        pViewShell = SfxViewShell::GetNext(*pViewShell);
2301
0
    }
2302
0
}
2303
2304
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */