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 <sfx2/app.hxx>
21 #include <vcl/svapp.hxx>
22 #include <vcl/weld.hxx>
23 #include <vcl/waitobj.hxx>
24 #include <svx/dataaccessdescriptor.hxx>
25 #include <svx/svdpage.hxx>
26 #include <svx/svdoole2.hxx>
27 #include <com/sun/star/sdb/CommandType.hpp>
28 #include <unotools/charclass.hxx>
29 
30 #include <dbdocfun.hxx>
31 #include <dbdata.hxx>
32 #include <undodat.hxx>
33 #include <docsh.hxx>
34 #include <docfunc.hxx>
35 #include <globstr.hrc>
36 #include <scresid.hxx>
37 #include <globalnames.hxx>
38 #include <tabvwsh.hxx>
39 #include <patattr.hxx>
40 #include <rangenam.hxx>
41 #include <olinetab.hxx>
42 #include <dpobject.hxx>
43 #include <dpsave.hxx>
44 #include <dociter.hxx>
45 #include <editable.hxx>
46 #include <attrib.hxx>
47 #include <drwlayer.hxx>
48 #include <dpshttab.hxx>
49 #include <hints.hxx>
50 #include <queryentry.hxx>
51 #include <markdata.hxx>
52 #include <progress.hxx>
53 #include <undosort.hxx>
54 #include <inputopt.hxx>
55 #include <scmod.hxx>
56 
57 #include <chartlis.hxx>
58 #include <ChartTools.hxx>
59 
60 #include <set>
61 #include <memory>
62 
63 using namespace ::com::sun::star;
64 
AddDBRange(const OUString & rName,const ScRange & rRange)65 bool ScDBDocFunc::AddDBRange( const OUString& rName, const ScRange& rRange )
66 {
67 
68     ScDocShellModificator aModificator( rDocShell );
69 
70     ScDocument& rDoc = rDocShell.GetDocument();
71     ScDBCollection* pDocColl = rDoc.GetDBCollection();
72     bool bUndo (rDoc.IsUndoEnabled());
73 
74     std::unique_ptr<ScDBCollection> pUndoColl;
75     if (bUndo)
76         pUndoColl.reset( new ScDBCollection( *pDocColl ) );
77 
78     std::unique_ptr<ScDBData> pNew(new ScDBData( rName, rRange.aStart.Tab(),
79                                     rRange.aStart.Col(), rRange.aStart.Row(),
80                                     rRange.aEnd.Col(), rRange.aEnd.Row() ));
81 
82     // #i55926# While loading XML, formula cells only have a single string token,
83     // so CompileDBFormula would never find any name (index) tokens, and would
84     // unnecessarily loop through all cells.
85     bool bCompile = !rDoc.IsImportingXML();
86     bool bOk;
87     if ( bCompile )
88         rDoc.PreprocessDBDataUpdate();
89     if ( rName == STR_DB_LOCAL_NONAME )
90     {
91         rDoc.SetAnonymousDBData(rRange.aStart.Tab(), std::move(pNew));
92         bOk = true;
93     }
94     else
95     {
96         bOk = pDocColl->getNamedDBs().insert(std::move(pNew));
97     }
98     if ( bCompile )
99         rDoc.CompileHybridFormula();
100 
101     if (!bOk)
102     {
103         return false;
104     }
105 
106     if (bUndo)
107     {
108         rDocShell.GetUndoManager()->AddUndoAction(
109                         std::make_unique<ScUndoDBData>( &rDocShell, std::move(pUndoColl),
110                             std::make_unique<ScDBCollection>( *pDocColl ) ) );
111     }
112 
113     aModificator.SetDocumentModified();
114     SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
115     return true;
116 }
117 
DeleteDBRange(const OUString & rName)118 bool ScDBDocFunc::DeleteDBRange(const OUString& rName)
119 {
120     bool bDone = false;
121     ScDocument& rDoc = rDocShell.GetDocument();
122     ScDBCollection* pDocColl = rDoc.GetDBCollection();
123     bool bUndo = rDoc.IsUndoEnabled();
124 
125     ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs();
126     auto const iter = rDBs.findByUpperName2(ScGlobal::pCharClass->uppercase(rName));
127     if (iter != rDBs.end())
128     {
129         ScDocShellModificator aModificator( rDocShell );
130 
131         std::unique_ptr<ScDBCollection> pUndoColl;
132         if (bUndo)
133             pUndoColl.reset( new ScDBCollection( *pDocColl ) );
134 
135         rDoc.PreprocessDBDataUpdate();
136         rDBs.erase(iter);
137         rDoc.CompileHybridFormula();
138 
139         if (bUndo)
140         {
141             rDocShell.GetUndoManager()->AddUndoAction(
142                             std::make_unique<ScUndoDBData>( &rDocShell, std::move(pUndoColl),
143                                 std::make_unique<ScDBCollection>( *pDocColl ) ) );
144         }
145 
146         aModificator.SetDocumentModified();
147         SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
148         bDone = true;
149     }
150 
151     return bDone;
152 }
153 
RenameDBRange(const OUString & rOld,const OUString & rNew)154 bool ScDBDocFunc::RenameDBRange( const OUString& rOld, const OUString& rNew )
155 {
156     bool bDone = false;
157     ScDocument& rDoc = rDocShell.GetDocument();
158     ScDBCollection* pDocColl = rDoc.GetDBCollection();
159     bool bUndo = rDoc.IsUndoEnabled();
160     ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs();
161     auto const iterOld = rDBs.findByUpperName2(ScGlobal::pCharClass->uppercase(rOld));
162     const ScDBData* pNew = rDBs.findByUpperName(ScGlobal::pCharClass->uppercase(rNew));
163     if (iterOld != rDBs.end() && !pNew)
164     {
165         ScDocShellModificator aModificator( rDocShell );
166 
167         std::unique_ptr<ScDBData> pNewData(new ScDBData(rNew, **iterOld));
168 
169         std::unique_ptr<ScDBCollection> pUndoColl( new ScDBCollection( *pDocColl ) );
170 
171         rDoc.PreprocessDBDataUpdate();
172         rDBs.erase(iterOld);
173         bool bInserted = rDBs.insert(std::move(pNewData));
174         if (!bInserted)                             // error -> restore old state
175         {
176             rDoc.SetDBCollection(std::move(pUndoColl));       // belongs to the document then
177         }
178 
179         rDoc.CompileHybridFormula();
180 
181         if (bInserted)                              // insertion worked
182         {
183             if (bUndo)
184             {
185                 rDocShell.GetUndoManager()->AddUndoAction(
186                                 std::make_unique<ScUndoDBData>( &rDocShell, std::move(pUndoColl),
187                                     std::make_unique<ScDBCollection>( *pDocColl ) ) );
188             }
189             else
190                 pUndoColl.reset();
191 
192             aModificator.SetDocumentModified();
193             SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
194             bDone = true;
195         }
196     }
197 
198     return bDone;
199 }
200 
ModifyDBData(const ScDBData & rNewData)201 void ScDBDocFunc::ModifyDBData( const ScDBData& rNewData )
202 {
203     ScDocument& rDoc = rDocShell.GetDocument();
204     ScDBCollection* pDocColl = rDoc.GetDBCollection();
205     bool bUndo = rDoc.IsUndoEnabled();
206 
207     ScDBData* pData = nullptr;
208     if (rNewData.GetName() == STR_DB_LOCAL_NONAME)
209     {
210         ScRange aRange;
211         rNewData.GetArea(aRange);
212         SCTAB nTab = aRange.aStart.Tab();
213         pData = rDoc.GetAnonymousDBData(nTab);
214     }
215     else
216         pData = pDocColl->getNamedDBs().findByUpperName(rNewData.GetUpperName());
217 
218     if (pData)
219     {
220         ScDocShellModificator aModificator( rDocShell );
221         ScRange aOldRange, aNewRange;
222         pData->GetArea(aOldRange);
223         rNewData.GetArea(aNewRange);
224         bool bAreaChanged = ( aOldRange != aNewRange );     // then a recompilation is needed
225 
226         std::unique_ptr<ScDBCollection> pUndoColl;
227         if (bUndo)
228             pUndoColl.reset( new ScDBCollection( *pDocColl ) );
229 
230         *pData = rNewData;
231         if (bAreaChanged)
232             rDoc.CompileDBFormula();
233 
234         if (bUndo)
235         {
236             rDocShell.GetUndoManager()->AddUndoAction(
237                             std::make_unique<ScUndoDBData>( &rDocShell, std::move(pUndoColl),
238                                 std::make_unique<ScDBCollection>( *pDocColl ) ) );
239         }
240 
241         aModificator.SetDocumentModified();
242     }
243 }
244 
ModifyAllDBData(const ScDBCollection & rNewColl,const std::vector<ScRange> & rDelAreaList)245 void ScDBDocFunc::ModifyAllDBData( const ScDBCollection& rNewColl, const std::vector<ScRange>& rDelAreaList )
246 {
247     ScDocShellModificator aModificator(rDocShell);
248     ScDocument& rDoc = rDocShell.GetDocument();
249     ScDBCollection* pOldColl = rDoc.GetDBCollection();
250     std::unique_ptr<ScDBCollection> pUndoColl;
251     bool bRecord = rDoc.IsUndoEnabled();
252 
253     for (const auto& rDelArea : rDelAreaList)
254     {
255         // unregistering target in SBA no longer necessary
256         const ScAddress& rStart = rDelArea.aStart;
257         const ScAddress& rEnd   = rDelArea.aEnd;
258         rDocShell.DBAreaDeleted(
259             rStart.Tab(), rStart.Col(), rStart.Row(), rEnd.Col());
260     }
261 
262     if (bRecord)
263         pUndoColl.reset( new ScDBCollection( *pOldColl ) );
264 
265     //  register target in SBA no longer necessary
266 
267     rDoc.PreprocessDBDataUpdate();
268     rDoc.SetDBCollection( std::unique_ptr<ScDBCollection>(new ScDBCollection( rNewColl )) );
269     rDoc.CompileHybridFormula();
270     pOldColl = nullptr;
271     rDocShell.PostPaint(ScRange(0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB), PaintPartFlags::Grid);
272     aModificator.SetDocumentModified();
273     SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
274 
275     if (bRecord)
276     {
277         rDocShell.GetUndoManager()->AddUndoAction(
278             std::make_unique<ScUndoDBData>(&rDocShell, std::move(pUndoColl),
279                 std::make_unique<ScDBCollection>(rNewColl)));
280     }
281 }
282 
RepeatDB(const OUString & rDBName,bool bApi,bool bIsUnnamed,SCTAB aTab)283 bool ScDBDocFunc::RepeatDB( const OUString& rDBName, bool bApi, bool bIsUnnamed, SCTAB aTab )
284 {
285     //! use also for ScDBFunc::RepeatDB !
286 
287     bool bDone = false;
288     ScDocument& rDoc = rDocShell.GetDocument();
289     bool bRecord = true;
290     if (!rDoc.IsUndoEnabled())
291         bRecord = false;
292     ScDBData* pDBData = nullptr;
293     if (bIsUnnamed)
294     {
295         pDBData = rDoc.GetAnonymousDBData( aTab );
296     }
297     else
298     {
299         ScDBCollection* pColl = rDoc.GetDBCollection();
300         if (pColl)
301             pDBData = pColl->getNamedDBs().findByUpperName(ScGlobal::pCharClass->uppercase(rDBName));
302     }
303 
304     if ( pDBData )
305     {
306         ScQueryParam aQueryParam;
307         pDBData->GetQueryParam( aQueryParam );
308         bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
309 
310         ScSortParam aSortParam;
311         pDBData->GetSortParam( aSortParam );
312         bool bSort = aSortParam.maKeyState[0].bDoSort;
313 
314         ScSubTotalParam aSubTotalParam;
315         pDBData->GetSubTotalParam( aSubTotalParam );
316         bool bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly;
317 
318         if ( bQuery || bSort || bSubTotal )
319         {
320             bool bQuerySize = false;
321             ScRange aOldQuery;
322             ScRange aNewQuery;
323             if (bQuery && !aQueryParam.bInplace)
324             {
325                 ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
326                                                         aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
327                 if (pDest && pDest->IsDoSize())
328                 {
329                     pDest->GetArea( aOldQuery );
330                     bQuerySize = true;
331                 }
332             }
333 
334             SCTAB nTab;
335             SCCOL nStartCol;
336             SCROW nStartRow;
337             SCCOL nEndCol;
338             SCROW nEndRow;
339             pDBData->GetArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
340 
341             //!     Undo needed data only ?
342 
343             ScDocumentUniquePtr pUndoDoc;
344             std::unique_ptr<ScOutlineTable> pUndoTab;
345             std::unique_ptr<ScRangeName> pUndoRange;
346             std::unique_ptr<ScDBCollection> pUndoDB;
347 
348             if (bRecord)
349             {
350                 SCTAB nTabCount = rDoc.GetTableCount();
351                 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
352                 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
353                 if (pTable)
354                 {
355                     pUndoTab.reset(new ScOutlineTable( *pTable ));
356 
357                     // column/row state
358                     SCCOLROW nOutStartCol, nOutEndCol;
359                     SCCOLROW nOutStartRow, nOutEndRow;
360                     pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
361                     pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
362 
363                     pUndoDoc->InitUndo( &rDoc, nTab, nTab, true, true );
364                     rDoc.CopyToDocument(static_cast<SCCOL>(nOutStartCol), 0,
365                                         nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab,
366                                         InsertDeleteFlags::NONE, false, *pUndoDoc);
367                     rDoc.CopyToDocument(0, static_cast<SCROW>(nOutStartRow),
368                                         nTab, rDoc.MaxCol(), static_cast<SCROW>(nOutEndRow), nTab,
369                                         InsertDeleteFlags::NONE, false, *pUndoDoc);
370                 }
371                 else
372                     pUndoDoc->InitUndo( &rDoc, nTab, nTab, false, true );
373 
374                 //  secure data range - incl. filtering result
375                 rDoc.CopyToDocument(0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab, InsertDeleteFlags::ALL, false, *pUndoDoc);
376 
377                 //  all formulas because of references
378                 rDoc.CopyToDocument(0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), nTabCount-1, InsertDeleteFlags::FORMULA, false, *pUndoDoc);
379 
380                 //  ranges of DB and other
381                 ScRangeName* pDocRange = rDoc.GetRangeName();
382                 if (!pDocRange->empty())
383                     pUndoRange.reset(new ScRangeName( *pDocRange ));
384                 ScDBCollection* pDocDB = rDoc.GetDBCollection();
385                 if (!pDocDB->empty())
386                     pUndoDB.reset(new ScDBCollection( *pDocDB ));
387             }
388 
389             if (bSort && bSubTotal)
390             {
391                 //  sort without SubTotals
392 
393                 aSubTotalParam.bRemoveOnly = true;      // will be reset again further down
394                 DoSubTotals( nTab, aSubTotalParam, false, bApi );
395             }
396 
397             if (bSort)
398             {
399                 pDBData->GetSortParam( aSortParam );            // range may have changed
400                 (void)Sort( nTab, aSortParam, false, false, bApi );
401             }
402             if (bQuery)
403             {
404                 pDBData->GetQueryParam( aQueryParam );          // range may have changed
405                 ScRange aAdvSource;
406                 if (pDBData->GetAdvancedQuerySource(aAdvSource))
407                     Query( nTab, aQueryParam, &aAdvSource, false, bApi );
408                 else
409                     Query( nTab, aQueryParam, nullptr, false, bApi );
410 
411                 //  at not-inplace the table may have been converted
412 //              if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
413 //                  SetTabNo( nTab );
414             }
415             if (bSubTotal)
416             {
417                 pDBData->GetSubTotalParam( aSubTotalParam );    // range may have changed
418                 aSubTotalParam.bRemoveOnly = false;
419                 DoSubTotals( nTab, aSubTotalParam, false, bApi );
420             }
421 
422             if (bRecord)
423             {
424                 SCTAB nDummyTab;
425                 SCCOL nDummyCol;
426                 SCROW nDummyRow;
427                 SCROW nNewEndRow;
428                 pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
429 
430                 const ScRange* pOld = nullptr;
431                 const ScRange* pNew = nullptr;
432                 if (bQuerySize)
433                 {
434                     ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
435                                                             aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
436                     if (pDest)
437                     {
438                         pDest->GetArea( aNewQuery );
439                         pOld = &aOldQuery;
440                         pNew = &aNewQuery;
441                     }
442                 }
443 
444                 rDocShell.GetUndoManager()->AddUndoAction(
445                     std::make_unique<ScUndoRepeatDB>( &rDocShell, nTab,
446                                             nStartCol, nStartRow, nEndCol, nEndRow,
447                                             nNewEndRow,
448                                             //nCurX, nCurY,
449                                             nStartCol, nStartRow,
450                                             std::move(pUndoDoc), std::move(pUndoTab),
451                                             std::move(pUndoRange), std::move(pUndoDB),
452                                             pOld, pNew ) );
453             }
454 
455             rDocShell.PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
456                                 PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
457             bDone = true;
458         }
459         else if (!bApi)     // "Don't execute any operations"
460             rDocShell.ErrorMessage(STR_MSSG_REPEATDB_0);
461     }
462 
463     return bDone;
464 }
465 
Sort(SCTAB nTab,const ScSortParam & rSortParam,bool bRecord,bool bPaint,bool bApi)466 bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam,
467                             bool bRecord, bool bPaint, bool bApi )
468 {
469     ScDocShellModificator aModificator( rDocShell );
470 
471     ScDocument& rDoc = rDocShell.GetDocument();
472     if (bRecord && !rDoc.IsUndoEnabled())
473         bRecord = false;
474 
475     ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rSortParam.nCol1, rSortParam.nRow1,
476                                                     rSortParam.nCol2, rSortParam.nRow2 );
477     if (!pDBData)
478     {
479         OSL_FAIL( "Sort: no DBData" );
480         return false;
481     }
482 
483     bool bCopy = !rSortParam.bInplace;
484     if ( bCopy && rSortParam.nDestCol == rSortParam.nCol1 &&
485                   rSortParam.nDestRow == rSortParam.nRow1 && rSortParam.nDestTab == nTab )
486         bCopy = false;
487 
488     ScSortParam aLocalParam( rSortParam );
489     if ( bCopy )
490     {
491         // Copy the data range to the destination then move the sort range to it.
492         ScRange aSrcRange(rSortParam.nCol1, rSortParam.nRow1, nTab, rSortParam.nCol2, rSortParam.nRow2, nTab);
493         ScAddress aDestPos(rSortParam.nDestCol,rSortParam.nDestRow,rSortParam.nDestTab);
494 
495         ScDocFunc& rDocFunc = rDocShell.GetDocFunc();
496         bool bRet = rDocFunc.MoveBlock(aSrcRange, aDestPos, false, bRecord, bPaint, bApi);
497 
498         if (!bRet)
499             return false;
500 
501         aLocalParam.MoveToDest();
502         nTab = aLocalParam.nDestTab;
503     }
504 
505     // tdf#119804: If there is a header row/column, it won't be affected by
506     // sorting; so we can exclude it from the test.
507     SCROW nStartingRowToEdit = aLocalParam.nRow1;
508     SCROW nStartingColToEdit = aLocalParam.nCol1;
509     if ( aLocalParam.bHasHeader )
510     {
511         if ( aLocalParam.bByRow )
512             nStartingRowToEdit++;
513         else
514             nStartingColToEdit++;
515     }
516     ScEditableTester aTester( &rDoc, nTab, nStartingColToEdit, nStartingRowToEdit,
517             aLocalParam.nCol2, aLocalParam.nRow2, true /*bNoMatrixAtAll*/ );
518     if (!aTester.IsEditable())
519     {
520         if (!bApi)
521             rDocShell.ErrorMessage(aTester.GetMessageId());
522         return false;
523     }
524 
525     // Adjust aLocalParam cols/rows to used data area. Keep sticky top row or
526     // column (depending on direction) in any case, not just if it has headers,
527     // so empty leading cells will be sorted to the end.
528     bool bShrunk = false;
529     rDoc.ShrinkToUsedDataArea( bShrunk, nTab, aLocalParam.nCol1, aLocalParam.nRow1,
530             aLocalParam.nCol2, aLocalParam.nRow2, false, aLocalParam.bByRow, !aLocalParam.bByRow,
531             aLocalParam.bIncludeComments, aLocalParam.bIncludeGraphicObjects );
532 
533     SCROW nStartRow = aLocalParam.nRow1;
534     if (aLocalParam.bByRow && aLocalParam.bHasHeader && nStartRow < aLocalParam.nRow2)
535         ++nStartRow;
536 
537     if ( aLocalParam.bIncludePattern && rDoc.HasAttrib(
538                                         aLocalParam.nCol1, nStartRow        , nTab,
539                                         aLocalParam.nCol2, aLocalParam.nRow2, nTab,
540                                         HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
541     {
542         //  merge attributes would be mixed up during sorting
543         if (!bApi)
544             rDocShell.ErrorMessage(STR_SORT_ERR_MERGED);
545         return false;
546     }
547 
548     //      execute
549 
550     weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
551 
552     // Calculate the script types for all cells in the sort range beforehand.
553     // This will speed up the row height adjustment that takes place after the
554     // sort.
555     rDoc.UpdateScriptTypes(
556         ScAddress(aLocalParam.nCol1,nStartRow,nTab),
557         aLocalParam.nCol2-aLocalParam.nCol1+1,
558         aLocalParam.nRow2-nStartRow+1);
559 
560     // No point adjusting row heights after the sort when all rows have the same height.
561     bool bUniformRowHeight =
562         rDoc.HasUniformRowHeight(nTab, nStartRow, aLocalParam.nRow2);
563 
564     bool bRepeatQuery = false;                          // repeat existing filter?
565     ScQueryParam aQueryParam;
566     pDBData->GetQueryParam( aQueryParam );
567     if ( aQueryParam.GetEntry(0).bDoQuery )
568         bRepeatQuery = true;
569 
570     sc::ReorderParam aUndoParam;
571 
572     // don't call ScDocument::Sort with an empty SortParam (may be empty here if bCopy is set)
573     if (aLocalParam.GetSortKeyCount() && aLocalParam.maKeyState[0].bDoSort)
574     {
575         ScInputOptions aInputOption = SC_MOD()->GetInputOptions();
576         bool bUpdateRefs = aInputOption.GetSortRefUpdate();
577         ScProgress aProgress(&rDocShell, ScResId(STR_PROGRESS_SORTING), 0, true);
578         rDoc.Sort(nTab, aLocalParam, bRepeatQuery, bUpdateRefs, &aProgress, &aUndoParam);
579     }
580 
581     if (bRecord)
582     {
583         // Set up an undo object.
584         rDocShell.GetUndoManager()->AddUndoAction(
585             std::make_unique<sc::UndoSort>(&rDocShell, aUndoParam));
586     }
587 
588     pDBData->SetSortParam(rSortParam);
589     // Remember additional settings on anonymous database ranges.
590     if (pDBData == rDoc.GetAnonymousDBData( nTab) || rDoc.GetDBCollection()->getAnonDBs().has( pDBData))
591         pDBData->UpdateFromSortParam( rSortParam);
592 
593     if (nStartRow <= aLocalParam.nRow2)
594     {
595         ScRange aDirtyRange(
596                 aLocalParam.nCol1, nStartRow, nTab,
597                 aLocalParam.nCol2, aLocalParam.nRow2, nTab);
598         rDoc.SetDirty( aDirtyRange, true );
599     }
600 
601     if (bPaint)
602     {
603         PaintPartFlags nPaint = PaintPartFlags::Grid;
604         SCCOL nStartX = aLocalParam.nCol1;
605         SCROW nStartY = aLocalParam.nRow1;
606         SCCOL nEndX = aLocalParam.nCol2;
607         SCROW nEndY = aLocalParam.nRow2;
608         if ( bRepeatQuery )
609         {
610             nPaint |= PaintPartFlags::Left;
611             nStartX = 0;
612             nEndX = rDoc.MaxCol();
613         }
614         rDocShell.PostPaint(ScRange(nStartX, nStartY, nTab, nEndX, nEndY, nTab), nPaint);
615     }
616 
617     if (!bUniformRowHeight && nStartRow <= aLocalParam.nRow2)
618         rDocShell.AdjustRowHeight(nStartRow, aLocalParam.nRow2, nTab);
619 
620     aModificator.SetDocumentModified();
621 
622     return true;
623 }
624 
Query(SCTAB nTab,const ScQueryParam & rQueryParam,const ScRange * pAdvSource,bool bRecord,bool bApi)625 bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam,
626                         const ScRange* pAdvSource, bool bRecord, bool bApi )
627 {
628     ScDocShellModificator aModificator( rDocShell );
629 
630     ScDocument& rDoc = rDocShell.GetDocument();
631 
632     if (ScTabViewShell::isAnyEditViewInRange(/*bColumns*/ false, rQueryParam.nRow1, rQueryParam.nRow2))
633     {
634         return false;
635     }
636 
637     if (bRecord && !rDoc.IsUndoEnabled())
638         bRecord = false;
639     ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rQueryParam.nCol1, rQueryParam.nRow1,
640                                                     rQueryParam.nCol2, rQueryParam.nRow2 );
641     if (!pDBData)
642     {
643         OSL_FAIL( "Query: no DBData" );
644         return false;
645     }
646 
647     //  Change from Inplace to non-Inplace, only then cancel Inplace:
648     //  (only if "Persistent"  is selected in the dialog)
649 
650     if ( !rQueryParam.bInplace && pDBData->HasQueryParam() && rQueryParam.bDestPers )
651     {
652         ScQueryParam aOldQuery;
653         pDBData->GetQueryParam(aOldQuery);
654         if (aOldQuery.bInplace)
655         {
656             //  cancel old filtering
657 
658             SCSIZE nEC = aOldQuery.GetEntryCount();
659             for (SCSIZE i=0; i<nEC; i++)
660                 aOldQuery.GetEntry(i).bDoQuery = false;
661             aOldQuery.bDuplicate = true;
662             Query( nTab, aOldQuery, nullptr, bRecord, bApi );
663         }
664     }
665 
666     ScQueryParam aLocalParam( rQueryParam );        // for Paint / destination range
667     bool bCopy = !rQueryParam.bInplace;             // copied in Table::Query
668     ScDBData* pDestData = nullptr;                  // range to be copied to
669     bool bDoSize = false;                           // adjust destination size (insert/delete)
670     SCCOL nFormulaCols = 0;                         // only at bDoSize
671     bool bKeepFmt = false;
672     ScRange aOldDest;
673     ScRange aDestTotal;
674     if ( bCopy && rQueryParam.nDestCol == rQueryParam.nCol1 &&
675                   rQueryParam.nDestRow == rQueryParam.nRow1 && rQueryParam.nDestTab == nTab )
676         bCopy = false;
677     SCTAB nDestTab = nTab;
678     if ( bCopy )
679     {
680         aLocalParam.MoveToDest();
681         nDestTab = rQueryParam.nDestTab;
682         if ( !ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) )
683         {
684             if (!bApi)
685                 rDocShell.ErrorMessage(STR_PASTE_FULL);
686             return false;
687         }
688 
689         ScEditableTester aTester( &rDoc, nDestTab, aLocalParam.nCol1,aLocalParam.nRow1,
690                                                 aLocalParam.nCol2,aLocalParam.nRow2);
691         if (!aTester.IsEditable())
692         {
693             if (!bApi)
694                 rDocShell.ErrorMessage(aTester.GetMessageId());
695             return false;
696         }
697 
698         pDestData = rDoc.GetDBAtCursor( rQueryParam.nDestCol, rQueryParam.nDestRow,
699                                             rQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
700         if (pDestData)
701         {
702             pDestData->GetArea( aOldDest );
703             aDestTotal=ScRange( rQueryParam.nDestCol,
704                                 rQueryParam.nDestRow,
705                                 nDestTab,
706                                 rQueryParam.nDestCol + rQueryParam.nCol2 - rQueryParam.nCol1,
707                                 rQueryParam.nDestRow + rQueryParam.nRow2 - rQueryParam.nRow1,
708                                 nDestTab );
709 
710             bDoSize = pDestData->IsDoSize();
711             //  test if formulas need to be filled in (nFormulaCols):
712             if ( bDoSize && aOldDest.aEnd.Col() == aDestTotal.aEnd.Col() )
713             {
714                 SCCOL nTestCol = aOldDest.aEnd.Col() + 1;       // next to the range
715                 SCROW nTestRow = rQueryParam.nDestRow +
716                                     ( aLocalParam.bHasHeader ? 1 : 0 );
717                 while ( nTestCol <= rDoc.MaxCol() &&
718                         rDoc.GetCellType(ScAddress( nTestCol, nTestRow, nTab )) == CELLTYPE_FORMULA )
719                 {
720                     ++nTestCol;
721                     ++nFormulaCols;
722                 }
723             }
724 
725             bKeepFmt = pDestData->IsKeepFmt();
726             if ( bDoSize && !rDoc.CanFitBlock( aOldDest, aDestTotal ) )
727             {
728                 if (!bApi)
729                     rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2);     // cannot insert rows
730                 return false;
731             }
732         }
733     }
734 
735     //      execute
736 
737     weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
738 
739     bool bKeepSub = false;                          // repeat existing partial results?
740     ScSubTotalParam aSubTotalParam;
741     if (rQueryParam.GetEntry(0).bDoQuery)           // not at cancellation
742     {
743         pDBData->GetSubTotalParam( aSubTotalParam );    // partial results exist?
744 
745         if ( aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly )
746             bKeepSub = true;
747     }
748 
749     ScDocumentUniquePtr pUndoDoc;
750     std::unique_ptr<ScDBCollection> pUndoDB;
751     const ScRange* pOld = nullptr;
752 
753     if ( bRecord )
754     {
755         pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
756         if (bCopy)
757         {
758             pUndoDoc->InitUndo( &rDoc, nDestTab, nDestTab, false, true );
759             rDoc.CopyToDocument(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
760                                 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab,
761                                 InsertDeleteFlags::ALL, false, *pUndoDoc);
762             //  secure attributes in case they were copied along
763 
764             if (pDestData)
765             {
766                 rDoc.CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, *pUndoDoc);
767                 pOld = &aOldDest;
768             }
769         }
770         else
771         {
772             pUndoDoc->InitUndo( &rDoc, nTab, nTab, false, true );
773             rDoc.CopyToDocument(0, rQueryParam.nRow1, nTab, rDoc.MaxCol(), rQueryParam.nRow2, nTab,
774                                 InsertDeleteFlags::NONE, false, *pUndoDoc);
775         }
776 
777         ScDBCollection* pDocDB = rDoc.GetDBCollection();
778         if (!pDocDB->empty())
779             pUndoDB.reset(new ScDBCollection( *pDocDB ));
780 
781         rDoc.BeginDrawUndo();
782     }
783 
784     std::unique_ptr<ScDocument> pAttribDoc;
785     ScRange aAttribRange;
786     if (pDestData)                                      // delete destination range
787     {
788         if ( bKeepFmt )
789         {
790             //  smaller of the end columns, header+1 row
791             aAttribRange = aOldDest;
792             if ( aAttribRange.aEnd.Col() > aDestTotal.aEnd.Col() )
793                 aAttribRange.aEnd.SetCol( aDestTotal.aEnd.Col() );
794             aAttribRange.aEnd.SetRow( aAttribRange.aStart.Row() +
795                                         ( aLocalParam.bHasHeader ? 1 : 0 ) );
796 
797             //  also for filled-in formulas
798             aAttribRange.aEnd.SetCol( aAttribRange.aEnd.Col() + nFormulaCols );
799 
800             pAttribDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
801             pAttribDoc->InitUndo( &rDoc, nDestTab, nDestTab, false, true );
802             rDoc.CopyToDocument(aAttribRange, InsertDeleteFlags::ATTRIB, false, *pAttribDoc);
803         }
804 
805         if ( bDoSize )
806             rDoc.FitBlock( aOldDest, aDestTotal );
807         else
808             rDoc.DeleteAreaTab(aOldDest, InsertDeleteFlags::ALL);         // simply delete
809     }
810 
811     //  execute filtering on the document
812     SCSIZE nCount = rDoc.Query( nTab, rQueryParam, bKeepSub );
813     pDBData->CalcSaveFilteredCount( nCount );
814     if (bCopy)
815     {
816         aLocalParam.nRow2 = aLocalParam.nRow1 + nCount;
817         if (!aLocalParam.bHasHeader && nCount > 0)
818             --aLocalParam.nRow2;
819 
820         if ( bDoSize )
821         {
822             //  adjust to the real result range
823             //  (this here is always a reduction)
824 
825             ScRange aNewDest( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
826                                 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab );
827             rDoc.FitBlock( aDestTotal, aNewDest, false );      // sal_False - don't delete
828 
829             if ( nFormulaCols > 0 )
830             {
831                 //  fill in formulas
832                 //! Undo (Query and Repeat) !!!
833 
834                 ScRange aNewForm( aLocalParam.nCol2+1, aLocalParam.nRow1, nDestTab,
835                                   aLocalParam.nCol2+nFormulaCols, aLocalParam.nRow2, nDestTab );
836                 ScRange aOldForm = aNewForm;
837                 aOldForm.aEnd.SetRow( aOldDest.aEnd.Row() );
838                 rDoc.FitBlock( aOldForm, aNewForm, false );
839 
840                 ScMarkData aMark(rDoc.MaxRow(), rDoc.MaxCol());
841                 aMark.SelectOneTable(nDestTab);
842                 SCROW nFStartY = aLocalParam.nRow1 + ( aLocalParam.bHasHeader ? 1 : 0 );
843 
844                 sal_uLong nProgCount = nFormulaCols;
845                 nProgCount *= aLocalParam.nRow2 - nFStartY;
846                 ScProgress aProgress( rDoc.GetDocumentShell(),
847                         ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
848 
849                 rDoc.Fill( aLocalParam.nCol2+1, nFStartY,
850                             aLocalParam.nCol2+nFormulaCols, nFStartY, &aProgress, aMark,
851                             aLocalParam.nRow2 - nFStartY,
852                             FILL_TO_BOTTOM, FILL_SIMPLE );
853             }
854         }
855 
856         if ( pAttribDoc )       // copy back the memorized attributes
857         {
858             //  Header
859             if (aLocalParam.bHasHeader)
860             {
861                 ScRange aHdrRange = aAttribRange;
862                 aHdrRange.aEnd.SetRow( aHdrRange.aStart.Row() );
863                 pAttribDoc->CopyToDocument(aHdrRange, InsertDeleteFlags::ATTRIB, false, rDoc);
864             }
865 
866             //  Data
867             SCCOL nAttrEndCol = aAttribRange.aEnd.Col();
868             SCROW nAttrRow = aAttribRange.aStart.Row() + ( aLocalParam.bHasHeader ? 1 : 0 );
869             for (SCCOL nCol = aAttribRange.aStart.Col(); nCol<=nAttrEndCol; nCol++)
870             {
871                 const ScPatternAttr* pSrcPattern = pAttribDoc->GetPattern(
872                                                     nCol, nAttrRow, nDestTab );
873                 OSL_ENSURE(pSrcPattern,"Pattern is 0");
874                 if (pSrcPattern)
875                 {
876                     rDoc.ApplyPatternAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
877                                                     nDestTab, *pSrcPattern );
878                     const ScStyleSheet* pStyle = pSrcPattern->GetStyleSheet();
879                     if (pStyle)
880                         rDoc.ApplyStyleAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
881                                                     nDestTab, *pStyle );
882                 }
883             }
884         }
885     }
886 
887     //  saving: Inplace always, otherwise depending on setting
888     //          old Inplace-Filter may have already been removed
889 
890     bool bSave = rQueryParam.bInplace || rQueryParam.bDestPers;
891     if (bSave)                                                  // memorize
892     {
893         pDBData->SetQueryParam( rQueryParam );
894         pDBData->SetHeader( rQueryParam.bHasHeader );       //! ???
895         pDBData->SetAdvancedQuerySource( pAdvSource );      // after SetQueryParam
896     }
897 
898     if (bCopy)                                              // memorize new DB range
899     {
900         //  Selection is done afterwards from outside (dbfunc).
901         //  Currently through the DB area at the destination position,
902         //  so a range must be created there in any case.
903 
904         ScDBData* pNewData;
905         if (pDestData)
906             pNewData = pDestData;               // range exists -> adjust (always!)
907         else                                    // create range
908             pNewData = rDocShell.GetDBData(
909                             ScRange( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
910                                      aLocalParam.nCol2, aLocalParam.nRow2, nDestTab ),
911                             SC_DB_MAKE, ScGetDBSelection::ForceMark );
912 
913         if (pNewData)
914         {
915             pNewData->SetArea( nDestTab, aLocalParam.nCol1, aLocalParam.nRow1,
916                                             aLocalParam.nCol2, aLocalParam.nRow2 );
917 
918             //  query parameter is no longer set at the destination, only leads to confusion
919             //  and mistakes with the query parameter at the source range (#37187#)
920         }
921         else
922         {
923             OSL_FAIL("Target are not available");
924         }
925     }
926 
927     if (!bCopy)
928     {
929         rDoc.InvalidatePageBreaks(nTab);
930         rDoc.UpdatePageBreaks( nTab );
931     }
932 
933     // #i23299# Subtotal functions depend on cell's filtered states.
934     ScRange aDirtyRange(0 , aLocalParam.nRow1, nDestTab, rDoc.MaxCol(), aLocalParam.nRow2, nDestTab);
935     rDoc.SetSubTotalCellsDirty(aDirtyRange);
936 
937     if ( bRecord )
938     {
939         // create undo action after executing, because of drawing layer undo
940         rDocShell.GetUndoManager()->AddUndoAction(
941                     std::make_unique<ScUndoQuery>( &rDocShell, nTab, rQueryParam, std::move(pUndoDoc), std::move(pUndoDB),
942                                         pOld, bDoSize, pAdvSource ) );
943     }
944 
945     ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
946     if ( pViewSh )
947     {
948         // could there be horizontal autofilter ?
949         // maybe it would be better to set bColumns to !rQueryParam.bByRow ?
950         // anyway at the beginning the value of bByRow is 'false'
951         // then after the first sort action it becomes 'true'
952         pViewSh->OnLOKShowHideColRow(/*bColumns*/ false, rQueryParam.nRow1 - 1);
953     }
954 
955     if (bCopy)
956     {
957         SCCOL nEndX = aLocalParam.nCol2;
958         SCROW nEndY = aLocalParam.nRow2;
959         if (pDestData)
960         {
961             if ( aOldDest.aEnd.Col() > nEndX )
962                 nEndX = aOldDest.aEnd.Col();
963             if ( aOldDest.aEnd.Row() > nEndY )
964                 nEndY = aOldDest.aEnd.Row();
965         }
966         if (bDoSize)
967             nEndY = rDoc.MaxRow();
968         rDocShell.PostPaint(
969             ScRange(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, nEndX, nEndY, nDestTab),
970             PaintPartFlags::Grid);
971     }
972     else
973         rDocShell.PostPaint(
974             ScRange(0, rQueryParam.nRow1, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
975             PaintPartFlags::Grid | PaintPartFlags::Left);
976     aModificator.SetDocumentModified();
977 
978     return true;
979 }
980 
DoSubTotals(SCTAB nTab,const ScSubTotalParam & rParam,bool bRecord,bool bApi)981 void ScDBDocFunc::DoSubTotals( SCTAB nTab, const ScSubTotalParam& rParam,
982                                bool bRecord, bool bApi )
983 {
984     //! use also for ScDBFunc::DoSubTotals !
985     //  then stays outside:
986     //  - mark new range (from DBData)
987     //  - SelectionChanged (?)
988 
989     bool bDo = !rParam.bRemoveOnly;                         // sal_False = only delete
990 
991     ScDocument& rDoc = rDocShell.GetDocument();
992     if (bRecord && !rDoc.IsUndoEnabled())
993         bRecord = false;
994     ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
995                                                 rParam.nCol2, rParam.nRow2 );
996     if (!pDBData)
997     {
998         OSL_FAIL( "SubTotals: no DBData" );
999         return;
1000     }
1001 
1002     ScEditableTester aTester( &rDoc, nTab, 0,rParam.nRow1+1, rDoc.MaxCol(),rDoc.MaxRow() );
1003     if (!aTester.IsEditable())
1004     {
1005         if (!bApi)
1006             rDocShell.ErrorMessage(aTester.GetMessageId());
1007         return;
1008     }
1009 
1010     if (rDoc.HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
1011                          rParam.nCol2, rParam.nRow2, nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
1012     {
1013         if (!bApi)
1014             rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0); // don't insert into merged
1015         return;
1016     }
1017 
1018     bool bOk = true;
1019     if (rParam.bReplace)
1020     {
1021         if (rDoc.TestRemoveSubTotals( nTab, rParam ))
1022         {
1023             std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
1024                                                       VclMessageType::Question,
1025                                                       VclButtonsType::YesNo, ScResId(STR_MSSG_DOSUBTOTALS_1))); // "Delete Data?"
1026             xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0)); // "StarCalc"
1027             bOk = xBox->run() == RET_YES;
1028         }
1029     }
1030 
1031     if (bOk)
1032     {
1033         weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
1034         ScDocShellModificator aModificator( rDocShell );
1035 
1036         ScSubTotalParam aNewParam( rParam );        // end of range is being changed
1037         ScDocumentUniquePtr pUndoDoc;
1038         std::unique_ptr<ScOutlineTable> pUndoTab;
1039         std::unique_ptr<ScRangeName> pUndoRange;
1040         std::unique_ptr<ScDBCollection> pUndoDB;
1041 
1042         if (bRecord)                                        // secure old data
1043         {
1044             bool bOldFilter = bDo && rParam.bDoSort;
1045 
1046             SCTAB nTabCount = rDoc.GetTableCount();
1047             pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
1048             ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
1049             if (pTable)
1050             {
1051                 pUndoTab.reset(new ScOutlineTable( *pTable ));
1052 
1053                 // column/row state
1054                 SCCOLROW nOutStartCol, nOutEndCol;
1055                 SCCOLROW nOutStartRow, nOutEndRow;
1056                 pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
1057                 pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
1058 
1059                 pUndoDoc->InitUndo( &rDoc, nTab, nTab, true, true );
1060                 rDoc.CopyToDocument(static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
1061                 rDoc.CopyToDocument(0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
1062             }
1063             else
1064                 pUndoDoc->InitUndo( &rDoc, nTab, nTab, false, bOldFilter );
1065 
1066             //  secure data range - incl. filtering result
1067             rDoc.CopyToDocument(0, rParam.nRow1+1,nTab, rDoc.MaxCol(),rParam.nRow2,nTab,
1068                                 InsertDeleteFlags::ALL, false, *pUndoDoc);
1069 
1070             //  all formulas because of references
1071             rDoc.CopyToDocument(0, 0, 0, rDoc.MaxCol(),rDoc.MaxRow(),nTabCount-1,
1072                                 InsertDeleteFlags::FORMULA, false, *pUndoDoc);
1073 
1074             //  ranges of DB and other
1075             ScRangeName* pDocRange = rDoc.GetRangeName();
1076             if (!pDocRange->empty())
1077                 pUndoRange.reset(new ScRangeName( *pDocRange ));
1078             ScDBCollection* pDocDB = rDoc.GetDBCollection();
1079             if (!pDocDB->empty())
1080                 pUndoDB.reset(new ScDBCollection( *pDocDB ));
1081         }
1082 
1083 //      rDoc.SetOutlineTable( nTab, NULL );
1084         ScOutlineTable* pOut = rDoc.GetOutlineTable( nTab );
1085         if (pOut)
1086             pOut->GetRowArray().RemoveAll();       // only delete row outlines
1087 
1088         if (rParam.bReplace)
1089             rDoc.RemoveSubTotals( nTab, aNewParam );
1090         bool bSuccess = true;
1091         if (bDo)
1092         {
1093             // sort
1094             if ( rParam.bDoSort )
1095             {
1096                 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
1097 
1098                 //  set partial result field to before the sorting
1099                 //  (Duplicates are omitted, so can be called again)
1100 
1101                 ScSortParam aOldSort;
1102                 pDBData->GetSortParam( aOldSort );
1103                 ScSortParam aSortParam( aNewParam, aOldSort );
1104                 Sort( nTab, aSortParam, false, false, bApi );
1105             }
1106 
1107             bSuccess = rDoc.DoSubTotals( nTab, aNewParam );
1108             rDoc.SetDrawPageSize(nTab);
1109         }
1110         ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
1111             aNewParam.nCol2, aNewParam.nRow2, nTab );
1112         rDoc.SetDirty( aDirtyRange, true );
1113 
1114         if (bRecord)
1115         {
1116 //          ScDBData* pUndoDBData = pDBData ? new ScDBData( *pDBData ) : NULL;
1117             rDocShell.GetUndoManager()->AddUndoAction(
1118                 std::make_unique<ScUndoSubTotals>( &rDocShell, nTab,
1119                                         rParam, aNewParam.nRow2,
1120                                         std::move(pUndoDoc), std::move(pUndoTab), // pUndoDBData,
1121                                         std::move(pUndoRange), std::move(pUndoDB) ) );
1122         }
1123 
1124         if (!bSuccess)
1125         {
1126             // "Cannot insert rows"
1127             if (!bApi)
1128                 rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
1129         }
1130 
1131                                                     // memorize
1132         pDBData->SetSubTotalParam( aNewParam );
1133         pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
1134         rDoc.CompileDBFormula();
1135 
1136         rDocShell.PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab),
1137                             PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
1138         aModificator.SetDocumentModified();
1139     }
1140 }
1141 
1142 namespace {
1143 
lcl_EmptyExcept(ScDocument * pDoc,const ScRange & rRange,const ScRange & rExcept)1144 bool lcl_EmptyExcept( ScDocument* pDoc, const ScRange& rRange, const ScRange& rExcept )
1145 {
1146     ScCellIterator aIter( pDoc, rRange );
1147     for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
1148     {
1149         if (!aIter.isEmpty())      // real content?
1150         {
1151             if (!rExcept.In(aIter.GetPos()))
1152                 return false;       // cell found
1153         }
1154     }
1155 
1156     return true;        // nothing found - empty
1157 }
1158 
isEditable(ScDocShell & rDocShell,const ScRangeList & rRanges,bool bApi)1159 bool isEditable(ScDocShell& rDocShell, const ScRangeList& rRanges, bool bApi)
1160 {
1161     ScDocument& rDoc = rDocShell.GetDocument();
1162     if (!rDocShell.IsEditable() || rDoc.GetChangeTrack())
1163     {
1164         //  not recorded -> disallow
1165         if (!bApi)
1166             rDocShell.ErrorMessage(STR_PROTECTIONERR);
1167 
1168         return false;
1169     }
1170 
1171     for (size_t i = 0, n = rRanges.size(); i < n; ++i)
1172     {
1173         const ScRange & r = rRanges[i];
1174         ScEditableTester aTester(&rDoc, r);
1175         if (!aTester.IsEditable())
1176         {
1177             if (!bApi)
1178                 rDocShell.ErrorMessage(aTester.GetMessageId());
1179 
1180             return false;
1181         }
1182     }
1183 
1184     return true;
1185 }
1186 
createUndoDoc(ScDocumentUniquePtr & pUndoDoc,ScDocument * pDoc,const ScRange & rRange)1187 void createUndoDoc(ScDocumentUniquePtr& pUndoDoc, ScDocument* pDoc, const ScRange& rRange)
1188 {
1189     SCTAB nTab = rRange.aStart.Tab();
1190     pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
1191     pUndoDoc->InitUndo(pDoc, nTab, nTab);
1192     pDoc->CopyToDocument(rRange, InsertDeleteFlags::ALL, false, *pUndoDoc);
1193 }
1194 
checkNewOutputRange(ScDPObject & rDPObj,ScDocShell & rDocShell,ScRange & rNewOut,bool bApi)1195 bool checkNewOutputRange(ScDPObject& rDPObj, ScDocShell& rDocShell, ScRange& rNewOut, bool bApi)
1196 {
1197     ScDocument& rDoc = rDocShell.GetDocument();
1198 
1199     bool bOverflow = false;
1200     rNewOut = rDPObj.GetNewOutputRange(bOverflow);
1201 
1202     // Test for overlap with source data range.
1203     // TODO: Check with other pivot tables as well.
1204     const ScSheetSourceDesc* pSheetDesc = rDPObj.GetSheetDesc();
1205     if (pSheetDesc && pSheetDesc->GetSourceRange().Intersects(rNewOut))
1206     {
1207         // New output range intersteps with the source data. Move it up to
1208         // where the old range is and see if that works.
1209         ScRange aOldRange = rDPObj.GetOutRange();
1210         SCROW nDiff = aOldRange.aStart.Row() - rNewOut.aStart.Row();
1211         rNewOut.aStart.SetRow(aOldRange.aStart.Row());
1212         rNewOut.aEnd.IncRow(nDiff);
1213         if (!ValidRow(rNewOut.aStart.Row()) || !ValidRow(rNewOut.aEnd.Row()))
1214             bOverflow = true;
1215     }
1216 
1217     if (bOverflow)
1218     {
1219         if (!bApi)
1220             rDocShell.ErrorMessage(STR_PIVOT_ERROR);
1221 
1222         return false;
1223     }
1224 
1225     ScEditableTester aTester(&rDoc, rNewOut);
1226     if (!aTester.IsEditable())
1227     {
1228         //  destination area isn't editable
1229         if (!bApi)
1230             rDocShell.ErrorMessage(aTester.GetMessageId());
1231 
1232         return false;
1233     }
1234 
1235     return true;
1236 }
1237 
1238 }
1239 
DataPilotUpdate(ScDPObject * pOldObj,const ScDPObject * pNewObj,bool bRecord,bool bApi,bool bAllowMove)1240 bool ScDBDocFunc::DataPilotUpdate( ScDPObject* pOldObj, const ScDPObject* pNewObj,
1241                                    bool bRecord, bool bApi, bool bAllowMove )
1242 {
1243     if (!pOldObj)
1244     {
1245         if (!pNewObj)
1246             return false;
1247 
1248         return CreatePivotTable(*pNewObj, bRecord, bApi);
1249     }
1250 
1251     if (!pNewObj)
1252         return RemovePivotTable(*pOldObj, bRecord, bApi);
1253 
1254     if (pOldObj == pNewObj)
1255         return UpdatePivotTable(*pOldObj, bRecord, bApi);
1256 
1257     OSL_ASSERT(pOldObj && pNewObj && pOldObj != pNewObj);
1258 
1259     ScDocShellModificator aModificator( rDocShell );
1260     weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
1261 
1262     ScRangeList aRanges;
1263     aRanges.push_back(pOldObj->GetOutRange());
1264     aRanges.push_back(pNewObj->GetOutRange().aStart); // at least one cell in the output position must be editable.
1265     if (!isEditable(rDocShell, aRanges, bApi))
1266         return false;
1267 
1268     ScDocumentUniquePtr pOldUndoDoc;
1269     ScDocumentUniquePtr pNewUndoDoc;
1270 
1271     ScDPObject aUndoDPObj(*pOldObj); // for undo or revert on failure
1272 
1273     ScDocument& rDoc = rDocShell.GetDocument();
1274     if (bRecord && !rDoc.IsUndoEnabled())
1275         bRecord = false;
1276 
1277     if (bRecord)
1278         createUndoDoc(pOldUndoDoc, &rDoc, pOldObj->GetOutRange());
1279 
1280     pNewObj->WriteSourceDataTo(*pOldObj);     // copy source data
1281 
1282     ScDPSaveData* pData = pNewObj->GetSaveData();
1283     OSL_ENSURE( pData, "no SaveData from living DPObject" );
1284     if (pData)
1285         pOldObj->SetSaveData(*pData);     // copy SaveData
1286 
1287     pOldObj->SetAllowMove(bAllowMove);
1288     pOldObj->ReloadGroupTableData();
1289     pOldObj->SyncAllDimensionMembers();
1290     pOldObj->InvalidateData();             // before getting the new output area
1291 
1292     //  make sure the table has a name (not set by dialog)
1293     if (pOldObj->GetName().isEmpty())
1294         pOldObj->SetName( rDoc.GetDPCollection()->CreateNewName() );
1295 
1296     ScRange aNewOut;
1297     if (!checkNewOutputRange(*pOldObj, rDocShell, aNewOut, bApi))
1298     {
1299         *pOldObj = aUndoDPObj;
1300         return false;
1301     }
1302 
1303     //  test if new output area is empty except for old area
1304     if (!bApi)
1305     {
1306         // OutRange of pOldObj (pDestObj) is still old area
1307         if (!lcl_EmptyExcept(&rDoc, aNewOut, pOldObj->GetOutRange()))
1308         {
1309             std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
1310                                                            VclMessageType::Question, VclButtonsType::YesNo,
1311                                                            ScResId(STR_PIVOT_NOTEMPTY)));
1312             xQueryBox->set_default_response(RET_YES);
1313             if (xQueryBox->run() == RET_NO)
1314             {
1315                 //! like above (not editable)
1316                 *pOldObj = aUndoDPObj;
1317                 return false;
1318             }
1319         }
1320     }
1321 
1322     if (bRecord)
1323         createUndoDoc(pNewUndoDoc, &rDoc, aNewOut);
1324 
1325     pOldObj->Output(aNewOut.aStart);
1326     rDocShell.PostPaintGridAll();           //! only necessary parts
1327 
1328     if (bRecord)
1329     {
1330         rDocShell.GetUndoManager()->AddUndoAction(
1331             std::make_unique<ScUndoDataPilot>(
1332                 &rDocShell, std::move(pOldUndoDoc), std::move(pNewUndoDoc), &aUndoDPObj, pOldObj, bAllowMove));
1333     }
1334 
1335     // notify API objects
1336     rDoc.BroadcastUno( ScDataPilotModifiedHint(pOldObj->GetName()) );
1337     aModificator.SetDocumentModified();
1338 
1339     return true;
1340 }
1341 
RemovePivotTable(ScDPObject & rDPObj,bool bRecord,bool bApi)1342 bool ScDBDocFunc::RemovePivotTable(ScDPObject& rDPObj, bool bRecord, bool bApi)
1343 {
1344     ScDocShellModificator aModificator(rDocShell);
1345     weld::WaitObject aWait(ScDocShell::GetActiveDialogParent());
1346 
1347     if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi))
1348         return false;
1349 
1350     ScDocument& rDoc = rDocShell.GetDocument();
1351 
1352     if (!bApi)
1353     {
1354         // If we come from GUI - ask to delete the associated pivot charts too...
1355         std::vector<SdrOle2Obj*> aListOfObjects =
1356                     sc::tools::getAllPivotChartsConntectedTo(rDPObj.GetName(), &rDocShell);
1357 
1358         ScDrawLayer* pModel = rDoc.GetDrawLayer();
1359 
1360         if (pModel && !aListOfObjects.empty())
1361         {
1362             std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
1363                                                            VclMessageType::Question, VclButtonsType::YesNo,
1364                                                            ScResId(STR_PIVOT_REMOVE_PIVOTCHART)));
1365             xQueryBox->set_default_response(RET_YES);
1366             if (xQueryBox->run() == RET_NO)
1367             {
1368                 return false;
1369             }
1370             else
1371             {
1372                 for (SdrOle2Obj* pChartObject : aListOfObjects)
1373                 {
1374                     rDoc.GetChartListenerCollection()->removeByName(pChartObject->GetName());
1375                     pModel->AddUndo(std::make_unique<SdrUndoDelObj>(*pChartObject));
1376                     pChartObject->getSdrPageFromSdrObject()->RemoveObject(pChartObject->GetOrdNum());
1377                 }
1378             }
1379         }
1380     }
1381 
1382     ScDocumentUniquePtr pOldUndoDoc;
1383     std::unique_ptr<ScDPObject> pUndoDPObj;
1384 
1385     if (bRecord)
1386         pUndoDPObj.reset(new ScDPObject(rDPObj));    // copy old settings for undo
1387 
1388     if (bRecord && !rDoc.IsUndoEnabled())
1389         bRecord = false;
1390 
1391     //  delete table
1392 
1393     ScRange aRange = rDPObj.GetOutRange();
1394     SCTAB nTab = aRange.aStart.Tab();
1395 
1396     if (bRecord)
1397         createUndoDoc(pOldUndoDoc, &rDoc, aRange);
1398 
1399     rDoc.DeleteAreaTab( aRange.aStart.Col(), aRange.aStart.Row(),
1400                          aRange.aEnd.Col(),   aRange.aEnd.Row(),
1401                          nTab, InsertDeleteFlags::ALL );
1402     rDoc.RemoveFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
1403                           aRange.aEnd.Col(),   aRange.aEnd.Row(),
1404                           nTab, ScMF::Auto );
1405 
1406     rDoc.GetDPCollection()->FreeTable(&rDPObj);  // object is deleted here
1407 
1408     rDocShell.PostPaintGridAll();   //! only necessary parts
1409     rDocShell.PostPaint(aRange, PaintPartFlags::Grid);
1410 
1411     if (bRecord)
1412     {
1413         rDocShell.GetUndoManager()->AddUndoAction(
1414             std::make_unique<ScUndoDataPilot>(
1415                 &rDocShell, std::move(pOldUndoDoc), nullptr, pUndoDPObj.get(), nullptr, false));
1416 
1417         // pUndoDPObj is copied
1418     }
1419 
1420     aModificator.SetDocumentModified();
1421     return true;
1422 }
1423 
CreatePivotTable(const ScDPObject & rDPObj,bool bRecord,bool bApi)1424 bool ScDBDocFunc::CreatePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi)
1425 {
1426     ScDocShellModificator aModificator(rDocShell);
1427     weld::WaitObject aWait(ScDocShell::GetActiveDialogParent());
1428 
1429     // At least one cell in the output range should be editable. Check in advance.
1430     if (!isEditable(rDocShell, ScRange(rDPObj.GetOutRange().aStart), bApi))
1431         return false;
1432 
1433     ScDocumentUniquePtr pNewUndoDoc;
1434 
1435     ScDocument& rDoc = rDocShell.GetDocument();
1436     if (bRecord && !rDoc.IsUndoEnabled())
1437         bRecord = false;
1438 
1439     //  output range must be set at pNewObj
1440     std::unique_ptr<ScDPObject> pDestObj(new ScDPObject(rDPObj));
1441 
1442     ScDPObject& rDestObj = *pDestObj;
1443 
1444     // #i94570# When changing the output position in the dialog, a new table is created
1445     // with the settings from the old table, including the name.
1446     // So we have to check for duplicate names here (before inserting).
1447     if (rDoc.GetDPCollection()->GetByName(rDestObj.GetName()))
1448         rDestObj.SetName(OUString());      // ignore the invalid name, create a new name below
1449 
1450     // Synchronize groups between linked tables
1451     {
1452         const ScDPDimensionSaveData* pGroups = nullptr;
1453         bool bRefFound = rDoc.GetDPCollection()->GetReferenceGroups(rDestObj, &pGroups);
1454         if (bRefFound)
1455         {
1456             ScDPSaveData* pSaveData = rDestObj.GetSaveData();
1457             if (pSaveData)
1458                 pSaveData->SetDimensionData(pGroups);
1459         }
1460     }
1461 
1462     rDoc.GetDPCollection()->InsertNewTable(std::move(pDestObj));
1463 
1464     rDestObj.ReloadGroupTableData();
1465     rDestObj.SyncAllDimensionMembers();
1466     rDestObj.InvalidateData();             // before getting the new output area
1467 
1468     //  make sure the table has a name (not set by dialog)
1469     if (rDestObj.GetName().isEmpty())
1470         rDestObj.SetName(rDoc.GetDPCollection()->CreateNewName());
1471 
1472     bool bOverflow = false;
1473     ScRange aNewOut = rDestObj.GetNewOutputRange(bOverflow);
1474 
1475     if (bOverflow)
1476     {
1477         if (!bApi)
1478             rDocShell.ErrorMessage(STR_PIVOT_ERROR);
1479 
1480         return false;
1481     }
1482 
1483     {
1484         ScEditableTester aTester(&rDoc, aNewOut);
1485         if (!aTester.IsEditable())
1486         {
1487             //  destination area isn't editable
1488             if (!bApi)
1489                 rDocShell.ErrorMessage(aTester.GetMessageId());
1490 
1491             return false;
1492         }
1493     }
1494 
1495     //  test if new output area is empty except for old area
1496     if (!bApi)
1497     {
1498         bool bEmpty = rDoc.IsBlockEmpty(
1499             aNewOut.aStart.Tab(), aNewOut.aStart.Col(), aNewOut.aStart.Row(),
1500             aNewOut.aEnd.Col(), aNewOut.aEnd.Row());
1501 
1502         if (!bEmpty)
1503         {
1504             std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
1505                                                            VclMessageType::Question, VclButtonsType::YesNo,
1506                                                            ScResId(STR_PIVOT_NOTEMPTY)));
1507             xQueryBox->set_default_response(RET_YES);
1508             if (xQueryBox->run() == RET_NO)
1509             {
1510                 //! like above (not editable)
1511                 return false;
1512             }
1513         }
1514     }
1515 
1516     if (bRecord)
1517         createUndoDoc(pNewUndoDoc, &rDoc, aNewOut);
1518 
1519     rDestObj.Output(aNewOut.aStart);
1520     rDocShell.PostPaintGridAll();           //! only necessary parts
1521 
1522     if (bRecord)
1523     {
1524         rDocShell.GetUndoManager()->AddUndoAction(
1525             std::make_unique<ScUndoDataPilot>(&rDocShell, nullptr, std::move(pNewUndoDoc), nullptr, &rDestObj, false));
1526     }
1527 
1528     // notify API objects
1529     rDoc.BroadcastUno(ScDataPilotModifiedHint(rDestObj.GetName()));
1530     aModificator.SetDocumentModified();
1531 
1532     return true;
1533 }
1534 
UpdatePivotTable(ScDPObject & rDPObj,bool bRecord,bool bApi)1535 bool ScDBDocFunc::UpdatePivotTable(ScDPObject& rDPObj, bool bRecord, bool bApi)
1536 {
1537     ScDocShellModificator aModificator( rDocShell );
1538     weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
1539 
1540     if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi))
1541         return false;
1542 
1543     ScDocumentUniquePtr pOldUndoDoc;
1544     ScDocumentUniquePtr pNewUndoDoc;
1545 
1546     ScDPObject aUndoDPObj(rDPObj); // For undo or revert on failure.
1547 
1548     ScDocument& rDoc = rDocShell.GetDocument();
1549     if (bRecord && !rDoc.IsUndoEnabled())
1550         bRecord = false;
1551 
1552     if (bRecord)
1553         createUndoDoc(pOldUndoDoc, &rDoc, rDPObj.GetOutRange());
1554 
1555     rDPObj.SetAllowMove(false);
1556     rDPObj.ReloadGroupTableData();
1557     if (!rDPObj.SyncAllDimensionMembers())
1558         return false;
1559 
1560     rDPObj.InvalidateData();             // before getting the new output area
1561 
1562     //  make sure the table has a name (not set by dialog)
1563     if (rDPObj.GetName().isEmpty())
1564         rDPObj.SetName( rDoc.GetDPCollection()->CreateNewName() );
1565 
1566     ScRange aNewOut;
1567     if (!checkNewOutputRange(rDPObj, rDocShell, aNewOut, bApi))
1568     {
1569         rDPObj = aUndoDPObj;
1570         return false;
1571     }
1572 
1573     //  test if new output area is empty except for old area
1574     if (!bApi)
1575     {
1576         if (!lcl_EmptyExcept(&rDoc, aNewOut, rDPObj.GetOutRange()))
1577         {
1578             std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
1579                                                            VclMessageType::Question, VclButtonsType::YesNo,
1580                                                            ScResId(STR_PIVOT_NOTEMPTY)));
1581             xQueryBox->set_default_response(RET_YES);
1582             if (xQueryBox->run() == RET_NO)
1583             {
1584                 rDPObj = aUndoDPObj;
1585                 return false;
1586             }
1587         }
1588     }
1589 
1590     if (bRecord)
1591         createUndoDoc(pNewUndoDoc, &rDoc, aNewOut);
1592 
1593     rDPObj.Output(aNewOut.aStart);
1594     rDocShell.PostPaintGridAll();           //! only necessary parts
1595 
1596     if (bRecord)
1597     {
1598         rDocShell.GetUndoManager()->AddUndoAction(
1599             std::make_unique<ScUndoDataPilot>(
1600                 &rDocShell, std::move(pOldUndoDoc), std::move(pNewUndoDoc), &aUndoDPObj, &rDPObj, false));
1601     }
1602 
1603     // notify API objects
1604     rDoc.BroadcastUno( ScDataPilotModifiedHint(rDPObj.GetName()) );
1605     aModificator.SetDocumentModified();
1606     return true;
1607 }
1608 
RefreshPivotTables(const ScDPObject * pDPObj,bool bApi)1609 void ScDBDocFunc::RefreshPivotTables(const ScDPObject* pDPObj, bool bApi)
1610 {
1611     ScDPCollection* pDPs = rDocShell.GetDocument().GetDPCollection();
1612     if (!pDPs)
1613         return;
1614 
1615     std::set<ScDPObject*> aRefs;
1616     const char* pErrId = pDPs->ReloadCache(pDPObj, aRefs);
1617     if (pErrId)
1618         return;
1619 
1620     for (ScDPObject* pObj : aRefs)
1621     {
1622         // This action is intentionally not undoable since it modifies cache.
1623         UpdatePivotTable(*pObj, false, bApi);
1624     }
1625 }
1626 
RefreshPivotTableGroups(ScDPObject * pDPObj)1627 void ScDBDocFunc::RefreshPivotTableGroups(ScDPObject* pDPObj)
1628 {
1629     if (!pDPObj)
1630         return;
1631 
1632     ScDPCollection* pDPs = rDocShell.GetDocument().GetDPCollection();
1633     if (!pDPs)
1634         return;
1635 
1636     ScDPSaveData* pSaveData = pDPObj->GetSaveData();
1637     if (!pSaveData)
1638         return;
1639 
1640     if (!pDPs->HasTable(pDPObj))
1641     {
1642         // This table is under construction so no need for a whole update (UpdatePivotTable()).
1643         pDPObj->ReloadGroupTableData();
1644         return;
1645     }
1646 
1647     // Update all linked tables, if this table is part of the cache (ScDPCollection)
1648     std::set<ScDPObject*> aRefs;
1649     if (!pDPs->ReloadGroupsInCache(pDPObj, aRefs))
1650         return;
1651 
1652     // We allow pDimData being NULL.
1653     const ScDPDimensionSaveData* pDimData = pSaveData->GetExistingDimensionData();
1654     for (ScDPObject* pObj : aRefs)
1655     {
1656         if (pObj != pDPObj)
1657         {
1658             pSaveData = pObj->GetSaveData();
1659             if (pSaveData)
1660                 pSaveData->SetDimensionData(pDimData);
1661         }
1662 
1663         // This action is intentionally not undoable since it modifies cache.
1664         UpdatePivotTable(*pObj, false, false);
1665     }
1666 }
1667 
1668 //      database import
1669 
UpdateImport(const OUString & rTarget,const svx::ODataAccessDescriptor & rDescriptor)1670 void ScDBDocFunc::UpdateImport( const OUString& rTarget, const svx::ODataAccessDescriptor& rDescriptor )
1671 {
1672     // rTarget is the name of a database range
1673 
1674     ScDocument& rDoc = rDocShell.GetDocument();
1675     ScDBCollection& rDBColl = *rDoc.GetDBCollection();
1676     const ScDBData* pData = rDBColl.getNamedDBs().findByUpperName(ScGlobal::pCharClass->uppercase(rTarget));
1677     if (!pData)
1678     {
1679         std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
1680                                                       VclMessageType::Info, VclButtonsType::Ok,
1681                                                       ScResId(STR_TARGETNOTFOUND)));
1682         xInfoBox->run();
1683         return;
1684     }
1685 
1686     SCTAB nTab;
1687     SCCOL nDummyCol;
1688     SCROW nDummyRow;
1689     pData->GetArea( nTab, nDummyCol,nDummyRow,nDummyCol,nDummyRow );
1690 
1691     ScImportParam aImportParam;
1692     pData->GetImportParam( aImportParam );
1693 
1694     OUString sDBName;
1695     OUString sDBTable;
1696     sal_Int32 nCommandType = 0;
1697     sDBName = rDescriptor.getDataSource();
1698     rDescriptor[svx::DataAccessDescriptorProperty::Command]     >>= sDBTable;
1699     rDescriptor[svx::DataAccessDescriptorProperty::CommandType] >>= nCommandType;
1700 
1701     aImportParam.aDBName    = sDBName;
1702     aImportParam.bSql       = ( nCommandType == sdb::CommandType::COMMAND );
1703     aImportParam.aStatement = sDBTable;
1704     aImportParam.bNative    = false;
1705     aImportParam.nType      = static_cast<sal_uInt8>( ( nCommandType == sdb::CommandType::QUERY ) ? ScDbQuery : ScDbTable );
1706     aImportParam.bImport    = true;
1707 
1708     bool bContinue = DoImport( nTab, aImportParam, &rDescriptor );
1709 
1710     //  repeat DB operations
1711 
1712     ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
1713     if (pViewSh)
1714     {
1715         ScRange aRange;
1716         pData->GetArea(aRange);
1717         pViewSh->MarkRange(aRange);         // select
1718 
1719         if ( bContinue )        // error at import -> abort
1720         {
1721             //  internal operations, if some are saved
1722 
1723             if ( pData->HasQueryParam() || pData->HasSortParam() || pData->HasSubTotalParam() )
1724                 pViewSh->RepeatDB();
1725 
1726             //  pivot tables which have the range as source data
1727 
1728             rDocShell.RefreshPivotTables(aRange);
1729         }
1730     }
1731 }
1732 
1733 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1734