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