summaryrefslogtreecommitdiffstats
path: root/sc/source/ui/docshell/docfunc.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sc/source/ui/docshell/docfunc.cxx
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sc/source/ui/docshell/docfunc.cxx')
-rw-r--r--sc/source/ui/docshell/docfunc.cxx5922
1 files changed, 5922 insertions, 0 deletions
diff --git a/sc/source/ui/docshell/docfunc.cxx b/sc/source/ui/docshell/docfunc.cxx
new file mode 100644
index 000000000..752843797
--- /dev/null
+++ b/sc/source/ui/docshell/docfunc.cxx
@@ -0,0 +1,5922 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+
+#include <comphelper/lok.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <sfx2/app.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/justifyitem.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/bindings.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/svdocapt.hxx>
+#include <sal/log.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/script/ModuleType.hpp>
+#include <com/sun/star/script/XLibraryContainer.hpp>
+#include <com/sun/star/script/vba/XVBAModuleInfo.hpp>
+
+#include <docfunc.hxx>
+
+#include <sc.hrc>
+#include <strings.hrc>
+
+#include <arealink.hxx>
+#include <attrib.hxx>
+#include <dociter.hxx>
+#include <autoform.hxx>
+#include <formulacell.hxx>
+#include <cellmergeoption.hxx>
+#include <detdata.hxx>
+#include <detfunc.hxx>
+#include <docpool.hxx>
+#include <docsh.hxx>
+#include <drwlayer.hxx>
+#include <editutil.hxx>
+#include <globstr.hrc>
+#include <olinetab.hxx>
+#include <patattr.hxx>
+#include <rangenam.hxx>
+#include <refundo.hxx>
+#include <scresid.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <tablink.hxx>
+#include <tabvwsh.hxx>
+#include <uiitems.hxx>
+#include <undoblk.hxx>
+#include <undocell.hxx>
+#include <undodraw.hxx>
+#include <undotab.hxx>
+#include <sizedev.hxx>
+#include <scmod.hxx>
+#include <inputhdl.hxx>
+#include <editable.hxx>
+#include <compiler.hxx>
+#include <scui_def.hxx>
+#include <tabprotection.hxx>
+#include <clipparam.hxx>
+#include <externalrefmgr.hxx>
+#include <undorangename.hxx>
+#include <progress.hxx>
+#include <dpobject.hxx>
+#include <stringutil.hxx>
+#include <cellvalue.hxx>
+#include <tokenarray.hxx>
+#include <rowheightcontext.hxx>
+#include <cellvalues.hxx>
+#include <undoconvert.hxx>
+#include <docfuncutil.hxx>
+#include <sheetevents.hxx>
+#include <conditio.hxx>
+#include <columnspanset.hxx>
+#include <validat.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineAttributes.hxx>
+#include <SparklineData.hxx>
+#include <undo/UndoInsertSparkline.hxx>
+#include <undo/UndoDeleteSparkline.hxx>
+#include <undo/UndoDeleteSparklineGroup.hxx>
+#include <undo/UndoEditSparklineGroup.hxx>
+#include <undo/UndoUngroupSparklines.hxx>
+#include <undo/UndoGroupSparklines.hxx>
+#include <undo/UndoEditSparkline.hxx>
+#include <config_features.h>
+
+#include <memory>
+#include <utility>
+#include <basic/basmgr.hxx>
+#include <set>
+#include <vector>
+#include <sfx2/viewfrm.hxx>
+
+using namespace com::sun::star;
+using ::std::vector;
+
+void ScDocFunc::NotifyDrawUndo( std::unique_ptr<SdrUndoAction> pUndoAction)
+{
+ // #i101118# if drawing layer collects the undo actions, add it there
+ ScDrawLayer* pDrawLayer = rDocShell.GetDocument().GetDrawLayer();
+ if( pDrawLayer && pDrawLayer->IsRecording() )
+ pDrawLayer->AddCalcUndo( std::move(pUndoAction) );
+ else
+ rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDraw>( std::move(pUndoAction), &rDocShell ) );
+ rDocShell.SetDrawModified();
+
+ // the affected sheet isn't known, so all stream positions are invalidated
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ rDoc.SetStreamValid(nTab, false);
+}
+
+// paint row above the range (because of lines after AdjustRowHeight)
+
+static void lcl_PaintAbove( ScDocShell& rDocShell, const ScRange& rRange )
+{
+ SCROW nRow = rRange.aStart.Row();
+ if ( nRow > 0 )
+ {
+ SCTAB nTab = rRange.aStart.Tab(); //! all of them?
+ --nRow;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ rDocShell.PostPaint( ScRange(0,nRow,nTab,rDoc.MaxCol(),nRow,nTab), PaintPartFlags::Grid );
+ }
+}
+
+bool ScDocFunc::AdjustRowHeight( const ScRange& rRange, bool bPaint, bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SfxViewShell* pSomeViewForThisDoc = rDocShell.GetBestViewShell(false);
+ if ( rDoc.IsImportingXML() )
+ {
+ // for XML import, all row heights are updated together after importing
+ return false;
+ }
+ if ( rDoc.IsAdjustHeightLocked() )
+ {
+ return false;
+ }
+
+ SCTAB nTab = rRange.aStart.Tab();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCROW nEndRow = rRange.aEnd.Row();
+
+ ScSizeDeviceProvider aProv( &rDocShell );
+ Fraction aOne(1,1);
+
+ sc::RowHeightContext aCxt(rDoc.MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), aOne, aOne, aProv.GetDevice());
+ bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartRow, nEndRow, nTab, bApi);
+ // tdf#76183: recalculate objects' positions
+ if (bChanged)
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pTabViewShell->GetDocId() == pSomeViewForThisDoc->GetDocId())
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nTab))
+ pPosHelper->invalidateByIndex(nStartRow);
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+ }
+ rDoc.SetDrawPageSize(nTab);
+ }
+
+ if ( bPaint && bChanged )
+ rDocShell.PostPaint(ScRange(0, nStartRow, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
+ PaintPartFlags::Grid | PaintPartFlags::Left);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(pSomeViewForThisDoc, ROW_HEADER, nTab);
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ pSomeViewForThisDoc, false /* bColumns */, true /* bRows */, true /* bSizes*/,
+ false /* bHidden */, false /* bFiltered */, false /* bGroups */, nTab);
+ }
+
+ return bChanged;
+}
+
+bool ScDocFunc::DetectiveAddPred(const ScAddress& rPos)
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ rDocShell.MakeDrawLayer();
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo (rDoc.IsUndoEnabled());
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+ bool bDone = ScDetectiveFunc(rDoc, nTab).ShowPred( nCol, nRow );
+ std::unique_ptr<SdrUndoGroup> pUndo;
+ if (bUndo)
+ pUndo = pModel->GetCalcUndo();
+ if (bDone)
+ {
+ ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDPRED );
+ rDoc.AddDetectiveOperation( aOperation );
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
+ }
+ aModificator.SetDocumentModified();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_DETECTIVE_REFRESH );
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::DetectiveDelPred(const ScAddress& rPos)
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bUndo(rDoc.IsUndoEnabled());
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return false;
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+ bool bDone = ScDetectiveFunc(rDoc, nTab).DeletePred( nCol, nRow );
+ std::unique_ptr<SdrUndoGroup> pUndo;
+ if (bUndo)
+ pUndo = pModel->GetCalcUndo();
+ if (bDone)
+ {
+ ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_DELPRED );
+ rDoc.AddDetectiveOperation( aOperation );
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
+ }
+ aModificator.SetDocumentModified();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_DETECTIVE_REFRESH );
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::DetectiveAddSucc(const ScAddress& rPos)
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ rDocShell.MakeDrawLayer();
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bUndo(rDoc.IsUndoEnabled());
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+ bool bDone = ScDetectiveFunc(rDoc, nTab).ShowSucc( nCol, nRow );
+ std::unique_ptr<SdrUndoGroup> pUndo;
+ if (bUndo)
+ pUndo = pModel->GetCalcUndo();
+ if (bDone)
+ {
+ ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDSUCC );
+ rDoc.AddDetectiveOperation( aOperation );
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
+ }
+ aModificator.SetDocumentModified();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_DETECTIVE_REFRESH );
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::DetectiveDelSucc(const ScAddress& rPos)
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bUndo (rDoc.IsUndoEnabled());
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return false;
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+ bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteSucc( nCol, nRow );
+ std::unique_ptr<SdrUndoGroup> pUndo;
+ if (bUndo)
+ pUndo = pModel->GetCalcUndo();
+ if (bDone)
+ {
+ ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_DELSUCC );
+ rDoc.AddDetectiveOperation( aOperation );
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
+ }
+ aModificator.SetDocumentModified();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_DETECTIVE_REFRESH );
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::DetectiveAddError(const ScAddress& rPos)
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ rDocShell.MakeDrawLayer();
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bUndo (rDoc.IsUndoEnabled());
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+ bool bDone = ScDetectiveFunc(rDoc, nTab).ShowError( nCol, nRow );
+ std::unique_ptr<SdrUndoGroup> pUndo;
+ if (bUndo)
+ pUndo = pModel->GetCalcUndo();
+ if (bDone)
+ {
+ ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDERROR );
+ rDoc.AddDetectiveOperation( aOperation );
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
+ }
+ aModificator.SetDocumentModified();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_DETECTIVE_REFRESH );
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::DetectiveMarkInvalid(SCTAB nTab)
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ rDocShell.MakeDrawLayer();
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bUndo (rDoc.IsUndoEnabled());
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+
+ std::unique_ptr<weld::WaitObject> xWaitWin(new weld::WaitObject(ScDocShell::GetActiveDialogParent()));
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+ bool bOverflow;
+ bool bDone = ScDetectiveFunc(rDoc, nTab).MarkInvalid( bOverflow );
+ std::unique_ptr<SdrUndoGroup> pUndo;
+ if (bUndo)
+ pUndo = pModel->GetCalcUndo();
+ xWaitWin.reset();
+ if (bDone)
+ {
+ if (pUndo && bUndo)
+ {
+ pUndo->SetComment( ScResId( STR_UNDO_DETINVALID ) );
+ rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndo) );
+ }
+ aModificator.SetDocumentModified();
+ if ( bOverflow )
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_DETINVALID_OVERFLOW)));
+ xInfoBox->run();
+ }
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::DetectiveDelAll(SCTAB nTab)
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bUndo (rDoc.IsUndoEnabled());
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return false;
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+ bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteAll( ScDetectiveDelete::Detective );
+ std::unique_ptr<SdrUndoGroup> pUndo;
+ if (bUndo)
+ pUndo = pModel->GetCalcUndo();
+ if (bDone)
+ {
+ ScDetOpList* pOldList = rDoc.GetDetOpList();
+ std::unique_ptr<ScDetOpList> pUndoList;
+ if (bUndo && pOldList)
+ pUndoList.reset(new ScDetOpList(*pOldList));
+
+ rDoc.ClearDetectiveOperations();
+
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), nullptr, std::move(pUndoList) ) );
+ }
+ aModificator.SetDocumentModified();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_DETECTIVE_REFRESH );
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::DetectiveRefresh( bool bAutomatic )
+{
+ bool bDone = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ ScDetOpList* pList = rDoc.GetDetOpList();
+ if ( pList && pList->Count() )
+ {
+ rDocShell.MakeDrawLayer();
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ const bool bUndo (rDoc.IsUndoEnabled());
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+
+ // Delete in all sheets
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ ScDetectiveFunc( rDoc,nTab ).DeleteAll( ScDetectiveDelete::Arrows ); // don't remove circles
+
+ // repeat
+
+ size_t nCount = pList->Count();
+ for (size_t i=0; i < nCount; ++i)
+ {
+ const ScDetOpData& rData = pList->GetObject(i);
+ const ScAddress& aPos = rData.GetPos();
+ ScDetectiveFunc aFunc( rDoc, aPos.Tab() );
+ SCCOL nCol = aPos.Col();
+ SCROW nRow = aPos.Row();
+ switch (rData.GetOperation())
+ {
+ case SCDETOP_ADDSUCC:
+ aFunc.ShowSucc( nCol, nRow );
+ break;
+ case SCDETOP_DELSUCC:
+ aFunc.DeleteSucc( nCol, nRow );
+ break;
+ case SCDETOP_ADDPRED:
+ aFunc.ShowPred( nCol, nRow );
+ break;
+ case SCDETOP_DELPRED:
+ aFunc.DeletePred( nCol, nRow );
+ break;
+ case SCDETOP_ADDERROR:
+ aFunc.ShowError( nCol, nRow );
+ break;
+ default:
+ OSL_FAIL("wrong operation in DetectiveRefresh");
+ }
+ }
+
+ if (bUndo)
+ {
+ std::unique_ptr<SdrUndoGroup> pUndo = pModel->GetCalcUndo();
+ if (pUndo)
+ {
+ pUndo->SetComment( ScResId( STR_UNDO_DETREFRESH ) );
+ // associate with the last action
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDraw>( std::move(pUndo), &rDocShell ),
+ bAutomatic );
+ }
+ }
+ rDocShell.SetDrawModified();
+ bDone = true;
+ }
+ return bDone;
+}
+
+static void lcl_collectAllPredOrSuccRanges(
+ const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens, ScDocShell& rDocShell,
+ bool bPred)
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ vector<ScTokenRef> aRefTokens;
+ if (rSrcRanges.empty())
+ return;
+ ScRange const & rFrontRange = rSrcRanges.front();
+ ScDetectiveFunc aDetFunc(rDoc, rFrontRange.aStart.Tab());
+ for (size_t i = 0, n = rSrcRanges.size(); i < n; ++i)
+ {
+ ScRange const & r = rSrcRanges[i];
+ if (bPred)
+ {
+ aDetFunc.GetAllPreds(
+ r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), aRefTokens);
+ }
+ else
+ {
+ aDetFunc.GetAllSuccs(
+ r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), aRefTokens);
+ }
+ }
+ rRefTokens.swap(aRefTokens);
+}
+
+void ScDocFunc::DetectiveCollectAllPreds(const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens)
+{
+ lcl_collectAllPredOrSuccRanges(rSrcRanges, rRefTokens, rDocShell, true);
+}
+
+void ScDocFunc::DetectiveCollectAllSuccs(const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens)
+{
+ lcl_collectAllPredOrSuccRanges(rSrcRanges, rRefTokens, rDocShell, false);
+}
+
+bool ScDocFunc::DeleteContents(
+ const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
+ {
+ OSL_FAIL("ScDocFunc::DeleteContents without markings");
+ return false;
+ }
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScEditableTester aTester( rDoc, rMark );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ ScMarkData aMultiMark = rMark;
+ aMultiMark.SetMarking(false); // for MarkToMulti
+
+ ScDocumentUniquePtr pUndoDoc;
+ bool bMulti = aMultiMark.IsMultiMarked();
+ aMultiMark.MarkToMulti();
+ const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea();
+ ScRange aExtendedRange(aMarkRange);
+ if ( rDoc.ExtendMerge( aExtendedRange, true ) )
+ bMulti = false;
+
+ // no objects on protected tabs
+ bool bObjects = (nFlags & InsertDeleteFlags::OBJECTS) && !sc::DocFuncUtil::hasProtectedTab(rDoc, rMark);
+
+ sal_uInt16 nExtFlags = 0; // extra flags are needed only if attributes are deleted
+ if ( nFlags & InsertDeleteFlags::ATTRIB )
+ rDocShell.UpdatePaintExt( nExtFlags, aMarkRange );
+
+ // order of operations:
+ // 1) BeginDrawUndo
+ // 2) Delete objects (DrawUndo will be filled)
+ // 3) Copy content for undo and set up undo actions
+ // 4) Delete content
+
+ bool bDrawUndo = bObjects || (nFlags & InsertDeleteFlags::NOTE);
+ if (bRecord && bDrawUndo)
+ rDoc.BeginDrawUndo();
+
+ if (bObjects)
+ {
+ if (bMulti)
+ rDoc.DeleteObjectsInSelection( aMultiMark );
+ else
+ rDoc.DeleteObjectsInArea( aMarkRange.aStart.Col(), aMarkRange.aStart.Row(),
+ aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(),
+ aMultiMark );
+ }
+
+ // To keep track of all non-empty cells within the deleted area.
+ std::shared_ptr<ScSimpleUndo::DataSpansType> pDataSpans;
+
+ if ( bRecord )
+ {
+ pUndoDoc = sc::DocFuncUtil::createDeleteContentsUndoDoc(rDoc, aMultiMark, aMarkRange, nFlags, bMulti);
+ pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, aMultiMark, aMarkRange);
+ }
+
+ rDoc.DeleteSelection( nFlags, aMultiMark );
+
+ // add undo action after drawing undo is complete (objects and note captions)
+ if( bRecord )
+ {
+ sc::DocFuncUtil::addDeleteContentsUndo(
+ rDocShell.GetUndoManager(), &rDocShell, aMultiMark, aExtendedRange,
+ std::move(pUndoDoc), nFlags, pDataSpans, bMulti, bDrawUndo);
+ }
+
+ if (!AdjustRowHeight( aExtendedRange, true, bApi ))
+ rDocShell.PostPaint( aExtendedRange, PaintPartFlags::Grid, nExtFlags );
+ else if (nExtFlags & SC_PF_LINES)
+ lcl_PaintAbove( rDocShell, aExtendedRange ); // for lines above the range
+
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+bool ScDocFunc::DeleteCell(
+ const ScAddress& rPos, const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi )
+{
+ ScDocShellModificator aModificator(rDocShell);
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScEditableTester aTester(rDoc, rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark);
+ if (!aTester.IsEditable())
+ {
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ // no objects on protected tabs
+ bool bObjects = (nFlags & InsertDeleteFlags::OBJECTS) && !sc::DocFuncUtil::hasProtectedTab(rDoc, rMark);
+
+ sal_uInt16 nExtFlags = 0; // extra flags are needed only if attributes are deleted
+ if (nFlags & InsertDeleteFlags::ATTRIB)
+ rDocShell.UpdatePaintExt(nExtFlags, rPos);
+
+ // order of operations:
+ // 1) BeginDrawUndo
+ // 2) delete objects (DrawUndo is filled)
+ // 3) copy contents for undo
+ // 4) delete contents
+ // 5) add undo-action
+
+ bool bDrawUndo = bObjects || (nFlags & InsertDeleteFlags::NOTE); // needed for shown notes
+ if (bDrawUndo && bRecord)
+ rDoc.BeginDrawUndo();
+
+ if (bObjects)
+ rDoc.DeleteObjectsInArea(rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark);
+
+ // To keep track of all non-empty cells within the deleted area.
+ std::shared_ptr<ScSimpleUndo::DataSpansType> pDataSpans;
+
+ ScDocumentUniquePtr pUndoDoc;
+ if (bRecord)
+ {
+ pUndoDoc = sc::DocFuncUtil::createDeleteContentsUndoDoc(rDoc, rMark, rPos, nFlags, false);
+ pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, rMark, rPos);
+ }
+
+ rDoc.DeleteArea(rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark, nFlags);
+
+ if (bRecord)
+ {
+ sc::DocFuncUtil::addDeleteContentsUndo(
+ rDocShell.GetUndoManager(), &rDocShell, rMark, rPos, std::move(pUndoDoc),
+ nFlags, pDataSpans, false, bDrawUndo);
+ }
+
+ if (!AdjustRowHeight(rPos, true, bApi))
+ rDocShell.PostPaint(
+ rPos.Col(), rPos.Row(), rPos.Tab(), rPos.Col(), rPos.Row(), rPos.Tab(),
+ PaintPartFlags::Grid, nExtFlags);
+
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+bool ScDocFunc::TransliterateText( const ScMarkData& rMark, TransliterationFlags nType,
+ bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScEditableTester aTester( rDoc, rMark );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ ScMarkData aMultiMark = rMark;
+ aMultiMark.SetMarking(false); // for MarkToMulti
+ aMultiMark.MarkToMulti();
+ const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea();
+
+ if (bRecord)
+ {
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCTAB nTabCount = rDoc.GetTableCount();
+
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab != nStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+
+ ScRange aCopyRange = aMarkRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument(aCopyRange, InsertDeleteFlags::CONTENTS, true, *pUndoDoc, &aMultiMark);
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoTransliterate>( &rDocShell, aMultiMark, std::move(pUndoDoc), nType ) );
+ }
+
+ rDoc.TransliterateText( aMultiMark, nType );
+
+ if (!AdjustRowHeight( aMarkRange, true, true ))
+ rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid );
+
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+bool ScDocFunc::SetNormalString( bool& o_rbNumFmtSet, const ScAddress& rPos, const OUString& rText, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bUndo(rDoc.IsUndoEnabled());
+ ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ bool bEditDeleted = (rDoc.GetCellType(rPos) == CELLTYPE_EDIT);
+ ScUndoEnterData::ValuesType aOldValues;
+
+ if (bUndo)
+ {
+ ScUndoEnterData::Value aOldValue;
+
+ aOldValue.mnTab = rPos.Tab();
+ aOldValue.maCell.assign(rDoc, rPos);
+
+ const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(),rPos.Row(),rPos.Tab() );
+ if ( const SfxUInt32Item* pItem = pPattern->GetItemSet().GetItemIfSet(
+ ATTR_VALUE_FORMAT,false) )
+ {
+ aOldValue.mbHasFormat = true;
+ aOldValue.mnFormat = pItem->GetValue();
+ }
+ else
+ aOldValue.mbHasFormat = false;
+
+ aOldValues.push_back(aOldValue);
+ }
+
+ o_rbNumFmtSet = rDoc.SetString( rPos.Col(), rPos.Row(), rPos.Tab(), rText );
+
+ if (bUndo)
+ {
+ // because of ChangeTracking, UndoAction can be created only after SetString was called
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoEnterData>(&rDocShell, rPos, aOldValues, rText, nullptr));
+ }
+
+ if ( bEditDeleted || rDoc.HasAttrib( ScRange(rPos), HasAttrFlags::NeedHeight ) )
+ AdjustRowHeight( ScRange(rPos), true, bApi );
+
+ rDocShell.PostPaintCell( rPos );
+ aModificator.SetDocumentModified();
+
+ // notify input handler here the same way as in PutCell
+ if (bApi)
+ NotifyInputHandler( rPos );
+
+ const SfxUInt32Item* pItem = rDoc.GetAttr(rPos, ATTR_VALIDDATA);
+ const ScValidationData* pData = rDoc.GetValidationEntry(pItem->GetValue());
+ if (pData)
+ {
+ ScRefCellValue aCell(rDoc, rPos);
+ if (pData->IsDataValid(aCell, rPos))
+ ScDetectiveFunc(rDoc, rPos.Tab()).DeleteCirclesAt(rPos.Col(), rPos.Row());
+ }
+
+ return true;
+}
+
+bool ScDocFunc::SetValueCell( const ScAddress& rPos, double fVal, bool bInteraction )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight);
+
+ ScCellValue aOldVal;
+ if (bUndo)
+ aOldVal.assign(rDoc, rPos);
+
+ rDoc.SetValue(rPos, fVal);
+
+ if (bUndo)
+ {
+ SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
+ ScCellValue aNewVal;
+ aNewVal.assign(rDoc, rPos);
+ pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
+ }
+
+ if (bHeight)
+ AdjustRowHeight(rPos, true, !bInteraction);
+
+ rDocShell.PostPaintCell( rPos );
+ aModificator.SetDocumentModified();
+
+ // #103934#; notify editline and cell in edit mode
+ if (!bInteraction)
+ NotifyInputHandler( rPos );
+
+ return true;
+}
+
+void ScDocFunc::SetValueCells( const ScAddress& rPos, const std::vector<double>& aVals, bool bInteraction )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ // Check for invalid range.
+ SCROW nLastRow = rPos.Row() + aVals.size() - 1;
+ if (nLastRow > rDoc.MaxRow())
+ // out of bound.
+ return;
+
+ ScRange aRange(rPos);
+ aRange.aEnd.SetRow(nLastRow);
+
+ ScDocShellModificator aModificator(rDocShell);
+
+ if (rDoc.IsUndoEnabled())
+ {
+ std::unique_ptr<sc::UndoSetCells> pUndoObj(new sc::UndoSetCells(&rDocShell, rPos));
+ rDoc.TransferCellValuesTo(rPos, aVals.size(), pUndoObj->GetOldValues());
+ pUndoObj->SetNewValues(aVals);
+ SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
+ pUndoMgr->AddUndoAction(std::move(pUndoObj));
+ }
+
+ rDoc.SetValues(rPos, aVals);
+
+ rDocShell.PostPaint(aRange, PaintPartFlags::Grid);
+ aModificator.SetDocumentModified();
+
+ // #103934#; notify editline and cell in edit mode
+ if (!bInteraction)
+ NotifyInputHandler(rPos);
+}
+
+bool ScDocFunc::SetStringCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight);
+
+ ScCellValue aOldVal;
+ if (bUndo)
+ aOldVal.assign(rDoc, rPos);
+
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rDoc.SetString(rPos, rStr, &aParam);
+
+ if (bUndo)
+ {
+ SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
+ ScCellValue aNewVal;
+ aNewVal.assign(rDoc, rPos);
+ pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
+ }
+
+ if (bHeight)
+ AdjustRowHeight(rPos, true, !bInteraction);
+
+ rDocShell.PostPaintCell( rPos );
+ aModificator.SetDocumentModified();
+
+ // #103934#; notify editline and cell in edit mode
+ if (!bInteraction)
+ NotifyInputHandler( rPos );
+
+ return true;
+}
+
+bool ScDocFunc::SetEditCell( const ScAddress& rPos, const EditTextObject& rStr, bool bInteraction )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight);
+
+ ScCellValue aOldVal;
+ if (bUndo)
+ aOldVal.assign(rDoc, rPos);
+
+ rDoc.SetEditText(rPos, rStr.Clone());
+
+ if (bUndo)
+ {
+ SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
+ ScCellValue aNewVal;
+ aNewVal.assign(rDoc, rPos);
+ pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
+ }
+
+ if (bHeight)
+ AdjustRowHeight(rPos, true, !bInteraction);
+
+ rDocShell.PostPaintCell( rPos );
+ aModificator.SetDocumentModified();
+
+ // #103934#; notify editline and cell in edit mode
+ if (!bInteraction)
+ NotifyInputHandler( rPos );
+
+ return true;
+}
+
+bool ScDocFunc::SetStringOrEditCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (ScStringUtil::isMultiline(rStr))
+ {
+ ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
+ rEngine.SetTextCurrentDefaults(rStr);
+ std::unique_ptr<EditTextObject> pEditText(rEngine.CreateTextObject());
+ return SetEditCell(rPos, *pEditText, bInteraction);
+ }
+ else
+ return SetStringCell(rPos, rStr, bInteraction);
+}
+
+bool ScDocFunc::SetFormulaCell( const ScAddress& rPos, ScFormulaCell* pCell, bool bInteraction )
+{
+ std::unique_ptr<ScFormulaCell> xCell(pCell);
+
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight);
+
+ ScCellValue aOldVal;
+ if (bUndo)
+ aOldVal.assign(rDoc, rPos);
+
+ pCell = rDoc.SetFormulaCell(rPos, xCell.release());
+
+ // For performance reasons API calls may disable calculation while
+ // operating and recalculate once when done. If through user interaction
+ // and AutoCalc is disabled, calculate the formula (without its
+ // dependencies) once so the result matches the current document's content.
+ if (bInteraction && !rDoc.GetAutoCalc() && pCell)
+ {
+ // calculate just the cell once and set Dirty again
+ pCell->Interpret();
+ pCell->SetDirtyVar();
+ rDoc.PutInFormulaTree( pCell);
+ }
+
+ if (bUndo)
+ {
+ SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
+ ScCellValue aNewVal;
+ aNewVal.assign(rDoc, rPos);
+ pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
+ }
+
+ if (bHeight)
+ AdjustRowHeight(rPos, true, !bInteraction);
+
+ rDocShell.PostPaintCell( rPos );
+ aModificator.SetDocumentModified();
+
+ // #103934#; notify editline and cell in edit mode
+ if (!bInteraction)
+ NotifyInputHandler( rPos );
+
+ return true;
+}
+
+bool ScDocFunc::SetFormulaCells( const ScAddress& rPos, std::vector<ScFormulaCell*>& rCells, bool bInteraction )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ const size_t nLength = rCells.size();
+ if (rPos.Row() + nLength - 1 > o3tl::make_unsigned(rDoc.MaxRow()))
+ // out of bound
+ return false;
+
+ ScRange aRange(rPos);
+ aRange.aEnd.IncRow(nLength - 1);
+
+ ScDocShellModificator aModificator( rDocShell );
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ std::unique_ptr<sc::UndoSetCells> pUndoObj;
+ if (bUndo)
+ {
+ pUndoObj.reset(new sc::UndoSetCells(&rDocShell, rPos));
+ rDoc.TransferCellValuesTo(rPos, nLength, pUndoObj->GetOldValues());
+ }
+
+ rDoc.SetFormulaCells(rPos, rCells);
+
+ // For performance reasons API calls may disable calculation while
+ // operating and recalculate once when done. If through user interaction
+ // and AutoCalc is disabled, calculate the formula (without its
+ // dependencies) once so the result matches the current document's content.
+ if (bInteraction && !rDoc.GetAutoCalc())
+ {
+ for (auto* pCell : rCells)
+ {
+ // calculate just the cell once and set Dirty again
+ pCell->Interpret();
+ pCell->SetDirtyVar();
+ rDoc.PutInFormulaTree( pCell);
+ }
+ }
+
+ if (bUndo)
+ {
+ pUndoObj->SetNewValues(rCells);
+ SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
+ pUndoMgr->AddUndoAction(std::move(pUndoObj));
+ }
+
+ rDocShell.PostPaint(aRange, PaintPartFlags::Grid);
+ aModificator.SetDocumentModified();
+
+ // #103934#; notify editline and cell in edit mode
+ if (!bInteraction)
+ NotifyInputHandler( rPos );
+
+ return true;
+}
+
+void ScDocFunc::NotifyInputHandler( const ScAddress& rPos )
+{
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ if ( !(pViewSh && pViewSh->GetViewData().GetDocShell() == &rDocShell) )
+ return;
+
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
+ if ( pInputHdl && pInputHdl->GetCursorPos() == rPos )
+ {
+ bool bIsEditMode(pInputHdl->IsEditMode());
+
+ // set modified if in editmode, because so the string is not set in the InputWindow like in the cell
+ // (the cell shows the same like the InputWindow)
+ if (bIsEditMode)
+ pInputHdl->SetModified();
+ pViewSh->UpdateInputHandler(false, !bIsEditMode);
+ }
+}
+
+namespace {
+
+ struct ScMyRememberItem
+ {
+ sal_Int32 nIndex;
+ SfxItemSet aItemSet;
+
+ ScMyRememberItem(const SfxItemSet& rItemSet, sal_Int32 nTempIndex) :
+ nIndex(nTempIndex), aItemSet(rItemSet) {}
+ };
+
+}
+
+void ScDocFunc::PutData( const ScAddress& rPos, ScEditEngineDefaulter& rEngine, bool bApi )
+{
+ // PutData calls PutCell or SetNormalString
+
+ bool bRet = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScEditAttrTester aTester( &rEngine );
+ bool bEditCell = aTester.NeedsObject();
+ if ( bEditCell )
+ {
+ // #i61702# With bLoseContent set, the content of rEngine isn't restored
+ // (used in loading XML, where after the removeActionLock call the API object's
+ // EditEngine isn't accessed again.
+ bool bLoseContent = rDoc.IsImportingXML();
+
+ const bool bUpdateMode = rEngine.SetUpdateLayout(false);
+
+ std::vector<std::unique_ptr<ScMyRememberItem>> aRememberItems;
+
+ // All paragraph attributes must be removed before calling CreateTextObject,
+ // not only alignment, so the object doesn't contain the cell attributes as
+ // paragraph attributes. Before removing the attributes store them in a vector to
+ // set them back to the EditEngine.
+ sal_Int32 nCount = rEngine.GetParagraphCount();
+ for (sal_Int32 i=0; i<nCount; i++)
+ {
+ const SfxItemSet& rOld = rEngine.GetParaAttribs( i );
+ if ( rOld.Count() )
+ {
+ if ( !bLoseContent )
+ {
+ aRememberItems.push_back(std::make_unique<ScMyRememberItem>(rEngine.GetParaAttribs(i), i));
+ }
+ rEngine.SetParaAttribs( i, SfxItemSet( *rOld.GetPool(), rOld.GetRanges() ) );
+ }
+ }
+
+ // A copy of pNewData will be stored in the cell.
+ std::unique_ptr<EditTextObject> pNewData(rEngine.CreateTextObject());
+ bRet = SetEditCell(rPos, *pNewData, !bApi);
+
+ // Set the paragraph attributes back to the EditEngine.
+ for (const auto& rxItem : aRememberItems)
+ {
+ rEngine.SetParaAttribs(rxItem->nIndex, rxItem->aItemSet);
+ }
+
+ // #i61702# if the content isn't accessed, there's no need to set the UpdateMode again
+ if ( bUpdateMode && !bLoseContent )
+ rEngine.SetUpdateLayout(true);
+ }
+ else
+ {
+ OUString aText = rEngine.GetText();
+ if (aText.isEmpty())
+ {
+ bool bNumFmtSet = false;
+ bRet = SetNormalString( bNumFmtSet, rPos, aText, bApi );
+ }
+ else
+ bRet = SetStringCell(rPos, aText, !bApi);
+ }
+
+ if ( !(bRet && aTester.NeedsCellAttr()) )
+ return;
+
+ const SfxItemSet& rEditAttr = aTester.GetAttribs();
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetFromEditItemSet( &rEditAttr );
+ aPattern.DeleteUnchanged( rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() ) );
+ aPattern.GetItemSet().ClearItem( ATTR_HOR_JUSTIFY ); // wasn't removed above if no edit object
+ if ( aPattern.GetItemSet().Count() > 0 )
+ {
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SelectTable( rPos.Tab(), true );
+ aMark.SetMarkArea( ScRange( rPos ) );
+ ApplyAttributes( aMark, aPattern, bApi );
+ }
+}
+
+bool ScDocFunc::SetCellText(
+ const ScAddress& rPos, const OUString& rText, bool bInterpret, bool bEnglish, bool bApi,
+ const formula::FormulaGrammar::Grammar eGrammar )
+{
+ bool bSet = false;
+ if ( bInterpret )
+ {
+ if ( bEnglish )
+ {
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ ::std::optional<ScExternalRefManager::ApiGuard> pExtRefGuard;
+ if (bApi)
+ pExtRefGuard.emplace(rDoc);
+
+ ScInputStringType aRes =
+ ScStringUtil::parseInputString(*rDoc.GetFormatTable(), rText, LANGUAGE_ENGLISH_US);
+
+ switch (aRes.meType)
+ {
+ case ScInputStringType::Formula:
+ bSet = SetFormulaCell(rPos, new ScFormulaCell(rDoc, rPos, aRes.maText, eGrammar), !bApi);
+ break;
+ case ScInputStringType::Number:
+ bSet = SetValueCell(rPos, aRes.mfValue, !bApi);
+ break;
+ case ScInputStringType::Text:
+ bSet = SetStringOrEditCell(rPos, aRes.maText, !bApi);
+ break;
+ default:
+ ;
+ }
+ }
+ // otherwise keep Null -> SetString with local formulas/number formats
+ }
+ else if (!rText.isEmpty())
+ {
+ bSet = SetStringOrEditCell(rPos, rText, !bApi);
+ }
+
+ if (!bSet)
+ {
+ bool bNumFmtSet = false;
+ bSet = SetNormalString( bNumFmtSet, rPos, rText, bApi );
+ }
+ return bSet;
+}
+
+bool ScDocFunc::ShowNote( const ScAddress& rPos, bool bShow )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScPostIt* pNote = rDoc.GetNote( rPos );
+ if( !pNote || (bShow == pNote->IsCaptionShown()) ||
+ (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations()) )
+ return false;
+
+ // move the caption to internal or hidden layer and create undo action
+ pNote->ShowCaption( rPos, bShow );
+ if( rDoc.IsUndoEnabled() )
+ rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideNote>( rDocShell, rPos, bShow ) );
+
+ rDoc.SetStreamValid(rPos.Tab(), false);
+
+ ScTabView::OnLOKNoteStateChanged(pNote);
+
+ if (ScViewData* pViewData = ScDocShell::GetViewData())
+ {
+ if (ScDrawView* pDrawView = pViewData->GetScDrawView())
+ pDrawView->SyncForGrid( pNote->GetCaption());
+ }
+
+ rDocShell.SetDocumentModified();
+
+ return true;
+}
+
+void ScDocFunc::SetNoteText( const ScAddress& rPos, const OUString& rText, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ OUString aNewText = convertLineEnd(rText, GetSystemLineEnd()); //! is this necessary ???
+
+ if( ScPostIt* pNote = (!aNewText.isEmpty()) ? rDoc.GetOrCreateNote( rPos ) : rDoc.GetNote(rPos) )
+ pNote->SetText( rPos, aNewText );
+
+ //! Undo !!!
+
+ rDoc.SetStreamValid(rPos.Tab(), false);
+
+ rDocShell.PostPaintCell( rPos );
+ aModificator.SetDocumentModified();
+}
+
+void ScDocFunc::ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, const OUString* pAuthor, const OUString* pDate, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
+ if (aTester.IsEditable())
+ {
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ SfxUndoManager* pUndoMgr = (pDrawLayer && rDoc.IsUndoEnabled()) ? rDocShell.GetUndoManager() : nullptr;
+
+ ScNoteData aOldData;
+ std::unique_ptr<ScPostIt> pOldNote = rDoc.ReleaseNote( rPos );
+ sal_uInt32 nNoteId = 0;
+ if( pOldNote )
+ {
+ nNoteId = pOldNote->GetId();
+ // ensure existing caption object before draw undo tracking starts
+ pOldNote->GetOrCreateCaption( rPos );
+ // rescue note data for undo
+ aOldData = pOldNote->GetNoteData();
+ }
+
+ // collect drawing undo actions for deleting/inserting caption objects
+ if( pUndoMgr )
+ pDrawLayer->BeginCalcUndo(false);
+
+ // delete the note (creates drawing undo action for the caption object)
+ bool hadOldNote(pOldNote);
+ pOldNote.reset();
+
+ // create new note (creates drawing undo action for the new caption object)
+ ScNoteData aNewData;
+ ScPostIt* pNewNote = nullptr;
+ if( (pNewNote = ScNoteUtil::CreateNoteFromString( rDoc, rPos, rNoteText, false, true, nNoteId )) )
+ {
+ if( pAuthor ) pNewNote->SetAuthor( *pAuthor );
+ if( pDate ) pNewNote->SetDate( *pDate );
+
+ // rescue note data for undo
+ aNewData = pNewNote->GetNoteData();
+ }
+
+ // create the undo action
+ if( pUndoMgr && (aOldData.mxCaption || aNewData.mxCaption) )
+ pUndoMgr->AddUndoAction( std::make_unique<ScUndoReplaceNote>( rDocShell, rPos, aOldData, aNewData, pDrawLayer->GetCalcUndo() ) );
+
+ // repaint cell (to make note marker visible)
+ rDocShell.PostPaintCell( rPos );
+
+ rDoc.SetStreamValid(rPos.Tab(), false);
+
+ aModificator.SetDocumentModified();
+
+ // Let our LOK clients know about the new/modified note
+ if (pNewNote)
+ {
+ ScDocShell::LOKCommentNotify(hadOldNote ? LOKCommentNotificationType::Modify : LOKCommentNotificationType::Add,
+ &rDoc, rPos, pNewNote);
+ }
+ }
+ else if (!bApi)
+ {
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ }
+}
+
+ScPostIt* ScDocFunc::ImportNote( const ScAddress& rPos, const OUString& rNoteText )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ std::unique_ptr<ScPostIt> pOldNote = rDoc.ReleaseNote( rPos );
+ SAL_WARN_IF(pOldNote, "sc.ui", "imported data has >1 notes on same cell? at pos " << rPos);
+
+ // create new note
+ ScPostIt* pNewNote = ScNoteUtil::CreateNoteFromString( rDoc, rPos, rNoteText, false, true, /*nNoteId*/0 );
+
+ rDoc.SetStreamValid(rPos.Tab(), false);
+
+ aModificator.SetDocumentModified();
+
+ return pNewNote;
+}
+
+bool ScDocFunc::ApplyAttributes( const ScMarkData& rMark, const ScPatternAttr& rPattern,
+ bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bRecord = true;
+ if ( !rDoc.IsUndoEnabled() )
+ bRecord = false;
+
+ bool bImportingXML = rDoc.IsImportingXML();
+ // Cell formats can still be set if the range isn't editable only because of matrix formulas.
+ // #i62483# When loading XML, the check can be skipped altogether.
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !bImportingXML && !rDoc.IsSelectionEditable( rMark, &bOnlyNotBecauseOfMatrix )
+ && !bOnlyNotBecauseOfMatrix )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR);
+ return false;
+ }
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ //! Border
+
+ ScRange aMultiRange;
+ bool bMulti = rMark.IsMultiMarked();
+ if ( bMulti )
+ aMultiRange = rMark.GetMultiMarkArea();
+ else
+ aMultiRange = rMark.GetMarkArea();
+
+ if ( bRecord )
+ {
+ ScDocumentUniquePtr pUndoDoc( new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, aMultiRange.aStart.Tab(), aMultiRange.aEnd.Tab() );
+ rDoc.CopyToDocument(aMultiRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &rMark);
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoSelectionAttr>(
+ &rDocShell, rMark,
+ aMultiRange.aStart.Col(), aMultiRange.aStart.Row(), aMultiRange.aStart.Tab(),
+ aMultiRange.aEnd.Col(), aMultiRange.aEnd.Row(), aMultiRange.aEnd.Tab(),
+ std::move(pUndoDoc), bMulti, &rPattern ) );
+ }
+
+ // While loading XML it is not necessary to ask HasAttrib. It needs too much time.
+ sal_uInt16 nExtFlags = 0;
+ if ( !bImportingXML )
+ rDocShell.UpdatePaintExt( nExtFlags, aMultiRange ); // content before the change
+
+ bool bChanged = false;
+ rDoc.ApplySelectionPattern( rPattern, rMark, nullptr, &bChanged );
+
+ if(bChanged)
+ {
+ if ( !bImportingXML )
+ rDocShell.UpdatePaintExt( nExtFlags, aMultiRange ); // content after the change
+
+ if (!AdjustRowHeight( aMultiRange, true, bApi ))
+ rDocShell.PostPaint( aMultiRange, PaintPartFlags::Grid, nExtFlags );
+ else if (nExtFlags & SC_PF_LINES)
+ lcl_PaintAbove( rDocShell, aMultiRange ); // because of lines above the range
+
+ aModificator.SetDocumentModified();
+ }
+
+ return true;
+}
+
+bool ScDocFunc::ApplyStyle( const ScMarkData& rMark, const OUString& rStyleName,
+ bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bRecord = true;
+ if ( !rDoc.IsUndoEnabled() )
+ bRecord = false;
+
+ bool bImportingXML = rDoc.IsImportingXML();
+ // Cell formats can still be set if the range isn't editable only because of matrix formulas.
+ // #i62483# When loading XML, the check can be skipped altogether.
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !bImportingXML && !rDoc.IsSelectionEditable( rMark, &bOnlyNotBecauseOfMatrix )
+ && !bOnlyNotBecauseOfMatrix )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR);
+ return false;
+ }
+
+ ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>( rDoc.GetStyleSheetPool()->Find(
+ rStyleName, SfxStyleFamily::Para ));
+ if (!pStyleSheet)
+ return false;
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScRange aMultiRange;
+ bool bMulti = rMark.IsMultiMarked();
+ if ( bMulti )
+ aMultiRange = rMark.GetMultiMarkArea();
+ else
+ aMultiRange = rMark.GetMarkArea();
+
+ if ( bRecord )
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ SCTAB nStartTab = aMultiRange.aStart.Tab();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab != nStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+
+ ScRange aCopyRange = aMultiRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &rMark );
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoSelectionStyle>(
+ &rDocShell, rMark, aMultiRange, rStyleName, std::move(pUndoDoc) ) );
+
+ }
+
+ rDoc.ApplySelectionStyle( *pStyleSheet, rMark );
+
+ if (!AdjustRowHeight( aMultiRange, true, bApi ))
+ rDocShell.PostPaint( aMultiRange, PaintPartFlags::Grid );
+
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+namespace {
+
+/**
+ * Check if this insertion attempt would end up cutting one or more pivot
+ * tables in half, which is not desirable.
+ *
+ * @return true if this insertion can be done safely without shearing any
+ * existing pivot tables, false otherwise.
+ */
+bool canInsertCellsByPivot(const ScRange& rRange, const ScMarkData& rMarkData, InsCellCmd eCmd, const ScDocument& rDoc)
+{
+ if (!rDoc.HasPivotTable())
+ // This document has no pivot tables.
+ return true;
+
+ const ScDPCollection* pDPs = rDoc.GetDPCollection();
+
+ ScRange aRange(rRange); // local copy
+ switch (eCmd)
+ {
+ case INS_INSROWS_BEFORE:
+ {
+ aRange.aStart.SetCol(0);
+ aRange.aEnd.SetCol(rDoc.MaxCol());
+ [[fallthrough]];
+ }
+ case INS_CELLSDOWN:
+ {
+ auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
+ return pDPs->IntersectsTableByColumns(aRange.aStart.Col(), aRange.aEnd.Col(), aRange.aStart.Row(), rTab); });
+ if (bIntersects)
+ // This column range cuts through at least one pivot table. Not good.
+ return false;
+
+ // Start row must be either at the top or above any pivot tables.
+ if (aRange.aStart.Row() < 0)
+ // I don't know how to handle this case.
+ return false;
+
+ if (aRange.aStart.Row() == 0)
+ // First row is always allowed.
+ return true;
+
+ ScRange aTest(aRange);
+ aTest.aStart.IncRow(-1); // Test one row up.
+ aTest.aEnd.SetRow(aTest.aStart.Row());
+ for (const auto& rTab : rMarkData)
+ {
+ aTest.aStart.SetTab(rTab);
+ aTest.aEnd.SetTab(rTab);
+ if (pDPs->HasTable(aTest))
+ return false;
+ }
+ }
+ break;
+ case INS_INSCOLS_BEFORE:
+ {
+ aRange.aStart.SetRow(0);
+ aRange.aEnd.SetRow(rDoc.MaxRow());
+ [[fallthrough]];
+ }
+ case INS_CELLSRIGHT:
+ {
+ auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
+ return pDPs->IntersectsTableByRows(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Row(), rTab); });
+ if (bIntersects)
+ // This column range cuts through at least one pivot table. Not good.
+ return false;
+
+ // Start row must be either at the top or above any pivot tables.
+ if (aRange.aStart.Col() < 0)
+ // I don't know how to handle this case.
+ return false;
+
+ if (aRange.aStart.Col() == 0)
+ // First row is always allowed.
+ return true;
+
+ ScRange aTest(aRange);
+ aTest.aStart.IncCol(-1); // Test one column to the left.
+ aTest.aEnd.SetCol(aTest.aStart.Col());
+ for (const auto& rTab : rMarkData)
+ {
+ aTest.aStart.SetTab(rTab);
+ aTest.aEnd.SetTab(rTab);
+ if (pDPs->HasTable(aTest))
+ return false;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ return true;
+}
+
+/**
+ * Check if this deletion attempt would end up cutting one or more pivot
+ * tables in half, which is not desirable.
+ *
+ * @return true if this deletion can be done safely without shearing any
+ * existing pivot tables, false otherwise.
+ */
+bool canDeleteCellsByPivot(const ScRange& rRange, const ScMarkData& rMarkData, DelCellCmd eCmd, const ScDocument& rDoc)
+{
+ if (!rDoc.HasPivotTable())
+ // This document has no pivot tables.
+ return true;
+
+ const ScDPCollection* pDPs = rDoc.GetDPCollection();
+
+ ScRange aRange(rRange); // local copy
+
+ switch (eCmd)
+ {
+ case DelCellCmd::Rows:
+ {
+ aRange.aStart.SetCol(0);
+ aRange.aEnd.SetCol(rDoc.MaxCol());
+ [[fallthrough]];
+ }
+ case DelCellCmd::CellsUp:
+ {
+ auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
+ return pDPs->IntersectsTableByColumns(aRange.aStart.Col(), aRange.aEnd.Col(), aRange.aStart.Row(), rTab); });
+ if (bIntersects)
+ // This column range cuts through at least one pivot table. Not good.
+ return false;
+
+ ScRange aTest(aRange);
+ for (const auto& rTab : rMarkData)
+ {
+ aTest.aStart.SetTab(rTab);
+ aTest.aEnd.SetTab(rTab);
+ if (pDPs->HasTable(aTest))
+ return false;
+ }
+ }
+ break;
+ case DelCellCmd::Cols:
+ {
+ aRange.aStart.SetRow(0);
+ aRange.aEnd.SetRow(rDoc.MaxRow());
+ [[fallthrough]];
+ }
+ case DelCellCmd::CellsLeft:
+ {
+ auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
+ return pDPs->IntersectsTableByRows(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Row(), rTab); });
+ if (bIntersects)
+ // This column range cuts through at least one pivot table. Not good.
+ return false;
+
+ ScRange aTest(aRange);
+ for (const auto& rTab : rMarkData)
+ {
+ aTest.aStart.SetTab(rTab);
+ aTest.aEnd.SetTab(rTab);
+ if (pDPs->HasTable(aTest))
+ return false;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ return true;
+}
+
+}
+
+bool ScDocFunc::InsertCells( const ScRange& rRange, const ScMarkData* pTabMark, InsCellCmd eCmd,
+ bool bRecord, bool bApi, bool bPartOfPaste )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (rDocShell.GetDocument().GetChangeTrack() &&
+ ((eCmd == INS_CELLSDOWN && (rRange.aStart.Col() != 0 || rRange.aEnd.Col() != rDoc.MaxCol())) ||
+ (eCmd == INS_CELLSRIGHT && (rRange.aStart.Row() != 0 || rRange.aEnd.Row() != rDoc.MaxRow()))))
+ {
+ // We should not reach this via UI disabled slots.
+ assert(bApi);
+ SAL_WARN("sc.ui","ScDocFunc::InsertCells - no change-tracking of partial cell shift");
+ return false;
+ }
+
+ ScRange aTargetRange( rRange );
+
+ // If insertion is for full cols/rows and after the current
+ // selection, then shift the range accordingly
+ if ( eCmd == INS_INSROWS_AFTER )
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ if (!aTargetRange.Move(0, rRange.aEnd.Row() - rRange.aStart.Row() + 1, 0, aErrorRange, rDoc))
+ {
+ return false;
+ }
+ }
+ if ( eCmd == INS_INSCOLS_AFTER )
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ if (!aTargetRange.Move(rRange.aEnd.Col() - rRange.aStart.Col() + 1, 0, 0, aErrorRange, rDoc))
+ {
+ return false;
+ }
+ }
+
+ SCCOL nStartCol = aTargetRange.aStart.Col();
+ SCROW nStartRow = aTargetRange.aStart.Row();
+ SCTAB nStartTab = aTargetRange.aStart.Tab();
+ SCCOL nEndCol = aTargetRange.aEnd.Col();
+ SCROW nEndRow = aTargetRange.aEnd.Row();
+ SCTAB nEndTab = aTargetRange.aEnd.Tab();
+
+ if ( !rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow) )
+ {
+ OSL_FAIL("invalid row in InsertCells");
+ return false;
+ }
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCCOL nPaintStartCol = nStartCol;
+ SCROW nPaintStartRow = nStartRow;
+ SCCOL nPaintEndCol = nEndCol;
+ SCROW nPaintEndRow = nEndRow;
+ PaintPartFlags nPaintFlags = PaintPartFlags::Grid;
+ bool bSuccess;
+
+ ScTabViewShell* pViewSh = rDocShell.GetBestViewShell(); //preserve current cursor position
+ SCCOL nCursorCol = 0;
+ SCROW nCursorRow = 0;
+ if( pViewSh )
+ {
+ nCursorCol = pViewSh->GetViewData().GetCurX();
+ nCursorRow = pViewSh->GetViewData().GetCurY();
+ }
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ SCTAB nCount = 0;
+ for( SCTAB i=0; i<nTabCount; i++ )
+ {
+ if( !rDoc.IsScenario(i) )
+ {
+ nCount++;
+ if( nCount == nEndTab+1 )
+ {
+ aMark.SelectTable( i, true );
+ break;
+ }
+ }
+ }
+ }
+
+ ScMarkData aFullMark( aMark ); // including scenario sheets
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ aFullMark.SelectTable( j, true );
+ }
+
+ SCTAB nSelCount = aMark.GetSelectCount();
+
+ // Adjust also related scenarios
+
+ SCCOL nMergeTestStartCol = nStartCol;
+ SCROW nMergeTestStartRow = nStartRow;
+ SCCOL nMergeTestEndCol = nEndCol;
+ SCROW nMergeTestEndRow = nEndRow;
+
+ ScRange aExtendMergeRange( aTargetRange );
+
+ if( aTargetRange.aStart == aTargetRange.aEnd && rDoc.HasAttrib(aTargetRange, HasAttrFlags::Merged) )
+ {
+ rDoc.ExtendMerge( aExtendMergeRange );
+ rDoc.ExtendOverlapped( aExtendMergeRange );
+ nMergeTestEndCol = aExtendMergeRange.aEnd.Col();
+ nMergeTestEndRow = aExtendMergeRange.aEnd.Row();
+ nPaintEndCol = nMergeTestEndCol;
+ nPaintEndRow = nMergeTestEndRow;
+ }
+
+ if ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER )
+ {
+ nMergeTestStartCol = 0;
+ nMergeTestEndCol = rDoc.MaxCol();
+ }
+ if ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER )
+ {
+ nMergeTestStartRow = 0;
+ nMergeTestEndRow = rDoc.MaxRow();
+ }
+ if ( eCmd == INS_CELLSDOWN )
+ nMergeTestEndRow = rDoc.MaxRow();
+ if ( eCmd == INS_CELLSRIGHT )
+ nMergeTestEndCol = rDoc.MaxCol();
+
+ bool bNeedRefresh = false;
+
+ SCCOL nEditTestEndCol = (eCmd==INS_INSCOLS_BEFORE || eCmd==INS_INSCOLS_AFTER) ? rDoc.MaxCol() : nMergeTestEndCol;
+ SCROW nEditTestEndRow = (eCmd==INS_INSROWS_BEFORE || eCmd==INS_INSROWS_AFTER) ? rDoc.MaxRow() : nMergeTestEndRow;
+
+ ScEditableTester aTester;
+
+ switch (eCmd)
+ {
+ case INS_INSCOLS_BEFORE:
+ aTester = ScEditableTester(
+ rDoc, sc::ColRowEditAction::InsertColumnsBefore, nMergeTestStartCol, nMergeTestEndCol, aMark);
+ break;
+ case INS_INSCOLS_AFTER:
+ aTester = ScEditableTester(
+ rDoc, sc::ColRowEditAction::InsertColumnsAfter, nMergeTestStartCol, nMergeTestEndCol, aMark);
+ break;
+ case INS_INSROWS_BEFORE:
+ aTester = ScEditableTester(
+ rDoc, sc::ColRowEditAction::InsertRowsBefore, nMergeTestStartRow, nMergeTestEndRow, aMark);
+ break;
+ case INS_INSROWS_AFTER:
+ aTester = ScEditableTester(
+ rDoc, sc::ColRowEditAction::InsertRowsAfter, nMergeTestStartRow, nMergeTestEndRow, aMark);
+ break;
+ default:
+ aTester = ScEditableTester(
+ rDoc, nMergeTestStartCol, nMergeTestStartRow, nEditTestEndCol, nEditTestEndRow, aMark);
+ }
+
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ // Check if this insertion is allowed with respect to pivot table.
+ if (!canInsertCellsByPivot(aTargetRange, aMark, eCmd, rDoc))
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_NO_INSERT_DELETE_OVER_PIVOT_TABLE);
+ return false;
+ }
+
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important due to TrackFormulas at UpdateReference
+
+ ScDocumentUniquePtr pRefUndoDoc;
+ std::unique_ptr<ScRefUndoData> pUndoData;
+ if ( bRecord )
+ {
+ pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pRefUndoDoc->InitUndo( rDoc, 0, nTabCount-1 );
+
+ // pRefUndoDoc is filled in InsertCol / InsertRow
+
+ pUndoData.reset(new ScRefUndoData( &rDoc ));
+
+ rDoc.BeginDrawUndo();
+ }
+
+ // #i8302 : we unmerge overwhelming ranges, before insertion all the actions are put in the same ListAction
+ // the patch comes from mloiseleur and maoyg
+ bool bInsertMerge = false;
+ std::vector<ScRange> qIncreaseRange;
+ OUString aUndo = ScResId( STR_UNDO_INSERTCELLS );
+ if (bRecord)
+ {
+ ViewShellId nViewShellId(-1);
+ if (pViewSh)
+ nViewShellId = pViewSh->GetViewShellId();
+ rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
+ }
+ std::unique_ptr<ScUndoRemoveMerge> pUndoRemoveMerge;
+
+ for (const SCTAB i : aMark)
+ {
+ if (i >= nTabCount)
+ break;
+
+ if( rDoc.HasAttrib( nMergeTestStartCol, nMergeTestStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ {
+ if (eCmd==INS_CELLSRIGHT)
+ bNeedRefresh = true;
+
+ SCCOL nMergeStartCol = nMergeTestStartCol;
+ SCROW nMergeStartRow = nMergeTestStartRow;
+ SCCOL nMergeEndCol = nMergeTestEndCol;
+ SCROW nMergeEndRow = nMergeTestEndRow;
+
+ rDoc.ExtendMerge( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
+ rDoc.ExtendOverlapped( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
+
+ if(( eCmd == INS_CELLSDOWN && ( nMergeStartCol != nMergeTestStartCol || nMergeEndCol != nMergeTestEndCol )) ||
+ (eCmd == INS_CELLSRIGHT && ( nMergeStartRow != nMergeTestStartRow || nMergeEndRow != nMergeTestEndRow )) )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0);
+ rDocShell.GetUndoManager()->LeaveListAction();
+ return false;
+ }
+
+ SCCOL nTestCol = -1;
+ SCROW nTestRow1 = -1;
+ SCROW nTestRow2 = -1;
+
+ ScDocAttrIterator aTestIter( rDoc, i, nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow );
+ ScRange aExtendRange( nMergeTestStartCol, nMergeTestStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i );
+ const ScPatternAttr* pPattern = nullptr;
+ while ( ( pPattern = aTestIter.GetNext( nTestCol, nTestRow1, nTestRow2 ) ) != nullptr )
+ {
+ const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE);
+ const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG);
+ ScMF nNewFlags = rMergeFlagAttr.GetValue() & (ScMF::Hor | ScMF::Ver);
+ if (rMergeFlag.IsMerged() || nNewFlags == ScMF::Hor || nNewFlags == ScMF::Ver)
+ {
+ ScRange aRange( nTestCol, nTestRow1, i );
+ rDoc.ExtendOverlapped(aRange);
+ rDoc.ExtendMerge(aRange, true);
+
+ if( nTestRow1 < nTestRow2 && nNewFlags == ScMF::Hor )
+ {
+ for( SCROW nTestRow = nTestRow1; nTestRow <= nTestRow2; nTestRow++ )
+ {
+ ScRange aTestRange( nTestCol, nTestRow, i );
+ rDoc.ExtendOverlapped( aTestRange );
+ rDoc.ExtendMerge( aTestRange, true);
+ ScRange aMergeRange( aTestRange.aStart.Col(),aTestRange.aStart.Row(), i );
+ if( !aExtendRange.Contains( aMergeRange ) )
+ {
+ qIncreaseRange.push_back( aTestRange );
+ bInsertMerge = true;
+ }
+ }
+ }
+ else
+ {
+ ScRange aMergeRange( aRange.aStart.Col(),aRange.aStart.Row(), i );
+ if( !aExtendRange.Contains( aMergeRange ) )
+ {
+ qIncreaseRange.push_back( aRange );
+ }
+ bInsertMerge = true;
+ }
+ }
+ }
+
+ if( bInsertMerge )
+ {
+ if( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER || eCmd == INS_CELLSDOWN )
+ {
+ nStartRow = aExtendMergeRange.aStart.Row();
+ nEndRow = aExtendMergeRange.aEnd.Row();
+
+ if( eCmd == INS_CELLSDOWN )
+ nEndCol = nMergeTestEndCol;
+ else
+ {
+ nStartCol = 0;
+ nEndCol = rDoc.MaxCol();
+ }
+ }
+ else if( eCmd == INS_CELLSRIGHT || eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER )
+ {
+
+ nStartCol = aExtendMergeRange.aStart.Col();
+ nEndCol = aExtendMergeRange.aEnd.Col();
+ if( eCmd == INS_CELLSRIGHT )
+ {
+ nEndRow = nMergeTestEndRow;
+ }
+ else
+ {
+ nStartRow = 0;
+ nEndRow = rDoc.MaxRow();
+ }
+ }
+
+ if( !qIncreaseRange.empty() )
+ {
+ if (bRecord && !pUndoRemoveMerge)
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, *aMark.begin(), *aMark.rbegin());
+ pUndoRemoveMerge.reset( new ScUndoRemoveMerge( &rDocShell, rRange, std::move(pUndoDoc) ));
+ }
+
+ for( const ScRange& aRange : qIncreaseRange )
+ {
+ if( rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
+ {
+ UnmergeCells( aRange, bRecord, pUndoRemoveMerge.get() );
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0);
+ rDocShell.GetUndoManager()->LeaveListAction();
+ return false;
+ }
+ }
+ }
+
+ if (bRecord && pUndoRemoveMerge)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndoRemoveMerge));
+ }
+
+ switch (eCmd)
+ {
+ case INS_CELLSDOWN:
+ bSuccess = rDoc.InsertRow( nStartCol, 0, nEndCol, MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &aFullMark );
+ nPaintEndRow = rDoc.MaxRow();
+ break;
+ case INS_INSROWS_BEFORE:
+ case INS_INSROWS_AFTER:
+ bSuccess = rDoc.InsertRow( 0, 0, rDoc.MaxCol(), MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &aFullMark );
+ nPaintStartCol = 0;
+ nPaintEndCol = rDoc.MaxCol();
+ nPaintEndRow = rDoc.MaxRow();
+ nPaintFlags |= PaintPartFlags::Left;
+ break;
+ case INS_CELLSRIGHT:
+ bSuccess = rDoc.InsertCol( nStartRow, 0, nEndRow, MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &aFullMark );
+ nPaintEndCol = rDoc.MaxCol();
+ break;
+ case INS_INSCOLS_BEFORE:
+ case INS_INSCOLS_AFTER:
+ bSuccess = rDoc.InsertCol( 0, 0, rDoc.MaxRow(), MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &aFullMark );
+ nPaintStartRow = 0;
+ nPaintEndRow = rDoc.MaxRow();
+ nPaintEndCol = rDoc.MaxCol();
+ nPaintFlags |= PaintPartFlags::Top;
+ break;
+ default:
+ OSL_FAIL("Wrong code at inserting");
+ bSuccess = false;
+ break;
+ }
+
+ if ( bSuccess )
+ {
+ SCTAB nUndoPos = 0;
+
+ if ( bRecord )
+ {
+ std::unique_ptr<SCTAB[]> pTabs(new SCTAB[nSelCount]);
+ std::unique_ptr<SCTAB[]> pScenarios(new SCTAB[nSelCount]);
+ nUndoPos = 0;
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ SCTAB nCount = 0;
+ for( SCTAB j=rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ nCount ++;
+
+ pScenarios[nUndoPos] = nCount;
+ pTabs[nUndoPos] = rTab;
+ nUndoPos ++;
+ }
+
+ if( !bInsertMerge )
+ {
+ rDocShell.GetUndoManager()->LeaveListAction();
+ }
+
+ rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoInsertCells>(
+ &rDocShell, ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ),
+ nUndoPos, std::move(pTabs), std::move(pScenarios), eCmd, std::move(pRefUndoDoc), std::move(pUndoData), bPartOfPaste ) );
+ }
+
+ // #i8302 : we remerge growing ranges, with the new part inserted
+
+ while( !qIncreaseRange.empty() )
+ {
+ ScRange aRange = qIncreaseRange.back();
+ if( !rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
+ {
+ switch (eCmd)
+ {
+ case INS_CELLSDOWN:
+ case INS_INSROWS_BEFORE:
+ case INS_INSROWS_AFTER:
+ aRange.aEnd.IncRow(static_cast<SCCOL>(nEndRow-nStartRow+1));
+ break;
+ case INS_CELLSRIGHT:
+ case INS_INSCOLS_BEFORE:
+ case INS_INSCOLS_AFTER:
+ aRange.aEnd.IncCol(static_cast<SCCOL>(nEndCol-nStartCol+1));
+ break;
+ default:
+ break;
+ }
+ ScCellMergeOption aMergeOption(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row() );
+ aMergeOption.maTabs.insert(aRange.aStart.Tab());
+ MergeCells(aMergeOption, false, true, true);
+ }
+ qIncreaseRange.pop_back();
+ }
+
+ if( bInsertMerge )
+ rDocShell.GetUndoManager()->LeaveListAction();
+
+ for (const SCTAB i : aMark)
+ {
+ if (i >= nTabCount)
+ break;
+
+ rDoc.SetDrawPageSize(i);
+
+ if (bNeedRefresh)
+ rDoc.ExtendMerge( nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow, i, true );
+ else
+ rDoc.RefreshAutoFilter( nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow, i );
+
+ if ( eCmd == INS_INSROWS_BEFORE ||eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSROWS_AFTER ||eCmd == INS_INSCOLS_AFTER )
+ rDoc.UpdatePageBreaks( i );
+
+ sal_uInt16 nExtFlags = 0;
+ rDocShell.UpdatePaintExt( nExtFlags, nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i );
+
+ SCTAB nScenarioCount = 0;
+
+ for( SCTAB j = i+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ nScenarioCount ++;
+
+ bool bAdjusted = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER ) ?
+ AdjustRowHeight(ScRange(0, nStartRow, i, rDoc.MaxCol(), nEndRow, i+nScenarioCount ), true, bApi) :
+ AdjustRowHeight(ScRange(0, nPaintStartRow, i, rDoc.MaxCol(), nPaintEndRow, i+nScenarioCount ), true, bApi);
+ if (bAdjusted)
+ {
+ // paint only what is not done by AdjustRowHeight
+ if (nPaintFlags & PaintPartFlags::Top)
+ rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i+nScenarioCount, PaintPartFlags::Top );
+ }
+ else
+ rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i+nScenarioCount, nPaintFlags, nExtFlags );
+ }
+ }
+ else
+ {
+ if( bInsertMerge )
+ {
+ while( !qIncreaseRange.empty() )
+ {
+ ScRange aRange = qIncreaseRange.back();
+ ScCellMergeOption aMergeOption(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row() );
+ MergeCells(aMergeOption, false, true, true);
+ qIncreaseRange.pop_back();
+ }
+
+ if( pViewSh )
+ {
+ pViewSh->MarkRange( aTargetRange, false );
+ pViewSh->SetCursor( nCursorCol, nCursorRow );
+ }
+ }
+
+ rDocShell.GetUndoManager()->LeaveListAction();
+ rDocShell.GetUndoManager()->RemoveLastUndoAction();
+
+ pRefUndoDoc.reset();
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_INSERT_FULL); // column/row full
+ }
+
+ // The cursor position needs to be modified earlier than updating
+ // any enabled edit view which is triggered by SetDocumentModified below.
+ if (bSuccess)
+ {
+ bool bInsertCols = ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER);
+ bool bInsertRows = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER );
+
+ if (bInsertCols)
+ {
+ pViewSh->OnLOKInsertDeleteColumn(rRange.aStart.Col(), 1);
+ }
+
+ if (bInsertRows)
+ {
+ pViewSh->OnLOKInsertDeleteRow(rRange.aStart.Row(), 1);
+ }
+ }
+
+ aModificator.SetDocumentModified();
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+ return bSuccess;
+}
+
+bool ScDocFunc::DeleteCells( const ScRange& rRange, const ScMarkData* pTabMark, DelCellCmd eCmd,
+ bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (rDocShell.GetDocument().GetChangeTrack() &&
+ ((eCmd == DelCellCmd::CellsUp && (rRange.aStart.Col() != 0 || rRange.aEnd.Col() != rDoc.MaxCol())) ||
+ (eCmd == DelCellCmd::CellsLeft && (rRange.aStart.Row() != 0 || rRange.aEnd.Row() != rDoc.MaxRow()))))
+ {
+ // We should not reach this via UI disabled slots.
+ assert(bApi);
+ SAL_WARN("sc.ui","ScDocFunc::DeleteCells - no change-tracking of partial cell shift");
+ return false;
+ }
+
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+
+ if ( !rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow) )
+ {
+ OSL_FAIL("invalid row in DeleteCells");
+ return false;
+ }
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCCOL nPaintStartCol = nStartCol;
+ SCROW nPaintStartRow = nStartRow;
+ SCCOL nPaintEndCol = nEndCol;
+ SCROW nPaintEndRow = nEndRow;
+ PaintPartFlags nPaintFlags = PaintPartFlags::Grid;
+
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ SCTAB nCount = 0;
+ for(SCTAB i=0; i<nTabCount; i++ )
+ {
+ if( !rDoc.IsScenario(i) )
+ {
+ nCount++;
+ if( nCount == nEndTab+1 )
+ {
+ aMark.SelectTable(i, true);
+ break;
+ }
+ }
+ }
+ }
+
+ ScMarkData aFullMark( aMark ); // including scenario sheets
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ aFullMark.SelectTable( j, true );
+ }
+
+ SCTAB nSelCount = aMark.GetSelectCount();
+
+ SCCOL nUndoStartCol = nStartCol;
+ SCROW nUndoStartRow = nStartRow;
+ SCCOL nUndoEndCol = nEndCol;
+ SCROW nUndoEndRow = nEndRow;
+
+ ScRange aExtendMergeRange( rRange );
+
+ if( rRange.aStart == rRange.aEnd && rDoc.HasAttrib(rRange, HasAttrFlags::Merged) )
+ {
+ rDoc.ExtendMerge( aExtendMergeRange );
+ rDoc.ExtendOverlapped( aExtendMergeRange );
+ nUndoEndCol = aExtendMergeRange.aEnd.Col();
+ nUndoEndRow = aExtendMergeRange.aEnd.Row();
+ nPaintEndCol = nUndoEndCol;
+ nPaintEndRow = nUndoEndRow;
+ }
+
+ if (eCmd==DelCellCmd::Rows)
+ {
+ nUndoStartCol = 0;
+ nUndoEndCol = rDoc.MaxCol();
+ }
+ if (eCmd==DelCellCmd::Cols)
+ {
+ nUndoStartRow = 0;
+ nUndoEndRow = rDoc.MaxRow();
+ }
+ // Test for cell protection
+
+ SCCOL nEditTestEndX = nUndoEndCol;
+ if ( eCmd==DelCellCmd::Cols || eCmd==DelCellCmd::CellsLeft )
+ nEditTestEndX = rDoc.MaxCol();
+ SCROW nEditTestEndY = nUndoEndRow;
+ if ( eCmd==DelCellCmd::Rows || eCmd==DelCellCmd::CellsUp )
+ nEditTestEndY = rDoc.MaxRow();
+
+ ScEditableTester aTester;
+
+ switch (eCmd)
+ {
+ case DelCellCmd::Cols:
+ aTester = ScEditableTester(
+ rDoc, sc::ColRowEditAction::DeleteColumns, nUndoStartCol, nUndoEndCol, aMark);
+ break;
+ case DelCellCmd::Rows:
+ aTester = ScEditableTester(
+ rDoc, sc::ColRowEditAction::DeleteRows, nUndoStartRow, nUndoEndRow, aMark);
+ break;
+ default:
+ aTester = ScEditableTester(
+ rDoc, nUndoStartCol, nUndoStartRow, nEditTestEndX, nEditTestEndY, aMark);
+ }
+
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ if (!canDeleteCellsByPivot(rRange, aMark, eCmd, rDoc))
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_NO_INSERT_DELETE_OVER_PIVOT_TABLE);
+ return false;
+ }
+ // Test for merged cells
+
+ SCCOL nMergeTestEndCol = (eCmd==DelCellCmd::CellsLeft) ? rDoc.MaxCol() : nUndoEndCol;
+ SCROW nMergeTestEndRow = (eCmd==DelCellCmd::CellsUp) ? rDoc.MaxRow() : nUndoEndRow;
+ SCCOL nExtendStartCol = nUndoStartCol;
+ SCROW nExtendStartRow = nUndoStartRow;
+ bool bNeedRefresh = false;
+
+ //Issue 8302 want to be able to insert into the middle of merged cells
+ //the patch comes from maoyg
+ ::std::vector<ScRange> qDecreaseRange;
+ bool bDeletingMerge = false;
+ OUString aUndo = ScResId( STR_UNDO_DELETECELLS );
+ if (bRecord)
+ {
+ ViewShellId nViewShellId(-1);
+ if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
+ nViewShellId = pViewSh->GetViewShellId();
+ rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
+ }
+ std::unique_ptr<ScUndoRemoveMerge> pUndoRemoveMerge;
+
+ for (const SCTAB i : aMark)
+ {
+ if (i >= nTabCount)
+ break;
+
+ if ( rDoc.HasAttrib( nUndoStartCol, nUndoStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ {
+ SCCOL nMergeStartCol = nUndoStartCol;
+ SCROW nMergeStartRow = nUndoStartRow;
+ SCCOL nMergeEndCol = nMergeTestEndCol;
+ SCROW nMergeEndRow = nMergeTestEndRow;
+
+ rDoc.ExtendMerge( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
+ rDoc.ExtendOverlapped( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
+ if( ( eCmd == DelCellCmd::CellsUp && ( nMergeStartCol != nUndoStartCol || nMergeEndCol != nMergeTestEndCol))||
+ ( eCmd == DelCellCmd::CellsLeft && ( nMergeStartRow != nUndoStartRow || nMergeEndRow != nMergeTestEndRow)))
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_DELETECELLS_0);
+ rDocShell.GetUndoManager()->LeaveListAction();
+ return false;
+ }
+
+ nExtendStartCol = nMergeStartCol;
+ nExtendStartRow = nMergeStartRow;
+ SCCOL nTestCol = -1;
+ SCROW nTestRow1 = -1;
+ SCROW nTestRow2 = -1;
+
+ ScDocAttrIterator aTestIter( rDoc, i, nUndoStartCol, nUndoStartRow, nMergeTestEndCol, nMergeTestEndRow );
+ ScRange aExtendRange( nUndoStartCol, nUndoStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i );
+ const ScPatternAttr* pPattern = nullptr;
+ while ( ( pPattern = aTestIter.GetNext( nTestCol, nTestRow1, nTestRow2 ) ) != nullptr )
+ {
+ const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE);
+ const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG);
+ ScMF nNewFlags = rMergeFlagAttr.GetValue() & (ScMF::Hor | ScMF::Ver);
+ if (rMergeFlag.IsMerged() || nNewFlags == ScMF::Hor || nNewFlags == ScMF::Ver)
+ {
+ ScRange aRange( nTestCol, nTestRow1, i );
+ rDoc.ExtendOverlapped( aRange );
+ rDoc.ExtendMerge( aRange, true );
+
+ if( nTestRow1 < nTestRow2 && nNewFlags == ScMF::Hor )
+ {
+ for( SCROW nTestRow = nTestRow1; nTestRow <= nTestRow2; nTestRow++ )
+ {
+ ScRange aTestRange( nTestCol, nTestRow, i );
+ rDoc.ExtendOverlapped( aTestRange );
+ rDoc.ExtendMerge( aTestRange, true );
+ ScRange aMergeRange( aTestRange.aStart.Col(),aTestRange.aStart.Row(), i );
+ if( !aExtendRange.Contains( aMergeRange ) )
+ {
+ qDecreaseRange.push_back( aTestRange );
+ bDeletingMerge = true;
+ }
+ }
+ }
+ else
+ {
+ ScRange aMergeRange( aRange.aStart.Col(),aRange.aStart.Row(), i );
+ if( !aExtendRange.Contains( aMergeRange ) )
+ {
+ qDecreaseRange.push_back( aRange );
+ }
+ bDeletingMerge = true;
+ }
+ }
+ }
+
+ if( bDeletingMerge )
+ {
+
+ if( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::CellsUp )
+ {
+ nStartRow = aExtendMergeRange.aStart.Row();
+ nEndRow = aExtendMergeRange.aEnd.Row();
+ bNeedRefresh = true;
+
+ if( eCmd == DelCellCmd::CellsUp )
+ {
+ nEndCol = aExtendMergeRange.aEnd.Col();
+ }
+ else
+ {
+ nStartCol = 0;
+ nEndCol = rDoc.MaxCol();
+ }
+ }
+ else if( eCmd == DelCellCmd::CellsLeft || eCmd == DelCellCmd::Cols )
+ {
+
+ nStartCol = aExtendMergeRange.aStart.Col();
+ nEndCol = aExtendMergeRange.aEnd.Col();
+ if( eCmd == DelCellCmd::CellsLeft )
+ {
+ nEndRow = aExtendMergeRange.aEnd.Row();
+ bNeedRefresh = true;
+ }
+ else
+ {
+ nStartRow = 0;
+ nEndRow = rDoc.MaxRow();
+ }
+ }
+
+ if( !qDecreaseRange.empty() )
+ {
+ if (bRecord && !pUndoRemoveMerge)
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, *aMark.begin(), *aMark.rbegin());
+ pUndoRemoveMerge.reset( new ScUndoRemoveMerge( &rDocShell, rRange, std::move(pUndoDoc) ));
+ }
+
+ for( const ScRange& aRange : qDecreaseRange )
+ {
+ if( rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
+ {
+ UnmergeCells( aRange, bRecord, pUndoRemoveMerge.get() );
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_DELETECELLS_0);
+ rDocShell.GetUndoManager()->LeaveListAction();
+ return false;
+ }
+ }
+ }
+
+ if (bRecord && pUndoRemoveMerge)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndoRemoveMerge));
+ }
+
+ // do it
+
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important because of TrackFormulas in UpdateReference
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScDocument> pRefUndoDoc;
+ std::unique_ptr<ScRefUndoData> pUndoData;
+ if ( bRecord )
+ {
+ // With the fix for #101329#, UpdateRef always puts cells into pRefUndoDoc at their old position,
+ // so it's no longer necessary to copy more than the deleted range into pUndoDoc.
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, 0, nTabCount-1, (eCmd==DelCellCmd::Cols), (eCmd==DelCellCmd::Rows) );
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ SCTAB nScenarioCount = 0;
+
+ for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ nScenarioCount ++;
+
+ rDoc.CopyToDocument( nUndoStartCol, nUndoStartRow, rTab, nUndoEndCol, nUndoEndRow, rTab+nScenarioCount,
+ InsertDeleteFlags::ALL | InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc );
+ }
+
+ pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pRefUndoDoc->InitUndo( rDoc, 0, nTabCount-1 );
+
+ pUndoData.reset(new ScRefUndoData( &rDoc ));
+
+ rDoc.BeginDrawUndo();
+ }
+
+ sal_uInt16 nExtFlags = 0;
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ rDocShell.UpdatePaintExt( nExtFlags, nStartCol, nStartRow, rTab, nEndCol, nEndRow, rTab );
+ }
+
+ switch (eCmd)
+ {
+ case DelCellCmd::CellsUp:
+ case DelCellCmd::CellsLeft:
+ rDoc.DeleteObjectsInArea(nStartCol, nStartRow, nEndCol, nEndRow, aMark, true);
+ break;
+ case DelCellCmd::Rows:
+ rDoc.DeleteObjectsInArea(0, nStartRow, rDoc.MaxCol(), nEndRow, aMark, true);
+ break;
+ case DelCellCmd::Cols:
+ rDoc.DeleteObjectsInArea(nStartCol, 0, nEndCol, rDoc.MaxRow(), aMark, true);
+ break;
+ default:
+ break;
+ }
+
+
+ bool bUndoOutline = false;
+ switch (eCmd)
+ {
+ case DelCellCmd::CellsUp:
+ rDoc.DeleteRow( nStartCol, 0, nEndCol, MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), nullptr, &aFullMark );
+ nPaintEndRow = rDoc.MaxRow();
+ break;
+ case DelCellCmd::Rows:
+ rDoc.DeleteRow( 0, 0, rDoc.MaxCol(), MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &bUndoOutline, &aFullMark );
+ nPaintStartCol = 0;
+ nPaintEndCol = rDoc.MaxCol();
+ nPaintEndRow = rDoc.MaxRow();
+ nPaintFlags |= PaintPartFlags::Left;
+ break;
+ case DelCellCmd::CellsLeft:
+ rDoc.DeleteCol( nStartRow, 0, nEndRow, MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), nullptr, &aFullMark );
+ nPaintEndCol = rDoc.MaxCol();
+ break;
+ case DelCellCmd::Cols:
+ rDoc.DeleteCol( 0, 0, rDoc.MaxRow(), MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &bUndoOutline, &aFullMark );
+ nPaintStartRow = 0;
+ nPaintEndRow = rDoc.MaxRow();
+ nPaintEndCol = rDoc.MaxCol();
+ nPaintFlags |= PaintPartFlags::Top;
+ break;
+ default:
+ OSL_FAIL("Wrong code at deleting");
+ break;
+ }
+
+ //! Test if the size of outline has changed
+
+ if ( bRecord )
+ {
+ for (const auto& rTab : aFullMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ pRefUndoDoc->DeleteAreaTab(nUndoStartCol,nUndoStartRow,nUndoEndCol,nUndoEndRow, rTab, InsertDeleteFlags::ALL);
+ }
+
+ // for all sheets, so that formulas can be copied
+ pUndoDoc->AddUndoTab( 0, nTabCount-1 );
+
+ // copy with bColRowFlags=false (#54194#)
+ pRefUndoDoc->CopyToDocument(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB,InsertDeleteFlags::FORMULA,false,*pUndoDoc,nullptr,false);
+ pRefUndoDoc.reset();
+
+ std::unique_ptr<SCTAB[]> pTabs( new SCTAB[nSelCount]);
+ std::unique_ptr<SCTAB[]> pScenarios( new SCTAB[nSelCount]);
+ SCTAB nUndoPos = 0;
+
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ SCTAB nCount = 0;
+ for( SCTAB j=rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ nCount ++;
+
+ pScenarios[nUndoPos] = nCount;
+ pTabs[nUndoPos] = rTab;
+ nUndoPos ++;
+ }
+
+ if( !bDeletingMerge )
+ {
+ rDocShell.GetUndoManager()->LeaveListAction();
+ }
+
+ rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDeleteCells>(
+ &rDocShell, ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ),
+ nUndoPos, std::move(pTabs), std::move(pScenarios),
+ eCmd, std::move(pUndoDoc), std::move(pUndoData) ) );
+ }
+
+ // #i8302 want to be able to insert into the middle of merged cells
+ // the patch comes from maoyg
+
+ while( !qDecreaseRange.empty() )
+ {
+ ScRange aRange = qDecreaseRange.back();
+
+ sal_Int32 nDecreaseRowCount = 0;
+ sal_Int32 nDecreaseColCount = 0;
+ if( eCmd == DelCellCmd::CellsUp || eCmd == DelCellCmd::Rows )
+ {
+ if( nStartRow >= aRange.aStart.Row() && nStartRow <= aRange.aEnd.Row() && nEndRow>= aRange.aStart.Row() && nEndRow <= aRange.aEnd.Row() )
+ nDecreaseRowCount = nEndRow-nStartRow+1;
+ else if( nStartRow >= aRange.aStart.Row() && nStartRow <= aRange.aEnd.Row() && nEndRow >= aRange.aStart.Row() && nEndRow >= aRange.aEnd.Row() )
+ nDecreaseRowCount = aRange.aEnd.Row()-nStartRow+1;
+ else if( nStartRow >= aRange.aStart.Row() && nStartRow >= aRange.aEnd.Row() && nEndRow>= aRange.aStart.Row() && nEndRow <= aRange.aEnd.Row() )
+ nDecreaseRowCount = aRange.aEnd.Row()-nEndRow+1;
+ }
+ else if( eCmd == DelCellCmd::CellsLeft || eCmd == DelCellCmd::Cols )
+ {
+ if( nStartCol >= aRange.aStart.Col() && nStartCol <= aRange.aEnd.Col() && nEndCol>= aRange.aStart.Col() && nEndCol <= aRange.aEnd.Col() )
+ nDecreaseColCount = nEndCol-nStartCol+1;
+ else if( nStartCol >= aRange.aStart.Col() && nStartCol <= aRange.aEnd.Col() && nEndCol >= aRange.aStart.Col() && nEndCol >= aRange.aEnd.Col() )
+ nDecreaseColCount = aRange.aEnd.Col()-nStartCol+1;
+ else if( nStartCol >= aRange.aStart.Col() && nStartCol >= aRange.aEnd.Col() && nEndCol>= aRange.aStart.Col() && nEndCol <= aRange.aEnd.Col() )
+ nDecreaseColCount = aRange.aEnd.Col()-nEndCol+1;
+ }
+
+ switch (eCmd)
+ {
+ case DelCellCmd::CellsUp:
+ case DelCellCmd::Rows:
+ aRange.aEnd.SetRow(static_cast<SCCOL>( aRange.aEnd.Row()-nDecreaseRowCount));
+ break;
+ case DelCellCmd::CellsLeft:
+ case DelCellCmd::Cols:
+ aRange.aEnd.SetCol(static_cast<SCCOL>( aRange.aEnd.Col()-nDecreaseColCount));
+ break;
+ default:
+ break;
+ }
+
+ if( !rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
+ {
+ ScCellMergeOption aMergeOption(aRange);
+ MergeCells( aMergeOption, false, true, true );
+ }
+ qDecreaseRange.pop_back();
+ }
+
+ if( bDeletingMerge )
+ rDocShell.GetUndoManager()->LeaveListAction();
+
+ if ( eCmd==DelCellCmd::Cols || eCmd==DelCellCmd::CellsLeft )
+ nMergeTestEndCol = rDoc.MaxCol();
+ if ( eCmd==DelCellCmd::Rows || eCmd==DelCellCmd::CellsUp )
+ nMergeTestEndRow = rDoc.MaxRow();
+ if ( bNeedRefresh )
+ {
+ // #i51445# old merge flag attributes must be deleted also for single cells,
+ // not only for whole columns/rows
+
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( ScMergeFlagAttr() );
+
+ rDoc.ApplyPatternArea( nExtendStartCol, nExtendStartRow, nMergeTestEndCol, nMergeTestEndRow, aMark, aPattern );
+
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ SCTAB nScenarioCount = 0;
+
+ for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ nScenarioCount ++;
+
+ ScRange aMergedRange( nExtendStartCol, nExtendStartRow, rTab, nMergeTestEndCol, nMergeTestEndRow, rTab+nScenarioCount );
+ rDoc.ExtendMerge( aMergedRange, true );
+ }
+ }
+
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ rDoc.RefreshAutoFilter( nExtendStartCol, nExtendStartRow, nMergeTestEndCol, nMergeTestEndRow, rTab );
+ }
+
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ rDoc.SetDrawPageSize(rTab);
+
+ if ( eCmd == DelCellCmd::Cols || eCmd == DelCellCmd::Rows )
+ rDoc.UpdatePageBreaks( rTab );
+
+ rDocShell.UpdatePaintExt( nExtFlags, nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab );
+
+ SCTAB nScenarioCount = 0;
+
+ for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ nScenarioCount ++;
+
+ // delete entire rows: do not adjust
+ if ( eCmd == DelCellCmd::Rows || !AdjustRowHeight(ScRange( 0, nPaintStartRow, rTab, rDoc.MaxCol(), nPaintEndRow, rTab+nScenarioCount ), true, bApi) )
+ rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount, nPaintFlags, nExtFlags );
+ else
+ {
+ // paint only what is not done by AdjustRowHeight
+ if (nExtFlags & SC_PF_LINES)
+ lcl_PaintAbove( rDocShell, ScRange( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount) );
+ if (nPaintFlags & PaintPartFlags::Top)
+ rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount, PaintPartFlags::Top );
+ }
+ }
+
+ // The cursor position needs to be modified earlier than updating
+ // any enabled edit view which is triggered by SetDocumentModified below.
+ ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
+ if (pViewSh)
+ {
+ if (eCmd == DelCellCmd::Cols)
+ {
+ pViewSh->OnLOKInsertDeleteColumn(rRange.aStart.Col(), -1);
+ }
+ if (eCmd == DelCellCmd::Rows)
+ {
+ pViewSh->OnLOKInsertDeleteRow(rRange.aStart.Row(), -1);
+ }
+ }
+
+ aModificator.SetDocumentModified();
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+
+ return true;
+}
+
+bool ScDocFunc::MoveBlock( const ScRange& rSource, const ScAddress& rDestPos,
+ bool bCut, bool bRecord, bool bPaint, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ SCCOL nStartCol = rSource.aStart.Col();
+ SCROW nStartRow = rSource.aStart.Row();
+ SCTAB nStartTab = rSource.aStart.Tab();
+ SCCOL nEndCol = rSource.aEnd.Col();
+ SCROW nEndRow = rSource.aEnd.Row();
+ SCTAB nEndTab = rSource.aEnd.Tab();
+ SCCOL nDestCol = rDestPos.Col();
+ SCROW nDestRow = rDestPos.Row();
+ SCTAB nDestTab = rDestPos.Tab();
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if ( !rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow) || !rDoc.ValidRow(nDestRow) )
+ {
+ OSL_FAIL("invalid row in MoveBlock");
+ return false;
+ }
+
+ // adjust related scenarios too - but only when moved within one sheet
+ bool bScenariosAdded = false;
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ if ( nDestTab == nStartTab && !rDoc.IsScenario(nEndTab) )
+ while ( nEndTab+1 < nTabCount && rDoc.IsScenario(nEndTab+1) )
+ {
+ ++nEndTab;
+ bScenariosAdded = true;
+ }
+
+ SCTAB nSrcTabCount = nEndTab-nStartTab+1;
+ SCTAB nDestEndTab = nDestTab+nSrcTabCount-1;
+ SCTAB nTab;
+
+ ScDocumentUniquePtr pClipDoc(new ScDocument(SCDOCMODE_CLIP));
+
+ ScMarkData aSourceMark(rDoc.GetSheetLimits());
+ for (nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aSourceMark.SelectTable( nTab, true ); // select source
+ aSourceMark.SetMarkArea( rSource );
+
+ ScDocShellRef aDragShellRef;
+ if ( rDoc.HasOLEObjectsInArea( rSource ) )
+ {
+ aDragShellRef = new ScDocShell; // DocShell needs a Ref immediately
+ aDragShellRef->DoInitNew();
+ }
+ ScDrawLayer::SetGlobalDrawPersist( aDragShellRef.get() );
+
+ ScClipParam aClipParam(ScRange(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nStartTab), bCut);
+ rDoc.CopyToClip(aClipParam, pClipDoc.get(), &aSourceMark, bScenariosAdded, true);
+
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+
+ SCCOL nOldEndCol = nEndCol;
+ SCROW nOldEndRow = nEndRow;
+ bool bClipOver = false;
+ for (nTab=nStartTab; nTab<=nEndTab; nTab++)
+ {
+ SCCOL nTmpEndCol = nOldEndCol;
+ SCROW nTmpEndRow = nOldEndRow;
+ if (rDoc.ExtendMerge( nStartCol, nStartRow, nTmpEndCol, nTmpEndRow, nTab ))
+ bClipOver = true;
+ if ( nTmpEndCol > nEndCol ) nEndCol = nTmpEndCol;
+ if ( nTmpEndRow > nEndRow ) nEndRow = nTmpEndRow;
+ }
+
+ SCCOL nDestEndCol = nDestCol + ( nOldEndCol-nStartCol );
+ SCROW nDestEndRow = nDestRow + ( nOldEndRow-nStartRow );
+
+ SCCOL nUndoEndCol = nDestCol + ( nEndCol-nStartCol ); // extended in destination block
+ SCROW nUndoEndRow = nDestRow + ( nEndRow-nStartRow );
+
+ bool bIncludeFiltered = bCut;
+ if ( !bIncludeFiltered )
+ {
+ // adjust sizes to include only non-filtered rows
+
+ SCCOL nClipX;
+ SCROW nClipY;
+ pClipDoc->GetClipArea( nClipX, nClipY, false );
+ SCROW nUndoAdd = nUndoEndRow - nDestEndRow;
+ nDestEndRow = nDestRow + nClipY;
+ nUndoEndRow = nDestEndRow + nUndoAdd;
+ }
+
+ if (!rDoc.ValidCol(nUndoEndCol) || !rDoc.ValidRow(nUndoEndRow))
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+
+ // Test for cell protection
+
+ ScEditableTester aTester;
+ for (nTab=nDestTab; nTab<=nDestEndTab; nTab++)
+ aTester.TestBlock( rDoc, nTab, nDestCol,nDestRow, nUndoEndCol,nUndoEndRow );
+ if (bCut)
+ for (nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aTester.TestBlock( rDoc, nTab, nStartCol,nStartRow, nEndCol,nEndRow );
+
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ // Test for merged cells- when moving after delete
+
+ if (bClipOver && !bCut)
+ if (rDoc.HasAttrib( nDestCol,nDestRow,nDestTab, nUndoEndCol,nUndoEndRow,nDestEndTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ { // "Merge of already merged cells not possible"
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_MOVEBLOCKTO_0);
+ return false;
+ }
+
+ // Are there borders in the cells? (for painting)
+
+ sal_uInt16 nSourceExt = 0;
+ rDocShell.UpdatePaintExt( nSourceExt, nStartCol,nStartRow,nStartTab, nEndCol,nEndRow,nEndTab );
+ sal_uInt16 nDestExt = 0;
+ rDocShell.UpdatePaintExt( nDestExt, nDestCol,nDestRow,nDestTab, nDestEndCol,nDestEndRow,nDestEndTab );
+
+ // do it
+
+ ScDocumentUniquePtr pUndoDoc;
+
+ if (bRecord)
+ {
+ bool bWholeCols = ( nStartRow == 0 && nEndRow == rDoc.MaxRow() );
+ bool bWholeRows = ( nStartCol == 0 && nEndCol == rDoc.MaxCol() );
+ InsertDeleteFlags nUndoFlags = (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS;
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab, bWholeCols, bWholeRows );
+
+ if (bCut)
+ {
+ rDoc.CopyToDocument( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab,
+ nUndoFlags, false, *pUndoDoc );
+ }
+
+ if ( nDestTab != nStartTab )
+ pUndoDoc->AddUndoTab( nDestTab, nDestEndTab, bWholeCols, bWholeRows );
+ rDoc.CopyToDocument( nDestCol, nDestRow, nDestTab,
+ nDestEndCol, nDestEndRow, nDestEndTab,
+ nUndoFlags, false, *pUndoDoc );
+ rDoc.BeginDrawUndo();
+ }
+
+ bool bSourceHeight = false; // adjust heights?
+ if (bCut)
+ {
+ ScMarkData aDelMark(rDoc.GetSheetLimits()); // only for tables
+ for (nTab=nStartTab; nTab<=nEndTab; nTab++)
+ {
+ rDoc.DeleteAreaTab( nStartCol,nStartRow, nOldEndCol,nOldEndRow, nTab, InsertDeleteFlags::ALL );
+ aDelMark.SelectTable( nTab, true );
+ }
+ rDoc.DeleteObjectsInArea( nStartCol,nStartRow, nOldEndCol,nOldEndRow, aDelMark );
+
+ // Test for merged cells
+
+ if (bClipOver)
+ if (rDoc.HasAttrib( nDestCol,nDestRow,nDestTab,
+ nUndoEndCol,nUndoEndRow,nDestEndTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ {
+ rDoc.CopyFromClip( rSource, aSourceMark, InsertDeleteFlags::ALL, nullptr, pClipDoc.get() );
+ for (nTab=nStartTab; nTab<=nEndTab; nTab++)
+ {
+ SCCOL nTmpEndCol = nEndCol;
+ SCROW nTmpEndRow = nEndRow;
+ rDoc.ExtendMerge( nStartCol, nStartRow, nTmpEndCol, nTmpEndRow, nTab, true );
+ }
+
+ // Report error only after restoring content
+ if (!bApi) // "Merge of already merged cells not possible"
+ rDocShell.ErrorMessage(STR_MSSG_MOVEBLOCKTO_0);
+
+ return false;
+ }
+
+ bSourceHeight = AdjustRowHeight( rSource, false, bApi );
+ }
+
+ ScRange aPasteDest( nDestCol, nDestRow, nDestTab, nDestEndCol, nDestEndRow, nDestEndTab );
+
+ ScMarkData aDestMark(rDoc.GetSheetLimits());
+ for (nTab=nDestTab; nTab<=nDestEndTab; nTab++)
+ aDestMark.SelectTable( nTab, true ); // select destination
+ aDestMark.SetMarkArea( aPasteDest );
+
+ /* Do not copy drawing objects here. While pasting, the
+ function ScDocument::UpdateReference() is called which calls
+ ScDrawLayer::MoveCells() which may move away inserted objects to wrong
+ positions (e.g. if source and destination range overlaps).*/
+
+ rDoc.CopyFromClip(
+ aPasteDest, aDestMark, InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS,
+ pUndoDoc.get(), pClipDoc.get(), true, false, bIncludeFiltered);
+
+ // skipped rows and merged cells don't mix
+ if ( !bIncludeFiltered && pClipDoc->HasClipFilteredRows() )
+ UnmergeCells( aPasteDest, false, nullptr );
+
+ bool bDestHeight = AdjustRowHeight(
+ ScRange( 0,nDestRow,nDestTab, rDoc.MaxCol(),nDestEndRow,nDestEndTab ),
+ false, bApi );
+
+ /* Paste drawing objects after adjusting formula references
+ and row heights. There are no cell notes or drawing objects, if the
+ clipdoc does not contain a drawing layer.*/
+ if ( pClipDoc->GetDrawLayer() )
+ rDoc.CopyFromClip( aPasteDest, aDestMark, InsertDeleteFlags::OBJECTS,
+ nullptr, pClipDoc.get(), true, false, bIncludeFiltered );
+
+ if (bRecord)
+ {
+ ScRange aUndoRange(nStartCol, nStartRow, nStartTab, nOldEndCol, nOldEndRow, nEndTab);
+ ScAddress aDestPos(nDestCol, nDestRow, nDestTab);
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDragDrop>(
+ &rDocShell, aUndoRange, aDestPos, bCut, std::move(pUndoDoc), bScenariosAdded));
+ }
+
+ SCCOL nDestPaintEndCol = nDestEndCol;
+ SCROW nDestPaintEndRow = nDestEndRow;
+ for (nTab=nDestTab; nTab<=nDestEndTab; nTab++)
+ {
+ SCCOL nTmpEndCol = nDestEndCol;
+ SCROW nTmpEndRow = nDestEndRow;
+ rDoc.ExtendMerge( nDestCol, nDestRow, nTmpEndCol, nTmpEndRow, nTab, true );
+ if (nTmpEndCol > nDestPaintEndCol) nDestPaintEndCol = nTmpEndCol;
+ if (nTmpEndRow > nDestPaintEndRow) nDestPaintEndRow = nTmpEndRow;
+ }
+
+ if (bCut)
+ for (nTab=nStartTab; nTab<=nEndTab; nTab++)
+ rDoc.RefreshAutoFilter( nStartCol, nStartRow, nEndCol, nEndRow, nTab );
+
+ if (bPaint)
+ {
+ // destination range:
+
+ SCCOL nPaintStartX = nDestCol;
+ SCROW nPaintStartY = nDestRow;
+ SCCOL nPaintEndX = nDestPaintEndCol;
+ SCROW nPaintEndY = nDestPaintEndRow;
+ PaintPartFlags nFlags = PaintPartFlags::Grid;
+
+ if ( nStartRow==0 && nEndRow==rDoc.MaxRow() ) // copy widths too?
+ {
+ nPaintEndX = rDoc.MaxCol();
+ nPaintStartY = 0;
+ nPaintEndY = rDoc.MaxRow();
+ nFlags |= PaintPartFlags::Top;
+ }
+ if ( bDestHeight || ( nStartCol == 0 && nEndCol == rDoc.MaxCol() ) )
+ {
+ nPaintEndY = rDoc.MaxRow();
+ nPaintStartX = 0;
+ nPaintEndX = rDoc.MaxCol();
+ nFlags |= PaintPartFlags::Left;
+ }
+ if ( bScenariosAdded )
+ {
+ nPaintStartX = 0;
+ nPaintStartY = 0;
+ nPaintEndX = rDoc.MaxCol();
+ nPaintEndY = rDoc.MaxRow();
+ }
+
+ rDocShell.PostPaint( nPaintStartX,nPaintStartY,nDestTab,
+ nPaintEndX,nPaintEndY,nDestEndTab, nFlags, nSourceExt | nDestExt );
+
+ if ( bCut )
+ {
+ // source range:
+
+ nPaintStartX = nStartCol;
+ nPaintStartY = nStartRow;
+ nPaintEndX = nEndCol;
+ nPaintEndY = nEndRow;
+ nFlags = PaintPartFlags::Grid;
+
+ if ( bSourceHeight )
+ {
+ nPaintEndY = rDoc.MaxRow();
+ nPaintStartX = 0;
+ nPaintEndX = rDoc.MaxCol();
+ nFlags |= PaintPartFlags::Left;
+ }
+ if ( bScenariosAdded )
+ {
+ nPaintStartX = 0;
+ nPaintStartY = 0;
+ nPaintEndX = rDoc.MaxCol();
+ nPaintEndY = rDoc.MaxRow();
+ }
+
+ rDocShell.PostPaint( nPaintStartX,nPaintStartY,nStartTab,
+ nPaintEndX,nPaintEndY,nEndTab, nFlags, nSourceExt );
+ }
+ }
+
+ aModificator.SetDocumentModified();
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+
+ return true;
+}
+
+static uno::Reference< uno::XInterface > GetDocModuleObject( const SfxObjectShell& rDocSh, const OUString& sCodeName )
+{
+ uno::Reference< lang::XMultiServiceFactory> xSF(rDocSh.GetModel(), uno::UNO_QUERY);
+ uno::Reference< container::XNameAccess > xVBACodeNamedObjectAccess;
+ uno::Reference< uno::XInterface > xDocModuleApiObject;
+ if ( xSF.is() )
+ {
+ xVBACodeNamedObjectAccess.set( xSF->createInstance("ooo.vba.VBAObjectModuleObjectProvider"), uno::UNO_QUERY );
+ xDocModuleApiObject.set( xVBACodeNamedObjectAccess->getByName( sCodeName ), uno::UNO_QUERY );
+ }
+ return xDocModuleApiObject;
+
+}
+
+static script::ModuleInfo lcl_InitModuleInfo( const SfxObjectShell& rDocSh, const OUString& sModule )
+{
+ script::ModuleInfo sModuleInfo;
+ sModuleInfo.ModuleType = script::ModuleType::DOCUMENT;
+ sModuleInfo.ModuleObject = GetDocModuleObject( rDocSh, sModule );
+ return sModuleInfo;
+}
+
+void VBA_InsertModule( ScDocument& rDoc, SCTAB nTab, const OUString& sSource )
+{
+ SfxObjectShell& rDocSh = *rDoc.GetDocumentShell();
+ uno::Reference< script::XLibraryContainer > xLibContainer = rDocSh.GetBasicContainer();
+ OSL_ENSURE( xLibContainer.is(), "No BasicContainer!" );
+
+ uno::Reference< container::XNameContainer > xLib;
+ if( xLibContainer.is() )
+ {
+ OUString aLibName( "Standard" );
+#if HAVE_FEATURE_SCRIPTING
+ if ( rDocSh.GetBasicManager() && !rDocSh.GetBasicManager()->GetName().isEmpty() )
+ {
+ aLibName = rDocSh.GetBasicManager()->GetName();
+ }
+#endif
+ uno::Any aLibAny = xLibContainer->getByName( aLibName );
+ aLibAny >>= xLib;
+ }
+ if( !xLib.is() )
+ return;
+
+ // if the Module with codename exists then find a new name
+ sal_Int32 nNum = 1;
+ OUString genModuleName = "Sheet1";
+ while( xLib->hasByName( genModuleName ) )
+ genModuleName = "Sheet" + OUString::number( ++nNum );
+
+ uno::Any aSourceAny;
+ OUString sTmpSource = sSource;
+ if ( sTmpSource.isEmpty() )
+ sTmpSource = "Rem Attribute VBA_ModuleType=VBADocumentModule\nOption VBASupport 1\n";
+ aSourceAny <<= sTmpSource;
+ uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, uno::UNO_QUERY );
+ if ( xVBAModuleInfo.is() )
+ {
+ rDoc.SetCodeName( nTab, genModuleName );
+ script::ModuleInfo sModuleInfo = lcl_InitModuleInfo( rDocSh, genModuleName );
+ xVBAModuleInfo->insertModuleInfo( genModuleName, sModuleInfo );
+ xLib->insertByName( genModuleName, aSourceAny );
+ }
+}
+
+void VBA_DeleteModule( ScDocShell& rDocSh, const OUString& sModuleName )
+{
+ uno::Reference< script::XLibraryContainer > xLibContainer = rDocSh.GetBasicContainer();
+ OSL_ENSURE( xLibContainer.is(), "No BasicContainer!" );
+
+ uno::Reference< container::XNameContainer > xLib;
+ if( xLibContainer.is() )
+ {
+ OUString aLibName( "Standard" );
+#if HAVE_FEATURE_SCRIPTING
+ if ( rDocSh.GetBasicManager() && !rDocSh.GetBasicManager()->GetName().isEmpty() )
+ {
+ aLibName = rDocSh.GetBasicManager()->GetName();
+ }
+#endif
+ uno::Any aLibAny = xLibContainer->getByName( aLibName );
+ aLibAny >>= xLib;
+ }
+ if( xLib.is() )
+ {
+ uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, uno::UNO_QUERY );
+ if( xLib->hasByName( sModuleName ) )
+ xLib->removeByName( sModuleName );
+ if ( xVBAModuleInfo.is() && xVBAModuleInfo->hasModuleInfo(sModuleName) )
+ xVBAModuleInfo->removeModuleInfo( sModuleName );
+
+ }
+}
+
+bool ScDocFunc::InsertTable( SCTAB nTab, const OUString& rName, bool bRecord, bool bApi )
+{
+ bool bSuccess = false;
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ // Strange loop, also basic is loaded too early ( InsertTable )
+ // is called via the xml import for sheets in described in ODF
+ bool bInsertDocModule = false;
+
+ if( !rDocShell.GetDocument().IsImportingXML() )
+ {
+ bInsertDocModule = rDoc.IsInVBAMode();
+ }
+ if ( bInsertDocModule || ( bRecord && !rDoc.IsUndoEnabled() ) )
+ bRecord = false;
+
+ if (bRecord)
+ rDoc.BeginDrawUndo(); // InsertTab generates SdrUndoNewPage
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ bool bAppend = ( nTab >= nTabCount );
+ if ( bAppend )
+ nTab = nTabCount; // important for Undo
+
+ if (rDoc.InsertTab( nTab, rName ))
+ {
+ if (bRecord)
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoInsertTab>( &rDocShell, nTab, bAppend, rName));
+ // Update views:
+ // Only insert vba modules if vba mode ( and not currently importing XML )
+ if( bInsertDocModule )
+ {
+ VBA_InsertModule( rDoc, nTab, OUString() );
+ }
+ rDocShell.Broadcast( ScTablesHint( SC_TAB_INSERTED, nTab ) );
+
+ rDocShell.PostPaintExtras();
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ bSuccess = true;
+ }
+ else if (!bApi)
+ rDocShell.ErrorMessage(STR_TABINSERT_ERROR);
+
+ return bSuccess;
+}
+
+bool ScDocFunc::DeleteTable( SCTAB nTab, bool bRecord )
+{
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bSuccess = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bVbaEnabled = rDoc.IsInVBAMode();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ if ( bVbaEnabled )
+ bRecord = false;
+ bool bWasLinked = rDoc.IsLinked(nTab);
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScRefUndoData> pUndoData;
+ if (bRecord)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ SCTAB nCount = rDoc.GetTableCount();
+
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true ); // only nTab with Flags
+ pUndoDoc->AddUndoTab( 0, nCount-1 ); // all sheets for references
+
+ rDoc.CopyToDocument(0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, InsertDeleteFlags::ALL,false, *pUndoDoc );
+ OUString aOldName;
+ rDoc.GetName( nTab, aOldName );
+ pUndoDoc->RenameTab( nTab, aOldName );
+ if (bWasLinked)
+ pUndoDoc->SetLink( nTab, rDoc.GetLinkMode(nTab), rDoc.GetLinkDoc(nTab),
+ rDoc.GetLinkFlt(nTab), rDoc.GetLinkOpt(nTab),
+ rDoc.GetLinkTab(nTab),
+ rDoc.GetLinkRefreshDelay(nTab) );
+
+ if ( rDoc.IsScenario(nTab) )
+ {
+ pUndoDoc->SetScenario( nTab, true );
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nScenFlags;
+ rDoc.GetScenarioData( nTab, aComment, aColor, nScenFlags );
+ pUndoDoc->SetScenarioData( nTab, aComment, aColor, nScenFlags );
+ bool bActive = rDoc.IsActiveScenario( nTab );
+ pUndoDoc->SetActiveScenario( nTab, bActive );
+ }
+ pUndoDoc->SetVisible( nTab, rDoc.IsVisible( nTab ) );
+ pUndoDoc->SetTabBgColor( nTab, rDoc.GetTabBgColor(nTab) );
+ auto pSheetEvents = rDoc.GetSheetEvents( nTab );
+ pUndoDoc->SetSheetEvents( nTab, std::unique_ptr<ScSheetEvents>(pSheetEvents ? new ScSheetEvents(*pSheetEvents) : nullptr) );
+
+ // Drawing-Layer has to take care of its own undo!!!
+ rDoc.BeginDrawUndo(); // DeleteTab generates SdrUndoDelPage
+
+ pUndoData.reset(new ScRefUndoData( &rDoc ));
+ }
+
+ if (rDoc.DeleteTab(nTab))
+ {
+ if (bRecord)
+ {
+ vector<SCTAB> theTabs;
+ theTabs.push_back(nTab);
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDeleteTab>( &rDocShell, theTabs, std::move(pUndoDoc), std::move(pUndoData) ));
+ }
+ // Update views:
+ if( bVbaEnabled )
+ {
+ OUString sCodeName;
+ if( rDoc.GetCodeName( nTab, sCodeName ) )
+ {
+ VBA_DeleteModule( rDocShell, sCodeName );
+ }
+ }
+ rDocShell.Broadcast( ScTablesHint( SC_TAB_DELETED, nTab ) );
+
+ if (bWasLinked)
+ {
+ rDocShell.UpdateLinks(); // update Link-Manager
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate(SID_LINKS);
+ }
+
+ rDocShell.PostPaintExtras();
+ aModificator.SetDocumentModified();
+
+ SfxApplication* pSfxApp = SfxGetpApp(); // Navigator
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+
+ bSuccess = true;
+ }
+ return bSuccess;
+}
+
+void ScDocFunc::SetTableVisible( SCTAB nTab, bool bVisible, bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+ if ( rDoc.IsVisible( nTab ) == bVisible )
+ return; // nothing to do - ok
+
+ if ( !rDoc.IsDocEditable() )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ if ( !bVisible && !rDoc.IsImportingXML() ) // #i57869# allow hiding in any order for loading
+ {
+ // do not disable all sheets
+
+ sal_uInt16 nVisCount = 0;
+ SCTAB nCount = rDoc.GetTableCount();
+ for (SCTAB i=0; i<nCount && nVisCount<2; i++)
+ if (rDoc.IsVisible(i))
+ ++nVisCount;
+
+ if (nVisCount <= 1)
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR); //! separate error message?
+ return;
+ }
+ }
+
+ rDoc.SetVisible( nTab, bVisible );
+ if (bUndo)
+ {
+ std::vector<SCTAB> undoTabs { nTab };
+ rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideTab>( &rDocShell, std::move(undoTabs), bVisible ) );
+ }
+
+ // update views
+ if (!bVisible)
+ rDocShell.Broadcast( ScTablesHint( SC_TAB_HIDDEN, nTab ) );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ rDocShell.PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Extras);
+ aModificator.SetDocumentModified();
+}
+
+bool ScDocFunc::SetLayoutRTL( SCTAB nTab, bool bRTL )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+ if ( rDoc.IsLayoutRTL( nTab ) == bRTL )
+ return true; // nothing to do - ok
+
+ //! protection (sheet or document?)
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ rDoc.SetLayoutRTL( nTab, bRTL, ScObjectHandling::MirrorRTLMode);
+
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoLayoutRTL>( &rDocShell, nTab, bRTL ) );
+ }
+
+ rDocShell.PostPaint( 0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::All );
+ aModificator.SetDocumentModified();
+
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_TAB_RTL );
+ pBindings->Invalidate( SID_ATTR_SIZE );
+ }
+
+ return true;
+}
+
+bool ScDocFunc::RenameTable( SCTAB nTab, const OUString& rName, bool bRecord, bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ if ( !rDoc.IsDocEditable() )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR);
+ return false;
+ }
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bSuccess = false;
+ OUString sOldName;
+ rDoc.GetName(nTab, sOldName);
+ if (rDoc.RenameTab( nTab, rName ))
+ {
+ if (bRecord)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRenameTab>( &rDocShell, nTab, sOldName, rName));
+ }
+ rDocShell.PostPaintExtras();
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+
+ bSuccess = true;
+ }
+ return bSuccess;
+}
+
+bool ScDocFunc::SetTabBgColor( SCTAB nTab, const Color& rColor, bool bRecord, bool bApi )
+{
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ if ( !rDoc.IsDocEditable() || rDoc.IsTabProtected(nTab) )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Check to see what this string is...
+ return false;
+ }
+
+ Color aOldTabBgColor = rDoc.GetTabBgColor(nTab);
+
+ bool bSuccess = false;
+ rDoc.SetTabBgColor(nTab, rColor);
+ if ( rDoc.GetTabBgColor(nTab) == rColor)
+ bSuccess = true;
+ if (bSuccess)
+ {
+ if (bRecord)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoTabColor>( &rDocShell, nTab, aOldTabBgColor, rColor));
+ }
+ rDocShell.PostPaintExtras();
+ ScDocShellModificator aModificator( rDocShell );
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+
+ bSuccess = true;
+ }
+ return bSuccess;
+}
+
+bool ScDocFunc::SetTabBgColor(
+ ScUndoTabColorInfo::List& rUndoTabColorList, bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ if ( !rDoc.IsDocEditable() )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Get a better String Error...
+ return false;
+ }
+
+ sal_uInt16 nTab;
+ Color aNewTabBgColor;
+ bool bSuccess = true;
+ size_t nTabProtectCount = 0;
+ size_t nTabListCount = rUndoTabColorList.size();
+ for ( size_t i = 0; i < nTabListCount; ++i )
+ {
+ ScUndoTabColorInfo& rInfo = rUndoTabColorList[i];
+ nTab = rInfo.mnTabId;
+ if ( !rDoc.IsTabProtected(nTab) )
+ {
+ aNewTabBgColor = rInfo.maNewTabBgColor;
+ rInfo.maOldTabBgColor = rDoc.GetTabBgColor(nTab);
+ rDoc.SetTabBgColor(nTab, aNewTabBgColor);
+ if ( rDoc.GetTabBgColor(nTab) != aNewTabBgColor)
+ {
+ bSuccess = false;
+ break;
+ }
+ }
+ else
+ {
+ nTabProtectCount++;
+ }
+ }
+
+ if ( nTabProtectCount == nTabListCount )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Get a better String Error...
+ return false;
+ }
+
+ if (bSuccess)
+ {
+ if (bRecord)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoTabColor>( &rDocShell, std::vector(rUndoTabColorList)));
+ }
+ rDocShell.PostPaintExtras();
+ ScDocShellModificator aModificator( rDocShell );
+ aModificator.SetDocumentModified();
+ }
+ return bSuccess;
+}
+
+//! SetWidthOrHeight - duplicated in ViewFunc !!!!!!
+//! Problems:
+//! - Optimal height of text cells is different for a printer and a screen
+//! - Optimal width needs a selection in order to take only selected cells into account
+
+static sal_uInt16 lcl_GetOptimalColWidth( ScDocShell& rDocShell, SCCOL nCol, SCTAB nTab )
+{
+ ScSizeDeviceProvider aProv(&rDocShell);
+ OutputDevice* pDev = aProv.GetDevice(); // has pixel MapMode
+ double nPPTX = aProv.GetPPTX();
+ double nPPTY = aProv.GetPPTY();
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ Fraction aOne(1,1);
+ sal_uInt16 nTwips = rDoc.GetOptimalColWidth( nCol, nTab, pDev, nPPTX, nPPTY, aOne, aOne,
+ false/*bFormula*/ );
+
+ return nTwips;
+}
+
+bool ScDocFunc::SetWidthOrHeight(
+ bool bWidth, const std::vector<sc::ColRowSpan>& rRanges, SCTAB nTab,
+ ScSizeMode eMode, sal_uInt16 nSizeTwips, bool bRecord, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ if (rRanges.empty())
+ return true;
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if ( bRecord && !rDoc.IsUndoEnabled() )
+ bRecord = false;
+
+ // import into read-only document is possible
+ if ( !rDoc.IsChangeReadOnlyEnabled() && !rDocShell.IsEditable() )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR); //! separate error message?
+ return false;
+ }
+
+ SCCOLROW nStart = rRanges[0].mnStart;
+ SCCOLROW nEnd = rRanges[0].mnEnd;
+
+ if ( eMode == SC_SIZE_OPTIMAL )
+ {
+ //! Option "Show formulas" - but where to get them from?
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScOutlineTable> pUndoTab;
+ std::vector<sc::ColRowSpan> aUndoRanges;
+
+ if ( bRecord )
+ {
+ rDoc.BeginDrawUndo(); // Drawing Updates
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ if (bWidth)
+ {
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true );
+ rDoc.CopyToDocument( static_cast<SCCOL>(nStart), 0, nTab, static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ }
+ else
+ {
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
+ rDoc.CopyToDocument( 0, static_cast<SCROW>(nStart), nTab, rDoc.MaxCol(), static_cast<SCROW>(nEnd), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ }
+
+ aUndoRanges = rRanges;
+
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (pTable)
+ pUndoTab.reset(new ScOutlineTable( *pTable ));
+ }
+
+ bool bShow = nSizeTwips > 0 || eMode != SC_SIZE_DIRECT;
+ bool bOutline = false;
+
+ for (const sc::ColRowSpan& rRange : rRanges)
+ {
+ SCCOLROW nStartNo = rRange.mnStart;
+ SCCOLROW nEndNo = rRange.mnEnd;
+
+ if ( !bWidth ) // deal with heights always in blocks
+ {
+ if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
+ {
+ bool bAll = ( eMode==SC_SIZE_OPTIMAL );
+ if (!bAll)
+ {
+ // delete for all that have CRFlags::ManualSize enabled
+ // then SetOptimalHeight with bShrink = FALSE
+ for (SCROW nRow=nStartNo; nRow<=nEndNo; nRow++)
+ {
+ CRFlags nOld = rDoc.GetRowFlags(nRow,nTab);
+ SCROW nLastRow = -1;
+ bool bHidden = rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow);
+ if ( !bHidden && ( nOld & CRFlags::ManualSize ) )
+ rDoc.SetRowFlags( nRow, nTab, nOld & ~CRFlags::ManualSize );
+ }
+ }
+
+ ScSizeDeviceProvider aProv( &rDocShell );
+ Fraction aOne(1,1);
+ sc::RowHeightContext aCxt(rDoc.MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), aOne, aOne, aProv.GetDevice());
+ aCxt.setForceAutoSize(bAll);
+ rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab, bApi);
+
+ if (bAll)
+ rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
+
+ // Manual flag will be set already in SetOptimalHeight if bAll=true
+ // (it is on when Extra-Height, otherwise off).
+ }
+ else if ( eMode==SC_SIZE_DIRECT || eMode==SC_SIZE_ORIGINAL )
+ {
+ if (nSizeTwips)
+ {
+ rDoc.SetRowHeightRange( nStartNo, nEndNo, nTab, nSizeTwips );
+ rDoc.SetManualHeight( nStartNo, nEndNo, nTab, true ); // height was set manually
+ }
+ if ( eMode != SC_SIZE_ORIGINAL )
+ rDoc.ShowRows( nStartNo, nEndNo, nTab, nSizeTwips != 0 );
+ }
+ else if ( eMode==SC_SIZE_SHOW )
+ {
+ rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
+ }
+ }
+ else // Column widths
+ {
+ for (SCCOL nCol=static_cast<SCCOL>(nStartNo); nCol<=static_cast<SCCOL>(nEndNo); nCol++)
+ {
+ if ( eMode != SC_SIZE_VISOPT || !rDoc.ColHidden(nCol, nTab) )
+ {
+ sal_uInt16 nThisSize = nSizeTwips;
+
+ if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
+ nThisSize = nSizeTwips +
+ lcl_GetOptimalColWidth( rDocShell, nCol, nTab );
+ if ( nThisSize )
+ rDoc.SetColWidth( nCol, nTab, nThisSize );
+
+ if ( eMode != SC_SIZE_ORIGINAL )
+ rDoc.ShowCol( nCol, nTab, bShow );
+ }
+ }
+ }
+
+ // adjust outlines
+
+ if ( eMode != SC_SIZE_ORIGINAL )
+ {
+ if (bWidth)
+ bOutline = bOutline || rDoc.UpdateOutlineCol(
+ static_cast<SCCOL>(nStartNo),
+ static_cast<SCCOL>(nEndNo), nTab, bShow );
+ else
+ bOutline = bOutline || rDoc.UpdateOutlineRow(
+ static_cast<SCROW>(nStartNo),
+ static_cast<SCROW>(nEndNo), nTab, bShow );
+ }
+ }
+ rDoc.SetDrawPageSize(nTab);
+
+ if (!bOutline)
+ pUndoTab.reset();
+
+ if (bRecord)
+ {
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SelectOneTable( nTab );
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoWidthOrHeight>(
+ &rDocShell, aMark, nStart, nTab, nEnd, nTab, std::move(pUndoDoc),
+ std::move(aUndoRanges), std::move(pUndoTab), eMode, nSizeTwips, bWidth));
+ }
+
+ rDoc.UpdatePageBreaks( nTab );
+
+ ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
+ if (pViewSh)
+ pViewSh->OnLOKSetWidthOrHeight(nStart, bWidth);
+
+ rDocShell.PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab,PaintPartFlags::All);
+ aModificator.SetDocumentModified();
+
+ return false;
+}
+
+bool ScDocFunc::InsertPageBreak( bool bColumn, const ScAddress& rPos,
+ bool bRecord, bool bSetModified )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ SCTAB nTab = rPos.Tab();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+
+ SCCOLROW nPos = bColumn ? static_cast<SCCOLROW>(rPos.Col()) :
+ static_cast<SCCOLROW>(rPos.Row());
+ if (nPos == 0)
+ return false; // first column / row
+
+ ScBreakType nBreak = bColumn ?
+ rDoc.HasColBreak(static_cast<SCCOL>(nPos), nTab) :
+ rDoc.HasRowBreak(static_cast<SCROW>(nPos), nTab);
+ if (nBreak & ScBreakType::Manual)
+ return true;
+
+ if (bRecord)
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoPageBreak>( &rDocShell, rPos.Col(), rPos.Row(), nTab, bColumn, true ) );
+
+ if (bColumn)
+ rDoc.SetColBreak(static_cast<SCCOL>(nPos), nTab, false, true);
+ else
+ rDoc.SetRowBreak(static_cast<SCROW>(nPos), nTab, false, true);
+
+ rDoc.InvalidatePageBreaks(nTab);
+ rDoc.UpdatePageBreaks( nTab );
+
+ rDoc.SetStreamValid(nTab, false);
+
+ if (bColumn)
+ {
+ rDocShell.PostPaint( static_cast<SCCOL>(nPos)-1, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_INS_COLBRK );
+ pBindings->Invalidate( FID_DEL_COLBRK );
+ }
+ }
+ else
+ {
+ rDocShell.PostPaint( 0, static_cast<SCROW>(nPos)-1, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_INS_ROWBRK );
+ pBindings->Invalidate( FID_DEL_ROWBRK );
+ }
+ }
+ if (pBindings)
+ pBindings->Invalidate( FID_DEL_MANUALBREAKS );
+
+ if (bSetModified)
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+bool ScDocFunc::RemovePageBreak( bool bColumn, const ScAddress& rPos,
+ bool bRecord, bool bSetModified )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ SCTAB nTab = rPos.Tab();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+
+ SCCOLROW nPos = bColumn ? static_cast<SCCOLROW>(rPos.Col()) :
+ static_cast<SCCOLROW>(rPos.Row());
+
+ ScBreakType nBreak;
+ if (bColumn)
+ nBreak = rDoc.HasColBreak(static_cast<SCCOL>(nPos), nTab);
+ else
+ nBreak = rDoc.HasRowBreak(static_cast<SCROW>(nPos), nTab);
+ if (!(nBreak & ScBreakType::Manual))
+ // There is no manual break.
+ return false;
+
+ if (bRecord)
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoPageBreak>( &rDocShell, rPos.Col(), rPos.Row(), nTab, bColumn, false ) );
+
+ if (bColumn)
+ rDoc.RemoveColBreak(static_cast<SCCOL>(nPos), nTab, false, true);
+ else
+ rDoc.RemoveRowBreak(static_cast<SCROW>(nPos), nTab, false, true);
+
+ rDoc.UpdatePageBreaks( nTab );
+
+ rDoc.SetStreamValid(nTab, false);
+
+ if (bColumn)
+ {
+ rDocShell.PostPaint( static_cast<SCCOL>(nPos)-1, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_INS_COLBRK );
+ pBindings->Invalidate( FID_DEL_COLBRK );
+ }
+ }
+ else
+ {
+ rDocShell.PostPaint( 0, nPos-1, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_INS_ROWBRK );
+ pBindings->Invalidate( FID_DEL_ROWBRK );
+ }
+ }
+ if (pBindings)
+ pBindings->Invalidate( FID_DEL_MANUALBREAKS );
+
+ if (bSetModified)
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+void ScDocFunc::ProtectSheet( SCTAB nTab, const ScTableProtection& rProtect )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ std::unique_ptr<ScTableProtection> p;
+ if (!rProtect.isProtected() && rDoc.IsUndoEnabled())
+ {
+ // In case of unprotecting, use a copy of passed ScTableProtection object for undo
+ p = std::make_unique<ScTableProtection>(rProtect);
+ }
+ rDoc.SetTabProtection(nTab, &rProtect);
+ if (rDoc.IsUndoEnabled())
+ {
+ if (!p)
+ {
+ // For protection case, use a copy of resulting ScTableProtection for undo
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ p = std::make_unique<ScTableProtection>(*pProtect);
+ }
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoTabProtect>(&rDocShell, nTab, std::move(p)));
+ // ownership of unique_ptr now transferred to ScUndoTabProtect.
+ }
+ for (SfxViewFrame* fr = SfxViewFrame::GetFirst(&rDocShell); fr;
+ fr = SfxViewFrame::GetNext(*fr, &rDocShell))
+ if (ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(fr->GetViewShell()))
+ pTabViewShell->SetTabProtectionSymbol(nTab, rProtect.isProtected());
+ rDocShell.PostPaintGridAll();
+ ScDocShellModificator aModificator(rDocShell);
+ aModificator.SetDocumentModified();
+}
+
+void ScDocFunc::ProtectDocument(const ScDocProtection& rProtect)
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ std::unique_ptr<ScDocProtection> p;
+ if (!rProtect.isProtected() && rDoc.IsUndoEnabled())
+ {
+ // In case of unprotecting, use a copy of passed ScTableProtection object for undo
+ p = std::make_unique<ScDocProtection>(rProtect);
+ }
+ rDoc.SetDocProtection(&rProtect);
+ if (rDoc.IsUndoEnabled())
+ {
+ if (!p)
+ {
+ // For protection case, use a copy of resulting ScTableProtection for undo
+ ScDocProtection* pProtect = rDoc.GetDocProtection();
+ p = std::make_unique<ScDocProtection>(*pProtect);
+ }
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDocProtect>(&rDocShell, std::move(p)));
+ // ownership of unique_ptr now transferred to ScUndoTabProtect.
+ }
+
+ rDocShell.PostPaintGridAll();
+ ScDocShellModificator aModificator(rDocShell);
+ aModificator.SetDocumentModified();
+}
+
+bool ScDocFunc::Protect( SCTAB nTab, const OUString& rPassword )
+{
+ if (nTab == TABLEID_DOC)
+ {
+ // document protection
+ ScDocProtection aProtection;
+ aProtection.setProtected(true);
+ aProtection.setPassword(rPassword);
+ ProtectDocument(aProtection);
+
+ }
+ else
+ {
+ // sheet protection
+
+ const ScTableProtection* pOldProtection = rDocShell.GetDocument().GetTabProtection(nTab);
+ ::std::unique_ptr<ScTableProtection> pNewProtection(pOldProtection ? new ScTableProtection(*pOldProtection) : new ScTableProtection());
+ pNewProtection->setProtected(true);
+ pNewProtection->setPassword(rPassword);
+ ProtectSheet(nTab, *pNewProtection);
+ }
+ return true;
+}
+
+bool ScDocFunc::Unprotect( SCTAB nTab, const OUString& rPassword, bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (nTab == TABLEID_DOC)
+ {
+ // document protection
+
+ ScDocProtection* pDocProtect = rDoc.GetDocProtection();
+ if (!pDocProtect || !pDocProtect->isProtected())
+ // already unprotected (should not happen)!
+ return true;
+
+ if (!pDocProtect->verifyPassword(rPassword))
+ {
+ if (!bApi)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(SCSTR_WRONGPASSWORD)));
+ xInfoBox->run();
+ }
+ return false;
+ }
+
+ ScDocProtection aNewProtection(*pDocProtect);
+ aNewProtection.setProtected(false);
+ ProtectDocument(aNewProtection);
+
+ }
+ else
+ {
+ // sheet protection
+
+ const ScTableProtection* pTabProtect = rDoc.GetTabProtection(nTab);
+ if (!pTabProtect || !pTabProtect->isProtected())
+ // already unprotected (should not happen)!
+ return true;
+ if (!pTabProtect->verifyPassword(rPassword))
+ {
+ if (!bApi)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(SCSTR_WRONGPASSWORD)));
+ xInfoBox->run();
+ }
+ return false;
+ }
+
+ ScTableProtection aNewProtection(*pTabProtect);
+ aNewProtection.setProtected(false);
+ ProtectSheet(nTab, aNewProtection);
+ }
+
+ return true;
+}
+
+void ScDocFunc::ClearItems( const ScMarkData& rMark, const sal_uInt16* pWhich, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo (rDoc.IsUndoEnabled());
+ ScEditableTester aTester( rDoc, rMark );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ // #i12940# ClearItems is called (from setPropertyToDefault) directly with uno object's cached
+ // MarkData (GetMarkData), so rMark must be changed to multi selection for ClearSelectionItems
+ // here.
+
+ ScMarkData aMultiMark = rMark;
+ aMultiMark.SetMarking(false); // for MarkToMulti
+ aMultiMark.MarkToMulti();
+ const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea();
+
+ if (bUndo)
+ {
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCTAB nEndTab = aMarkRange.aEnd.Tab();
+
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab );
+ rDoc.CopyToDocument( aMarkRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &aMultiMark );
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoClearItems>( &rDocShell, aMultiMark, std::move(pUndoDoc), pWhich ) );
+ }
+
+ rDoc.ClearSelectionItems( pWhich, aMultiMark );
+
+ rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+ aModificator.SetDocumentModified();
+
+ //! Bindings-Invalidate etc.?
+}
+
+bool ScDocFunc::ChangeIndent( const ScMarkData& rMark, bool bIncrement, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+ ScEditableTester aTester( rDoc, rMark );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ const ScRange& aMarkRange = rMark.GetMultiMarkArea();
+
+ if (bUndo)
+ {
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCTAB nTabCount = rDoc.GetTableCount();
+
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab != nStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+
+ ScRange aCopyRange = aMarkRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &rMark );
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoIndent>( &rDocShell, rMark, std::move(pUndoDoc), bIncrement ) );
+ }
+
+ rDoc.ChangeSelectionIndent( bIncrement, rMark );
+
+ rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+ aModificator.SetDocumentModified();
+
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ {
+ pBindings->Invalidate( SID_ALIGNLEFT ); // ChangeIndent aligns left
+ pBindings->Invalidate( SID_ALIGNRIGHT );
+ pBindings->Invalidate( SID_ALIGNBLOCK );
+ pBindings->Invalidate( SID_ALIGNCENTERHOR );
+ pBindings->Invalidate( SID_ATTR_LRSPACE );
+ pBindings->Invalidate( SID_ATTR_PARA_ADJUST_LEFT );
+ pBindings->Invalidate( SID_ATTR_PARA_ADJUST_RIGHT );
+ pBindings->Invalidate( SID_ATTR_PARA_ADJUST_BLOCK );
+ pBindings->Invalidate( SID_ATTR_PARA_ADJUST_CENTER);
+ // pseudo slots for Format menu
+ pBindings->Invalidate( SID_ALIGN_ANY_HDEFAULT );
+ pBindings->Invalidate( SID_ALIGN_ANY_LEFT );
+ pBindings->Invalidate( SID_ALIGN_ANY_HCENTER );
+ pBindings->Invalidate( SID_ALIGN_ANY_RIGHT );
+ pBindings->Invalidate( SID_ALIGN_ANY_JUSTIFIED );
+ }
+
+ return true;
+}
+
+bool ScDocFunc::AutoFormat( const ScRange& rRange, const ScMarkData* pTabMark,
+ sal_uInt16 nFormatNo, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aMark.SelectTable( nTab, true );
+ }
+
+ ScAutoFormat* pAutoFormat = ScGlobal::GetOrCreateAutoFormat();
+ ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
+ if ( nFormatNo < pAutoFormat->size() && aTester.IsEditable() )
+ {
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ bool bSize = pAutoFormat->findByIndex(nFormatNo)->GetIncludeWidthHeight();
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ ScDocumentUniquePtr pUndoDoc;
+ if ( bRecord )
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab, bSize, bSize );
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab != nStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab, bSize, bSize );
+ }
+
+ ScRange aCopyRange = rRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aStart.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, false, *pUndoDoc, &aMark );
+ if (bSize)
+ {
+ rDoc.CopyToDocument( nStartCol,0,0, nEndCol,rDoc.MaxRow(),nTabCount-1,
+ InsertDeleteFlags::NONE, false, *pUndoDoc, &aMark );
+ rDoc.CopyToDocument( 0,nStartRow,0, rDoc.MaxCol(),nEndRow,nTabCount-1,
+ InsertDeleteFlags::NONE, false, *pUndoDoc, &aMark );
+ }
+ rDoc.BeginDrawUndo();
+ }
+
+ rDoc.AutoFormat( nStartCol, nStartRow, nEndCol, nEndRow, nFormatNo, aMark );
+
+ if (bSize)
+ {
+ std::vector<sc::ColRowSpan> aCols(1, sc::ColRowSpan(nStartCol,nEndCol));
+ std::vector<sc::ColRowSpan> aRows(1, sc::ColRowSpan(nStartRow,nEndRow));
+
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ SetWidthOrHeight(true, aCols, rTab, SC_SIZE_VISOPT, STD_EXTRA_WIDTH, false, true);
+ SetWidthOrHeight(false, aRows, rTab, SC_SIZE_VISOPT, 0, false, false);
+ rDocShell.PostPaint( 0,0,rTab, rDoc.MaxCol(),rDoc.MaxRow(),rTab,
+ PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top );
+ }
+ }
+ else
+ {
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ bool bAdj = AdjustRowHeight( ScRange(nStartCol, nStartRow, rTab,
+ nEndCol, nEndRow, rTab), false, bApi );
+ if (bAdj)
+ rDocShell.PostPaint( 0,nStartRow,rTab, rDoc.MaxCol(),rDoc.MaxRow(),rTab,
+ PaintPartFlags::Grid | PaintPartFlags::Left );
+ else
+ rDocShell.PostPaint( nStartCol, nStartRow, rTab,
+ nEndCol, nEndRow, rTab, PaintPartFlags::Grid );
+ }
+ }
+
+ if ( bRecord ) // only now is Draw-Undo available
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoFormat>( &rDocShell, rRange, std::move(pUndoDoc), aMark, bSize, nFormatNo ) );
+ }
+
+ aModificator.SetDocumentModified();
+ }
+ else if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+
+ return false;
+}
+
+bool ScDocFunc::EnterMatrix( const ScRange& rRange, const ScMarkData* pTabMark,
+ const ScTokenArray* pTokenArray, const OUString& rString, bool bApi, bool bEnglish,
+ const OUString& rFormulaNmsp, const formula::FormulaGrammar::Grammar eGrammar )
+{
+ if (ScViewData::SelectionFillDOOM( rRange ))
+ return false;
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bSuccess = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aMark.SelectTable( nTab, true );
+ }
+
+ ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
+ if ( aTester.IsEditable() )
+ {
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ ScDocumentUniquePtr pUndoDoc;
+
+ const bool bUndo(rDoc.IsUndoEnabled());
+ if (bUndo)
+ {
+ //! take selected sheets into account also when undoing
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab );
+ rDoc.CopyToDocument( rRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, *pUndoDoc );
+ }
+
+ // use TokenArray if given, string (and flags) otherwise
+ if ( pTokenArray )
+ {
+ rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
+ aMark, OUString(), pTokenArray, eGrammar);
+ }
+ else if ( rDoc.IsImportingXML() )
+ {
+ ScTokenArray aCode(rDoc);
+ aCode.AssignXMLString( rString,
+ ((eGrammar == formula::FormulaGrammar::GRAM_EXTERNAL) ? rFormulaNmsp : OUString()));
+ rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
+ aMark, OUString(), &aCode, eGrammar);
+ rDoc.IncXMLImportedFormulaCount( rString.getLength() );
+ }
+ else if (bEnglish)
+ {
+ ScCompiler aComp( rDoc, rRange.aStart, eGrammar);
+ std::unique_ptr<ScTokenArray> pCode = aComp.CompileString( rString );
+ rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
+ aMark, OUString(), pCode.get(), eGrammar);
+ }
+ else
+ rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
+ aMark, rString, nullptr, eGrammar);
+
+ if (bUndo)
+ {
+ //! take selected sheets into account also when undoing
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoEnterMatrix>( &rDocShell, rRange, std::move(pUndoDoc), rString ) );
+ }
+
+ // Err522 painting of DDE-Formulas will be intercepted during interpreting
+ rDocShell.PostPaint( nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab, PaintPartFlags::Grid );
+ aModificator.SetDocumentModified();
+
+ bSuccess = true;
+ }
+ else if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+
+ return bSuccess;
+}
+
+bool ScDocFunc::TabOp( const ScRange& rRange, const ScMarkData* pTabMark,
+ const ScTabOpParam& rParam, bool bRecord, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bSuccess = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aMark.SelectTable( nTab, true );
+ }
+
+ ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
+ if ( aTester.IsEditable() )
+ {
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+ rDoc.SetDirty( rRange, false );
+ if ( bRecord )
+ {
+ //! take selected sheets into account also when undoing
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab );
+ rDoc.CopyToDocument( rRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, *pUndoDoc );
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoTabOp>( &rDocShell,
+ nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab, std::move(pUndoDoc),
+ rParam.aRefFormulaCell,
+ rParam.aRefFormulaEnd,
+ rParam.aRefRowCell,
+ rParam.aRefColCell,
+ rParam.meMode) );
+ }
+ rDoc.InsertTableOp(rParam, nStartCol, nStartRow, nEndCol, nEndRow, aMark);
+ rDocShell.PostPaintGridAll();
+ aModificator.SetDocumentModified();
+ bSuccess = true;
+ }
+ else if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+
+ return bSuccess;
+}
+
+static ScDirection DirFromFillDir( FillDir eDir )
+{
+ if (eDir==FILL_TO_BOTTOM)
+ return DIR_BOTTOM;
+ else if (eDir==FILL_TO_RIGHT)
+ return DIR_RIGHT;
+ else if (eDir==FILL_TO_TOP)
+ return DIR_TOP;
+ else // if (eDir==FILL_TO_LEFT)
+ return DIR_LEFT;
+}
+
+namespace {
+
+/**
+ * Expand the fill range as necessary, to allow copying of adjacent cell(s)
+ * even when those cells are not in the original range.
+ */
+void adjustFillRangeForAdjacentCopy(const ScDocument &rDoc, ScRange& rRange, FillDir eDir)
+{
+ switch (eDir)
+ {
+ case FILL_TO_BOTTOM:
+ {
+ if (rRange.aStart.Row() == 0)
+ return;
+
+ if (rRange.aStart.Row() != rRange.aEnd.Row())
+ return;
+
+ // Include the above row.
+ ScAddress& s = rRange.aStart;
+ s.SetRow(s.Row()-1);
+ }
+ break;
+ case FILL_TO_TOP:
+ {
+ if (rRange.aStart.Row() == rDoc.MaxRow())
+ return;
+
+ if (rRange.aStart.Row() != rRange.aEnd.Row())
+ return;
+
+ // Include the row below.
+ ScAddress& e = rRange.aEnd;
+ e.SetRow(e.Row()+1);
+ }
+ break;
+ case FILL_TO_LEFT:
+ {
+ if (rRange.aStart.Col() == rDoc.MaxCol())
+ return;
+
+ if (rRange.aStart.Col() != rRange.aEnd.Col())
+ return;
+
+ // Include the column to the right.
+ ScAddress& e = rRange.aEnd;
+ e.SetCol(e.Col()+1);
+ }
+ break;
+ case FILL_TO_RIGHT:
+ {
+ if (rRange.aStart.Col() == 0)
+ return;
+
+ if (rRange.aStart.Col() != rRange.aEnd.Col())
+ return;
+
+ // Include the column to the left.
+ ScAddress& s = rRange.aStart;
+ s.SetCol(s.Col()-1);
+ }
+ break;
+ default:
+ ;
+ }
+}
+
+}
+
+bool ScDocFunc::FillSimple( const ScRange& rRange, const ScMarkData* pTabMark,
+ FillDir eDir, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bSuccess = false;
+ ScRange aRange = rRange;
+ adjustFillRangeForAdjacentCopy(rDoc, aRange, eDir);
+
+ SCCOL nStartCol = aRange.aStart.Col();
+ SCROW nStartRow = aRange.aStart.Row();
+ SCTAB nStartTab = aRange.aStart.Tab();
+ SCCOL nEndCol = aRange.aEnd.Col();
+ SCROW nEndRow = aRange.aEnd.Row();
+ SCTAB nEndTab = aRange.aEnd.Tab();
+
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aMark.SelectTable( nTab, true );
+ }
+
+ ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
+ if ( aTester.IsEditable() )
+ {
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ ScRange aSourceArea = aRange;
+ ScRange aDestArea = aRange;
+
+ SCCOLROW nCount = 0;
+ switch (eDir)
+ {
+ case FILL_TO_BOTTOM:
+ nCount = aSourceArea.aEnd.Row()-aSourceArea.aStart.Row();
+ aSourceArea.aEnd.SetRow( aSourceArea.aStart.Row() );
+ break;
+ case FILL_TO_RIGHT:
+ nCount = aSourceArea.aEnd.Col()-aSourceArea.aStart.Col();
+ aSourceArea.aEnd.SetCol( aSourceArea.aStart.Col() );
+ break;
+ case FILL_TO_TOP:
+ nCount = aSourceArea.aEnd.Row()-aSourceArea.aStart.Row();
+ aSourceArea.aStart.SetRow( aSourceArea.aEnd.Row() );
+ break;
+ case FILL_TO_LEFT:
+ nCount = aSourceArea.aEnd.Col()-aSourceArea.aStart.Col();
+ aSourceArea.aStart.SetCol( aSourceArea.aEnd.Col() );
+ break;
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ if ( bRecord )
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nDestStartTab = aDestArea.aStart.Tab();
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nDestStartTab, nDestStartTab );
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab != nDestStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+
+ ScRange aCopyRange = aDestArea;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark );
+ }
+
+ sal_uLong nProgCount;
+ if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP)
+ nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1;
+ else
+ nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1;
+ nProgCount *= nCount;
+ ScProgress aProgress( rDoc.GetDocumentShell(),
+ ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
+
+ rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(),
+ aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress,
+ aMark, nCount, eDir, FILL_SIMPLE );
+ AdjustRowHeight(aRange, true, bApi);
+
+ if ( bRecord ) // only now is Draw-Undo available
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark,
+ eDir, FILL_SIMPLE, FILL_DAY, MAXDOUBLE, 1.0, 1e307) );
+ }
+
+ rDocShell.PostPaintGridAll();
+ aModificator.SetDocumentModified();
+
+ bSuccess = true;
+ }
+ else if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+
+ return bSuccess;
+}
+
+bool ScDocFunc::FillSeries( const ScRange& rRange, const ScMarkData* pTabMark,
+ FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd,
+ double fStart, double fStep, double fMax,
+ bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bSuccess = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aMark.SelectTable( nTab, true );
+ }
+
+ ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
+ if ( aTester.IsEditable() )
+ {
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ ScRange aSourceArea = rRange;
+ ScRange aDestArea = rRange;
+
+ SCSIZE nCount = rDoc.GetEmptyLinesInBlock(
+ aSourceArea.aStart.Col(), aSourceArea.aStart.Row(), aSourceArea.aStart.Tab(),
+ aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), aSourceArea.aEnd.Tab(),
+ DirFromFillDir(eDir) );
+
+ // keep at least one row/column as source range
+ SCSIZE nTotLines = ( eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP ) ?
+ static_cast<SCSIZE>( aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1 ) :
+ static_cast<SCSIZE>( aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1 );
+ if ( nCount >= nTotLines )
+ nCount = nTotLines - 1;
+
+ switch (eDir)
+ {
+ case FILL_TO_BOTTOM:
+ aSourceArea.aEnd.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aEnd.Row() - nCount ) );
+ break;
+ case FILL_TO_RIGHT:
+ aSourceArea.aEnd.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aEnd.Col() - nCount ) );
+ break;
+ case FILL_TO_TOP:
+ aSourceArea.aStart.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aStart.Row() + nCount ) );
+ break;
+ case FILL_TO_LEFT:
+ aSourceArea.aStart.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aStart.Col() + nCount ) );
+ break;
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ if ( bRecord )
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nDestStartTab = aDestArea.aStart.Tab();
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nDestStartTab, nDestStartTab );
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab != nDestStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+
+ rDoc.CopyToDocument(
+ aDestArea.aStart.Col(), aDestArea.aStart.Row(), 0,
+ aDestArea.aEnd.Col(), aDestArea.aEnd.Row(), nTabCount-1,
+ InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark );
+ }
+
+ if (aDestArea.aStart.Col() <= aDestArea.aEnd.Col() &&
+ aDestArea.aStart.Row() <= aDestArea.aEnd.Row())
+ {
+ if ( fStart != MAXDOUBLE )
+ {
+ SCCOL nValX = (eDir == FILL_TO_LEFT) ? aDestArea.aEnd.Col() : aDestArea.aStart.Col();
+ SCROW nValY = (eDir == FILL_TO_TOP ) ? aDestArea.aEnd.Row() : aDestArea.aStart.Row();
+ SCTAB nTab = aDestArea.aStart.Tab();
+ rDoc.SetValue( nValX, nValY, nTab, fStart );
+ }
+
+ sal_uLong nProgCount;
+ if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP)
+ nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1;
+ else
+ nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1;
+ nProgCount *= nCount;
+ ScProgress aProgress( rDoc.GetDocumentShell(),
+ ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
+
+ rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(),
+ aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress,
+ aMark, nCount, eDir, eCmd, eDateCmd, fStep, fMax );
+ AdjustRowHeight(rRange, true, bApi);
+
+ rDocShell.PostPaintGridAll();
+ aModificator.SetDocumentModified();
+ }
+
+ if ( bRecord ) // only now is Draw-Undo available
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark,
+ eDir, eCmd, eDateCmd, fStart, fStep, fMax) );
+ }
+
+ bSuccess = true;
+ }
+ else if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+
+ return bSuccess;
+}
+
+bool ScDocFunc::FillAuto( ScRange& rRange, const ScMarkData* pTabMark,
+ FillDir eDir, sal_uLong nCount, bool bApi )
+{
+ return FillAuto( rRange, pTabMark, eDir, FILL_AUTO, FILL_DAY, nCount, 1.0/*fStep*/, MAXDOUBLE/*fMax*/, true/*bRecord*/, bApi );
+}
+
+bool ScDocFunc::FillAuto( ScRange& rRange, const ScMarkData* pTabMark, FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd, sal_uLong nCount, double fStep, double fMax, bool bRecord, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aMark.SelectTable( nTab, true );
+ }
+
+ ScRange aSourceArea = rRange;
+ ScRange aDestArea = rRange;
+
+ switch (eDir)
+ {
+ case FILL_TO_BOTTOM:
+ aDestArea.aEnd.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aEnd.Row() + nCount ) );
+ break;
+ case FILL_TO_TOP:
+ if (nCount > sal::static_int_cast<sal_uLong>( aSourceArea.aStart.Row() ))
+ {
+ OSL_FAIL("FillAuto: Row < 0");
+ nCount = aSourceArea.aStart.Row();
+ }
+ aDestArea.aStart.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aStart.Row() - nCount ) );
+ break;
+ case FILL_TO_RIGHT:
+ aDestArea.aEnd.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aEnd.Col() + nCount ) );
+ break;
+ case FILL_TO_LEFT:
+ if (nCount > sal::static_int_cast<sal_uLong>( aSourceArea.aStart.Col() ))
+ {
+ OSL_FAIL("FillAuto: Col < 0");
+ nCount = aSourceArea.aStart.Col();
+ }
+ aDestArea.aStart.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aStart.Col() - nCount ) );
+ break;
+ default:
+ OSL_FAIL("Wrong direction with FillAuto");
+ break;
+ }
+
+ // Test for cell protection
+ //! Source range can be protected !!!
+ //! but can't contain matrix fragments !!!
+
+ ScEditableTester aTester( rDoc, aDestArea );
+ if ( !aTester.IsEditable() )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ if ( rDoc.HasSelectedBlockMatrixFragment( nStartCol, nStartRow,
+ nEndCol, nEndRow, aMark ) )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MATRIXFRAGMENTERR);
+ return false;
+ }
+
+ // FID_FILL_... slots should already had been disabled, check here for API
+ // calls, no message.
+ if (ScViewData::SelectionFillDOOM( aDestArea))
+ return false;
+
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ ScDocumentUniquePtr pUndoDoc;
+ if ( bRecord )
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nDestStartTab = aDestArea.aStart.Tab();
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nDestStartTab, nDestStartTab );
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab != nDestStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+
+ // do not clone note captions in undo document
+ rDoc.CopyToDocument(
+ aDestArea.aStart.Col(), aDestArea.aStart.Row(), 0,
+ aDestArea.aEnd.Col(), aDestArea.aEnd.Row(), nTabCount-1,
+ InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark );
+ }
+
+ sal_uLong nProgCount;
+ if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP)
+ nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1;
+ else
+ nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1;
+ nProgCount *= nCount;
+ ScProgress aProgress( rDoc.GetDocumentShell(),
+ ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
+
+ rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(),
+ aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress,
+ aMark, nCount, eDir, eCmd, eDateCmd, fStep, fMax );
+
+ AdjustRowHeight(aDestArea, true, bApi);
+
+ if ( bRecord ) // only now is Draw-Undo available
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark,
+ eDir, eCmd, eDateCmd, MAXDOUBLE, fStep, fMax) );
+ }
+
+ rDocShell.PostPaintGridAll();
+ aModificator.SetDocumentModified();
+
+ rRange = aDestArea; // return destination range (for marking)
+ return true;
+}
+
+bool ScDocFunc::MergeCells( const ScCellMergeOption& rOption, bool bContents, bool bRecord, bool bApi, bool bEmptyMergedCells /*=false*/ )
+{
+ using ::std::set;
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ SCCOL nStartCol = rOption.mnStartCol;
+ SCROW nStartRow = rOption.mnStartRow;
+ SCCOL nEndCol = rOption.mnEndCol;
+ SCROW nEndRow = rOption.mnEndRow;
+ if ((nStartCol == nEndCol && nStartRow == nEndRow) || rOption.maTabs.empty())
+ {
+ // Nothing to do. Bail out quickly
+ return true;
+ }
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCTAB nTab1 = *rOption.maTabs.begin(), nTab2 = *rOption.maTabs.rbegin();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ for (const auto& rTab : rOption.maTabs)
+ {
+ ScEditableTester aTester( rDoc, rTab, nStartCol, nStartRow, nEndCol, nEndRow );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ if ( rDoc.HasAttrib( nStartCol, nStartRow, rTab, nEndCol, nEndRow, rTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ {
+ // "Merge of already merged cells not possible"
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_MERGECELLS_0);
+ return false;
+ }
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ bool bNeedContentsUndo = false;
+ for (const SCTAB nTab : rOption.maTabs)
+ {
+ bool bIsBlockEmpty = ( nStartRow == nEndRow )
+ ? rDoc.IsEmptyData( nStartCol+1,nStartRow, nEndCol,nEndRow, nTab )
+ : rDoc.IsEmptyData( nStartCol,nStartRow+1, nStartCol,nEndRow, nTab ) &&
+ rDoc.IsEmptyData( nStartCol+1,nStartRow, nEndCol,nEndRow, nTab );
+ bool bNeedContents = bContents && !bIsBlockEmpty;
+ bool bNeedEmpty = bEmptyMergedCells && !bIsBlockEmpty && !bNeedContents; // if DoMergeContents then cells are emptied
+
+ if (bRecord)
+ {
+ // test if the range contains other notes which also implies that we need an undo document
+ bool bHasNotes = rDoc.HasNote(nTab, nStartCol, nStartRow, nEndCol, nEndRow);
+ if (!pUndoDoc)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo(rDoc, nTab1, nTab2);
+ }
+ // note captions are collected by drawing undo
+ rDoc.CopyToDocument( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab,
+ InsertDeleteFlags::ALL|InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc );
+ if( bHasNotes )
+ rDoc.BeginDrawUndo();
+ }
+
+ if (bNeedContents)
+ rDoc.DoMergeContents( nStartCol,nStartRow, nEndCol,nEndRow, nTab );
+ else if ( bNeedEmpty )
+ rDoc.DoEmptyBlock( nStartCol,nStartRow, nEndCol,nEndRow, nTab );
+ rDoc.DoMerge( nStartCol,nStartRow, nEndCol,nEndRow, nTab );
+
+ if (rOption.mbCenter)
+ {
+ rDoc.ApplyAttr( nStartCol, nStartRow, nTab, SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY ) );
+ rDoc.ApplyAttr( nStartCol, nStartRow, nTab, SvxVerJustifyItem( SvxCellVerJustify::Center, ATTR_VER_JUSTIFY ) );
+ }
+
+ if ( !AdjustRowHeight( ScRange( 0,nStartRow,nTab, rDoc.MaxCol(),nEndRow,nTab ), true, bApi ) )
+ rDocShell.PostPaint( nStartCol, nStartRow, nTab,
+ nEndCol, nEndRow, nTab, PaintPartFlags::Grid );
+ if (bNeedContents || rOption.mbCenter)
+ {
+ ScRange aRange(nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab);
+ rDoc.SetDirty(aRange, true);
+ }
+
+ bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteAll( ScDetectiveDelete::Circles );
+ if(bDone)
+ DetectiveMarkInvalid(nTab);
+
+ bNeedContentsUndo |= bNeedContents;
+ }
+
+ if (pUndoDoc)
+ {
+ std::unique_ptr<SdrUndoGroup> pDrawUndo = rDoc.GetDrawLayer() ? rDoc.GetDrawLayer()->GetCalcUndo() : nullptr;
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoMerge>(&rDocShell, rOption, bNeedContentsUndo, std::move(pUndoDoc), std::move(pDrawUndo)) );
+ }
+
+ aModificator.SetDocumentModified();
+
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_MERGE_ON );
+ pBindings->Invalidate( FID_MERGE_OFF );
+ pBindings->Invalidate( FID_MERGE_TOGGLE );
+ }
+
+ return true;
+}
+
+bool ScDocFunc::UnmergeCells( const ScRange& rRange, bool bRecord, ScUndoRemoveMerge* pUndoRemoveMerge )
+{
+ ScCellMergeOption aOption(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
+ SCTAB nTab1 = rRange.aStart.Tab(), nTab2 = rRange.aEnd.Tab();
+ for (SCTAB i = nTab1; i <= nTab2; ++i)
+ aOption.maTabs.insert(i);
+
+ return UnmergeCells(aOption, bRecord, pUndoRemoveMerge);
+}
+
+bool ScDocFunc::UnmergeCells( const ScCellMergeOption& rOption, bool bRecord, ScUndoRemoveMerge* pUndoRemoveMerge )
+{
+ using ::std::set;
+
+ if (rOption.maTabs.empty())
+ // Nothing to unmerge.
+ return true;
+
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScDocument* pUndoDoc = (pUndoRemoveMerge ? pUndoRemoveMerge->GetUndoDoc() : nullptr);
+ assert( pUndoDoc || !pUndoRemoveMerge );
+ for (const SCTAB nTab : rOption.maTabs)
+ {
+ ScRange aRange = rOption.getSingleRange(nTab);
+ if ( !rDoc.HasAttrib(aRange, HasAttrFlags::Merged) )
+ continue;
+
+ ScRange aExtended = aRange;
+ rDoc.ExtendMerge(aExtended);
+ ScRange aRefresh = aExtended;
+ rDoc.ExtendOverlapped(aRefresh);
+
+ if (bRecord)
+ {
+ if (!pUndoDoc)
+ {
+ pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
+ pUndoDoc->InitUndo(rDoc, *rOption.maTabs.begin(), *rOption.maTabs.rbegin());
+ }
+ rDoc.CopyToDocument(aExtended, InsertDeleteFlags::ATTRIB, false, *pUndoDoc);
+ }
+
+ const SfxPoolItem& rDefAttr = rDoc.GetPool()->GetDefaultItem( ATTR_MERGE );
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( rDefAttr );
+ rDoc.ApplyPatternAreaTab( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), nTab,
+ aPattern );
+
+ rDoc.RemoveFlagsTab( aExtended.aStart.Col(), aExtended.aStart.Row(),
+ aExtended.aEnd.Col(), aExtended.aEnd.Row(), nTab,
+ ScMF::Hor | ScMF::Ver );
+
+ rDoc.ExtendMerge( aRefresh, true );
+
+ if ( !AdjustRowHeight( aExtended, true, true ) )
+ rDocShell.PostPaint( aExtended, PaintPartFlags::Grid );
+
+ bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteAll( ScDetectiveDelete::Circles );
+ if(bDone)
+ DetectiveMarkInvalid(nTab);
+ }
+
+ if (bRecord)
+ {
+ if (pUndoRemoveMerge)
+ {
+ // If pUndoRemoveMerge was passed, the caller is responsible for
+ // adding it to Undo. Just add the current option.
+ pUndoRemoveMerge->AddCellMergeOption( rOption);
+ }
+ else
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRemoveMerge>( &rDocShell, rOption, ScDocumentUniquePtr(pUndoDoc) ) );
+ }
+ }
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+void ScDocFunc::ModifyRangeNames( const ScRangeName& rNewRanges, SCTAB nTab )
+{
+ SetNewRangeNames( std::unique_ptr<ScRangeName>(new ScRangeName(rNewRanges)), true, nTab );
+}
+
+void ScDocFunc::SetNewRangeNames( std::unique_ptr<ScRangeName> pNewRanges, bool bModifyDoc, SCTAB nTab ) // takes ownership of pNewRanges
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ OSL_ENSURE( pNewRanges, "pNewRanges is 0" );
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ if (bUndo)
+ {
+ ScRangeName* pOld;
+ if (nTab >=0)
+ {
+ pOld = rDoc.GetRangeName(nTab);
+ }
+ else
+ {
+ pOld = rDoc.GetRangeName();
+ }
+ std::unique_ptr<ScRangeName> pUndoRanges(new ScRangeName(*pOld));
+ std::unique_ptr<ScRangeName> pRedoRanges(new ScRangeName(*pNewRanges));
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRangeNames>( &rDocShell, std::move(pUndoRanges), std::move(pRedoRanges), nTab ) );
+ }
+
+ // #i55926# While loading XML, formula cells only have a single string token,
+ // so CompileNameFormula would never find any name (index) tokens, and would
+ // unnecessarily loop through all cells.
+ bool bCompile = ( !rDoc.IsImportingXML() && rDoc.GetNamedRangesLockCount() == 0 );
+
+ if ( bCompile )
+ rDoc.PreprocessRangeNameUpdate();
+ if (nTab >= 0)
+ rDoc.SetRangeName( nTab, std::move(pNewRanges) ); // takes ownership
+ else
+ rDoc.SetRangeName( std::move(pNewRanges) ); // takes ownership
+ if ( bCompile )
+ rDoc.CompileHybridFormula();
+
+ if (bModifyDoc)
+ {
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint(SfxHintId::ScAreasChanged) );
+ }
+}
+
+void ScDocFunc::ModifyAllRangeNames(const std::map<OUString, std::unique_ptr<ScRangeName>>& rRangeMap)
+{
+ ScDocShellModificator aModificator(rDocShell);
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (rDoc.IsUndoEnabled())
+ {
+ std::map<OUString, ScRangeName*> aOldRangeMap;
+ rDoc.GetRangeNameMap(aOldRangeMap);
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAllRangeNames>(&rDocShell, aOldRangeMap, rRangeMap));
+ }
+
+ rDoc.PreprocessAllRangeNamesUpdate(rRangeMap);
+ rDoc.SetAllRangeNames(rRangeMap);
+ rDoc.CompileHybridFormula();
+
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged));
+}
+
+void ScDocFunc::CreateOneName( ScRangeName& rList,
+ SCCOL nPosX, SCROW nPosY, SCTAB nTab,
+ SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
+ bool& rCancel, bool bApi )
+{
+ if (rCancel)
+ return;
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (rDoc.HasValueData( nPosX, nPosY, nTab ))
+ return;
+
+ OUString aName = rDoc.GetString(nPosX, nPosY, nTab);
+ ScRangeData::MakeValidName(rDoc, aName);
+ if (aName.isEmpty())
+ return;
+
+ OUString aContent( ScRange( nX1, nY1, nTab, nX2, nY2, nTab ).Format(
+ rDoc, ScRefFlags::RANGE_ABS_3D, ScAddress::Details( rDoc.GetAddressConvention(), nPosY, nPosX)));
+
+ bool bInsert = false;
+ ScRangeData* pOld = rList.findByUpperName(ScGlobal::getCharClass().uppercase(aName));
+ if (pOld)
+ {
+ OUString aOldStr = pOld->GetSymbol();
+ if (aOldStr != aContent)
+ {
+ if (bApi)
+ bInsert = true; // don't check via API
+ else
+ {
+ OUString aTemplate = ScResId( STR_CREATENAME_REPLACE );
+ OUString aMessage = o3tl::getToken(aTemplate, 0, '#' ) + aName + o3tl::getToken(aTemplate, 1, '#' );
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ aMessage));
+ xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
+ xQueryBox->set_default_response(RET_YES);
+
+ short nResult = xQueryBox->run();
+ if ( nResult == RET_YES )
+ {
+ rList.erase(*pOld);
+ bInsert = true;
+ }
+ else if ( nResult == RET_CANCEL )
+ rCancel = true;
+ }
+ }
+ }
+ else
+ bInsert = true;
+
+ if (bInsert)
+ {
+ ScRangeData* pData = new ScRangeData( rDoc, aName, aContent,
+ ScAddress( nPosX, nPosY, nTab));
+ if (!rList.insert(pData))
+ {
+ OSL_FAIL("nanu?");
+ }
+ }
+}
+
+bool ScDocFunc::CreateNames( const ScRange& rRange, CreateNameFlags nFlags, bool bApi, SCTAB aTab )
+{
+ if (nFlags == CreateNameFlags::NONE)
+ return false; // was nothing
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bDone = false;
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nTab = rRange.aStart.Tab();
+ OSL_ENSURE(rRange.aEnd.Tab() == nTab, "CreateNames: multiple tables not possible");
+
+ bool bValid = true;
+ if ( nFlags & ( CreateNameFlags::Top | CreateNameFlags::Bottom ) )
+ if ( nStartRow == nEndRow )
+ bValid = false;
+ if ( nFlags & ( CreateNameFlags::Left | CreateNameFlags::Right ) )
+ if ( nStartCol == nEndCol )
+ bValid = false;
+
+ if (bValid)
+ {
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScRangeName* pNames;
+ if (aTab >=0)
+ pNames = rDoc.GetRangeName(nTab);
+ else
+ pNames = rDoc.GetRangeName();
+
+ if (!pNames)
+ return false; // shouldn't happen
+ ScRangeName aNewRanges( *pNames );
+
+ bool bTop ( nFlags & CreateNameFlags::Top );
+ bool bLeft ( nFlags & CreateNameFlags::Left );
+ bool bBottom( nFlags & CreateNameFlags::Bottom );
+ bool bRight ( nFlags & CreateNameFlags::Right );
+
+ SCCOL nContX1 = nStartCol;
+ SCROW nContY1 = nStartRow;
+ SCCOL nContX2 = nEndCol;
+ SCROW nContY2 = nEndRow;
+
+ if ( bTop )
+ ++nContY1;
+ if ( bLeft )
+ ++nContX1;
+ if ( bBottom )
+ --nContY2;
+ if ( bRight )
+ --nContX2;
+
+ bool bCancel = false;
+ SCCOL i;
+ SCROW j;
+
+ if ( bTop )
+ for (i=nContX1; i<=nContX2; i++)
+ CreateOneName( aNewRanges, i,nStartRow,nTab, i,nContY1,i,nContY2, bCancel, bApi );
+ if ( bLeft )
+ for (j=nContY1; j<=nContY2; j++)
+ CreateOneName( aNewRanges, nStartCol,j,nTab, nContX1,j,nContX2,j, bCancel, bApi );
+ if ( bBottom )
+ for (i=nContX1; i<=nContX2; i++)
+ CreateOneName( aNewRanges, i,nEndRow,nTab, i,nContY1,i,nContY2, bCancel, bApi );
+ if ( bRight )
+ for (j=nContY1; j<=nContY2; j++)
+ CreateOneName( aNewRanges, nEndCol,j,nTab, nContX1,j,nContX2,j, bCancel, bApi );
+
+ if ( bTop && bLeft )
+ CreateOneName( aNewRanges, nStartCol,nStartRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
+ if ( bTop && bRight )
+ CreateOneName( aNewRanges, nEndCol,nStartRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
+ if ( bBottom && bLeft )
+ CreateOneName( aNewRanges, nStartCol,nEndRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
+ if ( bBottom && bRight )
+ CreateOneName( aNewRanges, nEndCol,nEndRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
+
+ ModifyRangeNames( aNewRanges, aTab );
+ bDone = true;
+
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::InsertNameList( const ScAddress& rStartPos, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bDone = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ const bool bRecord = rDoc.IsUndoEnabled();
+ SCTAB nTab = rStartPos.Tab();
+
+ //local names have higher priority than global names
+ ScRangeName* pLocalList = rDoc.GetRangeName(nTab);
+ sal_uInt16 nValidCount = 0;
+ for (const auto& rEntry : *pLocalList)
+ {
+ const ScRangeData& r = *rEntry.second;
+ if (!r.HasType(ScRangeData::Type::Database))
+ ++nValidCount;
+ }
+ ScRangeName* pList = rDoc.GetRangeName();
+ for (const auto& rEntry : *pList)
+ {
+ const ScRangeData& r = *rEntry.second;
+ if (!r.HasType(ScRangeData::Type::Database) && !pLocalList->findByUpperName(r.GetUpperName()))
+ ++nValidCount;
+ }
+
+ if (nValidCount)
+ {
+ SCCOL nStartCol = rStartPos.Col();
+ SCROW nStartRow = rStartPos.Row();
+ SCCOL nEndCol = nStartCol + 1;
+ SCROW nEndRow = nStartRow + static_cast<SCROW>(nValidCount) - 1;
+
+ ScEditableTester aTester( rDoc, nTab, nStartCol,nStartRow, nEndCol,nEndRow );
+ if (aTester.IsEditable())
+ {
+ ScDocumentUniquePtr pUndoDoc;
+
+ if (bRecord)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+ rDoc.CopyToDocument(nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab,
+ InsertDeleteFlags::ALL, false, *pUndoDoc);
+
+ rDoc.BeginDrawUndo(); // because of adjusting heights
+ }
+
+ std::unique_ptr<ScRangeData*[]> ppSortArray(new ScRangeData* [ nValidCount ]);
+ sal_uInt16 j = 0;
+ for (const auto& rEntry : *pLocalList)
+ {
+ ScRangeData& r = *rEntry.second;
+ if (!r.HasType(ScRangeData::Type::Database))
+ ppSortArray[j++] = &r;
+ }
+ for (const auto& [rName, rxData] : *pList)
+ {
+ ScRangeData& r = *rxData;
+ if (!r.HasType(ScRangeData::Type::Database) && !pLocalList->findByUpperName(rName))
+ ppSortArray[j++] = &r;
+ }
+ qsort( static_cast<void*>(ppSortArray.get()), nValidCount, sizeof(ScRangeData*),
+ &ScRangeData_QsortNameCompare );
+ OUString aName;
+ OUStringBuffer aContent;
+ OUString aFormula;
+ SCROW nOutRow = nStartRow;
+ for (j=0; j<nValidCount; j++)
+ {
+ ScRangeData* pData = ppSortArray[j];
+ pData->GetName(aName);
+ // adjust relative references to the left column in Excel-compliant way:
+ pData->UpdateSymbol(aContent, ScAddress( nStartCol, nOutRow, nTab ));
+ aFormula = "=" + aContent;
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rDoc.SetString(ScAddress(nStartCol,nOutRow,nTab), aName, &aParam);
+ rDoc.SetString(ScAddress(nEndCol,nOutRow,nTab), aFormula, &aParam);
+ ++nOutRow;
+ }
+
+ ppSortArray.reset();
+
+ if (bRecord)
+ {
+ ScDocumentUniquePtr pRedoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pRedoDoc->InitUndo( rDoc, nTab, nTab );
+ rDoc.CopyToDocument(nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab,
+ InsertDeleteFlags::ALL, false, *pRedoDoc);
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoListNames>( &rDocShell,
+ ScRange( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab ),
+ std::move(pUndoDoc), std::move(pRedoDoc) ) );
+ }
+
+ if (!AdjustRowHeight(ScRange(0,nStartRow,nTab,rDoc.MaxCol(),nEndRow,nTab), true, true))
+ rDocShell.PostPaint( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab, PaintPartFlags::Grid );
+
+ aModificator.SetDocumentModified();
+ bDone = true;
+ }
+ else if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ }
+ return bDone;
+}
+
+void ScDocFunc::ResizeMatrix( const ScRange& rOldRange, const ScAddress& rNewEnd )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCCOL nStartCol = rOldRange.aStart.Col();
+ SCROW nStartRow = rOldRange.aStart.Row();
+ SCTAB nTab = rOldRange.aStart.Tab();
+
+ OUString aFormula = rDoc.GetFormula( nStartCol, nStartRow, nTab );
+ if ( !(aFormula.startsWith("{") && aFormula.endsWith("}")) )
+ return;
+
+ OUString aUndo = ScResId( STR_UNDO_RESIZEMATRIX );
+ bool bUndo(rDoc.IsUndoEnabled());
+ if (bUndo)
+ {
+ ViewShellId nViewShellId(1);
+ if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
+ nViewShellId = pViewSh->GetViewShellId();
+ rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
+ }
+
+ aFormula = aFormula.copy(1, aFormula.getLength()-2);
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SetMarkArea( rOldRange );
+ aMark.SelectTable( nTab, true );
+ ScRange aNewRange( rOldRange.aStart, rNewEnd );
+
+ if ( DeleteContents( aMark, InsertDeleteFlags::CONTENTS, true, false/*bApi*/ ) )
+ {
+ // GRAM_API for API compatibility.
+ if (!EnterMatrix( aNewRange, &aMark, nullptr, aFormula, false/*bApi*/, false, OUString(), formula::FormulaGrammar::GRAM_API ))
+ {
+ // try to restore the previous state
+ EnterMatrix( rOldRange, &aMark, nullptr, aFormula, false/*bApi*/, false, OUString(), formula::FormulaGrammar::GRAM_API );
+ }
+ }
+
+ if (bUndo)
+ rDocShell.GetUndoManager()->LeaveListAction();
+}
+
+void ScDocFunc::InsertAreaLink( const OUString& rFile, const OUString& rFilter,
+ const OUString& rOptions, const OUString& rSource,
+ const ScRange& rDestRange, sal_Int32 nRefreshDelaySeconds,
+ bool bFitBlock, bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo (rDoc.IsUndoEnabled());
+
+ sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
+
+ // #i52120# if other area links exist at the same start position,
+ // remove them first (file format specifies only one link definition
+ // for a cell)
+
+ sal_uInt16 nLinkCount = pLinkManager->GetLinks().size();
+ sal_uInt16 nRemoved = 0;
+ sal_uInt16 nLinkPos = 0;
+ while (nLinkPos<nLinkCount)
+ {
+ ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[nLinkPos].get();
+ ScAreaLink* pLink = dynamic_cast<ScAreaLink*>(pBase);
+ if (pLink && pLink->GetDestArea().aStart == rDestRange.aStart)
+ {
+ if ( bUndo )
+ {
+ if ( !nRemoved )
+ {
+ // group all remove and the insert action
+ OUString aUndo = ScResId( STR_UNDO_INSERTAREALINK );
+ ViewShellId nViewShellId(-1);
+ if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
+ nViewShellId = pViewSh->GetViewShellId();
+ rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
+ }
+
+ ScAreaLink* pOldArea = static_cast<ScAreaLink*>(pBase);
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRemoveAreaLink>( &rDocShell,
+ pOldArea->GetFile(), pOldArea->GetFilter(), pOldArea->GetOptions(),
+ pOldArea->GetSource(), pOldArea->GetDestArea(), pOldArea->GetRefreshDelaySeconds() ) );
+ }
+ pLinkManager->Remove( pBase );
+ nLinkCount = pLinkManager->GetLinks().size();
+ ++nRemoved;
+ }
+ else
+ ++nLinkPos;
+ }
+
+ OUString aFilterName = rFilter;
+ OUString aNewOptions = rOptions;
+ if (aFilterName.isEmpty())
+ ScDocumentLoader::GetFilterName( rFile, aFilterName, aNewOptions, true, !bApi );
+
+ // remove application prefix from filter name here, so the filter options
+ // aren't reset when the filter name is changed in ScAreaLink::DataChanged
+ ScDocumentLoader::RemoveAppPrefix( aFilterName );
+
+ ScAreaLink* pLink = new ScAreaLink( &rDocShell, rFile, aFilterName,
+ aNewOptions, rSource, rDestRange, nRefreshDelaySeconds );
+ OUString aTmp = aFilterName;
+ pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, rFile, &aTmp, &rSource );
+
+ // Undo for an empty link
+
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoInsertAreaLink>( &rDocShell,
+ rFile, aFilterName, aNewOptions,
+ rSource, rDestRange, nRefreshDelaySeconds ) );
+ if ( nRemoved )
+ rDocShell.GetUndoManager()->LeaveListAction(); // undo for link update is still separate
+ }
+
+ // Update has its own undo
+ if (rDoc.IsExecuteLinkEnabled())
+ {
+ pLink->SetDoInsert(bFitBlock); // if applicable, don't insert anything on first update
+ pLink->Update(); // no SetInCreate -> carry out update
+ }
+ pLink->SetDoInsert(true); // Default = true
+
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_LINKS );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); // Navigator
+}
+
+void ScDocFunc::ReplaceConditionalFormat( sal_uLong nOldFormat, std::unique_ptr<ScConditionalFormat> pFormat, SCTAB nTab, const ScRangeList& rRanges )
+{
+ ScDocShellModificator aModificator(rDocShell);
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if(rDoc.IsTabProtected(nTab))
+ return;
+
+ bool bUndo = rDoc.IsUndoEnabled();
+ ScDocumentUniquePtr pUndoDoc;
+ ScRange aCombinedRange = rRanges.Combine();
+ ScRange aCompleteRange;
+ if(bUndo)
+ {
+ pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+
+ if(pFormat)
+ {
+ aCompleteRange = aCombinedRange;
+ }
+ if(nOldFormat)
+ {
+ ScConditionalFormat* pOldFormat = rDoc.GetCondFormList(nTab)->GetFormat(nOldFormat);
+ if(pOldFormat)
+ aCompleteRange.ExtendTo(pOldFormat->GetRange().Combine());
+ }
+
+ rDoc.CopyToDocument(aCompleteRange.aStart.Col(),aCompleteRange.aStart.Row(),nTab,
+ aCompleteRange.aEnd.Col(),aCompleteRange.aEnd.Row(),nTab,
+ InsertDeleteFlags::ALL, false, *pUndoDoc);
+ }
+
+ std::unique_ptr<ScRange> pRepaintRange;
+ if(nOldFormat)
+ {
+ ScConditionalFormat* pOldFormat = rDoc.GetCondFormList(nTab)->GetFormat(nOldFormat);
+ if(pOldFormat)
+ {
+ pRepaintRange.reset(new ScRange( pOldFormat->GetRange().Combine() ));
+ rDoc.RemoveCondFormatData(pOldFormat->GetRange(), nTab, pOldFormat->GetKey());
+ }
+
+ rDoc.DeleteConditionalFormat(nOldFormat, nTab);
+ rDoc.SetStreamValid(nTab, false);
+ }
+ if(pFormat)
+ {
+ if(pRepaintRange)
+ pRepaintRange->ExtendTo(aCombinedRange);
+ else
+ pRepaintRange.reset(new ScRange(aCombinedRange));
+
+ sal_uLong nIndex = rDoc.AddCondFormat(std::move(pFormat), nTab);
+
+ rDoc.AddCondFormatData(rRanges, nTab, nIndex);
+ rDoc.SetStreamValid(nTab, false);
+ }
+
+ if(bUndo)
+ {
+ ScDocumentUniquePtr pRedoDoc(new ScDocument(SCDOCMODE_UNDO));
+ pRedoDoc->InitUndo( rDoc, nTab, nTab );
+ rDoc.CopyToDocument(aCompleteRange.aStart.Col(),aCompleteRange.aStart.Row(),nTab,
+ aCompleteRange.aEnd.Col(),aCompleteRange.aEnd.Row(),nTab,
+ InsertDeleteFlags::ALL, false, *pRedoDoc);
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoConditionalFormat>(&rDocShell, std::move(pUndoDoc), std::move(pRedoDoc), aCompleteRange));
+ }
+
+ if(pRepaintRange)
+ rDocShell.PostPaint(*pRepaintRange, PaintPartFlags::Grid);
+
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged));
+}
+
+void ScDocFunc::SetConditionalFormatList( ScConditionalFormatList* pList, SCTAB nTab )
+{
+ ScDocShellModificator aModificator(rDocShell);
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if(rDoc.IsTabProtected(nTab))
+ return;
+
+ bool bUndo = rDoc.IsUndoEnabled();
+ ScDocumentUniquePtr pUndoDoc;
+ if (bUndo)
+ {
+ pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+
+ ScConditionalFormatList* pOld = rDoc.GetCondFormList(nTab);
+
+ if (pOld)
+ pUndoDoc->SetCondFormList(new ScConditionalFormatList(*pUndoDoc, *pOld), nTab);
+ else
+ pUndoDoc->SetCondFormList(nullptr, nTab);
+
+ }
+
+ // first remove all old entries
+ ScConditionalFormatList* pOldList = rDoc.GetCondFormList(nTab);
+ pOldList->RemoveFromDocument(rDoc);
+
+ // then set new entries
+ pList->AddToDocument(rDoc);
+
+ rDoc.SetCondFormList(pList, nTab);
+ rDocShell.PostPaintGridAll();
+
+ if(bUndo)
+ {
+ ScDocumentUniquePtr pRedoDoc(new ScDocument(SCDOCMODE_UNDO));
+ pRedoDoc->InitUndo( rDoc, nTab, nTab );
+ pRedoDoc->SetCondFormList(new ScConditionalFormatList(*pRedoDoc, *pList), nTab);
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoConditionalFormatList>(&rDocShell, std::move(pUndoDoc), std::move(pRedoDoc), nTab));
+ }
+
+ rDoc.SetStreamValid(nTab, false);
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged));
+}
+
+void ScDocFunc::ConvertFormulaToValue( const ScRange& rRange, bool bInteraction )
+{
+ ScDocShellModificator aModificator(rDocShell);
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScEditableTester aTester(rDoc, rRange);
+ if (!aTester.IsEditable())
+ {
+ if (bInteraction)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ sc::TableValues aUndoVals(rRange);
+ sc::TableValues* pUndoVals = bRecord ? &aUndoVals : nullptr;
+
+ rDoc.ConvertFormulaToValue(rRange, pUndoVals);
+
+ if (bRecord && pUndoVals)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<sc::UndoFormulaToValue>(&rDocShell, *pUndoVals));
+ }
+
+ rDocShell.PostPaint(rRange, PaintPartFlags::Grid);
+ rDocShell.PostDataChanged();
+ rDoc.BroadcastCells(rRange, SfxHintId::ScDataChanged);
+ aModificator.SetDocumentModified();
+}
+
+void ScDocFunc::EnterListAction(TranslateId pNameResId)
+{
+ OUString aUndo(ScResId(pNameResId));
+ ViewShellId nViewShellId(-1);
+ if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
+ nViewShellId = pViewSh->GetViewShellId();
+ rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
+}
+
+void ScDocFunc::EndListAction()
+{
+ rDocShell.GetUndoManager()->LeaveListAction();
+}
+
+bool ScDocFunc::InsertSparklines(ScRange const& rDataRange, ScRange const& rSparklineRange,
+ std::shared_ptr<sc::SparklineGroup> pSparklineGroup)
+{
+ std::vector<sc::SparklineData> aSparklineDataVector;
+
+ if (rSparklineRange.aStart.Col() == rSparklineRange.aEnd.Col())
+ {
+ sal_Int32 nOutputRowSize = rSparklineRange.aEnd.Row() - rSparklineRange.aStart.Row();
+
+ auto eInputOrientation = sc::calculateOrientation(nOutputRowSize, rDataRange);
+
+ if (eInputOrientation == sc::RangeOrientation::Unknown)
+ return false;
+
+ sal_Int32 nIndex = 0;
+
+ for (ScAddress aAddress = rSparklineRange.aStart; aAddress.Row() <= rSparklineRange.aEnd.Row();
+ aAddress.IncRow())
+ {
+ ScRange aInputRangeSlice = rDataRange;
+ if (eInputOrientation == sc::RangeOrientation::Row)
+ {
+ aInputRangeSlice.aStart.SetRow(rDataRange.aStart.Row() + nIndex);
+ aInputRangeSlice.aEnd.SetRow(rDataRange.aStart.Row() + nIndex);
+ }
+ else
+ {
+ aInputRangeSlice.aStart.SetCol(rDataRange.aStart.Col() + nIndex);
+ aInputRangeSlice.aEnd.SetCol(rDataRange.aStart.Col() + nIndex);
+ }
+
+ aSparklineDataVector.emplace_back(aAddress, aInputRangeSlice);
+
+ nIndex++;
+ }
+ }
+ else if (rSparklineRange.aStart.Row() == rSparklineRange.aEnd.Row())
+ {
+ sal_Int32 nOutputColSize = rSparklineRange.aEnd.Col() - rSparklineRange.aStart.Col();
+
+ auto eInputOrientation = sc::calculateOrientation(nOutputColSize, rDataRange);
+
+ if (eInputOrientation == sc::RangeOrientation::Unknown)
+ return false;
+
+ sal_Int32 nIndex = 0;
+
+ for (ScAddress aAddress = rSparklineRange.aStart; aAddress.Col() <= rSparklineRange.aEnd.Col();
+ aAddress.IncCol())
+ {
+ ScRange aInputRangeSlice = rDataRange;
+ if (eInputOrientation == sc::RangeOrientation::Row)
+ {
+ aInputRangeSlice.aStart.SetRow(rDataRange.aStart.Row() + nIndex);
+ aInputRangeSlice.aEnd.SetRow(rDataRange.aStart.Row() + nIndex);
+ }
+ else
+ {
+ aInputRangeSlice.aStart.SetCol(rDataRange.aStart.Col() + nIndex);
+ aInputRangeSlice.aEnd.SetCol(rDataRange.aStart.Col() + nIndex);
+ }
+
+ aSparklineDataVector.emplace_back(aAddress, aInputRangeSlice);
+
+ nIndex++;
+ }
+ }
+
+ if (aSparklineDataVector.empty())
+ return false;
+
+ auto pUndoInsertSparkline = std::make_unique<sc::UndoInsertSparkline>(rDocShell, aSparklineDataVector, pSparklineGroup);
+ // insert the sparkline by "redoing"
+ pUndoInsertSparkline->Redo();
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoInsertSparkline));
+
+ return true;
+}
+
+bool ScDocFunc::DeleteSparkline(ScAddress const& rAddress)
+{
+ auto& rDocument = rDocShell.GetDocument();
+
+ if (!rDocument.HasSparkline(rAddress))
+ return false;
+
+ auto pUndoDeleteSparkline = std::make_unique<sc::UndoDeleteSparkline>(rDocShell, rAddress);
+ // delete sparkline by "redoing"
+ pUndoDeleteSparkline->Redo();
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoDeleteSparkline));
+
+ return true;
+}
+
+bool ScDocFunc::DeleteSparklineGroup(std::shared_ptr<sc::SparklineGroup> const& pSparklineGroup, SCTAB nTab)
+{
+ if (!pSparklineGroup)
+ return false;
+
+ auto& rDocument = rDocShell.GetDocument();
+
+ if (!rDocument.HasTable(nTab))
+ return false;
+
+ auto pUndo = std::make_unique<sc::UndoDeleteSparklineGroup>(rDocShell, pSparklineGroup, nTab);
+ // delete sparkline group by "redoing"
+ pUndo->Redo();
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
+ return true;
+}
+
+bool ScDocFunc::ChangeSparklineGroupAttributes(std::shared_ptr<sc::SparklineGroup> const& pExistingSparklineGroup,
+ sc::SparklineAttributes const& rNewAttributes)
+{
+ auto pUndo = std::make_unique<sc::UndoEditSparklneGroup>(rDocShell, pExistingSparklineGroup, rNewAttributes);
+ // change sparkline group attributes by "redoing"
+ pUndo->Redo();
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
+ return true;
+}
+
+bool ScDocFunc::GroupSparklines(ScRange const& rRange, std::shared_ptr<sc::SparklineGroup> const& rpGroup)
+{
+ auto pUndo = std::make_unique<sc::UndoGroupSparklines>(rDocShell, rRange, rpGroup);
+ // group sparklines by "redoing"
+ pUndo->Redo();
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
+ return true;
+}
+
+bool ScDocFunc::UngroupSparklines(ScRange const& rRange)
+{
+ auto pUndo = std::make_unique<sc::UndoUngroupSparklines>(rDocShell, rRange);
+ // ungroup sparklines by "redoing"
+ pUndo->Redo();
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
+ return true;
+}
+
+bool ScDocFunc::ChangeSparkline(std::shared_ptr<sc::Sparkline> const& rpSparkline, SCTAB nTab, ScRangeList const& rDataRange)
+{
+ auto pUndo = std::make_unique<sc::UndoEditSparkline>(rDocShell, rpSparkline, nTab, rDataRange);
+ // change sparkline by "redoing"
+ pUndo->Redo();
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */