1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <vcl/virdev.hxx>
22 #include <svx/svdundo.hxx>
24 #include <undobase.hxx>
25 #include <refundo.hxx>
27 #include <tabvwsh.hxx>
28 #include <undoolk.hxx>
29 #include <undodraw.hxx>
32 #include <queryparam.hxx>
33 #include <subtotalparam.hxx>
34 #include <rowheightcontext.hxx>
36 #include <sortparam.hxx>
37 #include <columnspanset.hxx>
38 #include <undomanager.hxx>
39 #include <sizedev.hxx>
42 ScSimpleUndo::ScSimpleUndo( ScDocShell
* pDocSh
) :
46 if (ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell())
47 mnViewShellId
= pViewShell
->GetViewShellId();
50 ViewShellId
ScSimpleUndo::GetViewShellId() const
55 bool ScSimpleUndo::SetViewMarkData( const ScMarkData
& rMarkData
)
57 if ( IsPaintLocked() )
60 ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell();
64 pViewShell
->SetMarkData( rMarkData
);
68 bool ScSimpleUndo::Merge( SfxUndoAction
*pNextAction
)
70 // A SdrUndoGroup for updating detective arrows can belong
71 // to each Undo-Action.
72 // DetectiveRefresh is always called next,
73 // the SdrUndoGroup is encapsulated in a ScUndoDraw action.
74 // AddUndoAction is only called with bTryMerg=sal_True
75 // for automatic update.
77 if ( !pDetectiveUndo
&& dynamic_cast<const ScUndoDraw
*>( pNextAction
) != nullptr )
79 // Take SdrUndoAction from ScUndoDraw Action,
80 // ScUndoDraw is later deleted by the UndoManager
82 ScUndoDraw
* pCalcUndo
= static_cast<ScUndoDraw
*>(pNextAction
);
83 pDetectiveUndo
= pCalcUndo
->ReleaseDrawUndo();
90 void ScSimpleUndo::BeginUndo()
92 pDocShell
->SetInUndo( true );
94 ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell();
96 pViewShell
->HideAllCursors(); // for example due to merged cells
98 // detective updates happened last, must be undone first
100 pDetectiveUndo
->Undo();
105 class DisableUndoGuard
111 explicit DisableUndoGuard(ScDocShell
*pDocShell
)
112 : m_rDoc(pDocShell
->GetDocument())
113 , m_bUndoEnabled(m_rDoc
.IsUndoEnabled())
115 m_rDoc
.EnableUndo(false);
120 m_rDoc
.EnableUndo(m_bUndoEnabled
);
125 void ScSimpleUndo::EndUndo()
128 // rhbz#1352881 Temporarily turn off undo generation during
129 // SetDocumentModified
130 DisableUndoGuard
aGuard(pDocShell
);
131 pDocShell
->SetDocumentModified();
134 ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell();
137 pViewShell
->UpdateAutoFillMark();
138 pViewShell
->UpdateInputHandler();
139 pViewShell
->ShowAllCursors();
142 pDocShell
->SetInUndo( false );
145 void ScSimpleUndo::BeginRedo()
147 pDocShell
->SetInUndo( true ); //! own Flag for Redo?
149 ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell();
151 pViewShell
->HideAllCursors(); // for example due to merged cells
154 void ScSimpleUndo::EndRedo()
157 pDetectiveUndo
->Redo();
160 // rhbz#1352881 Temporarily turn off undo generation during
161 // SetDocumentModified
162 DisableUndoGuard
aGuard(pDocShell
);
163 pDocShell
->SetDocumentModified();
166 ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell();
169 pViewShell
->UpdateAutoFillMark();
170 pViewShell
->UpdateInputHandler();
171 pViewShell
->ShowAllCursors();
174 pDocShell
->SetInUndo( false );
177 void ScSimpleUndo::BroadcastChanges( const ScRange
& rRange
)
179 ScDocument
& rDoc
= pDocShell
->GetDocument();
180 rDoc
.BroadcastCells(rRange
, SfxHintId::ScDataChanged
);
185 class SpanBroadcaster
: public sc::ColumnSpanSet::ColumnAction
192 explicit SpanBroadcaster( ScDocument
& rDoc
) : mrDoc(rDoc
), mnCurTab(-1), mnCurCol(-1) {}
194 virtual void startColumn( ScColumn
* pCol
) override
196 mnCurTab
= pCol
->GetTab();
197 mnCurCol
= pCol
->GetCol();
200 virtual void execute( SCROW nRow1
, SCROW nRow2
, bool bVal
) override
205 ScRange
aRange(mnCurCol
, nRow1
, mnCurTab
, mnCurCol
, nRow2
, mnCurTab
);
206 mrDoc
.BroadcastCells(aRange
, SfxHintId::ScDataChanged
);
212 void ScSimpleUndo::BroadcastChanges( const DataSpansType
& rSpans
)
214 ScDocument
& rDoc
= pDocShell
->GetDocument();
215 SpanBroadcaster
aBroadcaster(rDoc
);
217 for (const auto& rEntry
: rSpans
)
219 const sc::ColumnSpanSet
& rSet
= *rEntry
.second
;
220 rSet
.executeColumnAction(rDoc
, aBroadcaster
);
224 void ScSimpleUndo::ShowTable( SCTAB nTab
)
226 ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell();
228 pViewShell
->SetTabNo( nTab
);
231 void ScSimpleUndo::ShowTable( const ScRange
& rRange
)
233 ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell();
236 SCTAB nStart
= rRange
.aStart
.Tab();
237 SCTAB nEnd
= rRange
.aEnd
.Tab();
238 SCTAB nTab
= pViewShell
->GetViewData().GetTabNo();
239 if ( nTab
< nStart
|| nTab
> nEnd
) // if not in range:
240 pViewShell
->SetTabNo( nStart
); // at beginning of the range
244 ScBlockUndo::ScBlockUndo( ScDocShell
* pDocSh
, const ScRange
& rRange
,
245 ScBlockUndoMode eBlockMode
) :
246 ScSimpleUndo( pDocSh
),
247 aBlockRange( rRange
),
250 pDrawUndo
= GetSdrUndoAction( &pDocShell
->GetDocument() );
253 ScBlockUndo::~ScBlockUndo()
258 void ScBlockUndo::BeginUndo()
260 ScSimpleUndo::BeginUndo();
261 EnableDrawAdjust( &pDocShell
->GetDocument(), false );
264 void ScBlockUndo::EndUndo()
266 if (eMode
== SC_UNDO_AUTOHEIGHT
)
269 EnableDrawAdjust( &pDocShell
->GetDocument(), true );
270 DoSdrUndoAction( pDrawUndo
.get(), &pDocShell
->GetDocument() );
272 // tdf#161712 invoke ScSimpleUndo::EndUndo() before ShowBlock()
273 // If this is an instance of ScUndoAutoFill, ShowBlock() will invoke
274 // ScTabViewShell::MoveCursorAbs() which will delete this instance
275 // so invoke ScSimpleUndo::EndUndo() first.
276 ScSimpleUndo::EndUndo();
280 void ScBlockUndo::EndRedo()
282 if (eMode
== SC_UNDO_AUTOHEIGHT
)
286 ScSimpleUndo::EndRedo();
289 bool ScBlockUndo::AdjustHeight()
291 ScDocument
& rDoc
= pDocShell
->GetDocument();
293 ScSizeDeviceProvider
aProv(pDocShell
);
294 Fraction
aZoomX( 1, 1 );
295 Fraction aZoomY
= aZoomX
;
297 ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell();
300 if (aProv
.IsPrinter())
302 nPPTX
= aProv
.GetPPTX();
303 nPPTY
= aProv
.GetPPTY();
307 ScViewData
& rData
= pViewShell
->GetViewData();
308 nPPTX
= rData
.GetPPTX();
309 nPPTY
= rData
.GetPPTY();
310 aZoomX
= rData
.GetZoomX();
311 aZoomY
= rData
.GetZoomY();
317 nPPTX
= ScGlobal::nScreenPPTX
;
318 nPPTY
= ScGlobal::nScreenPPTY
;
321 sc::RowHeightContext
aCxt(rDoc
.MaxRow(), nPPTX
, nPPTY
, aZoomX
, aZoomY
, aProv
.GetDevice());
322 bool bRet
= rDoc
.SetOptimalHeight(
323 aCxt
, aBlockRange
.aStart
.Row(), aBlockRange
.aEnd
.Row(), aBlockRange
.aStart
.Tab(), true);
327 // tdf#76183: recalculate objects' positions
328 rDoc
.SetDrawPageSize(aBlockRange
.aStart
.Tab());
330 pDocShell
->PostPaint( 0, aBlockRange
.aStart
.Row(), aBlockRange
.aStart
.Tab(),
331 rDoc
.MaxCol(), rDoc
.MaxRow(), aBlockRange
.aEnd
.Tab(),
332 PaintPartFlags::Grid
| PaintPartFlags::Left
);
337 void ScBlockUndo::ShowBlock()
339 if ( IsPaintLocked() )
342 ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell();
346 ShowTable( aBlockRange
); // with multiple sheets in range each of them is good
347 pViewShell
->MoveCursorAbs( aBlockRange
.aStart
.Col(), aBlockRange
.aStart
.Row(),
348 SC_FOLLOW_JUMP
, false, false );
349 SCTAB nTab
= pViewShell
->GetViewData().GetTabNo();
350 ScRange aRange
= aBlockRange
;
351 aRange
.aStart
.SetTab( nTab
);
352 aRange
.aEnd
.SetTab( nTab
);
353 pViewShell
->MarkRange( aRange
);
355 // not through SetMarkArea to MarkData, due to possibly lacking paint
358 ScMultiBlockUndo::ScMultiBlockUndo(
359 ScDocShell
* pDocSh
, ScRangeList aRanges
) :
360 ScSimpleUndo(pDocSh
),
361 maBlockRanges(std::move(aRanges
))
363 mpDrawUndo
= GetSdrUndoAction( &pDocShell
->GetDocument() );
366 ScMultiBlockUndo::~ScMultiBlockUndo()
371 void ScMultiBlockUndo::BeginUndo()
373 ScSimpleUndo::BeginUndo();
374 EnableDrawAdjust(&pDocShell
->GetDocument(), false);
377 void ScMultiBlockUndo::EndUndo()
379 EnableDrawAdjust(&pDocShell
->GetDocument(), true);
380 DoSdrUndoAction(mpDrawUndo
.get(), &pDocShell
->GetDocument());
383 ScSimpleUndo::EndUndo();
386 void ScMultiBlockUndo::EndRedo()
389 ScSimpleUndo::EndRedo();
392 void ScMultiBlockUndo::ShowBlock()
394 if ( IsPaintLocked() )
397 ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell();
401 if (maBlockRanges
.empty())
404 // Move to the sheet of the first range.
405 ScRange aRange
= maBlockRanges
.front();
407 pViewShell
->MoveCursorAbs(
408 aRange
.aStart
.Col(), aRange
.aStart
.Row(), SC_FOLLOW_JUMP
, false, false);
409 SCTAB nTab
= pViewShell
->GetViewData().GetTabNo();
410 aRange
.aStart
.SetTab(nTab
);
411 aRange
.aEnd
.SetTab(nTab
);
412 pViewShell
->MarkRange(aRange
, false);
414 for (size_t i
= 1, n
= maBlockRanges
.size(); i
< n
; ++i
)
416 aRange
= maBlockRanges
[i
];
417 aRange
.aStart
.SetTab(nTab
);
418 aRange
.aEnd
.SetTab(nTab
);
419 pViewShell
->MarkRange(aRange
, false, true);
423 ScMoveUndo::ScMoveUndo( ScDocShell
* pDocSh
, ScDocumentUniquePtr pRefDoc
, std::unique_ptr
<ScRefUndoData
> pRefData
) :
424 ScSimpleUndo( pDocSh
),
425 pRefUndoDoc( std::move(pRefDoc
) ),
426 pRefUndoData( std::move(pRefData
) )
428 ScDocument
& rDoc
= pDocShell
->GetDocument();
430 pRefUndoData
->DeleteUnchanged(&rDoc
);
431 pDrawUndo
= GetSdrUndoAction( &rDoc
);
434 ScMoveUndo::~ScMoveUndo()
436 pRefUndoData
.reset();
441 void ScMoveUndo::UndoRef()
443 ScDocument
& rDoc
= pDocShell
->GetDocument();
444 ScRange
aRange(0,0,0, rDoc
.MaxCol(),rDoc
.MaxRow(),pRefUndoDoc
->GetTableCount()-1);
445 pRefUndoDoc
->CopyToDocument(aRange
, InsertDeleteFlags::FORMULA
, false, rDoc
, nullptr, false);
447 pRefUndoData
->DoUndo( &rDoc
, false );
450 void ScMoveUndo::BeginUndo()
452 ScSimpleUndo::BeginUndo();
454 EnableDrawAdjust( &pDocShell
->GetDocument(), false );
457 void ScMoveUndo::EndUndo()
459 DoSdrUndoAction( pDrawUndo
.get(), &pDocShell
->GetDocument() ); // must also be called when pointer is null
464 EnableDrawAdjust( &pDocShell
->GetDocument(), true );
466 ScSimpleUndo::EndUndo();
469 ScDBFuncUndo::ScDBFuncUndo( ScDocShell
* pDocSh
, const ScRange
& rOriginal
) :
470 ScSimpleUndo( pDocSh
),
471 aOriginalRange( rOriginal
)
473 pAutoDBRange
= pDocSh
->GetOldAutoDBRange();
476 ScDBFuncUndo::~ScDBFuncUndo()
478 pAutoDBRange
.reset();
481 void ScDBFuncUndo::BeginUndo()
483 ScSimpleUndo::BeginUndo();
484 DoSdrUndoAction( nullptr, &pDocShell
->GetDocument() );
487 void ScDBFuncUndo::EndUndo()
489 ScSimpleUndo::EndUndo();
494 ScDocument
& rDoc
= pDocShell
->GetDocument();
495 SCTAB nTab
= rDoc
.GetVisibleTab();
496 ScDBData
* pNoNameData
= rDoc
.GetAnonymousDBData(nTab
);
505 pNoNameData
->GetArea( nRangeTab
, nRangeX1
, nRangeY1
, nRangeX2
, nRangeY2
);
506 pDocShell
->DBAreaDeleted( nRangeTab
, nRangeX1
, nRangeY1
, nRangeX2
);
508 *pNoNameData
= *pAutoDBRange
;
510 if ( pAutoDBRange
->HasAutoFilter() )
512 // restore AutoFilter buttons
513 pAutoDBRange
->GetArea( nRangeTab
, nRangeX1
, nRangeY1
, nRangeX2
, nRangeY2
);
514 rDoc
.ApplyFlagsTab( nRangeX1
, nRangeY1
, nRangeX2
, nRangeY1
, nRangeTab
, ScMF::Auto
);
515 pDocShell
->PostPaint( nRangeX1
, nRangeY1
, nRangeTab
, nRangeX2
, nRangeY1
, nRangeTab
, PaintPartFlags::Grid
);
519 void ScDBFuncUndo::BeginRedo()
521 RedoSdrUndoAction( nullptr );
524 // move the database range to this function's position again (see ScDocShell::GetDBData)
526 ScDocument
& rDoc
= pDocShell
->GetDocument();
527 ScDBData
* pNoNameData
= rDoc
.GetAnonymousDBData(aOriginalRange
.aStart
.Tab());
536 pNoNameData
->GetArea( nRangeTab
, nRangeX1
, nRangeY1
, nRangeX2
, nRangeY2
);
537 pDocShell
->DBAreaDeleted( nRangeTab
, nRangeX1
, nRangeY1
, nRangeX2
);
539 pNoNameData
->SetSortParam( ScSortParam() );
540 pNoNameData
->SetQueryParam( ScQueryParam() );
541 pNoNameData
->SetSubTotalParam( ScSubTotalParam() );
543 pNoNameData
->SetArea( aOriginalRange
.aStart
.Tab(),
544 aOriginalRange
.aStart
.Col(), aOriginalRange
.aStart
.Row(),
545 aOriginalRange
.aEnd
.Col(), aOriginalRange
.aEnd
.Row() );
547 pNoNameData
->SetByRow( true );
548 pNoNameData
->SetAutoFilter( false );
549 // header is always set with the operation in redo
553 ScSimpleUndo::BeginRedo();
556 void ScDBFuncUndo::EndRedo()
558 ScSimpleUndo::EndRedo();
561 ScUndoWrapper::ScUndoWrapper( std::unique_ptr
<SfxUndoAction
> pUndo
) :
562 pWrappedUndo( std::move(pUndo
) ),
566 mnViewShellId
= pWrappedUndo
->GetViewShellId();
569 ScUndoWrapper::~ScUndoWrapper()
573 OUString
ScUndoWrapper::GetComment() const
576 return pWrappedUndo
->GetComment();
580 ViewShellId
ScUndoWrapper::GetViewShellId() const
582 return mnViewShellId
;
585 OUString
ScUndoWrapper::GetRepeatComment(SfxRepeatTarget
& rTarget
) const
588 return pWrappedUndo
->GetRepeatComment(rTarget
);
592 bool ScUndoWrapper::Merge( SfxUndoAction
* pNextAction
)
595 return pWrappedUndo
->Merge(pNextAction
);
600 void ScUndoWrapper::Undo()
603 pWrappedUndo
->Undo();
606 void ScUndoWrapper::Redo()
609 pWrappedUndo
->Redo();
612 void ScUndoWrapper::Repeat(SfxRepeatTarget
& rTarget
)
615 pWrappedUndo
->Repeat(rTarget
);
618 bool ScUndoWrapper::CanRepeat(SfxRepeatTarget
& rTarget
) const
621 return pWrappedUndo
->CanRepeat(rTarget
);
626 ScUndoManager::~ScUndoManager() {}
629 * Checks if the topmost undo action owned by pView is independent from the topmost action undo
632 bool ScUndoManager::IsViewUndoActionIndependent(const SfxViewShell
* pView
, sal_uInt16
& rOffset
) const
634 if (GetUndoActionCount() <= 1)
636 // Single or less undo, owned by another view.
645 // Last undo action that doesn't belong to the view.
646 const SfxUndoAction
* pTopAction
= GetUndoAction();
648 ViewShellId nViewId
= pView
->GetViewShellId();
650 // Earlier undo action that belongs to the view, but is not the top one.
651 const SfxUndoAction
* pViewAction
= nullptr;
653 for (size_t i
= 0; i
< GetUndoActionCount(); ++i
)
655 const SfxUndoAction
* pAction
= GetUndoAction(i
);
656 if (pAction
->GetViewShellId() == nViewId
)
658 pViewAction
= pAction
;
666 // Found no earlier undo action that belongs to the view.
670 std::optional
<ScRange
> topRange
= getAffectedRangeFromUndo(pTopAction
);
674 std::optional
<ScRange
> viewRange
= getAffectedRangeFromUndo(pViewAction
);
678 if (topRange
->Intersects(*viewRange
))
681 for (size_t i
= 0; i
< GetRedoActionCount(); ++i
)
683 auto pRedoAction
= getScSimpleUndo(GetRedoAction(i
));
688 std::optional
<ScRange
> redoRange
= getAffectedRangeFromUndo(pRedoAction
);
689 if (!redoRange
|| (redoRange
->Intersects(*viewRange
) && pRedoAction
->GetViewShellId() != nViewId
))
691 // Dependent redo action and owned by another view.
700 std::optional
<ScRange
> ScUndoManager::getAffectedRangeFromUndo(const SfxUndoAction
* pAction
)
702 auto pSimpleUndo
= getScSimpleUndo(pAction
);
705 return pSimpleUndo
->getAffectedRange();
708 const ScSimpleUndo
* ScUndoManager::getScSimpleUndo(const SfxUndoAction
* pAction
)
710 const ScSimpleUndo
* pSimpleUndo
= dynamic_cast<const ScSimpleUndo
*>(pAction
);
713 auto pListAction
= dynamic_cast<const SfxListUndoAction
*>(pAction
);
716 if (pListAction
->maUndoActions
.size() > 1)
718 return dynamic_cast<ScSimpleUndo
*>(pListAction
->maUndoActions
[0].pAction
.get());
721 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */