tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / docshell / dbdocfun.cxx
blobbf6dd852614d10d0da73f421191421562021188b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sfx2/app.hxx>
21 #include <vcl/svapp.hxx>
22 #include <vcl/weld.hxx>
23 #include <svx/dataaccessdescriptor.hxx>
24 #include <svx/svdpage.hxx>
25 #include <svx/svdoole2.hxx>
26 #include <com/sun/star/sdb/CommandType.hpp>
27 #include <unotools/charclass.hxx>
28 #include <comphelper/lok.hxx>
29 #include <osl/diagnose.h>
31 #include <dbdocfun.hxx>
32 #include <dbdata.hxx>
33 #include <undodat.hxx>
34 #include <docsh.hxx>
35 #include <docfunc.hxx>
36 #include <globstr.hrc>
37 #include <scresid.hxx>
38 #include <globalnames.hxx>
39 #include <tabvwsh.hxx>
40 #include <patattr.hxx>
41 #include <rangenam.hxx>
42 #include <olinetab.hxx>
43 #include <dpobject.hxx>
44 #include <dpsave.hxx>
45 #include <dociter.hxx>
46 #include <editable.hxx>
47 #include <attrib.hxx>
48 #include <drwlayer.hxx>
49 #include <dpshttab.hxx>
50 #include <hints.hxx>
51 #include <queryentry.hxx>
52 #include <markdata.hxx>
53 #include <progress.hxx>
54 #include <undosort.hxx>
55 #include <inputopt.hxx>
56 #include <scmod.hxx>
58 #include <chartlis.hxx>
59 #include <ChartTools.hxx>
61 #include <memory>
63 using namespace ::com::sun::star;
65 bool ScDBDocFunc::AddDBRange( const OUString& rName, const ScRange& rRange )
68 ScDocShellModificator aModificator( rDocShell );
70 ScDocument& rDoc = rDocShell.GetDocument();
71 ScDBCollection* pDocColl = rDoc.GetDBCollection();
72 bool bUndo (rDoc.IsUndoEnabled());
74 std::unique_ptr<ScDBCollection> pUndoColl;
75 if (bUndo)
76 pUndoColl.reset( new ScDBCollection( *pDocColl ) );
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() ));
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 )
91 rDoc.SetAnonymousDBData(rRange.aStart.Tab(), std::move(pNew));
92 bOk = true;
94 else
96 bOk = pDocColl->getNamedDBs().insert(std::move(pNew));
98 if ( bCompile )
99 rDoc.CompileHybridFormula();
101 if (!bOk)
103 return false;
106 if (bUndo)
108 rDocShell.GetUndoManager()->AddUndoAction(
109 std::make_unique<ScUndoDBData>( &rDocShell, std::move(pUndoColl),
110 std::make_unique<ScDBCollection>( *pDocColl ) ) );
113 aModificator.SetDocumentModified();
114 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
115 return true;
118 bool ScDBDocFunc::DeleteDBRange(const OUString& rName)
120 bool bDone = false;
121 ScDocument& rDoc = rDocShell.GetDocument();
122 ScDBCollection* pDocColl = rDoc.GetDBCollection();
123 bool bUndo = rDoc.IsUndoEnabled();
125 ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs();
126 auto const iter = rDBs.findByUpperName2(ScGlobal::getCharClass().uppercase(rName));
127 if (iter != rDBs.end())
129 ScDocShellModificator aModificator( rDocShell );
131 std::unique_ptr<ScDBCollection> pUndoColl;
132 if (bUndo)
133 pUndoColl.reset( new ScDBCollection( *pDocColl ) );
135 rDoc.PreprocessDBDataUpdate();
136 rDBs.erase(iter);
137 rDoc.CompileHybridFormula();
139 if (bUndo)
141 rDocShell.GetUndoManager()->AddUndoAction(
142 std::make_unique<ScUndoDBData>( &rDocShell, std::move(pUndoColl),
143 std::make_unique<ScDBCollection>( *pDocColl ) ) );
146 aModificator.SetDocumentModified();
147 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
148 bDone = true;
151 return bDone;
154 bool ScDBDocFunc::RenameDBRange( const OUString& rOld, const OUString& rNew )
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::getCharClass().uppercase(rOld));
162 const ScDBData* pNew = rDBs.findByUpperName(ScGlobal::getCharClass().uppercase(rNew));
163 if (iterOld != rDBs.end() && !pNew)
165 ScDocShellModificator aModificator( rDocShell );
167 std::unique_ptr<ScDBData> pNewData(new ScDBData(rNew, **iterOld));
169 std::unique_ptr<ScDBCollection> pUndoColl( new ScDBCollection( *pDocColl ) );
171 rDoc.PreprocessDBDataUpdate();
172 rDBs.erase(iterOld);
173 bool bInserted = rDBs.insert(std::move(pNewData));
174 if (!bInserted) // error -> restore old state
176 rDoc.SetDBCollection(std::move(pUndoColl)); // belongs to the document then
179 rDoc.CompileHybridFormula();
181 if (bInserted) // insertion worked
183 if (bUndo)
185 rDocShell.GetUndoManager()->AddUndoAction(
186 std::make_unique<ScUndoDBData>( &rDocShell, std::move(pUndoColl),
187 std::make_unique<ScDBCollection>( *pDocColl ) ) );
189 else
190 pUndoColl.reset();
192 aModificator.SetDocumentModified();
193 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
194 bDone = true;
198 return bDone;
201 void ScDBDocFunc::ModifyDBData( const ScDBData& rNewData )
203 ScDocument& rDoc = rDocShell.GetDocument();
204 ScDBCollection* pDocColl = rDoc.GetDBCollection();
205 bool bUndo = rDoc.IsUndoEnabled();
207 ScDBData* pData = nullptr;
208 if (rNewData.GetName() == STR_DB_LOCAL_NONAME)
210 ScRange aRange;
211 rNewData.GetArea(aRange);
212 SCTAB nTab = aRange.aStart.Tab();
213 pData = rDoc.GetAnonymousDBData(nTab);
215 else
216 pData = pDocColl->getNamedDBs().findByUpperName(rNewData.GetUpperName());
218 if (!pData)
219 return;
221 ScDocShellModificator aModificator( rDocShell );
222 ScRange aOldRange, aNewRange;
223 pData->GetArea(aOldRange);
224 rNewData.GetArea(aNewRange);
225 bool bAreaChanged = ( aOldRange != aNewRange ); // then a recompilation is needed
227 std::unique_ptr<ScDBCollection> pUndoColl;
228 if (bUndo)
229 pUndoColl.reset( new ScDBCollection( *pDocColl ) );
231 *pData = rNewData;
232 if (bAreaChanged)
233 rDoc.CompileDBFormula();
235 if (bUndo)
237 rDocShell.GetUndoManager()->AddUndoAction(
238 std::make_unique<ScUndoDBData>( &rDocShell, std::move(pUndoColl),
239 std::make_unique<ScDBCollection>( *pDocColl ) ) );
242 aModificator.SetDocumentModified();
245 void ScDBDocFunc::ModifyAllDBData( const ScDBCollection& rNewColl, const std::vector<ScRange>& rDelAreaList )
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();
253 for (const auto& rDelArea : rDelAreaList)
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());
262 if (bRecord)
263 pUndoColl.reset( new ScDBCollection( *pOldColl ) );
265 // register target in SBA no longer necessary
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 ) );
275 if (bRecord)
277 rDocShell.GetUndoManager()->AddUndoAction(
278 std::make_unique<ScUndoDBData>(&rDocShell, std::move(pUndoColl),
279 std::make_unique<ScDBCollection>(rNewColl)));
283 bool ScDBDocFunc::RepeatDB( const OUString& rDBName, bool bApi, bool bIsUnnamed, SCTAB aTab )
285 //! use also for ScDBFunc::RepeatDB !
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)
295 pDBData = rDoc.GetAnonymousDBData( aTab );
297 else
299 ScDBCollection* pColl = rDoc.GetDBCollection();
300 if (pColl)
301 pDBData = pColl->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rDBName));
304 if ( pDBData )
306 ScQueryParam aQueryParam;
307 pDBData->GetQueryParam( aQueryParam );
308 bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
310 ScSortParam aSortParam;
311 pDBData->GetSortParam( aSortParam );
312 bool bSort = aSortParam.maKeyState[0].bDoSort;
314 ScSubTotalParam aSubTotalParam;
315 pDBData->GetSubTotalParam( aSubTotalParam );
316 bool bSubTotal = aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly;
318 if ( bQuery || bSort || bSubTotal )
320 bool bQuerySize = false;
321 ScRange aOldQuery;
322 ScRange aNewQuery;
323 if (bQuery && !aQueryParam.bInplace)
325 ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
326 aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
327 if (pDest && pDest->IsDoSize())
329 pDest->GetArea( aOldQuery );
330 bQuerySize = true;
334 SCTAB nTab;
335 SCCOL nStartCol;
336 SCROW nStartRow;
337 SCCOL nEndCol;
338 SCROW nEndRow;
339 pDBData->GetArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
341 //! Undo needed data only ?
343 ScDocumentUniquePtr pUndoDoc;
344 std::unique_ptr<ScOutlineTable> pUndoTab;
345 std::unique_ptr<ScRangeName> pUndoRange;
346 std::unique_ptr<ScDBCollection> pUndoDB;
348 if (bRecord)
350 SCTAB nTabCount = rDoc.GetTableCount();
351 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
352 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
353 if (pTable)
355 pUndoTab.reset(new ScOutlineTable( *pTable ));
357 // column/row state
358 SCCOLROW nOutStartCol, nOutEndCol;
359 SCCOLROW nOutStartRow, nOutEndRow;
360 pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
361 pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
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);
371 else
372 pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
374 // secure data range - incl. filtering result
375 rDoc.CopyToDocument(0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab, InsertDeleteFlags::ALL, false, *pUndoDoc);
377 // all formulas because of references
378 rDoc.CopyToDocument(0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), nTabCount-1, InsertDeleteFlags::FORMULA, false, *pUndoDoc);
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 ));
389 if (bSort && bSubTotal)
391 // sort without SubTotals
393 aSubTotalParam.bRemoveOnly = true; // will be reset again further down
394 DoSubTotals( nTab, aSubTotalParam, false, bApi );
397 if (bSort)
399 pDBData->GetSortParam( aSortParam ); // range may have changed
400 (void)Sort( nTab, aSortParam, false, false, bApi );
402 if (bQuery)
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 );
411 // at not-inplace the table may have been converted
412 // if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
413 // SetTabNo( nTab );
415 if (bSubTotal)
417 pDBData->GetSubTotalParam( aSubTotalParam ); // range may have changed
418 aSubTotalParam.bRemoveOnly = false;
419 DoSubTotals( nTab, aSubTotalParam, false, bApi );
422 if (bRecord)
424 SCTAB nDummyTab;
425 SCCOL nDummyCol;
426 SCROW nDummyRow;
427 SCROW nNewEndRow;
428 pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
430 const ScRange* pOld = nullptr;
431 const ScRange* pNew = nullptr;
432 if (bQuerySize)
434 ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
435 aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
436 if (pDest)
438 pDest->GetArea( aNewQuery );
439 pOld = &aOldQuery;
440 pNew = &aNewQuery;
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 ) );
455 rDocShell.PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
456 PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
457 bDone = true;
459 else if (!bApi) // "Don't execute any operations"
460 rDocShell.ErrorMessage(STR_MSSG_REPEATDB_0);
463 return bDone;
466 bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam,
467 bool bRecord, bool bPaint, bool bApi )
469 ScDocShellModificator aModificator( rDocShell );
471 ScDocument& rDoc = rDocShell.GetDocument();
472 if (bRecord && !rDoc.IsUndoEnabled())
473 bRecord = false;
475 ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rSortParam.nCol1, rSortParam.nRow1,
476 rSortParam.nCol2, rSortParam.nRow2 );
477 if (!pDBData)
479 OSL_FAIL( "Sort: no DBData" );
480 return false;
483 bool bCopy = !rSortParam.bInplace;
484 if ( bCopy && rSortParam.nDestCol == rSortParam.nCol1 &&
485 rSortParam.nDestRow == rSortParam.nRow1 && rSortParam.nDestTab == nTab )
486 bCopy = false;
488 ScSortParam aLocalParam( rSortParam );
489 if ( bCopy )
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);
495 ScDocFunc& rDocFunc = rDocShell.GetDocFunc();
496 bool bRet = rDocFunc.MoveBlock(aSrcRange, aDestPos, false, bRecord, bPaint, bApi);
498 if (!bRet)
499 return false;
501 aLocalParam.MoveToDest();
502 nTab = aLocalParam.nDestTab;
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 SCCOL nStartingColToEdit = aLocalParam.nCol1;
509 if ( aLocalParam.bHasHeader )
511 if ( aLocalParam.bByRow )
512 nStartingRowToEdit++;
513 else
514 nStartingColToEdit++;
516 ScEditableTester aTester( rDoc, nTab, nStartingColToEdit, nStartingRowToEdit,
517 aLocalParam.nCol2, aLocalParam.nRow2, true /*bNoMatrixAtAll*/ );
518 if (!aTester.IsEditable())
520 if (!bApi)
521 rDocShell.ErrorMessage(aTester.GetMessageId());
522 return false;
525 const ScInputOptions aInputOption = ScModule::get()->GetInputOptions();
526 const bool bUpdateRefs = aInputOption.GetSortRefUpdate();
528 // Adjust aLocalParam cols/rows to used data area. Keep sticky top row or
529 // column (depending on direction) in any case, not just if it has headers,
530 // so empty leading cells will be sorted to the end.
531 // aLocalParam.nCol/Row will encompass data content only, extras in
532 // aLocalParam.aDataAreaExtras.
533 bool bShrunk = false;
534 aLocalParam.aDataAreaExtras.resetArea();
535 rDoc.ShrinkToUsedDataArea(bShrunk, nTab, aLocalParam.nCol1, aLocalParam.nRow1,
536 aLocalParam.nCol2, aLocalParam.nRow2, false, aLocalParam.bByRow,
537 !aLocalParam.bByRow,
538 (aLocalParam.aDataAreaExtras.anyExtrasWanted() ?
539 &aLocalParam.aDataAreaExtras : nullptr));
541 SCROW nStartRow = aLocalParam.nRow1;
542 if (aLocalParam.bByRow && aLocalParam.bHasHeader && nStartRow < aLocalParam.nRow2)
543 ++nStartRow;
545 SCCOL nOverallCol1 = aLocalParam.nCol1;
546 SCROW nOverallRow1 = aLocalParam.nRow1;
547 SCCOL nOverallCol2 = aLocalParam.nCol2;
548 SCROW nOverallRow2 = aLocalParam.nRow2;
549 if (aLocalParam.aDataAreaExtras.anyExtrasWanted())
551 // Trailing empty excess columns/rows are excluded from being sorted,
552 // they stick at the end. Clip them.
553 const ScDataAreaExtras::Clip eClip = (aLocalParam.bByRow ?
554 ScDataAreaExtras::Clip::Row : ScDataAreaExtras::Clip::Col);
555 aLocalParam.aDataAreaExtras.GetOverallRange( nOverallCol1, nOverallRow1, nOverallCol2, nOverallRow2, eClip);
556 // Make it permanent.
557 aLocalParam.aDataAreaExtras.SetOverallRange( nOverallCol1, nOverallRow1, nOverallCol2, nOverallRow2);
559 if (bUpdateRefs)
561 // With update references the entire range needs to be handled as
562 // one entity for references pointing within to be moved along,
563 // even when there's no data content. For huge ranges we may be
564 // DOOMed then.
565 aLocalParam.nCol1 = nOverallCol1;
566 aLocalParam.nRow1 = nOverallRow1;
567 aLocalParam.nCol2 = nOverallCol2;
568 aLocalParam.nRow2 = nOverallRow2;
572 if (aLocalParam.aDataAreaExtras.mbCellFormats
573 && rDoc.HasAttrib( nOverallCol1, nStartRow, nTab, nOverallCol2, nOverallRow2, nTab,
574 HasAttrFlags::Merged | HasAttrFlags::Overlapped))
576 // Merge attributes would be mixed up during sorting.
577 if (!bApi)
578 rDocShell.ErrorMessage(STR_SORT_ERR_MERGED);
579 return false;
582 // execute
584 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
586 // Calculate the script types for all cells in the sort range beforehand.
587 // This will speed up the row height adjustment that takes place after the
588 // sort.
589 rDoc.UpdateScriptTypes(
590 ScAddress(aLocalParam.nCol1,nStartRow,nTab),
591 aLocalParam.nCol2-aLocalParam.nCol1+1,
592 aLocalParam.nRow2-nStartRow+1);
594 // No point adjusting row heights after the sort when all rows have the same height.
595 bool bUniformRowHeight = rDoc.HasUniformRowHeight(nTab, nStartRow, nOverallRow2);
597 bool bRepeatQuery = false; // repeat existing filter?
598 ScQueryParam aQueryParam;
599 pDBData->GetQueryParam( aQueryParam );
600 if ( aQueryParam.GetEntry(0).bDoQuery )
601 bRepeatQuery = true;
603 sc::ReorderParam aUndoParam;
605 // don't call ScDocument::Sort with an empty SortParam (may be empty here if bCopy is set)
606 if (aLocalParam.GetSortKeyCount() && aLocalParam.maKeyState[0].bDoSort)
608 ScProgress aProgress(&rDocShell, ScResId(STR_PROGRESS_SORTING), 0, true);
609 if (!bRepeatQuery)
610 bRepeatQuery = rDoc.HasHiddenRows(aLocalParam.nRow1, aLocalParam.nRow2, nTab);
611 rDoc.Sort(nTab, aLocalParam, bRepeatQuery, bUpdateRefs, &aProgress, &aUndoParam);
614 if (bRecord)
616 // Set up an undo object.
617 rDocShell.GetUndoManager()->AddUndoAction(
618 std::make_unique<sc::UndoSort>(&rDocShell, aUndoParam));
621 pDBData->SetSortParam(rSortParam);
622 // Remember additional settings on anonymous database ranges.
623 if (pDBData == rDoc.GetAnonymousDBData( nTab) || rDoc.GetDBCollection()->getAnonDBs().has( pDBData))
624 pDBData->UpdateFromSortParam( rSortParam);
626 if (SfxViewShell* pKitSomeViewForThisDoc = comphelper::LibreOfficeKit::isActive() ?
627 rDocShell.GetBestViewShell(false) : nullptr)
629 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
630 while (pViewShell)
632 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
633 if (pTabViewShell && pTabViewShell->GetDocId() == pKitSomeViewForThisDoc->GetDocId())
635 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nTab))
636 pPosHelper->invalidateByIndex(nStartRow);
638 pViewShell = SfxViewShell::GetNext(*pViewShell);
641 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
642 pKitSomeViewForThisDoc, false /* bColumns */, true /* bRows */, true /* bSizes*/,
643 true /* bHidden */, true /* bFiltered */, true /* bGroups */, nTab);
646 if (nStartRow <= aLocalParam.nRow2)
648 ScRange aDirtyRange(
649 aLocalParam.nCol1, nStartRow, nTab,
650 aLocalParam.nCol2, aLocalParam.nRow2, nTab);
651 rDoc.SetDirty( aDirtyRange, true );
654 if (bPaint)
656 PaintPartFlags nPaint = PaintPartFlags::Grid;
657 SCCOL nStartX = nOverallCol1;
658 SCROW nStartY = nOverallRow1;
659 SCCOL nEndX = nOverallCol2;
660 SCROW nEndY = nOverallRow2;
661 if ( bRepeatQuery )
663 nPaint |= PaintPartFlags::Left;
664 nStartX = 0;
665 nEndX = rDoc.MaxCol();
667 rDocShell.PostPaint(ScRange(nStartX, nStartY, nTab, nEndX, nEndY, nTab), nPaint);
670 if (!bUniformRowHeight && nStartRow <= nOverallRow2)
671 rDocShell.AdjustRowHeight(nStartRow, nOverallRow2, nTab);
673 aModificator.SetDocumentModified();
675 return true;
678 bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam,
679 const ScRange* pAdvSource, bool bRecord, bool bApi )
681 ScDocShellModificator aModificator( rDocShell );
683 ScDocument& rDoc = rDocShell.GetDocument();
685 ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
686 if (pViewSh && ScTabViewShell::isAnyEditViewInRange(pViewSh, /*bColumns*/ false, rQueryParam.nRow1, rQueryParam.nRow2))
688 return false;
691 if (bRecord && !rDoc.IsUndoEnabled())
692 bRecord = false;
693 ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rQueryParam.nCol1, rQueryParam.nRow1,
694 rQueryParam.nCol2, rQueryParam.nRow2 );
695 if (!pDBData)
697 OSL_FAIL( "Query: no DBData" );
698 return false;
701 // Change from Inplace to non-Inplace, only then cancel Inplace:
702 // (only if "Persistent" is selected in the dialog)
704 if ( !rQueryParam.bInplace && pDBData->HasQueryParam() && rQueryParam.bDestPers )
706 ScQueryParam aOldQuery;
707 pDBData->GetQueryParam(aOldQuery);
708 if (aOldQuery.bInplace)
710 // cancel old filtering
712 SCSIZE nEC = aOldQuery.GetEntryCount();
713 for (SCSIZE i=0; i<nEC; i++)
714 aOldQuery.GetEntry(i).bDoQuery = false;
715 aOldQuery.bDuplicate = true;
716 Query( nTab, aOldQuery, nullptr, bRecord, bApi );
720 ScQueryParam aLocalParam( rQueryParam ); // for Paint / destination range
721 bool bCopy = !rQueryParam.bInplace; // copied in Table::Query
722 ScDBData* pDestData = nullptr; // range to be copied to
723 bool bDoSize = false; // adjust destination size (insert/delete)
724 SCCOL nFormulaCols = 0; // only at bDoSize
725 bool bKeepFmt = false;
726 ScRange aOldDest;
727 ScRange aDestTotal;
728 if ( bCopy && rQueryParam.nDestCol == rQueryParam.nCol1 &&
729 rQueryParam.nDestRow == rQueryParam.nRow1 && rQueryParam.nDestTab == nTab )
730 bCopy = false;
731 SCTAB nDestTab = nTab;
732 if ( bCopy )
734 aLocalParam.MoveToDest();
735 nDestTab = rQueryParam.nDestTab;
736 if ( !rDoc.ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) )
738 if (!bApi)
739 rDocShell.ErrorMessage(STR_PASTE_FULL);
740 return false;
743 ScEditableTester aTester( rDoc, nDestTab, aLocalParam.nCol1,aLocalParam.nRow1,
744 aLocalParam.nCol2,aLocalParam.nRow2);
745 if (!aTester.IsEditable())
747 if (!bApi)
748 rDocShell.ErrorMessage(aTester.GetMessageId());
749 return false;
752 pDestData = rDoc.GetDBAtCursor( rQueryParam.nDestCol, rQueryParam.nDestRow,
753 rQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
754 if (pDestData)
756 pDestData->GetArea( aOldDest );
757 aDestTotal=ScRange( rQueryParam.nDestCol,
758 rQueryParam.nDestRow,
759 nDestTab,
760 rQueryParam.nDestCol + rQueryParam.nCol2 - rQueryParam.nCol1,
761 rQueryParam.nDestRow + rQueryParam.nRow2 - rQueryParam.nRow1,
762 nDestTab );
764 bDoSize = pDestData->IsDoSize();
765 // test if formulas need to be filled in (nFormulaCols):
766 if ( bDoSize && aOldDest.aEnd.Col() == aDestTotal.aEnd.Col() )
768 SCCOL nTestCol = aOldDest.aEnd.Col() + 1; // next to the range
769 SCROW nTestRow = rQueryParam.nDestRow +
770 ( aLocalParam.bHasHeader ? 1 : 0 );
771 while ( nTestCol <= rDoc.MaxCol() &&
772 rDoc.GetCellType(ScAddress( nTestCol, nTestRow, nTab )) == CELLTYPE_FORMULA )
774 ++nTestCol;
775 ++nFormulaCols;
779 bKeepFmt = pDestData->IsKeepFmt();
780 if ( bDoSize && !rDoc.CanFitBlock( aOldDest, aDestTotal ) )
782 if (!bApi)
783 rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2); // cannot insert rows
784 return false;
789 // execute
791 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
793 bool bKeepSub = false; // repeat existing partial results?
794 if (rQueryParam.GetEntry(0).bDoQuery) // not at cancellation
796 ScSubTotalParam aSubTotalParam;
797 pDBData->GetSubTotalParam( aSubTotalParam ); // partial results exist?
799 if (aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly)
800 bKeepSub = true;
803 ScDocumentUniquePtr pUndoDoc;
804 std::unique_ptr<ScDBCollection> pUndoDB;
805 const ScRange* pOld = nullptr;
807 if ( bRecord )
809 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
810 if (bCopy)
812 pUndoDoc->InitUndo( rDoc, nDestTab, nDestTab, false, true );
813 rDoc.CopyToDocument(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
814 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab,
815 InsertDeleteFlags::ALL, false, *pUndoDoc);
816 // secure attributes in case they were copied along
818 if (pDestData)
820 rDoc.CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, *pUndoDoc);
821 pOld = &aOldDest;
824 else
826 pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
827 rDoc.CopyToDocument(0, rQueryParam.nRow1, nTab, rDoc.MaxCol(), rQueryParam.nRow2, nTab,
828 InsertDeleteFlags::NONE, false, *pUndoDoc);
831 ScDBCollection* pDocDB = rDoc.GetDBCollection();
832 if (!pDocDB->empty())
833 pUndoDB.reset(new ScDBCollection( *pDocDB ));
835 rDoc.BeginDrawUndo();
838 std::unique_ptr<ScDocument> pAttribDoc;
839 ScRange aAttribRange;
840 if (pDestData) // delete destination range
842 if ( bKeepFmt )
844 // smaller of the end columns, header+1 row
845 aAttribRange = aOldDest;
846 if ( aAttribRange.aEnd.Col() > aDestTotal.aEnd.Col() )
847 aAttribRange.aEnd.SetCol( aDestTotal.aEnd.Col() );
848 aAttribRange.aEnd.SetRow( aAttribRange.aStart.Row() +
849 ( aLocalParam.bHasHeader ? 1 : 0 ) );
851 // also for filled-in formulas
852 aAttribRange.aEnd.SetCol( aAttribRange.aEnd.Col() + nFormulaCols );
854 pAttribDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
855 pAttribDoc->InitUndo( rDoc, nDestTab, nDestTab, false, true );
856 rDoc.CopyToDocument(aAttribRange, InsertDeleteFlags::ATTRIB, false, *pAttribDoc);
859 if ( bDoSize )
860 rDoc.FitBlock( aOldDest, aDestTotal );
861 else
862 rDoc.DeleteAreaTab(aOldDest, InsertDeleteFlags::ALL); // simply delete
865 // execute filtering on the document
866 SCSIZE nCount = rDoc.Query( nTab, rQueryParam, bKeepSub );
867 pDBData->CalcSaveFilteredCount( nCount );
868 if (bCopy)
870 aLocalParam.nRow2 = aLocalParam.nRow1 + nCount;
871 if (!aLocalParam.bHasHeader && nCount > 0)
872 --aLocalParam.nRow2;
874 if ( bDoSize )
876 // adjust to the real result range
877 // (this here is always a reduction)
879 ScRange aNewDest( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
880 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab );
881 rDoc.FitBlock( aDestTotal, aNewDest, false ); // sal_False - don't delete
883 if ( nFormulaCols > 0 )
885 // fill in formulas
886 //! Undo (Query and Repeat) !!!
888 ScRange aNewForm( aLocalParam.nCol2+1, aLocalParam.nRow1, nDestTab,
889 aLocalParam.nCol2+nFormulaCols, aLocalParam.nRow2, nDestTab );
890 ScRange aOldForm = aNewForm;
891 aOldForm.aEnd.SetRow( aOldDest.aEnd.Row() );
892 rDoc.FitBlock( aOldForm, aNewForm, false );
894 ScMarkData aMark(rDoc.GetSheetLimits());
895 aMark.SelectOneTable(nDestTab);
896 SCROW nFStartY = aLocalParam.nRow1 + ( aLocalParam.bHasHeader ? 1 : 0 );
898 sal_uLong nProgCount = nFormulaCols;
899 nProgCount *= aLocalParam.nRow2 - nFStartY;
900 ScProgress aProgress( rDoc.GetDocumentShell(),
901 ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
903 rDoc.Fill( aLocalParam.nCol2+1, nFStartY,
904 aLocalParam.nCol2+nFormulaCols, nFStartY, &aProgress, aMark,
905 aLocalParam.nRow2 - nFStartY,
906 FILL_TO_BOTTOM, FILL_SIMPLE );
910 if ( pAttribDoc ) // copy back the memorized attributes
912 // Header
913 if (aLocalParam.bHasHeader)
915 ScRange aHdrRange = aAttribRange;
916 aHdrRange.aEnd.SetRow( aHdrRange.aStart.Row() );
917 pAttribDoc->CopyToDocument(aHdrRange, InsertDeleteFlags::ATTRIB, false, rDoc);
920 // Data
921 SCCOL nAttrEndCol = aAttribRange.aEnd.Col();
922 SCROW nAttrRow = aAttribRange.aStart.Row() + ( aLocalParam.bHasHeader ? 1 : 0 );
923 for (SCCOL nCol = aAttribRange.aStart.Col(); nCol<=nAttrEndCol; nCol++)
925 const ScPatternAttr* pSrcPattern = pAttribDoc->GetPattern(
926 nCol, nAttrRow, nDestTab );
927 OSL_ENSURE(pSrcPattern,"Pattern is 0");
928 if (pSrcPattern)
930 rDoc.ApplyPatternAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
931 nDestTab, *pSrcPattern );
932 const ScStyleSheet* pStyle = pSrcPattern->GetStyleSheet();
933 if (pStyle)
934 rDoc.ApplyStyleAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
935 nDestTab, *pStyle );
941 // saving: Inplace always, otherwise depending on setting
942 // old Inplace-Filter may have already been removed
944 bool bSave = rQueryParam.bInplace || rQueryParam.bDestPers;
945 if (bSave) // memorize
947 pDBData->SetQueryParam( rQueryParam );
948 pDBData->SetHeader( rQueryParam.bHasHeader ); //! ???
949 pDBData->SetAdvancedQuerySource( pAdvSource ); // after SetQueryParam
952 if (bCopy) // memorize new DB range
954 // Selection is done afterwards from outside (dbfunc).
955 // Currently through the DB area at the destination position,
956 // so a range must be created there in any case.
958 ScDBData* pNewData;
959 if (pDestData)
960 pNewData = pDestData; // range exists -> adjust (always!)
961 else // create range
962 pNewData = rDocShell.GetDBData(
963 ScRange( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
964 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab ),
965 SC_DB_MAKE, ScGetDBSelection::ForceMark );
967 if (pNewData)
969 pNewData->SetArea( nDestTab, aLocalParam.nCol1, aLocalParam.nRow1,
970 aLocalParam.nCol2, aLocalParam.nRow2 );
972 // query parameter is no longer set at the destination, only leads to confusion
973 // and mistakes with the query parameter at the source range (#37187#)
975 else
977 OSL_FAIL("Target are not available");
981 if (!bCopy)
983 rDoc.InvalidatePageBreaks(nTab);
984 rDoc.UpdatePageBreaks( nTab );
987 // #i23299# Subtotal functions depend on cell's filtered states.
988 ScRange aDirtyRange(0 , aLocalParam.nRow1, nDestTab, rDoc.MaxCol(), aLocalParam.nRow2, nDestTab);
989 rDoc.SetSubTotalCellsDirty(aDirtyRange);
991 if ( bRecord )
993 // create undo action after executing, because of drawing layer undo
994 rDocShell.GetUndoManager()->AddUndoAction(
995 std::make_unique<ScUndoQuery>( &rDocShell, nTab, rQueryParam, std::move(pUndoDoc), std::move(pUndoDB),
996 pOld, bDoSize, pAdvSource ) );
999 if ( pViewSh )
1001 // could there be horizontal autofilter ?
1002 // maybe it would be better to set bColumns to !rQueryParam.bByRow ?
1003 // anyway at the beginning the value of bByRow is 'false'
1004 // then after the first sort action it becomes 'true'
1005 pViewSh->OnLOKShowHideColRow(/*bColumns*/ false, rQueryParam.nRow1 - 1);
1008 if (bCopy)
1010 SCCOL nEndX = aLocalParam.nCol2;
1011 SCROW nEndY = aLocalParam.nRow2;
1012 if (pDestData)
1014 if ( aOldDest.aEnd.Col() > nEndX )
1015 nEndX = aOldDest.aEnd.Col();
1016 if ( aOldDest.aEnd.Row() > nEndY )
1017 nEndY = aOldDest.aEnd.Row();
1019 if (bDoSize)
1020 nEndY = rDoc.MaxRow();
1022 // remove AutoFilter button flags
1023 rDocShell.DBAreaDeleted(nDestTab, aLocalParam.nCol1, aLocalParam.nRow1, aLocalParam.nCol2);
1025 rDocShell.PostPaint(
1026 ScRange(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, nEndX, nEndY, nDestTab),
1027 PaintPartFlags::Grid);
1029 else
1030 rDocShell.PostPaint(
1031 ScRange(0, rQueryParam.nRow1, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
1032 PaintPartFlags::Grid | PaintPartFlags::Left);
1033 aModificator.SetDocumentModified();
1035 return true;
1038 void ScDBDocFunc::DoSubTotals( SCTAB nTab, const ScSubTotalParam& rParam,
1039 bool bRecord, bool bApi )
1041 //! use also for ScDBFunc::DoSubTotals !
1042 // then stays outside:
1043 // - mark new range (from DBData)
1044 // - SelectionChanged (?)
1046 bool bDo = !rParam.bRemoveOnly; // sal_False = only delete
1048 ScDocument& rDoc = rDocShell.GetDocument();
1049 if (bRecord && !rDoc.IsUndoEnabled())
1050 bRecord = false;
1051 ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
1052 rParam.nCol2, rParam.nRow2 );
1053 if (!pDBData)
1055 OSL_FAIL( "SubTotals: no DBData" );
1056 return;
1059 ScEditableTester aTester( rDoc, nTab, 0,rParam.nRow1+1, rDoc.MaxCol(),rDoc.MaxRow() );
1060 if (!aTester.IsEditable())
1062 if (!bApi)
1063 rDocShell.ErrorMessage(aTester.GetMessageId());
1064 return;
1067 if (rDoc.HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
1068 rParam.nCol2, rParam.nRow2, nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
1070 if (!bApi)
1071 rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0); // don't insert into merged
1072 return;
1075 bool bOk = true;
1076 if (rParam.bReplace)
1078 if (rDoc.TestRemoveSubTotals( nTab, rParam ))
1080 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
1081 VclMessageType::Question,
1082 VclButtonsType::YesNo, ScResId(STR_MSSG_DOSUBTOTALS_1))); // "Delete Data?"
1083 xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0)); // "StarCalc"
1084 bOk = xBox->run() == RET_YES;
1088 if (!bOk)
1089 return;
1091 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
1092 ScDocShellModificator aModificator( rDocShell );
1094 ScSubTotalParam aNewParam( rParam ); // end of range is being changed
1095 ScDocumentUniquePtr pUndoDoc;
1096 std::unique_ptr<ScOutlineTable> pUndoTab;
1097 std::unique_ptr<ScRangeName> pUndoRange;
1098 std::unique_ptr<ScDBCollection> pUndoDB;
1100 if (bRecord) // secure old data
1102 bool bOldFilter = bDo && rParam.bDoSort;
1104 SCTAB nTabCount = rDoc.GetTableCount();
1105 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
1106 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
1107 if (pTable)
1109 pUndoTab.reset(new ScOutlineTable( *pTable ));
1111 // column/row state
1112 SCCOLROW nOutStartCol, nOutEndCol;
1113 SCCOLROW nOutStartRow, nOutEndRow;
1114 pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
1115 pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
1117 pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
1118 rDoc.CopyToDocument(static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
1119 rDoc.CopyToDocument(0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
1121 else
1122 pUndoDoc->InitUndo( rDoc, nTab, nTab, false, bOldFilter );
1124 // secure data range - incl. filtering result
1125 rDoc.CopyToDocument(0, rParam.nRow1+1,nTab, rDoc.MaxCol(),rParam.nRow2,nTab,
1126 InsertDeleteFlags::ALL, false, *pUndoDoc);
1128 // all formulas because of references
1129 rDoc.CopyToDocument(0, 0, 0, rDoc.MaxCol(),rDoc.MaxRow(),nTabCount-1,
1130 InsertDeleteFlags::FORMULA, false, *pUndoDoc);
1132 // ranges of DB and other
1133 ScRangeName* pDocRange = rDoc.GetRangeName();
1134 if (!pDocRange->empty())
1135 pUndoRange.reset(new ScRangeName( *pDocRange ));
1136 ScDBCollection* pDocDB = rDoc.GetDBCollection();
1137 if (!pDocDB->empty())
1138 pUndoDB.reset(new ScDBCollection( *pDocDB ));
1141 // rDoc.SetOutlineTable( nTab, NULL );
1142 ScOutlineTable* pOut = rDoc.GetOutlineTable( nTab );
1143 if (pOut)
1144 pOut->GetRowArray().RemoveAll(); // only delete row outlines
1146 if (rParam.bReplace)
1147 rDoc.RemoveSubTotals( nTab, aNewParam );
1148 bool bSuccess = true;
1149 if (bDo)
1151 // sort
1152 if ( rParam.bDoSort )
1154 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
1156 // set partial result field to before the sorting
1157 // (Duplicates are omitted, so can be called again)
1159 ScSortParam aOldSort;
1160 pDBData->GetSortParam( aOldSort );
1161 ScSortParam aSortParam( aNewParam, aOldSort );
1162 Sort( nTab, aSortParam, false, false, bApi );
1165 bSuccess = rDoc.DoSubTotals( nTab, aNewParam );
1166 rDoc.SetDrawPageSize(nTab);
1168 ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
1169 aNewParam.nCol2, aNewParam.nRow2, nTab );
1170 rDoc.SetDirty( aDirtyRange, true );
1172 if (bRecord)
1174 // ScDBData* pUndoDBData = pDBData ? new ScDBData( *pDBData ) : NULL;
1175 rDocShell.GetUndoManager()->AddUndoAction(
1176 std::make_unique<ScUndoSubTotals>( &rDocShell, nTab,
1177 rParam, aNewParam.nRow2,
1178 std::move(pUndoDoc), std::move(pUndoTab), // pUndoDBData,
1179 std::move(pUndoRange), std::move(pUndoDB) ) );
1182 if (!bSuccess)
1184 // "Cannot insert rows"
1185 if (!bApi)
1186 rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
1189 // memorize
1190 pDBData->SetSubTotalParam( aNewParam );
1191 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
1192 rDoc.CompileDBFormula();
1194 rDocShell.PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab),
1195 PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
1196 aModificator.SetDocumentModified();
1199 namespace {
1201 bool lcl_EmptyExcept( ScDocument& rDoc, const ScRange& rRange, const ScRange& rExcept )
1203 ScCellIterator aIter( rDoc, rRange );
1204 for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
1206 if (!aIter.isEmpty()) // real content?
1208 if (!rExcept.Contains(aIter.GetPos()))
1209 return false; // cell found
1213 return true; // nothing found - empty
1216 bool isEditable(ScDocShell& rDocShell, const ScRangeList& rRanges, bool bApi,
1217 sc::EditAction eAction = sc::EditAction::Unknown)
1219 ScDocument& rDoc = rDocShell.GetDocument();
1220 if (!rDocShell.IsEditable() || rDoc.GetChangeTrack())
1222 // not recorded -> disallow
1223 if (!bApi)
1224 rDocShell.ErrorMessage(STR_PROTECTIONERR);
1226 return false;
1229 for (size_t i = 0, n = rRanges.size(); i < n; ++i)
1231 const ScRange & r = rRanges[i];
1232 ScEditableTester aTester(rDoc, r, eAction);
1233 if (!aTester.IsEditable())
1235 if (!bApi)
1236 rDocShell.ErrorMessage(aTester.GetMessageId());
1238 return false;
1242 return true;
1245 void createUndoDoc(ScDocumentUniquePtr& pUndoDoc, ScDocument& rDoc, const ScRange& rRange)
1247 SCTAB nTab = rRange.aStart.Tab();
1248 pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
1249 pUndoDoc->InitUndo(rDoc, nTab, nTab);
1250 rDoc.CopyToDocument(rRange, InsertDeleteFlags::ALL, false, *pUndoDoc);
1253 bool checkNewOutputRange(ScDPObject& rDPObj, ScDocShell& rDocShell, ScRange& rNewOut, bool bApi,
1254 sc::EditAction eAction = sc::EditAction::Unknown)
1256 ScDocument& rDoc = rDocShell.GetDocument();
1258 bool bOverflow = false;
1259 rNewOut = rDPObj.GetNewOutputRange(bOverflow);
1261 // Test for overlap with source data range.
1262 // TODO: Check with other pivot tables as well.
1263 const ScSheetSourceDesc* pSheetDesc = rDPObj.GetSheetDesc();
1264 if (pSheetDesc && pSheetDesc->GetSourceRange().Intersects(rNewOut))
1266 // New output range intersteps with the source data. Move it up to
1267 // where the old range is and see if that works.
1268 ScRange aOldRange = rDPObj.GetOutRange();
1269 SCROW nDiff = aOldRange.aStart.Row() - rNewOut.aStart.Row();
1270 rNewOut.aStart.SetRow(aOldRange.aStart.Row());
1271 rNewOut.aEnd.IncRow(nDiff);
1272 if (!rDoc.ValidRow(rNewOut.aStart.Row()) || !rDoc.ValidRow(rNewOut.aEnd.Row()))
1273 bOverflow = true;
1276 if (bOverflow)
1278 if (!bApi)
1279 rDocShell.ErrorMessage(STR_PIVOT_ERROR);
1281 return false;
1284 if (!rDoc.IsImportingXML())
1286 ScEditableTester aTester(rDoc, rNewOut, eAction);
1287 if (!aTester.IsEditable())
1289 // destination area isn't editable
1290 if (!bApi)
1291 rDocShell.ErrorMessage(aTester.GetMessageId());
1293 return false;
1297 return true;
1302 bool ScDBDocFunc::DataPilotUpdate( ScDPObject* pOldObj, const ScDPObject* pNewObj,
1303 bool bRecord, bool bApi, bool bAllowMove )
1305 if (!pOldObj)
1307 if (!pNewObj)
1308 return false;
1310 return CreatePivotTable(*pNewObj, bRecord, bApi);
1313 if (!pNewObj)
1314 return RemovePivotTable(*pOldObj, bRecord, bApi);
1316 if (pOldObj == pNewObj)
1317 return UpdatePivotTable(*pOldObj, bRecord, bApi);
1319 OSL_ASSERT(pOldObj && pNewObj && pOldObj != pNewObj);
1321 ScDocShellModificator aModificator( rDocShell );
1322 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
1324 ScRangeList aRanges;
1325 aRanges.push_back(pOldObj->GetOutRange());
1326 aRanges.push_back(ScRange(pNewObj->GetOutRange().aStart)); // at least one cell in the output position must be editable.
1327 if (!isEditable(rDocShell, aRanges, bApi))
1328 return false;
1330 ScDocumentUniquePtr pOldUndoDoc;
1331 ScDocumentUniquePtr pNewUndoDoc;
1333 ScDPObject aUndoDPObj(*pOldObj); // for undo or revert on failure
1335 ScDocument& rDoc = rDocShell.GetDocument();
1336 if (bRecord && !rDoc.IsUndoEnabled())
1337 bRecord = false;
1339 if (bRecord)
1340 createUndoDoc(pOldUndoDoc, rDoc, pOldObj->GetOutRange());
1342 pNewObj->WriteSourceDataTo(*pOldObj); // copy source data
1344 ScDPSaveData* pData = pNewObj->GetSaveData();
1345 OSL_ENSURE( pData, "no SaveData from living DPObject" );
1346 if (pData)
1347 pOldObj->SetSaveData(*pData); // copy SaveData
1349 pOldObj->SetAllowMove(bAllowMove);
1350 pOldObj->ReloadGroupTableData();
1351 pOldObj->SyncAllDimensionMembers();
1352 pOldObj->InvalidateData(); // before getting the new output area
1354 // make sure the table has a name (not set by dialog)
1355 if (pOldObj->GetName().isEmpty())
1356 pOldObj->SetName( rDoc.GetDPCollection()->CreateNewName() );
1358 ScRange aNewOut;
1359 if (!checkNewOutputRange(*pOldObj, rDocShell, aNewOut, bApi))
1361 *pOldObj = aUndoDPObj;
1362 return false;
1365 // test if new output area is empty except for old area
1366 if (!bApi)
1368 // OutRange of pOldObj (pDestObj) is still old area
1369 if (!lcl_EmptyExcept(rDoc, aNewOut, pOldObj->GetOutRange()))
1371 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
1372 VclMessageType::Question, VclButtonsType::YesNo,
1373 ScResId(STR_PIVOT_NOTEMPTY)));
1374 xQueryBox->set_default_response(RET_YES);
1375 if (xQueryBox->run() == RET_NO)
1377 //! like above (not editable)
1378 *pOldObj = aUndoDPObj;
1379 return false;
1384 if (bRecord)
1385 createUndoDoc(pNewUndoDoc, rDoc, aNewOut);
1387 pOldObj->Output(aNewOut.aStart);
1388 rDocShell.PostPaintGridAll(); //! only necessary parts
1390 if (bRecord)
1392 rDocShell.GetUndoManager()->AddUndoAction(
1393 std::make_unique<ScUndoDataPilot>(
1394 &rDocShell, std::move(pOldUndoDoc), std::move(pNewUndoDoc), &aUndoDPObj, pOldObj, bAllowMove));
1397 // notify API objects
1398 rDoc.BroadcastUno( ScDataPilotModifiedHint(pOldObj->GetName()) );
1399 aModificator.SetDocumentModified();
1401 return true;
1404 bool ScDBDocFunc::RemovePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi)
1406 ScDocShellModificator aModificator(rDocShell);
1407 weld::WaitObject aWait(ScDocShell::GetActiveDialogParent());
1409 if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi))
1410 return false;
1412 ScDocument& rDoc = rDocShell.GetDocument();
1414 if (!bApi)
1416 // If we come from GUI - ask to delete the associated pivot charts too...
1417 std::vector<SdrOle2Obj*> aListOfObjects =
1418 sc::tools::getAllPivotChartsConnectedTo(rDPObj.GetName(), &rDocShell);
1420 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1422 if (pModel && !aListOfObjects.empty())
1424 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
1425 VclMessageType::Question, VclButtonsType::YesNo,
1426 ScResId(STR_PIVOT_REMOVE_PIVOTCHART)));
1427 xQueryBox->set_default_response(RET_YES);
1428 if (xQueryBox->run() == RET_NO)
1430 return false;
1432 else
1434 for (SdrOle2Obj* pChartObject : aListOfObjects)
1436 rDoc.GetChartListenerCollection()->removeByName(pChartObject->GetName());
1437 pModel->AddUndo(std::make_unique<SdrUndoDelObj>(*pChartObject));
1438 pChartObject->getSdrPageFromSdrObject()->RemoveObject(pChartObject->GetOrdNum());
1444 ScDocumentUniquePtr pOldUndoDoc;
1445 std::unique_ptr<ScDPObject> pUndoDPObj;
1447 if (bRecord)
1448 pUndoDPObj.reset(new ScDPObject(rDPObj)); // copy old settings for undo
1450 if (bRecord && !rDoc.IsUndoEnabled())
1451 bRecord = false;
1453 // delete table
1455 ScRange aRange = rDPObj.GetOutRange();
1456 SCTAB nTab = aRange.aStart.Tab();
1458 if (bRecord)
1459 createUndoDoc(pOldUndoDoc, rDoc, aRange);
1461 rDoc.DeleteAreaTab( aRange.aStart.Col(), aRange.aStart.Row(),
1462 aRange.aEnd.Col(), aRange.aEnd.Row(),
1463 nTab, InsertDeleteFlags::ALL );
1464 rDoc.RemoveFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
1465 aRange.aEnd.Col(), aRange.aEnd.Row(),
1466 nTab, ScMF::Auto );
1468 rDoc.GetDPCollection()->FreeTable(&rDPObj); // object is deleted here
1470 rDocShell.PostPaintGridAll(); //! only necessary parts
1471 rDocShell.PostPaint(aRange, PaintPartFlags::Grid);
1473 if (bRecord)
1475 rDocShell.GetUndoManager()->AddUndoAction(
1476 std::make_unique<ScUndoDataPilot>(
1477 &rDocShell, std::move(pOldUndoDoc), nullptr, pUndoDPObj.get(), nullptr, false));
1479 // pUndoDPObj is copied
1482 aModificator.SetDocumentModified();
1483 return true;
1486 bool ScDBDocFunc::CreatePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi)
1488 ScDocShellModificator aModificator(rDocShell);
1489 weld::WaitObject aWait(ScDocShell::GetActiveDialogParent());
1491 // At least one cell in the output range should be editable. Check in advance.
1492 ScDocument& rDoc = rDocShell.GetDocument();
1493 if (!rDoc.IsImportingXML() && !isEditable(rDocShell, ScRange(rDPObj.GetOutRange().aStart), bApi))
1494 return false;
1496 ScDocumentUniquePtr pNewUndoDoc;
1498 if (bRecord && !rDoc.IsUndoEnabled())
1499 bRecord = false;
1501 // output range must be set at pNewObj
1502 std::unique_ptr<ScDPObject> pDestObj(new ScDPObject(rDPObj));
1504 ScDPObject& rDestObj = *pDestObj;
1506 // #i94570# When changing the output position in the dialog, a new table is created
1507 // with the settings from the old table, including the name.
1508 // So we have to check for duplicate names here (before inserting).
1509 if (rDoc.GetDPCollection()->GetByName(rDestObj.GetName()))
1510 rDestObj.SetName(OUString()); // ignore the invalid name, create a new name below
1512 // Synchronize groups between linked tables
1514 const ScDPDimensionSaveData* pGroups = nullptr;
1515 bool bRefFound = rDoc.GetDPCollection()->GetReferenceGroups(rDestObj, &pGroups);
1516 if (bRefFound)
1518 ScDPSaveData* pSaveData = rDestObj.GetSaveData();
1519 if (pSaveData)
1520 pSaveData->SetDimensionData(pGroups);
1524 rDoc.GetDPCollection()->InsertNewTable(std::move(pDestObj));
1526 rDestObj.ReloadGroupTableData();
1527 rDestObj.SyncAllDimensionMembers();
1528 rDestObj.InvalidateData(); // before getting the new output area
1530 // make sure the table has a name (not set by dialog)
1531 if (rDestObj.GetName().isEmpty())
1532 rDestObj.SetName(rDoc.GetDPCollection()->CreateNewName());
1534 bool bOverflow = false;
1535 ScRange aNewOut = rDestObj.GetNewOutputRange(bOverflow);
1537 if (bOverflow)
1539 if (!bApi)
1540 rDocShell.ErrorMessage(STR_PIVOT_ERROR);
1542 return false;
1545 if (!rDoc.IsImportingXML())
1547 ScEditableTester aTester(rDoc, aNewOut, sc::EditAction::Unknown);
1548 if (!aTester.IsEditable())
1550 // destination area isn't editable
1551 if (!bApi)
1552 rDocShell.ErrorMessage(aTester.GetMessageId());
1554 return false;
1558 // test if new output area is empty except for old area
1559 if (!bApi)
1561 bool bEmpty = rDoc.IsBlockEmpty(
1562 aNewOut.aStart.Col(), aNewOut.aStart.Row(),
1563 aNewOut.aEnd.Col(), aNewOut.aEnd.Row(), aNewOut.aStart.Tab() );
1565 if (!bEmpty)
1567 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
1568 VclMessageType::Question, VclButtonsType::YesNo,
1569 ScResId(STR_PIVOT_NOTEMPTY)));
1570 xQueryBox->set_default_response(RET_YES);
1571 if (xQueryBox->run() == RET_NO)
1573 //! like above (not editable)
1574 return false;
1579 if (bRecord)
1580 createUndoDoc(pNewUndoDoc, rDoc, aNewOut);
1582 rDestObj.Output(aNewOut.aStart);
1583 rDocShell.PostPaintGridAll(); //! only necessary parts
1585 if (bRecord)
1587 rDocShell.GetUndoManager()->AddUndoAction(
1588 std::make_unique<ScUndoDataPilot>(&rDocShell, nullptr, std::move(pNewUndoDoc), nullptr, &rDestObj, false));
1591 // notify API objects
1592 rDoc.BroadcastUno(ScDataPilotModifiedHint(rDestObj.GetName()));
1593 aModificator.SetDocumentModified();
1595 return true;
1598 bool ScDBDocFunc::UpdatePivotTable(ScDPObject& rDPObj, bool bRecord, bool bApi)
1600 ScDocShellModificator aModificator( rDocShell );
1601 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
1603 if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi, sc::EditAction::UpdatePivotTable))
1604 return false;
1606 ScDocumentUniquePtr pOldUndoDoc;
1607 ScDocumentUniquePtr pNewUndoDoc;
1609 ScDPObject aUndoDPObj(rDPObj); // For undo or revert on failure.
1611 ScDocument& rDoc = rDocShell.GetDocument();
1612 if (bRecord && !rDoc.IsUndoEnabled())
1613 bRecord = false;
1615 if (bRecord)
1616 createUndoDoc(pOldUndoDoc, rDoc, rDPObj.GetOutRange());
1618 rDPObj.SetAllowMove(false);
1619 rDPObj.ReloadGroupTableData();
1620 if (!rDPObj.SyncAllDimensionMembers())
1621 return false;
1623 rDPObj.InvalidateData(); // before getting the new output area
1625 // make sure the table has a name (not set by dialog)
1626 if (rDPObj.GetName().isEmpty())
1627 rDPObj.SetName( rDoc.GetDPCollection()->CreateNewName() );
1629 ScRange aNewOut;
1630 if (!checkNewOutputRange(rDPObj, rDocShell, aNewOut, bApi, sc::EditAction::UpdatePivotTable))
1632 rDPObj = aUndoDPObj;
1633 return false;
1636 // test if new output area is empty except for old area
1637 if (!bApi)
1639 if (!lcl_EmptyExcept(rDoc, aNewOut, rDPObj.GetOutRange()))
1641 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
1642 VclMessageType::Question, VclButtonsType::YesNo,
1643 ScResId(STR_PIVOT_NOTEMPTY)));
1644 xQueryBox->set_default_response(RET_YES);
1645 if (xQueryBox->run() == RET_NO)
1647 rDPObj = aUndoDPObj;
1648 return false;
1653 if (bRecord)
1654 createUndoDoc(pNewUndoDoc, rDoc, aNewOut);
1656 rDPObj.Output(aNewOut.aStart);
1657 rDocShell.PostPaintGridAll(); //! only necessary parts
1659 if (bRecord)
1661 rDocShell.GetUndoManager()->AddUndoAction(
1662 std::make_unique<ScUndoDataPilot>(
1663 &rDocShell, std::move(pOldUndoDoc), std::move(pNewUndoDoc), &aUndoDPObj, &rDPObj, false));
1666 // notify API objects
1667 rDoc.BroadcastUno( ScDataPilotModifiedHint(rDPObj.GetName()) );
1668 aModificator.SetDocumentModified();
1669 return true;
1672 void ScDBDocFunc::RefreshPivotTables(const ScDPObject* pDPObj, bool bApi)
1674 ScDPCollection* pDPs = rDocShell.GetDocument().GetDPCollection();
1675 if (!pDPs)
1676 return;
1678 o3tl::sorted_vector<ScDPObject*> aRefs;
1679 TranslateId pErrId = pDPs->ReloadCache(pDPObj, aRefs);
1680 if (pErrId)
1681 return;
1683 for (ScDPObject* pObj : aRefs)
1685 // This action is intentionally not undoable since it modifies cache.
1686 UpdatePivotTable(*pObj, false, bApi);
1690 void ScDBDocFunc::RefreshPivotTableGroups(ScDPObject* pDPObj)
1692 if (!pDPObj)
1693 return;
1695 ScDPCollection* pDPs = rDocShell.GetDocument().GetDPCollection();
1696 if (!pDPs)
1697 return;
1699 ScDPSaveData* pSaveData = pDPObj->GetSaveData();
1700 if (!pSaveData)
1701 return;
1703 if (!pDPs->HasTable(pDPObj))
1705 // This table is under construction so no need for a whole update (UpdatePivotTable()).
1706 pDPObj->ReloadGroupTableData();
1707 return;
1710 // Update all linked tables, if this table is part of the cache (ScDPCollection)
1711 o3tl::sorted_vector<ScDPObject*> aRefs;
1712 if (!pDPs->ReloadGroupsInCache(pDPObj, aRefs))
1713 return;
1715 // We allow pDimData being NULL.
1716 const ScDPDimensionSaveData* pDimData = pSaveData->GetExistingDimensionData();
1717 for (ScDPObject* pObj : aRefs)
1719 if (pObj != pDPObj)
1721 pSaveData = pObj->GetSaveData();
1722 if (pSaveData)
1723 pSaveData->SetDimensionData(pDimData);
1726 // This action is intentionally not undoable since it modifies cache.
1727 UpdatePivotTable(*pObj, false, false);
1731 // database import
1733 void ScDBDocFunc::UpdateImport( const OUString& rTarget, const svx::ODataAccessDescriptor& rDescriptor )
1735 // rTarget is the name of a database range
1737 ScDocument& rDoc = rDocShell.GetDocument();
1738 ScDBCollection& rDBColl = *rDoc.GetDBCollection();
1739 const ScDBData* pData = rDBColl.getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rTarget));
1740 if (!pData)
1742 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
1743 VclMessageType::Info, VclButtonsType::Ok,
1744 ScResId(STR_TARGETNOTFOUND)));
1745 xInfoBox->run();
1746 return;
1749 SCTAB nTab;
1750 SCCOL nDummyCol;
1751 SCROW nDummyRow;
1752 pData->GetArea( nTab, nDummyCol,nDummyRow,nDummyCol,nDummyRow );
1754 ScImportParam aImportParam;
1755 pData->GetImportParam( aImportParam );
1757 OUString sDBName;
1758 OUString sDBTable;
1759 sal_Int32 nCommandType = 0;
1760 sDBName = rDescriptor.getDataSource();
1761 rDescriptor[svx::DataAccessDescriptorProperty::Command] >>= sDBTable;
1762 rDescriptor[svx::DataAccessDescriptorProperty::CommandType] >>= nCommandType;
1764 aImportParam.aDBName = sDBName;
1765 aImportParam.bSql = ( nCommandType == sdb::CommandType::COMMAND );
1766 aImportParam.aStatement = sDBTable;
1767 aImportParam.bNative = false;
1768 aImportParam.nType = static_cast<sal_uInt8>( ( nCommandType == sdb::CommandType::QUERY ) ? ScDbQuery : ScDbTable );
1769 aImportParam.bImport = true;
1771 bool bContinue = DoImport( nTab, aImportParam, &rDescriptor );
1773 // repeat DB operations
1775 ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
1776 if (!pViewSh)
1777 return;
1779 ScRange aRange;
1780 pData->GetArea(aRange);
1781 pViewSh->MarkRange(aRange); // select
1783 if ( bContinue ) // error at import -> abort
1785 // internal operations, if some are saved
1787 if ( pData->HasQueryParam() || pData->HasSortParam() || pData->HasSubTotalParam() )
1788 pViewSh->RepeatDB();
1790 // pivot tables which have the range as source data
1792 rDocShell.RefreshPivotTables(aRange);
1796 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */