1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <vcl/virdev.hxx>
21 #include <svx/svdundo.hxx>
22 
23 #include <undobase.hxx>
24 #include <refundo.hxx>
25 #include <docsh.hxx>
26 #include <tabvwsh.hxx>
27 #include <undoolk.hxx>
28 #include <undodraw.hxx>
29 #include <dbdata.hxx>
30 #include <attrib.hxx>
31 #include <queryparam.hxx>
32 #include <subtotalparam.hxx>
33 #include <rowheightcontext.hxx>
34 #include <column.hxx>
35 #include <sortparam.hxx>
36 #include <columnspanset.hxx>
37 
38 
ScSimpleUndo(ScDocShell * pDocSh)39 ScSimpleUndo::ScSimpleUndo( ScDocShell* pDocSh ) :
40     pDocShell( pDocSh ),
41     mnViewShellId(-1)
42 {
43     if (ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell())
44         mnViewShellId = pViewShell->GetViewShellId();
45 }
46 
GetViewShellId() const47 ViewShellId ScSimpleUndo::GetViewShellId() const
48 {
49     return mnViewShellId;
50 }
51 
SetViewMarkData(const ScMarkData & rMarkData)52 bool ScSimpleUndo::SetViewMarkData( const ScMarkData& rMarkData )
53 {
54     if ( IsPaintLocked() )
55         return false;
56 
57     ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
58     if ( !pViewShell )
59         return false;
60 
61     pViewShell->SetMarkData( rMarkData );
62     return true;
63 }
64 
Merge(SfxUndoAction * pNextAction)65 bool ScSimpleUndo::Merge( SfxUndoAction *pNextAction )
66 {
67     // A SdrUndoGroup for updating detective arrows can belong
68     // to each Undo-Action.
69     // DetectiveRefresh is always called next,
70     // the SdrUndoGroup is encapsulated in a ScUndoDraw action.
71     // AddUndoAction is only called with bTryMerg=sal_True
72     // for automatic update.
73 
74     if ( !pDetectiveUndo && dynamic_cast<const ScUndoDraw*>( pNextAction) !=  nullptr )
75     {
76         // Take SdrUndoAction from ScUndoDraw Action,
77         // ScUndoDraw is later deleted by the UndoManager
78 
79         ScUndoDraw* pCalcUndo = static_cast<ScUndoDraw*>(pNextAction);
80         pDetectiveUndo = pCalcUndo->ReleaseDrawUndo();
81         return true;
82     }
83 
84     return false;
85 }
86 
BeginUndo()87 void ScSimpleUndo::BeginUndo()
88 {
89     pDocShell->SetInUndo( true );
90 
91     ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
92     if (pViewShell)
93         pViewShell->HideAllCursors();       // for example due to merged cells
94 
95     //  detective updates happened last, must be undone first
96     if (pDetectiveUndo)
97         pDetectiveUndo->Undo();
98 }
99 
100 namespace
101 {
102     class DisableUndoGuard
103     {
104     private:
105         ScDocument& m_rDoc;
106         bool const m_bUndoEnabled;
107     public:
DisableUndoGuard(ScDocShell * pDocShell)108         explicit DisableUndoGuard(ScDocShell *pDocShell)
109             : m_rDoc(pDocShell->GetDocument())
110             , m_bUndoEnabled(m_rDoc.IsUndoEnabled())
111         {
112             m_rDoc.EnableUndo(false);
113         }
114 
~DisableUndoGuard()115         ~DisableUndoGuard()
116         {
117             m_rDoc.EnableUndo(m_bUndoEnabled);
118         }
119     };
120 }
121 
EndUndo()122 void ScSimpleUndo::EndUndo()
123 {
124     {
125         // rhbz#1352881 Temporarily turn off undo generation during
126         // SetDocumentModified
127         DisableUndoGuard aGuard(pDocShell);
128         pDocShell->SetDocumentModified();
129     }
130 
131     ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
132     if (pViewShell)
133     {
134         pViewShell->UpdateAutoFillMark();
135         pViewShell->UpdateInputHandler();
136         pViewShell->ShowAllCursors();
137     }
138 
139     pDocShell->SetInUndo( false );
140 }
141 
BeginRedo()142 void ScSimpleUndo::BeginRedo()
143 {
144     pDocShell->SetInUndo( true );   //! own Flag for Redo?
145 
146     ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
147     if (pViewShell)
148         pViewShell->HideAllCursors();       // for example due to merged cells
149 }
150 
EndRedo()151 void ScSimpleUndo::EndRedo()
152 {
153     if (pDetectiveUndo)
154         pDetectiveUndo->Redo();
155 
156     {
157         // rhbz#1352881 Temporarily turn off undo generation during
158         // SetDocumentModified
159         DisableUndoGuard aGuard(pDocShell);
160         pDocShell->SetDocumentModified();
161     }
162 
163     ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
164     if (pViewShell)
165     {
166         pViewShell->UpdateAutoFillMark();
167         pViewShell->UpdateInputHandler();
168         pViewShell->ShowAllCursors();
169     }
170 
171     pDocShell->SetInUndo( false );
172 }
173 
BroadcastChanges(const ScRange & rRange)174 void ScSimpleUndo::BroadcastChanges( const ScRange& rRange )
175 {
176     ScDocument& rDoc = pDocShell->GetDocument();
177     rDoc.BroadcastCells(rRange, SfxHintId::ScDataChanged);
178 }
179 
180 namespace {
181 
182 class SpanBroadcaster : public sc::ColumnSpanSet::ColumnAction
183 {
184     ScDocument& mrDoc;
185     SCTAB mnCurTab;
186     SCCOL mnCurCol;
187 
188 public:
SpanBroadcaster(ScDocument & rDoc)189     explicit SpanBroadcaster( ScDocument& rDoc ) : mrDoc(rDoc), mnCurTab(-1), mnCurCol(-1) {}
190 
startColumn(ScColumn * pCol)191     virtual void startColumn( ScColumn* pCol ) override
192     {
193         mnCurTab = pCol->GetTab();
194         mnCurCol = pCol->GetCol();
195     }
196 
execute(SCROW nRow1,SCROW nRow2,bool bVal)197     virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
198     {
199         if (!bVal)
200             return;
201 
202         ScRange aRange(mnCurCol, nRow1, mnCurTab, mnCurCol, nRow2, mnCurTab);
203         mrDoc.BroadcastCells(aRange, SfxHintId::ScDataChanged);
204     };
205 };
206 
207 }
208 
BroadcastChanges(const DataSpansType & rSpans)209 void ScSimpleUndo::BroadcastChanges( const DataSpansType& rSpans )
210 {
211     ScDocument& rDoc = pDocShell->GetDocument();
212     SpanBroadcaster aBroadcaster(rDoc);
213 
214     for (const auto& rEntry : rSpans)
215     {
216         const sc::ColumnSpanSet& rSet = *rEntry.second;
217         rSet.executeColumnAction(rDoc, aBroadcaster);
218     }
219 }
220 
ShowTable(SCTAB nTab)221 void ScSimpleUndo::ShowTable( SCTAB nTab )
222 {
223     ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
224     if (pViewShell)
225         pViewShell->SetTabNo( nTab );
226 }
227 
ShowTable(const ScRange & rRange)228 void ScSimpleUndo::ShowTable( const ScRange& rRange )
229 {
230     ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
231     if (pViewShell)
232     {
233         SCTAB nStart = rRange.aStart.Tab();
234         SCTAB nEnd   = rRange.aEnd.Tab();
235         SCTAB nTab = pViewShell->GetViewData().GetTabNo();
236         if ( nTab < nStart || nTab > nEnd )                     // if not in range:
237             pViewShell->SetTabNo( nStart );                     // at beginning of the range
238     }
239 }
240 
ScBlockUndo(ScDocShell * pDocSh,const ScRange & rRange,ScBlockUndoMode eBlockMode)241 ScBlockUndo::ScBlockUndo( ScDocShell* pDocSh, const ScRange& rRange,
242                                             ScBlockUndoMode eBlockMode ) :
243     ScSimpleUndo( pDocSh ),
244     aBlockRange( rRange ),
245     eMode( eBlockMode )
246 {
247     pDrawUndo = GetSdrUndoAction( &pDocShell->GetDocument() );
248 }
249 
~ScBlockUndo()250 ScBlockUndo::~ScBlockUndo()
251 {
252     pDrawUndo.reset();
253 }
254 
BeginUndo()255 void ScBlockUndo::BeginUndo()
256 {
257     ScSimpleUndo::BeginUndo();
258     EnableDrawAdjust( &pDocShell->GetDocument(), false );
259 }
260 
EndUndo()261 void ScBlockUndo::EndUndo()
262 {
263     if (eMode == SC_UNDO_AUTOHEIGHT)
264         AdjustHeight();
265 
266     EnableDrawAdjust( &pDocShell->GetDocument(), true );
267     DoSdrUndoAction( pDrawUndo.get(), &pDocShell->GetDocument() );
268 
269     ShowBlock();
270     ScSimpleUndo::EndUndo();
271 }
272 
EndRedo()273 void ScBlockUndo::EndRedo()
274 {
275     if (eMode == SC_UNDO_AUTOHEIGHT)
276         AdjustHeight();
277 
278     ShowBlock();
279     ScSimpleUndo::EndRedo();
280 }
281 
AdjustHeight()282 bool ScBlockUndo::AdjustHeight()
283 {
284     ScDocument& rDoc = pDocShell->GetDocument();
285 
286     ScopedVclPtrInstance< VirtualDevice > pVirtDev;
287     Fraction aZoomX( 1, 1 );
288     Fraction aZoomY = aZoomX;
289     double nPPTX, nPPTY;
290     ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
291     if (pViewShell)
292     {
293         ScViewData& rData = pViewShell->GetViewData();
294         nPPTX = rData.GetPPTX();
295         nPPTY = rData.GetPPTY();
296         aZoomX = rData.GetZoomX();
297         aZoomY = rData.GetZoomY();
298     }
299     else
300     {
301         // Leave zoom at 100
302         nPPTX = ScGlobal::nScreenPPTX;
303         nPPTY = ScGlobal::nScreenPPTY;
304     }
305 
306     sc::RowHeightContext aCxt(nPPTX, nPPTY, aZoomX, aZoomY, pVirtDev);
307     bool bRet = rDoc.SetOptimalHeight(
308         aCxt, aBlockRange.aStart.Row(), aBlockRange.aEnd.Row(), aBlockRange.aStart.Tab());
309 
310     if (bRet)
311     {
312         // tdf#76183: recalculate objects' positions
313         rDoc.SetDrawPageSize(aBlockRange.aStart.Tab());
314 
315         pDocShell->PostPaint( 0,      aBlockRange.aStart.Row(), aBlockRange.aStart.Tab(),
316                               rDoc.MaxCol(), rDoc.MaxRow(),                   aBlockRange.aEnd.Tab(),
317                               PaintPartFlags::Grid | PaintPartFlags::Left );
318     }
319     return bRet;
320 }
321 
ShowBlock()322 void ScBlockUndo::ShowBlock()
323 {
324     if ( IsPaintLocked() )
325         return;
326 
327     ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
328     if (pViewShell)
329     {
330         ShowTable( aBlockRange );       // with multiple sheets in range each of them is good
331         pViewShell->MoveCursorAbs( aBlockRange.aStart.Col(), aBlockRange.aStart.Row(),
332                                    SC_FOLLOW_JUMP, false, false );
333         SCTAB nTab = pViewShell->GetViewData().GetTabNo();
334         ScRange aRange = aBlockRange;
335         aRange.aStart.SetTab( nTab );
336         aRange.aEnd.SetTab( nTab );
337         pViewShell->MarkRange( aRange );
338 
339         // not through SetMarkArea to MarkData, due to possibly lacking paint
340     }
341 }
342 
ScMultiBlockUndo(ScDocShell * pDocSh,const ScRangeList & rRanges)343 ScMultiBlockUndo::ScMultiBlockUndo(
344     ScDocShell* pDocSh, const ScRangeList& rRanges) :
345     ScSimpleUndo(pDocSh),
346     maBlockRanges(rRanges)
347 {
348     mpDrawUndo = GetSdrUndoAction( &pDocShell->GetDocument() );
349 }
350 
~ScMultiBlockUndo()351 ScMultiBlockUndo::~ScMultiBlockUndo()
352 {
353     mpDrawUndo.reset();
354 }
355 
BeginUndo()356 void ScMultiBlockUndo::BeginUndo()
357 {
358     ScSimpleUndo::BeginUndo();
359     EnableDrawAdjust(&pDocShell->GetDocument(), false);
360 }
361 
EndUndo()362 void ScMultiBlockUndo::EndUndo()
363 {
364     EnableDrawAdjust(&pDocShell->GetDocument(), true);
365     DoSdrUndoAction(mpDrawUndo.get(), &pDocShell->GetDocument());
366 
367     ShowBlock();
368     ScSimpleUndo::EndUndo();
369 }
370 
EndRedo()371 void ScMultiBlockUndo::EndRedo()
372 {
373     ShowBlock();
374     ScSimpleUndo::EndRedo();
375 }
376 
ShowBlock()377 void ScMultiBlockUndo::ShowBlock()
378 {
379     if ( IsPaintLocked() )
380         return;
381 
382     ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
383     if (!pViewShell)
384         return;
385 
386     if (maBlockRanges.empty())
387         return;
388 
389     // Move to the sheet of the first range.
390     ScRange aRange = maBlockRanges.front();
391     ShowTable(aRange);
392     pViewShell->MoveCursorAbs(
393         aRange.aStart.Col(), aRange.aStart.Row(), SC_FOLLOW_JUMP, false, false);
394     SCTAB nTab = pViewShell->GetViewData().GetTabNo();
395     aRange.aStart.SetTab(nTab);
396     aRange.aEnd.SetTab(nTab);
397     pViewShell->MarkRange(aRange, false);
398 
399     for (size_t i = 1, n = maBlockRanges.size(); i < n; ++i)
400     {
401         aRange = maBlockRanges[i];
402         aRange.aStart.SetTab(nTab);
403         aRange.aEnd.SetTab(nTab);
404         pViewShell->MarkRange(aRange, false, true);
405     }
406 }
407 
ScMoveUndo(ScDocShell * pDocSh,ScDocumentUniquePtr pRefDoc,std::unique_ptr<ScRefUndoData> pRefData)408 ScMoveUndo::ScMoveUndo( ScDocShell* pDocSh, ScDocumentUniquePtr pRefDoc, std::unique_ptr<ScRefUndoData> pRefData ) :
409     ScSimpleUndo( pDocSh ),
410     pRefUndoDoc( std::move(pRefDoc) ),
411     pRefUndoData( std::move(pRefData) )
412 {
413     ScDocument& rDoc = pDocShell->GetDocument();
414     if (pRefUndoData)
415         pRefUndoData->DeleteUnchanged(&rDoc);
416     pDrawUndo = GetSdrUndoAction( &rDoc );
417 }
418 
~ScMoveUndo()419 ScMoveUndo::~ScMoveUndo()
420 {
421     pRefUndoData.reset();
422     pRefUndoDoc.reset();
423     pDrawUndo.reset();
424 }
425 
UndoRef()426 void ScMoveUndo::UndoRef()
427 {
428     ScDocument& rDoc = pDocShell->GetDocument();
429     ScRange aRange(0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),pRefUndoDoc->GetTableCount()-1);
430     pRefUndoDoc->CopyToDocument(aRange, InsertDeleteFlags::FORMULA, false, rDoc, nullptr, false);
431     if (pRefUndoData)
432         pRefUndoData->DoUndo( &rDoc, false );
433 }
434 
BeginUndo()435 void ScMoveUndo::BeginUndo()
436 {
437     ScSimpleUndo::BeginUndo();
438 
439     EnableDrawAdjust( &pDocShell->GetDocument(), false );
440 }
441 
EndUndo()442 void ScMoveUndo::EndUndo()
443 {
444     DoSdrUndoAction( pDrawUndo.get(), &pDocShell->GetDocument() );     // must also be called when pointer is null
445 
446     if (pRefUndoDoc)
447         UndoRef();
448 
449     EnableDrawAdjust( &pDocShell->GetDocument(), true );
450 
451     ScSimpleUndo::EndUndo();
452 }
453 
ScDBFuncUndo(ScDocShell * pDocSh,const ScRange & rOriginal)454 ScDBFuncUndo::ScDBFuncUndo( ScDocShell* pDocSh, const ScRange& rOriginal ) :
455     ScSimpleUndo( pDocSh ),
456     aOriginalRange( rOriginal )
457 {
458     pAutoDBRange = pDocSh->GetOldAutoDBRange();
459 }
460 
~ScDBFuncUndo()461 ScDBFuncUndo::~ScDBFuncUndo()
462 {
463     pAutoDBRange.reset();
464 }
465 
BeginUndo()466 void ScDBFuncUndo::BeginUndo()
467 {
468     ScSimpleUndo::BeginUndo();
469     DoSdrUndoAction( nullptr, &pDocShell->GetDocument() );
470 }
471 
EndUndo()472 void ScDBFuncUndo::EndUndo()
473 {
474     ScSimpleUndo::EndUndo();
475 
476     if ( pAutoDBRange )
477     {
478         ScDocument& rDoc = pDocShell->GetDocument();
479         SCTAB nTab = rDoc.GetVisibleTab();
480         ScDBData* pNoNameData = rDoc.GetAnonymousDBData(nTab);
481         if (pNoNameData )
482         {
483             SCCOL nRangeX1;
484             SCROW nRangeY1;
485             SCCOL nRangeX2;
486             SCROW nRangeY2;
487             SCTAB nRangeTab;
488             pNoNameData->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
489             pDocShell->DBAreaDeleted( nRangeTab, nRangeX1, nRangeY1, nRangeX2 );
490 
491             *pNoNameData = *pAutoDBRange;
492 
493             if ( pAutoDBRange->HasAutoFilter() )
494             {
495                 // restore AutoFilter buttons
496                 pAutoDBRange->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
497                 rDoc.ApplyFlagsTab( nRangeX1, nRangeY1, nRangeX2, nRangeY1, nRangeTab, ScMF::Auto );
498                 pDocShell->PostPaint( nRangeX1, nRangeY1, nRangeTab, nRangeX2, nRangeY1, nRangeTab, PaintPartFlags::Grid );
499             }
500         }
501     }
502 }
503 
BeginRedo()504 void ScDBFuncUndo::BeginRedo()
505 {
506     RedoSdrUndoAction( nullptr );
507     if ( pAutoDBRange )
508     {
509         // move the database range to this function's position again (see ScDocShell::GetDBData)
510 
511         ScDocument& rDoc = pDocShell->GetDocument();
512         ScDBData* pNoNameData = rDoc.GetAnonymousDBData(aOriginalRange.aStart.Tab());
513         if ( pNoNameData )
514         {
515 
516             SCCOL nRangeX1;
517             SCROW nRangeY1;
518             SCCOL nRangeX2;
519             SCROW nRangeY2;
520             SCTAB nRangeTab;
521             pNoNameData->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
522             pDocShell->DBAreaDeleted( nRangeTab, nRangeX1, nRangeY1, nRangeX2 );
523 
524             pNoNameData->SetSortParam( ScSortParam() );
525             pNoNameData->SetQueryParam( ScQueryParam() );
526             pNoNameData->SetSubTotalParam( ScSubTotalParam() );
527 
528             pNoNameData->SetArea( aOriginalRange.aStart.Tab(),
529                                   aOriginalRange.aStart.Col(), aOriginalRange.aStart.Row(),
530                                   aOriginalRange.aEnd.Col(), aOriginalRange.aEnd.Row() );
531 
532             pNoNameData->SetByRow( true );
533             pNoNameData->SetAutoFilter( false );
534             // header is always set with the operation in redo
535         }
536     }
537 
538     ScSimpleUndo::BeginRedo();
539 }
540 
EndRedo()541 void ScDBFuncUndo::EndRedo()
542 {
543     ScSimpleUndo::EndRedo();
544 }
545 
ScUndoWrapper(std::unique_ptr<SfxUndoAction> pUndo)546 ScUndoWrapper::ScUndoWrapper( std::unique_ptr<SfxUndoAction> pUndo ) :
547     pWrappedUndo( std::move(pUndo) ),
548     mnViewShellId( -1 )
549 {
550     if (pWrappedUndo)
551         mnViewShellId = pWrappedUndo->GetViewShellId();
552 }
553 
~ScUndoWrapper()554 ScUndoWrapper::~ScUndoWrapper()
555 {
556 }
557 
ForgetWrappedUndo()558 void ScUndoWrapper::ForgetWrappedUndo()
559 {
560     pWrappedUndo = nullptr;    // don't delete in dtor - pointer must be stored outside
561 }
562 
GetComment() const563 OUString ScUndoWrapper::GetComment() const
564 {
565     if (pWrappedUndo)
566         return pWrappedUndo->GetComment();
567     return OUString();
568 }
569 
GetViewShellId() const570 ViewShellId ScUndoWrapper::GetViewShellId() const
571 {
572     return mnViewShellId;
573 }
574 
GetRepeatComment(SfxRepeatTarget & rTarget) const575 OUString ScUndoWrapper::GetRepeatComment(SfxRepeatTarget& rTarget) const
576 {
577     if (pWrappedUndo)
578         return pWrappedUndo->GetRepeatComment(rTarget);
579     return OUString();
580 }
581 
Merge(SfxUndoAction * pNextAction)582 bool ScUndoWrapper::Merge( SfxUndoAction* pNextAction )
583 {
584     if (pWrappedUndo)
585         return pWrappedUndo->Merge(pNextAction);
586     else
587         return false;
588 }
589 
Undo()590 void ScUndoWrapper::Undo()
591 {
592     if (pWrappedUndo)
593         pWrappedUndo->Undo();
594 }
595 
Redo()596 void ScUndoWrapper::Redo()
597 {
598     if (pWrappedUndo)
599         pWrappedUndo->Redo();
600 }
601 
Repeat(SfxRepeatTarget & rTarget)602 void ScUndoWrapper::Repeat(SfxRepeatTarget& rTarget)
603 {
604     if (pWrappedUndo)
605         pWrappedUndo->Repeat(rTarget);
606 }
607 
CanRepeat(SfxRepeatTarget & rTarget) const608 bool ScUndoWrapper::CanRepeat(SfxRepeatTarget& rTarget) const
609 {
610     if (pWrappedUndo)
611         return pWrappedUndo->CanRepeat(rTarget);
612     else
613         return false;
614 }
615 
616 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
617