1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
33 #include <undodat.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>
45 #include <dociter.hxx>
46 #include <editable.hxx>
48 #include <drwlayer.hxx>
49 #include <dpshttab.hxx>
51 #include <queryentry.hxx>
52 #include <markdata.hxx>
53 #include <progress.hxx>
54 #include <undosort.hxx>
55 #include <inputopt.hxx>
58 #include <chartlis.hxx>
59 #include <ChartTools.hxx>
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
;
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();
88 rDoc
.PreprocessDBDataUpdate();
89 if ( rName
== STR_DB_LOCAL_NONAME
)
91 rDoc
.SetAnonymousDBData(rRange
.aStart
.Tab(), std::move(pNew
));
96 bOk
= pDocColl
->getNamedDBs().insert(std::move(pNew
));
99 rDoc
.CompileHybridFormula();
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
) );
118 bool ScDBDocFunc::DeleteDBRange(const OUString
& rName
)
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
;
133 pUndoColl
.reset( new ScDBCollection( *pDocColl
) );
135 rDoc
.PreprocessDBDataUpdate();
137 rDoc
.CompileHybridFormula();
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
) );
154 bool ScDBDocFunc::RenameDBRange( const OUString
& rOld
, const OUString
& rNew
)
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();
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
185 rDocShell
.GetUndoManager()->AddUndoAction(
186 std::make_unique
<ScUndoDBData
>( &rDocShell
, std::move(pUndoColl
),
187 std::make_unique
<ScDBCollection
>( *pDocColl
) ) );
192 aModificator
.SetDocumentModified();
193 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged
) );
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
)
211 rNewData
.GetArea(aRange
);
212 SCTAB nTab
= aRange
.aStart
.Tab();
213 pData
= rDoc
.GetAnonymousDBData(nTab
);
216 pData
= pDocColl
->getNamedDBs().findByUpperName(rNewData
.GetUpperName());
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
;
229 pUndoColl
.reset( new ScDBCollection( *pDocColl
) );
233 rDoc
.CompileDBFormula();
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());
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();
271 rDocShell
.PostPaint(ScRange(0, 0, 0, rDoc
.MaxCol(), rDoc
.MaxRow(), MAXTAB
), PaintPartFlags::Grid
);
272 aModificator
.SetDocumentModified();
273 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged
) );
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 !
288 ScDocument
& rDoc
= rDocShell
.GetDocument();
290 if (!rDoc
.IsUndoEnabled())
292 ScDBData
* pDBData
= nullptr;
295 pDBData
= rDoc
.GetAnonymousDBData( aTab
);
299 ScDBCollection
* pColl
= rDoc
.GetDBCollection();
301 pDBData
= pColl
->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rDBName
));
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;
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
);
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
;
350 SCTAB nTabCount
= rDoc
.GetTableCount();
351 pUndoDoc
.reset(new ScDocument( SCDOCMODE_UNDO
));
352 ScOutlineTable
* pTable
= rDoc
.GetOutlineTable( nTab
);
355 pUndoTab
.reset(new ScOutlineTable( *pTable
));
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
);
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
);
399 pDBData
->GetSortParam( aSortParam
); // range may have changed
400 (void)Sort( nTab
, aSortParam
, false, false, bApi
);
404 pDBData
->GetQueryParam( aQueryParam
); // range may have changed
406 if (pDBData
->GetAdvancedQuerySource(aAdvSource
))
407 Query( nTab
, aQueryParam
, &aAdvSource
, false, bApi
);
409 Query( nTab
, aQueryParam
, nullptr, false, bApi
);
411 // at not-inplace the table may have been converted
412 // if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
417 pDBData
->GetSubTotalParam( aSubTotalParam
); // range may have changed
418 aSubTotalParam
.bRemoveOnly
= false;
419 DoSubTotals( nTab
, aSubTotalParam
, false, bApi
);
428 pDBData
->GetArea( nDummyTab
, nDummyCol
,nDummyRow
, nDummyCol
,nNewEndRow
);
430 const ScRange
* pOld
= nullptr;
431 const ScRange
* pNew
= nullptr;
434 ScDBData
* pDest
= rDoc
.GetDBAtCursor( aQueryParam
.nDestCol
, aQueryParam
.nDestRow
,
435 aQueryParam
.nDestTab
, ScDBDataPortion::TOP_LEFT
);
438 pDest
->GetArea( aNewQuery
);
444 rDocShell
.GetUndoManager()->AddUndoAction(
445 std::make_unique
<ScUndoRepeatDB
>( &rDocShell
, nTab
,
446 nStartCol
, nStartRow
, nEndCol
, nEndRow
,
449 nStartCol
, nStartRow
,
450 std::move(pUndoDoc
), std::move(pUndoTab
),
451 std::move(pUndoRange
), std::move(pUndoDB
),
455 rDocShell
.PostPaint(ScRange(0, 0, nTab
, rDoc
.MaxCol(), rDoc
.MaxRow(), nTab
),
456 PaintPartFlags::Grid
| PaintPartFlags::Left
| PaintPartFlags::Top
| PaintPartFlags::Size
);
459 else if (!bApi
) // "Don't execute any operations"
460 rDocShell
.ErrorMessage(STR_MSSG_REPEATDB_0
);
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())
475 ScDBData
* pDBData
= rDoc
.GetDBAtArea( nTab
, rSortParam
.nCol1
, rSortParam
.nRow1
,
476 rSortParam
.nCol2
, rSortParam
.nRow2
);
479 OSL_FAIL( "Sort: no DBData" );
483 bool bCopy
= !rSortParam
.bInplace
;
484 if ( bCopy
&& rSortParam
.nDestCol
== rSortParam
.nCol1
&&
485 rSortParam
.nDestRow
== rSortParam
.nRow1
&& rSortParam
.nDestTab
== nTab
)
488 ScSortParam
aLocalParam( rSortParam
);
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
);
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
++;
514 nStartingColToEdit
++;
516 ScEditableTester
aTester( rDoc
, nTab
, nStartingColToEdit
, nStartingRowToEdit
,
517 aLocalParam
.nCol2
, aLocalParam
.nRow2
, true /*bNoMatrixAtAll*/ );
518 if (!aTester
.IsEditable())
521 rDocShell
.ErrorMessage(aTester
.GetMessageId());
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
,
538 (aLocalParam
.aDataAreaExtras
.anyExtrasWanted() ?
539 &aLocalParam
.aDataAreaExtras
: nullptr));
541 SCROW nStartRow
= aLocalParam
.nRow1
;
542 if (aLocalParam
.bByRow
&& aLocalParam
.bHasHeader
&& nStartRow
< aLocalParam
.nRow2
)
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
);
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
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.
578 rDocShell
.ErrorMessage(STR_SORT_ERR_MERGED
);
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
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
)
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);
610 bRepeatQuery
= rDoc
.HasHiddenRows(aLocalParam
.nRow1
, aLocalParam
.nRow2
, nTab
);
611 rDoc
.Sort(nTab
, aLocalParam
, bRepeatQuery
, bUpdateRefs
, &aProgress
, &aUndoParam
);
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();
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
)
649 aLocalParam
.nCol1
, nStartRow
, nTab
,
650 aLocalParam
.nCol2
, aLocalParam
.nRow2
, nTab
);
651 rDoc
.SetDirty( aDirtyRange
, true );
656 PaintPartFlags nPaint
= PaintPartFlags::Grid
;
657 SCCOL nStartX
= nOverallCol1
;
658 SCROW nStartY
= nOverallRow1
;
659 SCCOL nEndX
= nOverallCol2
;
660 SCROW nEndY
= nOverallRow2
;
663 nPaint
|= PaintPartFlags::Left
;
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();
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
))
691 if (bRecord
&& !rDoc
.IsUndoEnabled())
693 ScDBData
* pDBData
= rDoc
.GetDBAtArea( nTab
, rQueryParam
.nCol1
, rQueryParam
.nRow1
,
694 rQueryParam
.nCol2
, rQueryParam
.nRow2
);
697 OSL_FAIL( "Query: no DBData" );
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;
728 if ( bCopy
&& rQueryParam
.nDestCol
== rQueryParam
.nCol1
&&
729 rQueryParam
.nDestRow
== rQueryParam
.nRow1
&& rQueryParam
.nDestTab
== nTab
)
731 SCTAB nDestTab
= nTab
;
734 aLocalParam
.MoveToDest();
735 nDestTab
= rQueryParam
.nDestTab
;
736 if ( !rDoc
.ValidColRow( aLocalParam
.nCol2
, aLocalParam
.nRow2
) )
739 rDocShell
.ErrorMessage(STR_PASTE_FULL
);
743 ScEditableTester
aTester( rDoc
, nDestTab
, aLocalParam
.nCol1
,aLocalParam
.nRow1
,
744 aLocalParam
.nCol2
,aLocalParam
.nRow2
);
745 if (!aTester
.IsEditable())
748 rDocShell
.ErrorMessage(aTester
.GetMessageId());
752 pDestData
= rDoc
.GetDBAtCursor( rQueryParam
.nDestCol
, rQueryParam
.nDestRow
,
753 rQueryParam
.nDestTab
, ScDBDataPortion::TOP_LEFT
);
756 pDestData
->GetArea( aOldDest
);
757 aDestTotal
=ScRange( rQueryParam
.nDestCol
,
758 rQueryParam
.nDestRow
,
760 rQueryParam
.nDestCol
+ rQueryParam
.nCol2
- rQueryParam
.nCol1
,
761 rQueryParam
.nDestRow
+ rQueryParam
.nRow2
- rQueryParam
.nRow1
,
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
)
779 bKeepFmt
= pDestData
->IsKeepFmt();
780 if ( bDoSize
&& !rDoc
.CanFitBlock( aOldDest
, aDestTotal
) )
783 rDocShell
.ErrorMessage(STR_MSSG_DOSUBTOTALS_2
); // cannot insert rows
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
)
803 ScDocumentUniquePtr pUndoDoc
;
804 std::unique_ptr
<ScDBCollection
> pUndoDB
;
805 const ScRange
* pOld
= nullptr;
809 pUndoDoc
.reset(new ScDocument( SCDOCMODE_UNDO
));
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
820 rDoc
.CopyToDocument(aOldDest
, InsertDeleteFlags::ALL
, false, *pUndoDoc
);
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
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
);
860 rDoc
.FitBlock( aOldDest
, aDestTotal
);
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
);
870 aLocalParam
.nRow2
= aLocalParam
.nRow1
+ nCount
;
871 if (!aLocalParam
.bHasHeader
&& nCount
> 0)
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 )
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
913 if (aLocalParam
.bHasHeader
)
915 ScRange aHdrRange
= aAttribRange
;
916 aHdrRange
.aEnd
.SetRow( aHdrRange
.aStart
.Row() );
917 pAttribDoc
->CopyToDocument(aHdrRange
, InsertDeleteFlags::ATTRIB
, false, rDoc
);
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");
930 rDoc
.ApplyPatternAreaTab( nCol
, nAttrRow
, nCol
, aLocalParam
.nRow2
,
931 nDestTab
, *pSrcPattern
);
932 const ScStyleSheet
* pStyle
= pSrcPattern
->GetStyleSheet();
934 rDoc
.ApplyStyleAreaTab( nCol
, nAttrRow
, nCol
, aLocalParam
.nRow2
,
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.
960 pNewData
= pDestData
; // range exists -> adjust (always!)
962 pNewData
= rDocShell
.GetDBData(
963 ScRange( aLocalParam
.nCol1
, aLocalParam
.nRow1
, nDestTab
,
964 aLocalParam
.nCol2
, aLocalParam
.nRow2
, nDestTab
),
965 SC_DB_MAKE
, ScGetDBSelection::ForceMark
);
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#)
977 OSL_FAIL("Target are not available");
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
);
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
) );
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);
1010 SCCOL nEndX
= aLocalParam
.nCol2
;
1011 SCROW nEndY
= aLocalParam
.nRow2
;
1014 if ( aOldDest
.aEnd
.Col() > nEndX
)
1015 nEndX
= aOldDest
.aEnd
.Col();
1016 if ( aOldDest
.aEnd
.Row() > nEndY
)
1017 nEndY
= aOldDest
.aEnd
.Row();
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
);
1030 rDocShell
.PostPaint(
1031 ScRange(0, rQueryParam
.nRow1
, nTab
, rDoc
.MaxCol(), rDoc
.MaxRow(), nTab
),
1032 PaintPartFlags::Grid
| PaintPartFlags::Left
);
1033 aModificator
.SetDocumentModified();
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())
1051 ScDBData
* pDBData
= rDoc
.GetDBAtArea( nTab
, rParam
.nCol1
, rParam
.nRow1
,
1052 rParam
.nCol2
, rParam
.nRow2
);
1055 OSL_FAIL( "SubTotals: no DBData" );
1059 ScEditableTester
aTester( rDoc
, nTab
, 0,rParam
.nRow1
+1, rDoc
.MaxCol(),rDoc
.MaxRow() );
1060 if (!aTester
.IsEditable())
1063 rDocShell
.ErrorMessage(aTester
.GetMessageId());
1067 if (rDoc
.HasAttrib( rParam
.nCol1
, rParam
.nRow1
+1, nTab
,
1068 rParam
.nCol2
, rParam
.nRow2
, nTab
, HasAttrFlags::Merged
| HasAttrFlags::Overlapped
))
1071 rDocShell
.ErrorMessage(STR_MSSG_INSERTCELLS_0
); // don't insert into merged
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
;
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
);
1109 pUndoTab
.reset(new ScOutlineTable( *pTable
));
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
);
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
);
1144 pOut
->GetRowArray().RemoveAll(); // only delete row outlines
1146 if (rParam
.bReplace
)
1147 rDoc
.RemoveSubTotals( nTab
, aNewParam
);
1148 bool bSuccess
= true;
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 );
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
) ) );
1184 // "Cannot insert rows"
1186 rDocShell
.ErrorMessage(STR_MSSG_DOSUBTOTALS_2
);
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();
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
1224 rDocShell
.ErrorMessage(STR_PROTECTIONERR
);
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())
1236 rDocShell
.ErrorMessage(aTester
.GetMessageId());
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()))
1279 rDocShell
.ErrorMessage(STR_PIVOT_ERROR
);
1284 if (!rDoc
.IsImportingXML())
1286 ScEditableTester
aTester(rDoc
, rNewOut
, eAction
);
1287 if (!aTester
.IsEditable())
1289 // destination area isn't editable
1291 rDocShell
.ErrorMessage(aTester
.GetMessageId());
1302 bool ScDBDocFunc::DataPilotUpdate( ScDPObject
* pOldObj
, const ScDPObject
* pNewObj
,
1303 bool bRecord
, bool bApi
, bool bAllowMove
)
1310 return CreatePivotTable(*pNewObj
, bRecord
, bApi
);
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
))
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())
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" );
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() );
1359 if (!checkNewOutputRange(*pOldObj
, rDocShell
, aNewOut
, bApi
))
1361 *pOldObj
= aUndoDPObj
;
1365 // test if new output area is empty except for old area
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
;
1385 createUndoDoc(pNewUndoDoc
, rDoc
, aNewOut
);
1387 pOldObj
->Output(aNewOut
.aStart
);
1388 rDocShell
.PostPaintGridAll(); //! only necessary parts
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();
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
))
1412 ScDocument
& rDoc
= rDocShell
.GetDocument();
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
)
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
;
1448 pUndoDPObj
.reset(new ScDPObject(rDPObj
)); // copy old settings for undo
1450 if (bRecord
&& !rDoc
.IsUndoEnabled())
1455 ScRange aRange
= rDPObj
.GetOutRange();
1456 SCTAB nTab
= aRange
.aStart
.Tab();
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(),
1468 rDoc
.GetDPCollection()->FreeTable(&rDPObj
); // object is deleted here
1470 rDocShell
.PostPaintGridAll(); //! only necessary parts
1471 rDocShell
.PostPaint(aRange
, PaintPartFlags::Grid
);
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();
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
))
1496 ScDocumentUniquePtr pNewUndoDoc
;
1498 if (bRecord
&& !rDoc
.IsUndoEnabled())
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
);
1518 ScDPSaveData
* pSaveData
= rDestObj
.GetSaveData();
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
);
1540 rDocShell
.ErrorMessage(STR_PIVOT_ERROR
);
1545 if (!rDoc
.IsImportingXML())
1547 ScEditableTester
aTester(rDoc
, aNewOut
, sc::EditAction::Unknown
);
1548 if (!aTester
.IsEditable())
1550 // destination area isn't editable
1552 rDocShell
.ErrorMessage(aTester
.GetMessageId());
1558 // test if new output area is empty except for old area
1561 bool bEmpty
= rDoc
.IsBlockEmpty(
1562 aNewOut
.aStart
.Col(), aNewOut
.aStart
.Row(),
1563 aNewOut
.aEnd
.Col(), aNewOut
.aEnd
.Row(), aNewOut
.aStart
.Tab() );
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)
1580 createUndoDoc(pNewUndoDoc
, rDoc
, aNewOut
);
1582 rDestObj
.Output(aNewOut
.aStart
);
1583 rDocShell
.PostPaintGridAll(); //! only necessary parts
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();
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
))
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())
1616 createUndoDoc(pOldUndoDoc
, rDoc
, rDPObj
.GetOutRange());
1618 rDPObj
.SetAllowMove(false);
1619 rDPObj
.ReloadGroupTableData();
1620 if (!rDPObj
.SyncAllDimensionMembers())
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() );
1630 if (!checkNewOutputRange(rDPObj
, rDocShell
, aNewOut
, bApi
, sc::EditAction::UpdatePivotTable
))
1632 rDPObj
= aUndoDPObj
;
1636 // test if new output area is empty except for old area
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
;
1654 createUndoDoc(pNewUndoDoc
, rDoc
, aNewOut
);
1656 rDPObj
.Output(aNewOut
.aStart
);
1657 rDocShell
.PostPaintGridAll(); //! only necessary parts
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();
1672 void ScDBDocFunc::RefreshPivotTables(const ScDPObject
* pDPObj
, bool bApi
)
1674 ScDPCollection
* pDPs
= rDocShell
.GetDocument().GetDPCollection();
1678 o3tl::sorted_vector
<ScDPObject
*> aRefs
;
1679 TranslateId pErrId
= pDPs
->ReloadCache(pDPObj
, aRefs
);
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
)
1695 ScDPCollection
* pDPs
= rDocShell
.GetDocument().GetDPCollection();
1699 ScDPSaveData
* pSaveData
= pDPObj
->GetSaveData();
1703 if (!pDPs
->HasTable(pDPObj
))
1705 // This table is under construction so no need for a whole update (UpdatePivotTable()).
1706 pDPObj
->ReloadGroupTableData();
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
))
1715 // We allow pDimData being NULL.
1716 const ScDPDimensionSaveData
* pDimData
= pSaveData
->GetExistingDimensionData();
1717 for (ScDPObject
* pObj
: aRefs
)
1721 pSaveData
= pObj
->GetSaveData();
1723 pSaveData
->SetDimensionData(pDimData
);
1726 // This action is intentionally not undoable since it modifies cache.
1727 UpdatePivotTable(*pObj
, false, false);
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
));
1742 std::unique_ptr
<weld::MessageDialog
> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
1743 VclMessageType::Info
, VclButtonsType::Ok
,
1744 ScResId(STR_TARGETNOTFOUND
)));
1752 pData
->GetArea( nTab
, nDummyCol
,nDummyRow
,nDummyCol
,nDummyRow
);
1754 ScImportParam aImportParam
;
1755 pData
->GetImportParam( aImportParam
);
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();
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: */