summaryrefslogtreecommitdiffstats
path: root/sc/source/ui/undo/undobase.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/ui/undo/undobase.cxx')
-rw-r--r--sc/source/ui/undo/undobase.cxx714
1 files changed, 714 insertions, 0 deletions
diff --git a/sc/source/ui/undo/undobase.cxx b/sc/source/ui/undo/undobase.cxx
new file mode 100644
index 0000000000..c9a2336042
--- /dev/null
+++ b/sc/source/ui/undo/undobase.cxx
@@ -0,0 +1,714 @@
+/* -*- 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 <utility>
+#include <vcl/virdev.hxx>
+#include <svx/svdundo.hxx>
+
+#include <undobase.hxx>
+#include <undocell.hxx>
+#include <refundo.hxx>
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <undoolk.hxx>
+#include <undodraw.hxx>
+#include <dbdata.hxx>
+#include <attrib.hxx>
+#include <queryparam.hxx>
+#include <subtotalparam.hxx>
+#include <rowheightcontext.hxx>
+#include <column.hxx>
+#include <sortparam.hxx>
+#include <columnspanset.hxx>
+#include <undomanager.hxx>
+
+
+ScSimpleUndo::ScSimpleUndo( ScDocShell* pDocSh ) :
+ pDocShell( pDocSh ),
+ mnViewShellId(-1)
+{
+ if (ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell())
+ mnViewShellId = pViewShell->GetViewShellId();
+}
+
+ViewShellId ScSimpleUndo::GetViewShellId() const
+{
+ return mnViewShellId;
+}
+
+bool ScSimpleUndo::SetViewMarkData( const ScMarkData& rMarkData )
+{
+ if ( IsPaintLocked() )
+ return false;
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if ( !pViewShell )
+ return false;
+
+ pViewShell->SetMarkData( rMarkData );
+ return true;
+}
+
+bool ScSimpleUndo::Merge( SfxUndoAction *pNextAction )
+{
+ // A SdrUndoGroup for updating detective arrows can belong
+ // to each Undo-Action.
+ // DetectiveRefresh is always called next,
+ // the SdrUndoGroup is encapsulated in a ScUndoDraw action.
+ // AddUndoAction is only called with bTryMerg=sal_True
+ // for automatic update.
+
+ if ( !pDetectiveUndo && dynamic_cast<const ScUndoDraw*>( pNextAction) != nullptr )
+ {
+ // Take SdrUndoAction from ScUndoDraw Action,
+ // ScUndoDraw is later deleted by the UndoManager
+
+ ScUndoDraw* pCalcUndo = static_cast<ScUndoDraw*>(pNextAction);
+ pDetectiveUndo = pCalcUndo->ReleaseDrawUndo();
+ return true;
+ }
+
+ return false;
+}
+
+void ScSimpleUndo::BeginUndo()
+{
+ pDocShell->SetInUndo( true );
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->HideAllCursors(); // for example due to merged cells
+
+ // detective updates happened last, must be undone first
+ if (pDetectiveUndo)
+ pDetectiveUndo->Undo();
+}
+
+namespace
+{
+ class DisableUndoGuard
+ {
+ private:
+ ScDocument& m_rDoc;
+ bool m_bUndoEnabled;
+ public:
+ explicit DisableUndoGuard(ScDocShell *pDocShell)
+ : m_rDoc(pDocShell->GetDocument())
+ , m_bUndoEnabled(m_rDoc.IsUndoEnabled())
+ {
+ m_rDoc.EnableUndo(false);
+ }
+
+ ~DisableUndoGuard()
+ {
+ m_rDoc.EnableUndo(m_bUndoEnabled);
+ }
+ };
+}
+
+void ScSimpleUndo::EndUndo()
+{
+ {
+ // rhbz#1352881 Temporarily turn off undo generation during
+ // SetDocumentModified
+ DisableUndoGuard aGuard(pDocShell);
+ pDocShell->SetDocumentModified();
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ pViewShell->UpdateAutoFillMark();
+ pViewShell->UpdateInputHandler();
+ pViewShell->ShowAllCursors();
+ }
+
+ pDocShell->SetInUndo( false );
+}
+
+void ScSimpleUndo::BeginRedo()
+{
+ pDocShell->SetInUndo( true ); //! own Flag for Redo?
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->HideAllCursors(); // for example due to merged cells
+}
+
+void ScSimpleUndo::EndRedo()
+{
+ if (pDetectiveUndo)
+ pDetectiveUndo->Redo();
+
+ {
+ // rhbz#1352881 Temporarily turn off undo generation during
+ // SetDocumentModified
+ DisableUndoGuard aGuard(pDocShell);
+ pDocShell->SetDocumentModified();
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ pViewShell->UpdateAutoFillMark();
+ pViewShell->UpdateInputHandler();
+ pViewShell->ShowAllCursors();
+ }
+
+ pDocShell->SetInUndo( false );
+}
+
+void ScSimpleUndo::BroadcastChanges( const ScRange& rRange )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.BroadcastCells(rRange, SfxHintId::ScDataChanged);
+}
+
+namespace {
+
+class SpanBroadcaster : public sc::ColumnSpanSet::ColumnAction
+{
+ ScDocument& mrDoc;
+ SCTAB mnCurTab;
+ SCCOL mnCurCol;
+
+public:
+ explicit SpanBroadcaster( ScDocument& rDoc ) : mrDoc(rDoc), mnCurTab(-1), mnCurCol(-1) {}
+
+ virtual void startColumn( ScColumn* pCol ) override
+ {
+ mnCurTab = pCol->GetTab();
+ mnCurCol = pCol->GetCol();
+ }
+
+ virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
+ {
+ if (!bVal)
+ return;
+
+ ScRange aRange(mnCurCol, nRow1, mnCurTab, mnCurCol, nRow2, mnCurTab);
+ mrDoc.BroadcastCells(aRange, SfxHintId::ScDataChanged);
+ };
+};
+
+}
+
+void ScSimpleUndo::BroadcastChanges( const DataSpansType& rSpans )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SpanBroadcaster aBroadcaster(rDoc);
+
+ for (const auto& rEntry : rSpans)
+ {
+ const sc::ColumnSpanSet& rSet = *rEntry.second;
+ rSet.executeColumnAction(rDoc, aBroadcaster);
+ }
+}
+
+void ScSimpleUndo::ShowTable( SCTAB nTab )
+{
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->SetTabNo( nTab );
+}
+
+void ScSimpleUndo::ShowTable( const ScRange& rRange )
+{
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ SCTAB nStart = rRange.aStart.Tab();
+ SCTAB nEnd = rRange.aEnd.Tab();
+ SCTAB nTab = pViewShell->GetViewData().GetTabNo();
+ if ( nTab < nStart || nTab > nEnd ) // if not in range:
+ pViewShell->SetTabNo( nStart ); // at beginning of the range
+ }
+}
+
+ScBlockUndo::ScBlockUndo( ScDocShell* pDocSh, const ScRange& rRange,
+ ScBlockUndoMode eBlockMode ) :
+ ScSimpleUndo( pDocSh ),
+ aBlockRange( rRange ),
+ eMode( eBlockMode )
+{
+ pDrawUndo = GetSdrUndoAction( &pDocShell->GetDocument() );
+}
+
+ScBlockUndo::~ScBlockUndo()
+{
+ pDrawUndo.reset();
+}
+
+void ScBlockUndo::BeginUndo()
+{
+ ScSimpleUndo::BeginUndo();
+ EnableDrawAdjust( &pDocShell->GetDocument(), false );
+}
+
+void ScBlockUndo::EndUndo()
+{
+ if (eMode == SC_UNDO_AUTOHEIGHT)
+ AdjustHeight();
+
+ EnableDrawAdjust( &pDocShell->GetDocument(), true );
+ DoSdrUndoAction( pDrawUndo.get(), &pDocShell->GetDocument() );
+
+ ShowBlock();
+ ScSimpleUndo::EndUndo();
+}
+
+void ScBlockUndo::EndRedo()
+{
+ if (eMode == SC_UNDO_AUTOHEIGHT)
+ AdjustHeight();
+
+ ShowBlock();
+ ScSimpleUndo::EndRedo();
+}
+
+bool ScBlockUndo::AdjustHeight()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScopedVclPtrInstance< VirtualDevice > pVirtDev;
+ Fraction aZoomX( 1, 1 );
+ Fraction aZoomY = aZoomX;
+ double nPPTX, nPPTY;
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ ScViewData& rData = pViewShell->GetViewData();
+ nPPTX = rData.GetPPTX();
+ nPPTY = rData.GetPPTY();
+ aZoomX = rData.GetZoomX();
+ aZoomY = rData.GetZoomY();
+ }
+ else
+ {
+ // Leave zoom at 100
+ nPPTX = ScGlobal::nScreenPPTX;
+ nPPTY = ScGlobal::nScreenPPTY;
+ }
+
+ sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, pVirtDev);
+ bool bRet = rDoc.SetOptimalHeight(
+ aCxt, aBlockRange.aStart.Row(), aBlockRange.aEnd.Row(), aBlockRange.aStart.Tab(), true);
+
+ if (bRet)
+ {
+ // tdf#76183: recalculate objects' positions
+ rDoc.SetDrawPageSize(aBlockRange.aStart.Tab());
+
+ pDocShell->PostPaint( 0, aBlockRange.aStart.Row(), aBlockRange.aStart.Tab(),
+ rDoc.MaxCol(), rDoc.MaxRow(), aBlockRange.aEnd.Tab(),
+ PaintPartFlags::Grid | PaintPartFlags::Left );
+ }
+ return bRet;
+}
+
+void ScBlockUndo::ShowBlock()
+{
+ if ( IsPaintLocked() )
+ return;
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (!pViewShell)
+ return;
+
+ ShowTable( aBlockRange ); // with multiple sheets in range each of them is good
+ pViewShell->MoveCursorAbs( aBlockRange.aStart.Col(), aBlockRange.aStart.Row(),
+ SC_FOLLOW_JUMP, false, false );
+ SCTAB nTab = pViewShell->GetViewData().GetTabNo();
+ ScRange aRange = aBlockRange;
+ aRange.aStart.SetTab( nTab );
+ aRange.aEnd.SetTab( nTab );
+ pViewShell->MarkRange( aRange );
+
+ // not through SetMarkArea to MarkData, due to possibly lacking paint
+}
+
+ScMultiBlockUndo::ScMultiBlockUndo(
+ ScDocShell* pDocSh, ScRangeList aRanges) :
+ ScSimpleUndo(pDocSh),
+ maBlockRanges(std::move(aRanges))
+{
+ mpDrawUndo = GetSdrUndoAction( &pDocShell->GetDocument() );
+}
+
+ScMultiBlockUndo::~ScMultiBlockUndo()
+{
+ mpDrawUndo.reset();
+}
+
+void ScMultiBlockUndo::BeginUndo()
+{
+ ScSimpleUndo::BeginUndo();
+ EnableDrawAdjust(&pDocShell->GetDocument(), false);
+}
+
+void ScMultiBlockUndo::EndUndo()
+{
+ EnableDrawAdjust(&pDocShell->GetDocument(), true);
+ DoSdrUndoAction(mpDrawUndo.get(), &pDocShell->GetDocument());
+
+ ShowBlock();
+ ScSimpleUndo::EndUndo();
+}
+
+void ScMultiBlockUndo::EndRedo()
+{
+ ShowBlock();
+ ScSimpleUndo::EndRedo();
+}
+
+void ScMultiBlockUndo::ShowBlock()
+{
+ if ( IsPaintLocked() )
+ return;
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (!pViewShell)
+ return;
+
+ if (maBlockRanges.empty())
+ return;
+
+ // Move to the sheet of the first range.
+ ScRange aRange = maBlockRanges.front();
+ ShowTable(aRange);
+ pViewShell->MoveCursorAbs(
+ aRange.aStart.Col(), aRange.aStart.Row(), SC_FOLLOW_JUMP, false, false);
+ SCTAB nTab = pViewShell->GetViewData().GetTabNo();
+ aRange.aStart.SetTab(nTab);
+ aRange.aEnd.SetTab(nTab);
+ pViewShell->MarkRange(aRange, false);
+
+ for (size_t i = 1, n = maBlockRanges.size(); i < n; ++i)
+ {
+ aRange = maBlockRanges[i];
+ aRange.aStart.SetTab(nTab);
+ aRange.aEnd.SetTab(nTab);
+ pViewShell->MarkRange(aRange, false, true);
+ }
+}
+
+ScMoveUndo::ScMoveUndo( ScDocShell* pDocSh, ScDocumentUniquePtr pRefDoc, std::unique_ptr<ScRefUndoData> pRefData ) :
+ ScSimpleUndo( pDocSh ),
+ pRefUndoDoc( std::move(pRefDoc) ),
+ pRefUndoData( std::move(pRefData) )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if (pRefUndoData)
+ pRefUndoData->DeleteUnchanged(&rDoc);
+ pDrawUndo = GetSdrUndoAction( &rDoc );
+}
+
+ScMoveUndo::~ScMoveUndo()
+{
+ pRefUndoData.reset();
+ pRefUndoDoc.reset();
+ pDrawUndo.reset();
+}
+
+void ScMoveUndo::UndoRef()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScRange aRange(0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),pRefUndoDoc->GetTableCount()-1);
+ pRefUndoDoc->CopyToDocument(aRange, InsertDeleteFlags::FORMULA, false, rDoc, nullptr, false);
+ if (pRefUndoData)
+ pRefUndoData->DoUndo( &rDoc, false );
+}
+
+void ScMoveUndo::BeginUndo()
+{
+ ScSimpleUndo::BeginUndo();
+
+ EnableDrawAdjust( &pDocShell->GetDocument(), false );
+}
+
+void ScMoveUndo::EndUndo()
+{
+ DoSdrUndoAction( pDrawUndo.get(), &pDocShell->GetDocument() ); // must also be called when pointer is null
+
+ if (pRefUndoDoc)
+ UndoRef();
+
+ EnableDrawAdjust( &pDocShell->GetDocument(), true );
+
+ ScSimpleUndo::EndUndo();
+}
+
+ScDBFuncUndo::ScDBFuncUndo( ScDocShell* pDocSh, const ScRange& rOriginal ) :
+ ScSimpleUndo( pDocSh ),
+ aOriginalRange( rOriginal )
+{
+ pAutoDBRange = pDocSh->GetOldAutoDBRange();
+}
+
+ScDBFuncUndo::~ScDBFuncUndo()
+{
+ pAutoDBRange.reset();
+}
+
+void ScDBFuncUndo::BeginUndo()
+{
+ ScSimpleUndo::BeginUndo();
+ DoSdrUndoAction( nullptr, &pDocShell->GetDocument() );
+}
+
+void ScDBFuncUndo::EndUndo()
+{
+ ScSimpleUndo::EndUndo();
+
+ if ( !pAutoDBRange )
+ return;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTab = rDoc.GetVisibleTab();
+ ScDBData* pNoNameData = rDoc.GetAnonymousDBData(nTab);
+ if (!pNoNameData )
+ return;
+
+ SCCOL nRangeX1;
+ SCROW nRangeY1;
+ SCCOL nRangeX2;
+ SCROW nRangeY2;
+ SCTAB nRangeTab;
+ pNoNameData->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
+ pDocShell->DBAreaDeleted( nRangeTab, nRangeX1, nRangeY1, nRangeX2 );
+
+ *pNoNameData = *pAutoDBRange;
+
+ if ( pAutoDBRange->HasAutoFilter() )
+ {
+ // restore AutoFilter buttons
+ pAutoDBRange->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
+ rDoc.ApplyFlagsTab( nRangeX1, nRangeY1, nRangeX2, nRangeY1, nRangeTab, ScMF::Auto );
+ pDocShell->PostPaint( nRangeX1, nRangeY1, nRangeTab, nRangeX2, nRangeY1, nRangeTab, PaintPartFlags::Grid );
+ }
+}
+
+void ScDBFuncUndo::BeginRedo()
+{
+ RedoSdrUndoAction( nullptr );
+ if ( pAutoDBRange )
+ {
+ // move the database range to this function's position again (see ScDocShell::GetDBData)
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDBData* pNoNameData = rDoc.GetAnonymousDBData(aOriginalRange.aStart.Tab());
+ if ( pNoNameData )
+ {
+
+ SCCOL nRangeX1;
+ SCROW nRangeY1;
+ SCCOL nRangeX2;
+ SCROW nRangeY2;
+ SCTAB nRangeTab;
+ pNoNameData->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
+ pDocShell->DBAreaDeleted( nRangeTab, nRangeX1, nRangeY1, nRangeX2 );
+
+ pNoNameData->SetSortParam( ScSortParam() );
+ pNoNameData->SetQueryParam( ScQueryParam() );
+ pNoNameData->SetSubTotalParam( ScSubTotalParam() );
+
+ pNoNameData->SetArea( aOriginalRange.aStart.Tab(),
+ aOriginalRange.aStart.Col(), aOriginalRange.aStart.Row(),
+ aOriginalRange.aEnd.Col(), aOriginalRange.aEnd.Row() );
+
+ pNoNameData->SetByRow( true );
+ pNoNameData->SetAutoFilter( false );
+ // header is always set with the operation in redo
+ }
+ }
+
+ ScSimpleUndo::BeginRedo();
+}
+
+void ScDBFuncUndo::EndRedo()
+{
+ ScSimpleUndo::EndRedo();
+}
+
+ScUndoWrapper::ScUndoWrapper( std::unique_ptr<SfxUndoAction> pUndo ) :
+ pWrappedUndo( std::move(pUndo) ),
+ mnViewShellId( -1 )
+{
+ if (pWrappedUndo)
+ mnViewShellId = pWrappedUndo->GetViewShellId();
+}
+
+ScUndoWrapper::~ScUndoWrapper()
+{
+}
+
+void ScUndoWrapper::ForgetWrappedUndo()
+{
+ pWrappedUndo = nullptr; // don't delete in dtor - pointer must be stored outside
+}
+
+OUString ScUndoWrapper::GetComment() const
+{
+ if (pWrappedUndo)
+ return pWrappedUndo->GetComment();
+ return OUString();
+}
+
+ViewShellId ScUndoWrapper::GetViewShellId() const
+{
+ return mnViewShellId;
+}
+
+OUString ScUndoWrapper::GetRepeatComment(SfxRepeatTarget& rTarget) const
+{
+ if (pWrappedUndo)
+ return pWrappedUndo->GetRepeatComment(rTarget);
+ return OUString();
+}
+
+bool ScUndoWrapper::Merge( SfxUndoAction* pNextAction )
+{
+ if (pWrappedUndo)
+ return pWrappedUndo->Merge(pNextAction);
+ else
+ return false;
+}
+
+void ScUndoWrapper::Undo()
+{
+ if (pWrappedUndo)
+ pWrappedUndo->Undo();
+}
+
+void ScUndoWrapper::Redo()
+{
+ if (pWrappedUndo)
+ pWrappedUndo->Redo();
+}
+
+void ScUndoWrapper::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (pWrappedUndo)
+ pWrappedUndo->Repeat(rTarget);
+}
+
+bool ScUndoWrapper::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ if (pWrappedUndo)
+ return pWrappedUndo->CanRepeat(rTarget);
+ else
+ return false;
+}
+
+ScUndoManager::~ScUndoManager() {}
+
+/**
+ * Checks if the topmost undo action owned by pView is independent from the topmost action undo
+ * action.
+ */
+bool ScUndoManager::IsViewUndoActionIndependent(const SfxViewShell* pView, sal_uInt16& rOffset) const
+{
+ if (GetUndoActionCount() <= 1)
+ {
+ // Single or less undo, owned by another view.
+ return false;
+ }
+
+ if (!pView)
+ {
+ return false;
+ }
+
+ // Last undo action that doesn't belong to the view.
+ const SfxUndoAction* pTopAction = GetUndoAction();
+
+ ViewShellId nViewId = pView->GetViewShellId();
+
+ // Earlier undo action that belongs to the view, but is not the top one.
+ const SfxUndoAction* pViewAction = nullptr;
+ size_t nOffset = 0;
+ for (size_t i = 0; i < GetUndoActionCount(); ++i)
+ {
+ const SfxUndoAction* pAction = GetUndoAction(i);
+ if (pAction->GetViewShellId() == nViewId)
+ {
+ pViewAction = pAction;
+ nOffset = i;
+ break;
+ }
+ }
+
+ if (!pViewAction)
+ {
+ // Found no earlier undo action that belongs to the view.
+ return false;
+ }
+
+ std::optional<ScRange> topRange = getAffectedRangeFromUndo(pTopAction);
+ if (!topRange)
+ return false;
+
+ std::optional<ScRange> viewRange = getAffectedRangeFromUndo(pViewAction);
+ if (!viewRange)
+ return false;
+
+ if (topRange->Intersects(*viewRange))
+ return false;
+
+ for (size_t i = 0; i < GetRedoActionCount(); ++i)
+ {
+ auto pRedoAction = getScSimpleUndo(GetRedoAction(i));
+ if (!pRedoAction)
+ {
+ return false;
+ }
+ std::optional<ScRange> redoRange = getAffectedRangeFromUndo(pRedoAction);
+ if (!redoRange || (redoRange->Intersects(*viewRange) && pRedoAction->GetViewShellId() != nViewId))
+ {
+ // Dependent redo action and owned by another view.
+ return false;
+ }
+ }
+
+ rOffset = nOffset;
+ return true;
+}
+
+std::optional<ScRange> ScUndoManager::getAffectedRangeFromUndo(const SfxUndoAction* pAction)
+{
+ auto pSimpleUndo = getScSimpleUndo(pAction);
+ if (!pSimpleUndo)
+ return std::nullopt;
+ return pSimpleUndo->getAffectedRange();
+}
+
+const ScSimpleUndo* ScUndoManager::getScSimpleUndo(const SfxUndoAction* pAction)
+{
+ const ScSimpleUndo* pSimpleUndo = dynamic_cast<const ScSimpleUndo*>(pAction);
+ if (pSimpleUndo)
+ return pSimpleUndo;
+ auto pListAction = dynamic_cast<const SfxListUndoAction*>(pAction);
+ if (!pListAction)
+ return nullptr;
+ if (pListAction->maUndoActions.size() > 1)
+ return nullptr;
+ return dynamic_cast<ScSimpleUndo*>(pListAction->maUndoActions[0].pAction.get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */