Avoid potential negative array index access to cached text.
[LibreOffice.git] / sc / source / core / tool / chgtrack.cxx
blob53fe660f103d341241276cbcfdd844a56ca60c41
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <chgtrack.hxx>
21 #include <compiler.hxx>
22 #include <formulacell.hxx>
23 #include <document.hxx>
24 #include <docsh.hxx>
25 #include <dociter.hxx>
26 #include <global.hxx>
27 #include <scmod.hxx>
28 #include <inputopt.hxx>
29 #include <patattr.hxx>
30 #include <hints.hxx>
31 #include <markdata.hxx>
32 #include <globstr.hrc>
33 #include <scresid.hxx>
34 #include <editutil.hxx>
35 #include <tokenarray.hxx>
36 #include <refupdatecontext.hxx>
37 #include <refupdat.hxx>
39 #include <osl/diagnose.h>
40 #include <svl/numformat.hxx>
41 #include <sfx2/objsh.hxx>
42 #include <unotools/useroptions.hxx>
43 #include <unotools/datetime.hxx>
44 #include <tools/json_writer.hxx>
45 #include <algorithm>
46 #include <memory>
47 #include <strings.hrc>
48 #include <utility>
50 ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScRange& rRange )
52 aBigRange( rRange ),
53 aDateTime( DateTime::SYSTEM ),
54 pNext( nullptr ),
55 pPrev( nullptr ),
56 pLinkAny( nullptr ),
57 pLinkDeletedIn( nullptr ),
58 pLinkDeleted( nullptr ),
59 pLinkDependent( nullptr ),
60 nAction( 0 ),
61 nRejectAction( 0 ),
62 eType( eTypeP ),
63 eState( SC_CAS_VIRGIN )
65 aDateTime.ConvertToUTC();
68 ScChangeAction::ScChangeAction(
69 ScChangeActionType eTypeP, ScBigRange aRange,
70 const sal_uLong nTempAction, const sal_uLong nTempRejectAction,
71 const ScChangeActionState eTempState, const DateTime& aTempDateTime,
72 OUString aTempUser, OUString aTempComment) :
73 aBigRange(std::move( aRange )),
74 aDateTime( aTempDateTime ),
75 aUser(std::move( aTempUser )),
76 aComment(std::move( aTempComment )),
77 pNext( nullptr ),
78 pPrev( nullptr ),
79 pLinkAny( nullptr ),
80 pLinkDeletedIn( nullptr ),
81 pLinkDeleted( nullptr ),
82 pLinkDependent( nullptr ),
83 nAction( nTempAction ),
84 nRejectAction( nTempRejectAction ),
85 eType( eTypeP ),
86 eState( eTempState )
90 ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, ScBigRange aRange,
91 const sal_uLong nTempAction)
93 aBigRange(std::move( aRange )),
94 aDateTime( DateTime::SYSTEM ),
95 pNext( nullptr ),
96 pPrev( nullptr ),
97 pLinkAny( nullptr ),
98 pLinkDeletedIn( nullptr ),
99 pLinkDeleted( nullptr ),
100 pLinkDependent( nullptr ),
101 nAction( nTempAction ),
102 nRejectAction( 0 ),
103 eType( eTypeP ),
104 eState( SC_CAS_VIRGIN )
106 aDateTime.ConvertToUTC();
109 ScChangeAction::~ScChangeAction()
111 RemoveAllLinks();
114 bool ScChangeAction::IsInsertType() const
116 return eType == SC_CAT_INSERT_COLS || eType == SC_CAT_INSERT_ROWS || eType == SC_CAT_INSERT_TABS;
119 bool ScChangeAction::IsDeleteType() const
121 return eType == SC_CAT_DELETE_COLS || eType == SC_CAT_DELETE_ROWS || eType == SC_CAT_DELETE_TABS;
124 bool ScChangeAction::IsVirgin() const
126 return eState == SC_CAS_VIRGIN;
129 bool ScChangeAction::IsAccepted() const
131 return eState == SC_CAS_ACCEPTED;
134 bool ScChangeAction::IsRejected() const
136 return eState == SC_CAS_REJECTED;
139 bool ScChangeAction::IsRejecting() const
141 return nRejectAction != 0;
144 bool ScChangeAction::IsVisible() const
146 // sequence order of execution is significant!
147 if ( IsRejected() || GetType() == SC_CAT_DELETE_TABS || IsDeletedIn() )
148 return false;
149 if ( GetType() == SC_CAT_CONTENT )
150 return static_cast<const ScChangeActionContent*>(this)->IsTopContent();
151 return true;
154 bool ScChangeAction::IsTouchable() const
156 // sequence order of execution is significant!
157 if ( IsRejected() || GetType() == SC_CAT_REJECT || IsDeletedIn() )
158 return false;
159 // content may reject and be touchable if on top
160 if ( GetType() == SC_CAT_CONTENT )
161 return static_cast<const ScChangeActionContent*>(this)->IsTopContent();
162 if ( IsRejecting() )
163 return false;
164 return true;
167 bool ScChangeAction::IsClickable() const
169 // sequence order of execution is significant!
170 if ( !IsVirgin() )
171 return false;
172 if ( IsDeletedIn() )
173 return false;
174 if ( GetType() == SC_CAT_CONTENT )
176 ScChangeActionContentCellType eCCT =
177 ScChangeActionContent::GetContentCellType(
178 static_cast<const ScChangeActionContent*>(this)->GetNewCell() );
179 if ( eCCT == SC_CACCT_MATREF )
180 return false;
181 if ( eCCT == SC_CACCT_MATORG )
182 { // no Accept-Select if one of the references is in a deleted col/row
183 const ScChangeActionLinkEntry* pL =
184 static_cast<const ScChangeActionContent*>(this)->GetFirstDependentEntry();
185 while ( pL )
187 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
188 if ( p && p->IsDeletedIn() )
189 return false;
190 pL = pL->GetNext();
193 return true; // for Select() a content doesn't have to be touchable
195 return IsTouchable(); // Accept()/Reject() only on touchables
198 bool ScChangeAction::IsRejectable() const
200 // sequence order of execution is significant!
201 if ( !IsClickable() )
202 return false;
203 if ( GetType() == SC_CAT_CONTENT )
205 if ( static_cast<const ScChangeActionContent*>(this)->IsOldMatrixReference() )
206 return false;
207 ScChangeActionContent* pNextContent =
208 static_cast<const ScChangeActionContent*>(this)->GetNextContent();
209 if ( pNextContent == nullptr )
210 return true; // *this is TopContent
211 return pNextContent->IsRejected(); // *this is next rejectable
213 return IsTouchable();
216 bool ScChangeAction::IsInternalRejectable() const
218 // sequence order of execution is significant!
219 if ( !IsVirgin() )
220 return false;
221 if ( IsDeletedIn() )
222 return false;
223 if ( GetType() == SC_CAT_CONTENT )
225 ScChangeActionContent* pNextContent =
226 static_cast<const ScChangeActionContent*>(this)->GetNextContent();
227 if ( pNextContent == nullptr )
228 return true; // *this is TopContent
229 return pNextContent->IsRejected(); // *this is next rejectable
231 return IsTouchable();
234 bool ScChangeAction::IsDialogRoot() const
236 return IsInternalRejectable(); // only rejectables in root
239 bool ScChangeAction::IsDialogParent() const
241 // sequence order of execution is significant!
242 if ( GetType() == SC_CAT_CONTENT )
244 if ( !IsDialogRoot() )
245 return false;
246 if ( static_cast<const ScChangeActionContent*>(this)->IsMatrixOrigin() && HasDependent() )
247 return true;
248 ScChangeActionContent* pPrevContent =
249 static_cast<const ScChangeActionContent*>(this)->GetPrevContent();
250 return pPrevContent && pPrevContent->IsVirgin();
252 if ( HasDependent() )
253 return IsDeleteType() || !IsDeletedIn();
254 if ( HasDeleted() )
256 if ( IsDeleteType() )
258 if ( IsDialogRoot() )
259 return true;
260 ScChangeActionLinkEntry* pL = pLinkDeleted;
261 while ( pL )
263 ScChangeAction* p = pL->GetAction();
264 if ( p && p->GetType() != eType )
265 return true;
266 pL = pL->GetNext();
269 else
270 return true;
272 return false;
275 bool ScChangeAction::IsMasterDelete() const
277 if ( !IsDeleteType() )
278 return false;
279 const ScChangeActionDel* pDel = static_cast<const ScChangeActionDel*>(this);
280 return pDel->IsMultiDelete() && (pDel->IsTopDelete() || pDel->IsRejectable());
283 void ScChangeAction::RemoveAllLinks()
285 while (pLinkAny)
287 // coverity[use_after_free] - Moves up by itself
288 delete pLinkAny;
291 RemoveAllDeletedIn();
293 while (pLinkDeleted)
295 // coverity[use_after_free] - Moves up by itself
296 delete pLinkDeleted;
299 RemoveAllDependent();
302 bool ScChangeAction::RemoveDeletedIn( const ScChangeAction* p )
304 bool bRemoved = false;
305 ScChangeActionLinkEntry* pL = GetDeletedIn();
306 while ( pL )
308 ScChangeActionLinkEntry* pNextLink = pL->GetNext();
309 if ( pL->GetAction() == p )
311 delete pL;
312 bRemoved = true;
314 pL = pNextLink;
316 return bRemoved;
319 bool ScChangeAction::IsDeletedIn() const
321 return GetDeletedIn() != nullptr;
324 bool ScChangeAction::IsDeletedIn( const ScChangeAction* p ) const
326 ScChangeActionLinkEntry* pL = GetDeletedIn();
327 while ( pL )
329 if ( pL->GetAction() == p )
330 return true;
331 pL = pL->GetNext();
333 return false;
336 void ScChangeAction::RemoveAllDeletedIn()
338 //TODO: Not from TopContent, but really this one
339 while (pLinkDeletedIn)
341 // coverity[use_after_free] - Moves up by itself
342 delete pLinkDeletedIn;
346 bool ScChangeAction::IsDeletedInDelType( ScChangeActionType eDelType ) const
348 ScChangeActionLinkEntry* pL = GetDeletedIn();
349 if ( pL )
351 // InsertType for MergePrepare/MergeOwn
352 ScChangeActionType eInsType;
353 switch ( eDelType )
355 case SC_CAT_DELETE_COLS :
356 eInsType = SC_CAT_INSERT_COLS;
357 break;
358 case SC_CAT_DELETE_ROWS :
359 eInsType = SC_CAT_INSERT_ROWS;
360 break;
361 case SC_CAT_DELETE_TABS :
362 eInsType = SC_CAT_INSERT_TABS;
363 break;
364 default:
365 eInsType = SC_CAT_NONE;
367 while ( pL )
369 ScChangeAction* p = pL->GetAction();
370 if ( p != nullptr && (p->GetType() == eDelType || p->GetType() == eInsType) )
371 return true;
372 pL = pL->GetNext();
375 return false;
378 bool ScChangeAction::HasDependent() const
380 return pLinkDependent != nullptr;
383 bool ScChangeAction::HasDeleted() const
385 return pLinkDeleted != nullptr;
388 void ScChangeAction::SetDeletedIn( ScChangeAction* p )
390 ScChangeActionLinkEntry* pLink1 = new ScChangeActionLinkEntry( GetDeletedInAddress(), p );
391 ScChangeActionLinkEntry* pLink2;
392 if ( GetType() == SC_CAT_CONTENT )
393 pLink2 = p->AddDeleted( static_cast<ScChangeActionContent*>(this)->GetTopContent() );
394 else
395 pLink2 = p->AddDeleted( this );
396 pLink1->SetLink( pLink2 );
399 void ScChangeAction::RemoveAllDependent()
401 while (pLinkDependent)
403 // coverity[use_after_free] - Moves up by itself
404 delete pLinkDependent;
408 DateTime ScChangeAction::GetDateTime() const
410 DateTime aDT( aDateTime );
411 aDT.ConvertToLocalTime();
412 return aDT;
415 void ScChangeAction::UpdateReference( const ScChangeTrack* /* pTrack */,
416 UpdateRefMode eMode, const ScBigRange& rRange,
417 sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz )
419 ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() );
422 OUString ScChangeAction::GetDescription(
423 ScDocument& /* rDoc */, bool /* bSplitRange */, bool bWarning ) const
425 if (!IsRejecting() || !bWarning)
426 return OUString();
428 // Add comment if rejection may have resulted in references
429 // not properly restored in formulas. See specification at
430 // http://specs.openoffice.org/calc/ease-of-use/redlining_comment.sxw
432 if (GetType() == SC_CAT_MOVE)
434 return ScResId(STR_CHANGED_MOVE_REJECTION_WARNING) + " ";
437 if (IsInsertType())
439 return ScResId(STR_CHANGED_DELETE_REJECTION_WARNING) + " ";
442 const ScChangeTrack* pCT = GetChangeTrack();
443 if (!pCT)
444 return OUString();
446 ScChangeAction* pReject = pCT->GetActionOrGenerated(GetRejectAction());
448 if (!pReject)
449 return OUString();
451 if (pReject->GetType() == SC_CAT_MOVE)
453 return ScResId(STR_CHANGED_MOVE_REJECTION_WARNING) + " ";
456 if (pReject->IsDeleteType())
458 return ScResId(STR_CHANGED_DELETE_REJECTION_WARNING) + " ";
461 if (!pReject->HasDependent())
462 return OUString();
464 ScChangeActionMap aMap;
465 pCT->GetDependents( pReject, aMap, false, true );
466 ScChangeActionMap::iterator itChangeAction = std::find_if(aMap.begin(), aMap.end(),
467 [&pReject](const ScChangeActionMap::value_type& rEntry) {
468 return rEntry.second->GetType() == SC_CAT_MOVE || pReject->IsDeleteType(); });
469 if (itChangeAction == aMap.end())
470 return OUString();
472 if( itChangeAction->second->GetType() == SC_CAT_MOVE)
473 return ScResId(STR_CHANGED_MOVE_REJECTION_WARNING) + " ";
474 else
475 return ScResId(STR_CHANGED_DELETE_REJECTION_WARNING) + " ";
478 OUString ScChangeAction::GetRefString(
479 const ScBigRange& rRange, const ScDocument& rDoc, bool bFlag3D ) const
481 OUStringBuffer aBuf;
482 ScRefFlags nFlags = ( rRange.IsValid( rDoc ) ? ScRefFlags::VALID : ScRefFlags::ZERO );
483 if ( nFlags == ScRefFlags::ZERO )
484 aBuf.append(ScCompiler::GetNativeSymbol(ocErrRef));
485 else
487 ScRange aTmpRange( rRange.MakeRange( rDoc ) );
488 switch ( GetType() )
490 case SC_CAT_INSERT_COLS :
491 case SC_CAT_DELETE_COLS :
492 if ( bFlag3D )
494 OUString aTmp;
495 rDoc.GetName( aTmpRange.aStart.Tab(), aTmp );
496 aBuf.append(aTmp + ".");
498 aBuf.append(ScColToAlpha(aTmpRange.aStart.Col())
499 + ":" + ScColToAlpha(aTmpRange.aEnd.Col()));
500 break;
501 case SC_CAT_INSERT_ROWS :
502 case SC_CAT_DELETE_ROWS :
503 if ( bFlag3D )
505 OUString aTmp;
506 rDoc.GetName( aTmpRange.aStart.Tab(), aTmp );
507 aBuf.append(aTmp + ".");
509 aBuf.append(OUString::number(static_cast<sal_Int64>(aTmpRange.aStart.Row()+1))
510 + ":" + OUString::number(static_cast<sal_Int64>(aTmpRange.aEnd.Row()+1)));
511 break;
512 default:
514 if ( bFlag3D || GetType() == SC_CAT_INSERT_TABS )
515 nFlags |= ScRefFlags::TAB_3D;
517 aBuf.append(aTmpRange.Format(rDoc, nFlags, rDoc.GetAddressConvention()));
520 if ( (bFlag3D && IsDeleteType()) || IsDeletedIn() )
522 aBuf.insert(0, '(');
523 aBuf.append(')');
526 return aBuf.makeStringAndClear();
529 void ScChangeAction::SetUser( const OUString& r )
531 aUser = r;
534 void ScChangeAction::SetComment( const OUString& rStr )
536 aComment = rStr;
539 OUString ScChangeAction::GetRefString( ScDocument& rDoc, bool bFlag3D ) const
541 return GetRefString( GetBigRange(), rDoc, bFlag3D );
544 void ScChangeAction::Accept()
546 if ( IsVirgin() )
548 SetState( SC_CAS_ACCEPTED );
549 DeleteCellEntries();
553 void ScChangeAction::SetRejected()
555 if ( IsVirgin() )
557 SetState( SC_CAS_REJECTED );
558 RemoveAllLinks();
559 DeleteCellEntries();
563 void ScChangeAction::RejectRestoreContents( ScChangeTrack* pTrack,
564 SCCOL nDx, SCROW nDy )
566 // Construct list of Contents
567 std::vector<ScChangeActionContent*> aContentsList;
568 for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() )
570 ScChangeAction* p = pL->GetAction();
571 if ( p && p->GetType() == SC_CAT_CONTENT )
573 aContentsList.push_back(static_cast<ScChangeActionContent*>(p) );
576 SetState( SC_CAS_REJECTED ); // Before UpdateReference for Move
577 pTrack->UpdateReference( this, true ); // Free LinkDeleted
578 OSL_ENSURE( !pLinkDeleted, "ScChangeAction::RejectRestoreContents: pLinkDeleted != NULL" );
580 // Work through list of Contents and delete
581 ScDocument& rDoc = pTrack->GetDocument();
582 for (ScChangeActionContent* pContent : aContentsList)
584 if ( !pContent->IsDeletedIn() &&
585 pContent->GetBigRange().aStart.IsValid( rDoc ) )
586 pContent->PutNewValueToDoc( &rDoc, nDx, nDy );
588 DeleteCellEntries(); // Remove generated ones
591 void ScChangeAction::SetDeletedInThis( sal_uLong nActionNumber,
592 const ScChangeTrack* pTrack )
594 if ( nActionNumber )
596 ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber );
597 OSL_ENSURE( pAct, "ScChangeAction::SetDeletedInThis: missing Action" );
598 if ( pAct )
599 pAct->SetDeletedIn( this );
603 void ScChangeAction::AddDependent( sal_uLong nActionNumber,
604 const ScChangeTrack* pTrack )
606 if ( nActionNumber )
608 ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber );
609 OSL_ENSURE( pAct, "ScChangeAction::AddDependent: missing Action" );
610 if ( pAct )
612 ScChangeActionLinkEntry* pLink = AddDependent( pAct );
613 pAct->AddLink( this, pLink );
618 // ScChangeActionIns
619 ScChangeActionIns::ScChangeActionIns( const ScDocument* pDoc, const ScRange& rRange, bool bEndOfList ) :
620 ScChangeAction(SC_CAT_NONE, rRange),
621 mbEndOfList(bEndOfList)
623 if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == pDoc->MaxCol() )
625 aBigRange.aStart.SetCol( ScBigRange::nRangeMin );
626 aBigRange.aEnd.SetCol( ScBigRange::nRangeMax );
627 if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
629 SetType( SC_CAT_INSERT_TABS );
630 aBigRange.aStart.SetRow( ScBigRange::nRangeMin );
631 aBigRange.aEnd.SetRow( ScBigRange::nRangeMax );
633 else
634 SetType( SC_CAT_INSERT_ROWS );
636 else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
638 SetType( SC_CAT_INSERT_COLS );
639 aBigRange.aStart.SetRow( ScBigRange::nRangeMin );
640 aBigRange.aEnd.SetRow( ScBigRange::nRangeMax );
642 else
644 OSL_FAIL( "ScChangeActionIns: Block not supported!" );
648 ScChangeActionIns::ScChangeActionIns(
649 const sal_uLong nActionNumber, const ScChangeActionState eStateP,
650 const sal_uLong nRejectingNumber, const ScBigRange& aBigRangeP,
651 const OUString& aUserP, const DateTime& aDateTimeP,
652 const OUString& sComment, const ScChangeActionType eTypeP,
653 bool bEndOfList ) :
654 ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
655 mbEndOfList(bEndOfList)
659 ScChangeActionIns::~ScChangeActionIns()
663 OUString ScChangeActionIns::GetDescription( ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
665 OUString str = ScChangeAction::GetDescription( rDoc, bSplitRange, bWarning );
667 TranslateId pWhatId;
668 switch ( GetType() )
670 case SC_CAT_INSERT_COLS :
671 pWhatId = STR_COLUMN;
672 break;
673 case SC_CAT_INSERT_ROWS :
674 pWhatId = STR_ROW;
675 break;
676 default:
677 pWhatId = STR_AREA;
680 OUString aRsc = ScResId(STR_CHANGED_INSERT);
681 sal_Int32 nPos = aRsc.indexOf("#1");
682 if (nPos < 0)
683 return str;
685 // Construct a range string to replace '#1' first.
686 OUString aRangeStr = ScResId(pWhatId) +
687 " " +
688 GetRefString(GetBigRange(), rDoc);
690 aRsc = aRsc.replaceAt(nPos, 2, aRangeStr); // replace '#1' with the range string.
692 return str + aRsc;
695 bool ScChangeActionIns::IsEndOfList() const
697 return mbEndOfList;
700 bool ScChangeActionIns::Reject( ScDocument& rDoc )
702 if ( !aBigRange.IsValid( rDoc ) )
703 return false;
705 ScRange aRange( aBigRange.MakeRange( rDoc ) );
706 if ( !rDoc.IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(),
707 aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) )
708 return false;
710 switch ( GetType() )
712 case SC_CAT_INSERT_COLS :
713 rDoc.DeleteCol( aRange );
714 break;
715 case SC_CAT_INSERT_ROWS :
716 rDoc.DeleteRow( aRange );
717 break;
718 case SC_CAT_INSERT_TABS :
719 rDoc.DeleteTab( aRange.aStart.Tab() );
720 break;
721 default:
723 // added to avoid warnings
726 SetState( SC_CAS_REJECTED );
727 RemoveAllLinks();
728 return true;
731 // ScChangeActionDel
732 ScChangeActionDel::ScChangeActionDel( const ScDocument* pDoc, const ScRange& rRange,
733 SCCOL nDxP, SCROW nDyP, ScChangeTrack* pTrackP )
735 ScChangeAction( SC_CAT_NONE, rRange ),
736 pTrack( pTrackP ),
737 pCutOff( nullptr ),
738 nCutOff( 0 ),
739 pLinkMove( nullptr ),
740 nDx( nDxP ),
741 nDy( nDyP )
743 if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == pDoc->MaxCol() )
745 aBigRange.aStart.SetCol( ScBigRange::nRangeMin );
746 aBigRange.aEnd.SetCol( ScBigRange::nRangeMax );
747 if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
749 SetType( SC_CAT_DELETE_TABS );
750 aBigRange.aStart.SetRow( ScBigRange::nRangeMin );
751 aBigRange.aEnd.SetRow( ScBigRange::nRangeMax );
753 else
754 SetType( SC_CAT_DELETE_ROWS );
756 else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
758 SetType( SC_CAT_DELETE_COLS );
759 aBigRange.aStart.SetRow( ScBigRange::nRangeMin );
760 aBigRange.aEnd.SetRow( ScBigRange::nRangeMax );
762 else
764 OSL_FAIL( "ScChangeActionDel: Block not supported!" );
768 ScChangeActionDel::ScChangeActionDel(
769 const sal_uLong nActionNumber, const ScChangeActionState eStateP,
770 const sal_uLong nRejectingNumber, const ScBigRange& aBigRangeP,
771 const OUString& aUserP, const DateTime& aDateTimeP, const OUString &sComment,
772 const ScChangeActionType eTypeP, const SCCOLROW nD, ScChangeTrack* pTrackP) : // which of nDx and nDy is set depends on the type
773 ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
774 pTrack( pTrackP ),
775 pCutOff( nullptr ),
776 nCutOff( 0 ),
777 pLinkMove( nullptr ),
778 nDx( 0 ),
779 nDy( 0 )
781 if (eType == SC_CAT_DELETE_COLS)
782 nDx = static_cast<SCCOL>(nD);
783 else if (eType == SC_CAT_DELETE_ROWS)
784 nDy = static_cast<SCROW>(nD);
787 ScChangeActionDel::~ScChangeActionDel()
789 DeleteCellEntries();
790 while (pLinkMove)
792 // coverity[use_after_free] - Moves up by itself
793 delete pLinkMove;
797 void ScChangeActionDel::AddContent( ScChangeActionContent* pContent )
799 mvCells.push_back(pContent);
802 void ScChangeActionDel::DeleteCellEntries()
804 pTrack->DeleteCellEntries( mvCells, this );
807 bool ScChangeActionDel::IsBaseDelete() const
809 return !GetDx() && !GetDy();
812 bool ScChangeActionDel::IsTopDelete() const
814 const ScChangeAction* p = GetNext();
815 if ( !p || p->GetType() != GetType() )
816 return true;
817 return static_cast<const ScChangeActionDel*>(p)->IsBaseDelete();
820 bool ScChangeActionDel::IsMultiDelete() const
822 if ( GetDx() || GetDy() )
823 return true;
824 const ScChangeAction* p = GetNext();
825 if ( !p || p->GetType() != GetType() )
826 return false;
827 const ScChangeActionDel* pDel = static_cast<const ScChangeActionDel*>(p);
828 return (pDel->GetDx() > GetDx() || pDel->GetDy() > GetDy()) &&
829 pDel->GetBigRange() == aBigRange;
832 bool ScChangeActionDel::IsTabDeleteCol() const
834 if ( GetType() != SC_CAT_DELETE_COLS )
835 return false;
836 const ScChangeAction* p = this;
837 while ( p && p->GetType() == SC_CAT_DELETE_COLS &&
838 !static_cast<const ScChangeActionDel*>(p)->IsTopDelete() )
839 p = p->GetNext();
840 return p && p->GetType() == SC_CAT_DELETE_TABS;
843 ScChangeActionDelMoveEntry* ScChangeActionDel::AddCutOffMove(
844 ScChangeActionMove* pMove, short nFrom, short nTo )
846 return new ScChangeActionDelMoveEntry(&pLinkMove, pMove, nFrom, nTo);
849 void ScChangeActionDel::UpdateReference( const ScChangeTrack* /* pTrack */,
850 UpdateRefMode eMode, const ScBigRange& rRange,
851 sal_Int32 nDxP, sal_Int32 nDyP, sal_Int32 nDz )
853 ScRefUpdate::Update( eMode, rRange, nDxP, nDyP, nDz, GetBigRange() );
855 if ( !IsDeletedIn() )
856 return ;
858 // Correct in the ones who slipped through
859 for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() )
861 ScChangeAction* p = pL->GetAction();
862 if ( p && p->GetType() == SC_CAT_CONTENT &&
863 !GetBigRange().Contains( p->GetBigRange() ) )
865 switch ( GetType() )
867 case SC_CAT_DELETE_COLS :
868 p->GetBigRange().aStart.SetCol( GetBigRange().aStart.Col() );
869 p->GetBigRange().aEnd.SetCol( GetBigRange().aStart.Col() );
870 break;
871 case SC_CAT_DELETE_ROWS :
872 p->GetBigRange().aStart.SetRow( GetBigRange().aStart.Row() );
873 p->GetBigRange().aEnd.SetRow( GetBigRange().aStart.Row() );
874 break;
875 case SC_CAT_DELETE_TABS :
876 p->GetBigRange().aStart.SetTab( GetBigRange().aStart.Tab() );
877 p->GetBigRange().aEnd.SetTab( GetBigRange().aStart.Tab() );
878 break;
879 default:
881 // added to avoid warnings
888 ScBigRange ScChangeActionDel::GetOverAllRange() const
890 ScBigRange aTmpRange( GetBigRange() );
891 aTmpRange.aEnd.SetCol( aTmpRange.aEnd.Col() + GetDx() );
892 aTmpRange.aEnd.SetRow( aTmpRange.aEnd.Row() + GetDy() );
893 return aTmpRange;
896 OUString ScChangeActionDel::GetDescription( ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
898 OUString str = ScChangeAction::GetDescription( rDoc, bSplitRange, bWarning );
900 TranslateId pWhatId;
901 switch ( GetType() )
903 case SC_CAT_DELETE_COLS :
904 pWhatId = STR_COLUMN;
905 break;
906 case SC_CAT_DELETE_ROWS :
907 pWhatId = STR_ROW;
908 break;
909 default:
910 pWhatId = STR_AREA;
913 ScBigRange aTmpRange( GetBigRange() );
914 if ( !IsRejected() )
916 if ( bSplitRange )
918 aTmpRange.aStart.SetCol( aTmpRange.aStart.Col() + GetDx() );
919 aTmpRange.aStart.SetRow( aTmpRange.aStart.Row() + GetDy() );
921 aTmpRange.aEnd.SetCol( aTmpRange.aEnd.Col() + GetDx() );
922 aTmpRange.aEnd.SetRow( aTmpRange.aEnd.Row() + GetDy() );
925 OUString aRsc = ScResId(STR_CHANGED_DELETE);
926 sal_Int32 nPos = aRsc.indexOf("#1");
927 if (nPos < 0)
928 return str;
930 // Build a string to replace with.
931 OUString aRangeStr = ScResId(pWhatId) + " " +
932 GetRefString(aTmpRange, rDoc);
933 aRsc = aRsc.replaceAt(nPos, 2, aRangeStr); // replace '#1' with the string.
935 return str + aRsc; // append to the original.
938 bool ScChangeActionDel::Reject( ScDocument& rDoc )
940 if ( !aBigRange.IsValid( rDoc ) && GetType() != SC_CAT_DELETE_TABS )
941 return false;
943 if ( IsTopDelete() )
944 { // Restore whole section in one go
945 bool bOk = true;
946 ScBigRange aTmpRange( GetOverAllRange() );
947 if ( !aTmpRange.IsValid( rDoc ) )
949 if ( GetType() == SC_CAT_DELETE_TABS )
950 { // Do we attach a Tab?
951 if ( aTmpRange.aStart.Tab() > rDoc.GetMaxTableNumber() )
952 bOk = false;
954 else
955 bOk = false;
957 if ( bOk )
959 ScRange aRange( aTmpRange.MakeRange( rDoc ) );
960 // InDelete... for formula UpdateReference in Document
961 pTrack->SetInDeleteRange( aRange );
962 pTrack->SetInDeleteTop( true );
963 pTrack->SetInDeleteUndo( true );
964 pTrack->SetInDelete( true );
965 switch ( GetType() )
967 case SC_CAT_DELETE_COLS :
968 if ( aRange.aStart.Col() != 0 || aRange.aEnd.Col() != rDoc.MaxCol() )
969 { // Only if not TabDelete
970 bOk = rDoc.CanInsertCol( aRange ) && rDoc.InsertCol( aRange );
972 break;
973 case SC_CAT_DELETE_ROWS :
974 bOk = rDoc.CanInsertRow( aRange ) && rDoc.InsertRow( aRange );
975 break;
976 case SC_CAT_DELETE_TABS :
978 //TODO: Remember table names?
979 OUString aName;
980 rDoc.CreateValidTabName( aName );
981 bOk = rDoc.ValidNewTabName( aName ) && rDoc.InsertTab( aRange.aStart.Tab(), aName );
983 break;
984 default:
986 // added to avoid warnings
989 pTrack->SetInDelete( false );
990 pTrack->SetInDeleteUndo( false );
992 if ( !bOk )
994 pTrack->SetInDeleteTop( false );
995 return false;
997 // Keep InDeleteTop for UpdateReference Undo
1000 // Sets rejected and calls UpdateReference-Undo and DeleteCellEntries
1001 RejectRestoreContents( pTrack, GetDx(), GetDy() );
1003 pTrack->SetInDeleteTop( false );
1004 RemoveAllLinks();
1005 return true;
1008 void ScChangeActionDel::UndoCutOffMoves()
1009 { // Restore cut off Moves; delete Entries/Links
1010 while ( pLinkMove )
1012 // coverity[deref_arg] - the call on delete pLinkMove at the block end Moves a new entry into pLinkMode by itself
1013 ScChangeActionMove* pMove = pLinkMove->GetMove();
1014 short nFrom = pLinkMove->GetCutOffFrom();
1015 short nTo = pLinkMove->GetCutOffTo();
1016 switch ( GetType() )
1018 case SC_CAT_DELETE_COLS :
1019 if ( nFrom > 0 )
1020 pMove->GetFromRange().aStart.IncCol( -nFrom );
1021 else if ( nFrom < 0 )
1022 pMove->GetFromRange().aEnd.IncCol( -nFrom );
1023 if ( nTo > 0 )
1024 pMove->GetBigRange().aStart.IncCol( -nTo );
1025 else if ( nTo < 0 )
1026 pMove->GetBigRange().aEnd.IncCol( -nTo );
1027 break;
1028 case SC_CAT_DELETE_ROWS :
1029 if ( nFrom > 0 )
1030 pMove->GetFromRange().aStart.IncRow( -nFrom );
1031 else if ( nFrom < 0 )
1032 pMove->GetFromRange().aEnd.IncRow( -nFrom );
1033 if ( nTo > 0 )
1034 pMove->GetBigRange().aStart.IncRow( -nTo );
1035 else if ( nTo < 0 )
1036 pMove->GetBigRange().aEnd.IncRow( -nTo );
1037 break;
1038 case SC_CAT_DELETE_TABS :
1039 if ( nFrom > 0 )
1040 pMove->GetFromRange().aStart.IncTab( -nFrom );
1041 else if ( nFrom < 0 )
1042 pMove->GetFromRange().aEnd.IncTab( -nFrom );
1043 if ( nTo > 0 )
1044 pMove->GetBigRange().aStart.IncTab( -nTo );
1045 else if ( nTo < 0 )
1046 pMove->GetBigRange().aEnd.IncTab( -nTo );
1047 break;
1048 default:
1050 // added to avoid warnings
1053 delete pLinkMove; // Moves up by itself
1057 void ScChangeActionDel::UndoCutOffInsert()
1058 { //Restore cut off Insert
1059 if ( !pCutOff )
1060 return;
1062 switch ( pCutOff->GetType() )
1064 case SC_CAT_INSERT_COLS :
1065 if ( nCutOff < 0 )
1066 pCutOff->GetBigRange().aEnd.IncCol( -nCutOff );
1067 else
1068 pCutOff->GetBigRange().aStart.IncCol( -nCutOff );
1069 break;
1070 case SC_CAT_INSERT_ROWS :
1071 if ( nCutOff < 0 )
1072 pCutOff->GetBigRange().aEnd.IncRow( -nCutOff );
1073 else
1074 pCutOff->GetBigRange().aStart.IncRow( -nCutOff );
1075 break;
1076 case SC_CAT_INSERT_TABS :
1077 if ( nCutOff < 0 )
1078 pCutOff->GetBigRange().aEnd.IncTab( -nCutOff );
1079 else
1080 pCutOff->GetBigRange().aStart.IncTab( -nCutOff );
1081 break;
1082 default:
1084 // added to avoid warnings
1087 SetCutOffInsert( nullptr, 0 );
1090 // ScChangeActionMove
1091 ScChangeActionMove::ScChangeActionMove(
1092 const sal_uLong nActionNumber, const ScChangeActionState eStateP,
1093 const sal_uLong nRejectingNumber, const ScBigRange& aToBigRange,
1094 const OUString& aUserP, const DateTime& aDateTimeP,
1095 const OUString &sComment, ScBigRange aFromBigRange,
1096 ScChangeTrack* pTrackP) : // which of nDx and nDy is set depends on the type
1097 ScChangeAction(SC_CAT_MOVE, aToBigRange, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
1098 aFromRange(std::move(aFromBigRange)),
1099 pTrack( pTrackP ),
1100 nStartLastCut(0),
1101 nEndLastCut(0)
1105 ScChangeActionMove::~ScChangeActionMove()
1107 DeleteCellEntries();
1110 void ScChangeActionMove::AddContent( ScChangeActionContent* pContent )
1112 mvCells.push_back(pContent);
1115 void ScChangeActionMove::DeleteCellEntries()
1117 pTrack->DeleteCellEntries( mvCells, this );
1120 void ScChangeActionMove::UpdateReference( const ScChangeTrack* /* pTrack */,
1121 UpdateRefMode eMode, const ScBigRange& rRange,
1122 sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz )
1124 ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aFromRange );
1125 ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() );
1128 void ScChangeActionMove::GetDelta( sal_Int32& nDx, sal_Int32& nDy, sal_Int32& nDz ) const
1130 const ScBigAddress& rToPos = GetBigRange().aStart;
1131 const ScBigAddress& rFromPos = GetFromRange().aStart;
1132 nDx = rToPos.Col() - rFromPos.Col();
1133 nDy = rToPos.Row() - rFromPos.Row();
1134 nDz = rToPos.Tab() - rFromPos.Tab();
1137 OUString ScChangeActionMove::GetDescription(
1138 ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
1140 OUString str = ScChangeAction::GetDescription( rDoc, bSplitRange, bWarning );
1142 bool bFlag3D = GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab();
1144 OUString aRsc = ScResId(STR_CHANGED_MOVE);
1146 OUString aTmpStr = ScChangeAction::GetRefString(GetFromRange(), rDoc, bFlag3D);
1147 sal_Int32 nPos = aRsc.indexOf("#1");
1148 if (nPos >= 0)
1150 aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
1151 nPos += aTmpStr.getLength();
1154 aTmpStr = ScChangeAction::GetRefString(GetBigRange(), rDoc, bFlag3D);
1155 nPos = nPos >= 0 ? aRsc.indexOf("#2", nPos) : -1;
1156 if (nPos >= 0)
1158 aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
1161 return str + aRsc; // append to the original string.
1164 OUString ScChangeActionMove::GetRefString( ScDocument& rDoc, bool bFlag3D ) const
1166 if ( !bFlag3D )
1167 bFlag3D = ( GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab() );
1169 return ScChangeAction::GetRefString(GetFromRange(), rDoc, bFlag3D)
1170 + ", "
1171 + ScChangeAction::GetRefString(GetBigRange(), rDoc, bFlag3D);
1174 bool ScChangeActionMove::Reject( ScDocument& rDoc )
1176 if ( !(aBigRange.IsValid( rDoc ) && aFromRange.IsValid( rDoc )) )
1177 return false;
1179 ScRange aToRange( aBigRange.MakeRange( rDoc ) );
1180 ScRange aFrmRange( aFromRange.MakeRange( rDoc ) );
1182 bool bOk = rDoc.IsBlockEditable( aToRange.aStart.Tab(),
1183 aToRange.aStart.Col(), aToRange.aStart.Row(),
1184 aToRange.aEnd.Col(), aToRange.aEnd.Row() );
1185 if ( bOk )
1186 bOk = rDoc.IsBlockEditable( aFrmRange.aStart.Tab(),
1187 aFrmRange.aStart.Col(), aFrmRange.aStart.Row(),
1188 aFrmRange.aEnd.Col(), aFrmRange.aEnd.Row() );
1189 if ( !bOk )
1190 return false;
1192 pTrack->LookUpContents( aToRange, &rDoc, 0, 0, 0 ); // Contents to be moved
1194 rDoc.DeleteAreaTab( aToRange, InsertDeleteFlags::ALL );
1195 rDoc.DeleteAreaTab( aFrmRange, InsertDeleteFlags::ALL );
1196 // Adjust formula in the Document
1197 sc::RefUpdateContext aCxt(rDoc);
1198 aCxt.meMode = URM_MOVE;
1199 aCxt.maRange = aFrmRange;
1200 aCxt.mnColDelta = aFrmRange.aStart.Col() - aToRange.aStart.Col();
1201 aCxt.mnRowDelta = aFrmRange.aStart.Row() - aToRange.aStart.Row();
1202 aCxt.mnTabDelta = aFrmRange.aStart.Tab() - aToRange.aStart.Tab();
1203 rDoc.UpdateReference(aCxt);
1205 // Free LinkDependent, set succeeding UpdateReference Undo
1206 // ToRange->FromRange Dependents
1207 RemoveAllDependent();
1209 // Sets rejected and calls UpdateReference Undo and DeleteCellEntries
1210 RejectRestoreContents( pTrack, 0, 0 );
1212 while ( pLinkDependent )
1214 ScChangeAction* p = pLinkDependent->GetAction();
1215 if ( p && p->GetType() == SC_CAT_CONTENT )
1217 ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(p);
1218 if ( !pContent->IsDeletedIn() &&
1219 pContent->GetBigRange().aStart.IsValid( rDoc ) )
1220 pContent->PutNewValueToDoc( &rDoc, 0, 0 );
1221 // Delete the ones created in LookUpContents
1222 if ( pTrack->IsGenerated( pContent->GetActionNumber() ) &&
1223 !pContent->IsDeletedIn() )
1225 pLinkDependent->UnLink(); // Else this one is also deleted!
1226 pTrack->DeleteGeneratedDelContent( pContent );
1229 delete pLinkDependent;
1232 RemoveAllLinks();
1233 return true;
1236 ScChangeActionContent::ScChangeActionContent( const ScRange& rRange ) :
1237 ScChangeAction(SC_CAT_CONTENT, rRange),
1238 pNextContent(nullptr),
1239 pPrevContent(nullptr),
1240 pNextInSlot(nullptr),
1241 ppPrevInSlot(nullptr)
1244 ScChangeActionContent::ScChangeActionContent( const sal_uLong nActionNumber,
1245 const ScChangeActionState eStateP, const sal_uLong nRejectingNumber,
1246 const ScBigRange& aBigRangeP, const OUString& aUserP,
1247 const DateTime& aDateTimeP, const OUString& sComment,
1248 ScCellValue aOldCell, const ScDocument* pDoc, const OUString& sOldValue ) :
1249 ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
1250 maOldCell(std::move(aOldCell)),
1251 maOldValue(sOldValue),
1252 pNextContent(nullptr),
1253 pPrevContent(nullptr),
1254 pNextInSlot(nullptr),
1255 ppPrevInSlot(nullptr)
1257 if (!maOldCell.isEmpty())
1258 SetCell(maOldValue, maOldCell, 0, pDoc);
1260 if (!sOldValue.isEmpty()) // #i40704# don't overwrite SetCell result with empty string
1261 maOldValue = sOldValue; // set again, because SetCell removes it
1264 ScChangeActionContent::ScChangeActionContent( const sal_uLong nActionNumber,
1265 ScCellValue aNewCell, const ScBigRange& aBigRangeP,
1266 const ScDocument* pDoc, const OUString& sNewValue ) :
1267 ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber),
1268 maNewCell(std::move(aNewCell)),
1269 maNewValue(sNewValue),
1270 pNextContent(nullptr),
1271 pPrevContent(nullptr),
1272 pNextInSlot(nullptr),
1273 ppPrevInSlot(nullptr)
1275 if (!maNewCell.isEmpty())
1276 SetCell(maNewValue, maNewCell, 0, pDoc);
1278 if (!sNewValue.isEmpty()) // #i40704# don't overwrite SetCell result with empty string
1279 maNewValue = sNewValue; // set again, because SetCell removes it
1282 ScChangeActionContent::~ScChangeActionContent()
1284 ClearTrack();
1287 void ScChangeActionContent::ClearTrack()
1289 RemoveFromSlot();
1290 if ( pPrevContent )
1291 pPrevContent->pNextContent = pNextContent;
1292 if ( pNextContent )
1293 pNextContent->pPrevContent = pPrevContent;
1296 ScChangeActionContent* ScChangeActionContent::GetTopContent() const
1298 if ( pNextContent )
1300 ScChangeActionContent* pContent = pNextContent;
1301 while ( pContent->pNextContent && pContent != pContent->pNextContent )
1302 pContent = pContent->pNextContent;
1303 return pContent;
1305 return const_cast<ScChangeActionContent*>(this);
1308 ScChangeActionLinkEntry* ScChangeActionContent::GetDeletedIn() const
1310 if ( pNextContent )
1311 return GetTopContent()->pLinkDeletedIn;
1312 return pLinkDeletedIn;
1315 ScChangeActionLinkEntry** ScChangeActionContent::GetDeletedInAddress()
1317 if ( pNextContent )
1318 return GetTopContent()->GetDeletedInAddress();
1319 return &pLinkDeletedIn;
1322 void ScChangeActionContent::SetOldValue(
1323 const ScCellValue& rCell, const ScDocument* pFromDoc, ScDocument* pToDoc, sal_uLong nFormat )
1325 SetValue(maOldValue, maOldCell, nFormat, rCell, pFromDoc, pToDoc);
1328 void ScChangeActionContent::SetOldValue(
1329 const ScCellValue& rCell, const ScDocument* pFromDoc, ScDocument* pToDoc )
1331 SetValue(maOldValue, maOldCell, aBigRange.aStart.MakeAddress(*pFromDoc), rCell, pFromDoc, pToDoc);
1334 void ScChangeActionContent::SetNewValue( const ScCellValue& rCell, ScDocument* pDoc )
1336 SetValue(maNewValue, maNewCell, aBigRange.aStart.MakeAddress(*pDoc), rCell, pDoc, pDoc);
1339 void ScChangeActionContent::SetOldNewCells(
1340 const ScCellValue& rOldCell, sal_uLong nOldFormat, const ScCellValue& rNewCell,
1341 sal_uLong nNewFormat, const ScDocument* pDoc )
1343 maOldCell = rOldCell;
1344 maNewCell = rNewCell;
1345 SetCell(maOldValue, maOldCell, nOldFormat, pDoc);
1346 SetCell(maNewValue, maNewCell, nNewFormat, pDoc);
1349 void ScChangeActionContent::SetNewCell(
1350 const ScCellValue& rCell, const ScDocument* pDoc, const OUString& rFormatted )
1352 maNewCell = rCell;
1353 SetCell(maNewValue, maNewCell, 0, pDoc);
1355 // #i40704# allow to set formatted text here - don't call SetNewValue with string from XML filter
1356 if (!rFormatted.isEmpty())
1357 maNewValue = rFormatted;
1360 void ScChangeActionContent::SetValueString(
1361 OUString& rValue, ScCellValue& rCell, const OUString& rStr, ScDocument* pDoc )
1363 rCell.clear();
1364 if ( rStr.getLength() > 1 && rStr[0] == '=' )
1366 rValue.clear();
1367 rCell.set(new ScFormulaCell(
1368 *pDoc, aBigRange.aStart.MakeAddress(*pDoc), rStr,
1369 pDoc->GetGrammar() ));
1370 rCell.getFormula()->SetInChangeTrack(true);
1372 else
1373 rValue = rStr;
1376 void ScChangeActionContent::SetOldValue( const OUString& rOld, ScDocument* pDoc )
1378 SetValueString(maOldValue, maOldCell, rOld, pDoc);
1381 OUString ScChangeActionContent::GetOldString( const ScDocument* pDoc ) const
1383 return GetValueString(maOldValue, maOldCell, pDoc);
1386 OUString ScChangeActionContent::GetNewString( const ScDocument* pDoc ) const
1388 return GetValueString(maNewValue, maNewCell, pDoc);
1391 OUString ScChangeActionContent::GetDescription(
1392 ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
1394 OUString str = ScChangeAction::GetDescription( rDoc, bSplitRange, bWarning );
1396 OUString aRsc = ScResId(STR_CHANGED_CELL);
1398 OUString aTmpStr = GetRefString(rDoc);
1400 sal_Int32 nPos = aRsc.indexOf("#1", 0);
1401 if (nPos >= 0)
1403 aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
1404 nPos += aTmpStr.getLength();
1407 aTmpStr = GetOldString( &rDoc );
1408 if (aTmpStr.isEmpty())
1409 aTmpStr = ScResId( STR_CHANGED_BLANK );
1411 nPos = nPos >= 0 ? aRsc.indexOf("#2", nPos) : -1;
1412 if (nPos >= 0)
1414 aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
1415 nPos += aTmpStr.getLength();
1418 aTmpStr = GetNewString( &rDoc );
1419 if (aTmpStr.isEmpty())
1420 aTmpStr = ScResId( STR_CHANGED_BLANK );
1422 nPos = nPos >= 0 ? aRsc.indexOf("#3", nPos) : -1;
1423 if (nPos >= 0)
1425 aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
1428 return str + aRsc; // append to the original string.
1431 OUString ScChangeActionContent::GetRefString(
1432 ScDocument& rDoc, bool bFlag3D ) const
1434 ScRefFlags nFlags = ( GetBigRange().IsValid( rDoc ) ? ScRefFlags::VALID : ScRefFlags::ZERO );
1435 if ( nFlags != ScRefFlags::ZERO )
1437 const ScCellValue& rCell = GetNewCell();
1438 if ( GetContentCellType(rCell) == SC_CACCT_MATORG )
1440 ScBigRange aLocalBigRange( GetBigRange() );
1441 SCCOL nC;
1442 SCROW nR;
1443 rCell.getFormula()->GetMatColsRows( nC, nR );
1444 aLocalBigRange.aEnd.IncCol( nC-1 );
1445 aLocalBigRange.aEnd.IncRow( nR-1 );
1446 return ScChangeAction::GetRefString( aLocalBigRange, rDoc, bFlag3D );
1449 ScAddress aTmpAddress( GetBigRange().aStart.MakeAddress( rDoc ) );
1450 if ( bFlag3D )
1451 nFlags |= ScRefFlags::TAB_3D;
1452 OUString str = aTmpAddress.Format(nFlags, &rDoc, rDoc.GetAddressConvention());
1453 if ( IsDeletedIn() )
1455 // Insert the parentheses.
1456 str = "(" + str + ")";
1458 return str;
1460 else
1461 return ScCompiler::GetNativeSymbol(ocErrRef);
1464 bool ScChangeActionContent::Reject( ScDocument& rDoc )
1466 if ( !aBigRange.IsValid( rDoc ) )
1467 return false;
1469 PutOldValueToDoc( &rDoc, 0, 0 );
1471 SetState( SC_CAS_REJECTED );
1472 RemoveAllLinks();
1474 return true;
1477 bool ScChangeActionContent::Select( ScDocument& rDoc, ScChangeTrack* pTrack,
1478 bool bOldest, ::std::stack<ScChangeActionContent*>* pRejectActions )
1480 if ( !aBigRange.IsValid( rDoc ) )
1481 return false;
1483 ScChangeActionContent* pContent = this;
1484 // accept previous contents
1485 while ( ( pContent = pContent->pPrevContent ) != nullptr )
1487 if ( pContent->IsVirgin() )
1488 pContent->SetState( SC_CAS_ACCEPTED );
1490 ScChangeActionContent* pEnd = pContent = this;
1491 // reject subsequent contents
1492 while ( ( pContent = pContent->pNextContent ) != nullptr )
1494 // MatrixOrigin may have dependents, no dependency recursion needed
1495 const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry();
1496 while ( pL )
1498 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
1499 if ( p )
1500 p->SetRejected();
1501 pL = pL->GetNext();
1503 pContent->SetRejected();
1504 pEnd = pContent;
1507 // If not oldest: Is it anyone else than the last one?
1508 if ( bOldest || pEnd != this )
1509 { ScRange aRange( aBigRange.aStart.MakeAddress( rDoc ) );
1510 const ScAddress& rPos = aRange.aStart;
1512 ScChangeActionContent* pNew = new ScChangeActionContent( aRange );
1513 ScCellValue aCell;
1514 aCell.assign(rDoc, rPos);
1515 pNew->SetOldValue(aCell, &rDoc, &rDoc);
1517 if ( bOldest )
1518 PutOldValueToDoc( &rDoc, 0, 0 );
1519 else
1520 PutNewValueToDoc( &rDoc, 0, 0 );
1522 pNew->SetRejectAction( bOldest ? GetActionNumber() : pEnd->GetActionNumber() );
1523 pNew->SetState( SC_CAS_ACCEPTED );
1524 if ( pRejectActions )
1525 pRejectActions->push( pNew );
1526 else
1528 aCell.assign(rDoc, rPos);
1529 pNew->SetNewValue(aCell, &rDoc);
1530 pTrack->Append( pNew );
1534 if ( bOldest )
1535 SetRejected();
1536 else
1537 SetState( SC_CAS_ACCEPTED );
1539 return true;
1542 OUString ScChangeActionContent::GetStringOfCell(
1543 const ScCellValue& rCell, const ScDocument* pDoc, const ScAddress& rPos )
1545 if (NeedsNumberFormat(rCell))
1546 return GetStringOfCell(rCell, pDoc, pDoc->GetNumberFormat(rPos));
1547 else
1548 return GetStringOfCell(rCell, pDoc, 0);
1551 OUString ScChangeActionContent::GetStringOfCell(
1552 const ScCellValue& rCell, const ScDocument* pDoc, sal_uLong nFormat )
1554 if (!GetContentCellType(rCell))
1555 return OUString();
1557 switch (rCell.getType())
1559 case CELLTYPE_VALUE:
1561 OUString str;
1562 pDoc->GetFormatTable()->GetInputLineString(rCell.getDouble(), nFormat, str);
1563 return str;
1565 case CELLTYPE_STRING:
1566 return rCell.getSharedString()->getString();
1567 case CELLTYPE_EDIT:
1568 if (rCell.getEditText())
1569 return ScEditUtil::GetString(*rCell.getEditText(), pDoc);
1570 return OUString();
1571 case CELLTYPE_FORMULA:
1572 return rCell.getFormula()->GetFormula();
1573 default:
1574 return OUString();
1578 ScChangeActionContentCellType ScChangeActionContent::GetContentCellType( const ScCellValue& rCell )
1580 switch (rCell.getType())
1582 case CELLTYPE_VALUE :
1583 case CELLTYPE_STRING :
1584 case CELLTYPE_EDIT :
1585 return SC_CACCT_NORMAL;
1586 case CELLTYPE_FORMULA :
1587 switch (rCell.getFormula()->GetMatrixFlag())
1589 case ScMatrixMode::NONE :
1590 return SC_CACCT_NORMAL;
1591 case ScMatrixMode::Formula :
1592 return SC_CACCT_MATORG;
1593 case ScMatrixMode::Reference :
1594 return SC_CACCT_MATREF;
1596 return SC_CACCT_NORMAL;
1597 default:
1598 return SC_CACCT_NONE;
1602 ScChangeActionContentCellType ScChangeActionContent::GetContentCellType( const ScRefCellValue& rCell )
1604 switch (rCell.getType())
1606 case CELLTYPE_VALUE:
1607 case CELLTYPE_STRING:
1608 case CELLTYPE_EDIT:
1609 return SC_CACCT_NORMAL;
1610 case CELLTYPE_FORMULA:
1612 const ScFormulaCell* pCell = rCell.getFormula();
1613 switch (pCell->GetMatrixFlag())
1615 case ScMatrixMode::NONE :
1616 return SC_CACCT_NORMAL;
1617 case ScMatrixMode::Formula :
1618 return SC_CACCT_MATORG;
1619 case ScMatrixMode::Reference :
1620 return SC_CACCT_MATREF;
1622 return SC_CACCT_NORMAL;
1624 default:
1628 return SC_CACCT_NONE;
1631 bool ScChangeActionContent::NeedsNumberFormat( const ScCellValue& rVal )
1633 return rVal.getType() == CELLTYPE_VALUE;
1636 void ScChangeActionContent::SetValue(
1637 OUString& rStr, ScCellValue& rCell, const ScAddress& rPos, const ScCellValue& rOrgCell,
1638 const ScDocument* pFromDoc, ScDocument* pToDoc )
1640 sal_uInt32 nFormat = NeedsNumberFormat(rOrgCell) ? pFromDoc->GetNumberFormat(rPos) : 0;
1641 SetValue(rStr, rCell, nFormat, rOrgCell, pFromDoc, pToDoc);
1644 void ScChangeActionContent::SetValue(
1645 OUString& rStr, ScCellValue& rCell, sal_uLong nFormat, const ScCellValue& rOrgCell,
1646 const ScDocument* pFromDoc, ScDocument* pToDoc )
1648 rStr.clear();
1650 if (GetContentCellType(rOrgCell))
1652 rCell.assign(rOrgCell, *pToDoc);
1653 switch (rOrgCell.getType())
1655 case CELLTYPE_VALUE :
1656 { // E.g.: Remember date as such
1657 pFromDoc->GetFormatTable()->GetInputLineString(
1658 rOrgCell.getDouble(), nFormat, rStr);
1660 break;
1661 case CELLTYPE_FORMULA :
1662 rCell.getFormula()->SetInChangeTrack(true);
1663 break;
1664 default:
1666 // added to avoid warnings
1670 else
1671 rCell.clear();
1674 void ScChangeActionContent::SetCell( OUString& rStr, ScCellValue& rCell, sal_uLong nFormat, const ScDocument* pDoc )
1676 rStr.clear();
1677 if (rCell.isEmpty())
1678 return;
1680 switch (rCell.getType())
1682 case CELLTYPE_VALUE :
1683 // e.g. remember date as date string
1684 pDoc->GetFormatTable()->GetInputLineString(rCell.getDouble(), nFormat, rStr);
1685 break;
1686 case CELLTYPE_FORMULA :
1687 rCell.getFormula()->SetInChangeTrack(true);
1688 break;
1689 default:
1691 // added to avoid warnings
1696 OUString ScChangeActionContent::GetValueString(
1697 const OUString& rValue, const ScCellValue& rCell, const ScDocument* pDoc ) const
1699 if (!rValue.isEmpty())
1701 return rValue;
1704 switch (rCell.getType())
1706 case CELLTYPE_STRING :
1707 return rCell.getSharedString()->getString();
1708 case CELLTYPE_EDIT :
1709 if (rCell.getEditText())
1710 return ScEditUtil::GetString(*rCell.getEditText(), pDoc);
1711 return OUString();
1712 case CELLTYPE_VALUE : // Is always in rValue
1713 return rValue;
1714 case CELLTYPE_FORMULA :
1715 return GetFormulaString(rCell.getFormula());
1716 case CELLTYPE_NONE:
1717 default:
1718 return OUString();
1722 OUString ScChangeActionContent::GetFormulaString(
1723 const ScFormulaCell* pCell ) const
1725 ScAddress aPos( aBigRange.aStart.MakeAddress( pCell->GetDocument()) );
1726 if ( aPos == pCell->aPos || IsDeletedIn() )
1727 return pCell->GetFormula();
1728 else
1730 OSL_FAIL( "ScChangeActionContent::GetFormulaString: aPos != pCell->aPos" );
1731 ScFormulaCell aNew( *pCell, pCell->GetDocument(), aPos );
1732 return aNew.GetFormula();
1736 void ScChangeActionContent::PutOldValueToDoc( ScDocument* pDoc,
1737 SCCOL nDx, SCROW nDy ) const
1739 PutValueToDoc(maOldCell, maOldValue, pDoc, nDx, nDy);
1742 void ScChangeActionContent::PutNewValueToDoc( ScDocument* pDoc,
1743 SCCOL nDx, SCROW nDy ) const
1745 PutValueToDoc(maNewCell, maNewValue, pDoc, nDx, nDy);
1748 void ScChangeActionContent::PutValueToDoc(
1749 const ScCellValue& rCell, const OUString& rValue, ScDocument* pDoc,
1750 SCCOL nDx, SCROW nDy ) const
1752 ScAddress aPos( aBigRange.aStart.MakeAddress( *pDoc ) );
1753 if ( nDx )
1754 aPos.IncCol( nDx );
1755 if ( nDy )
1756 aPos.IncRow( nDy );
1758 if (!rValue.isEmpty())
1760 pDoc->SetString(aPos, rValue);
1761 return;
1764 if (rCell.isEmpty())
1766 pDoc->SetEmptyCell(aPos);
1767 return;
1770 if (rCell.getType() == CELLTYPE_VALUE)
1772 pDoc->SetString( aPos.Col(), aPos.Row(), aPos.Tab(), rValue );
1773 return;
1776 switch (GetContentCellType(rCell))
1778 case SC_CACCT_MATORG :
1780 SCCOL nC;
1781 SCROW nR;
1782 rCell.getFormula()->GetMatColsRows(nC, nR);
1783 OSL_ENSURE( nC>0 && nR>0, "ScChangeActionContent::PutValueToDoc: MatColsRows?" );
1784 ScRange aRange( aPos );
1785 if ( nC > 1 )
1786 aRange.aEnd.IncCol( nC-1 );
1787 if ( nR > 1 )
1788 aRange.aEnd.IncRow( nR-1 );
1789 ScMarkData aDestMark(pDoc->GetSheetLimits());
1790 aDestMark.SelectOneTable( aPos.Tab() );
1791 aDestMark.SetMarkArea( aRange );
1792 pDoc->InsertMatrixFormula( aPos.Col(), aPos.Row(),
1793 aRange.aEnd.Col(), aRange.aEnd.Row(),
1794 aDestMark, OUString(), rCell.getFormula()->GetCode());
1796 break;
1797 case SC_CACCT_MATREF :
1798 // nothing
1799 break;
1800 default:
1801 rCell.commit(*pDoc, aPos);
1805 static void lcl_InvalidateReference( const ScDocument& rDoc, formula::FormulaToken& rTok, const ScBigAddress& rPos )
1807 ScSingleRefData& rRef1 = *rTok.GetSingleRef();
1808 if ( rPos.Col() < 0 || rDoc.MaxCol() < rPos.Col() )
1810 rRef1.SetColDeleted( true );
1812 if ( rPos.Row() < 0 || rDoc.MaxRow() < rPos.Row() )
1814 rRef1.SetRowDeleted( true );
1816 if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() )
1818 rRef1.SetTabDeleted( true );
1820 if ( rTok.GetType() != formula::svDoubleRef )
1821 return;
1823 ScSingleRefData& rRef2 = rTok.GetDoubleRef()->Ref2;
1824 if ( rPos.Col() < 0 || rDoc.MaxCol() < rPos.Col() )
1826 rRef2.SetColDeleted( true );
1828 if ( rPos.Row() < 0 || rDoc.MaxRow() < rPos.Row() )
1830 rRef2.SetRowDeleted( true );
1832 if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() )
1834 rRef2.SetTabDeleted( true );
1838 void ScChangeActionContent::UpdateReference( const ScChangeTrack* pTrack,
1839 UpdateRefMode eMode, const ScBigRange& rRange,
1840 sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz )
1842 SCSIZE nOldSlot = pTrack->ComputeContentSlot( aBigRange.aStart.Row() );
1843 ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aBigRange );
1844 SCSIZE nNewSlot = pTrack->ComputeContentSlot( aBigRange.aStart.Row() );
1845 if ( nNewSlot != nOldSlot )
1847 RemoveFromSlot();
1848 InsertInSlot( &(pTrack->GetContentSlots()[nNewSlot]) );
1851 if ( pTrack->IsInDelete() && !pTrack->IsInDeleteTop() )
1852 return ; // Formula only update whole range
1854 bool bOldFormula = maOldCell.getType() == CELLTYPE_FORMULA;
1855 bool bNewFormula = maNewCell.getType() == CELLTYPE_FORMULA;
1856 if ( !(bOldFormula || bNewFormula) )
1857 return;
1859 // Adjust UpdateReference via ScFormulaCell (there)
1860 if ( pTrack->IsInDelete() )
1862 const ScRange& rDelRange = pTrack->GetInDeleteRange();
1863 if ( nDx > 0 )
1864 nDx = rDelRange.aEnd.Col() - rDelRange.aStart.Col() + 1;
1865 else if ( nDx < 0 )
1866 nDx = -(rDelRange.aEnd.Col() - rDelRange.aStart.Col() + 1);
1867 if ( nDy > 0 )
1868 nDy = rDelRange.aEnd.Row() - rDelRange.aStart.Row() + 1;
1869 else if ( nDy < 0 )
1870 nDy = -(rDelRange.aEnd.Row() - rDelRange.aStart.Row() + 1);
1871 if ( nDz > 0 )
1872 nDz = rDelRange.aEnd.Tab() - rDelRange.aStart.Tab() + 1;
1873 else if ( nDz < 0 )
1874 nDz = -(rDelRange.aEnd.Tab() - rDelRange.aStart.Tab() + 1);
1876 ScBigRange aTmpRange( rRange );
1877 switch ( eMode )
1879 case URM_INSDEL :
1880 if ( nDx < 0 || nDy < 0 || nDz < 0 )
1881 { // Delete starts there after removed range
1882 // Position is changed there
1883 if ( nDx )
1884 aTmpRange.aStart.IncCol( -nDx );
1885 if ( nDy )
1886 aTmpRange.aStart.IncRow( -nDy );
1887 if ( nDz )
1888 aTmpRange.aStart.IncTab( -nDz );
1890 break;
1891 case URM_MOVE :
1892 // Move is Source here and Target there
1893 // Position needs to be adjusted before that
1894 if ( bOldFormula )
1895 maOldCell.getFormula()->aPos = aBigRange.aStart.MakeAddress(pTrack->GetDocument());
1896 if ( bNewFormula )
1897 maNewCell.getFormula()->aPos = aBigRange.aStart.MakeAddress(pTrack->GetDocument());
1898 if ( nDx )
1900 aTmpRange.aStart.IncCol( nDx );
1901 aTmpRange.aEnd.IncCol( nDx );
1903 if ( nDy )
1905 aTmpRange.aStart.IncRow( nDy );
1906 aTmpRange.aEnd.IncRow( nDy );
1908 if ( nDz )
1910 aTmpRange.aStart.IncTab( nDz );
1911 aTmpRange.aEnd.IncTab( nDz );
1913 break;
1914 default:
1916 // added to avoid warnings
1919 ScRange aRange( aTmpRange.MakeRange(pTrack->GetDocument()) );
1921 sc::RefUpdateContext aRefCxt(pTrack->GetDocument());
1922 aRefCxt.meMode = eMode;
1923 aRefCxt.maRange = aRange;
1924 aRefCxt.mnColDelta = nDx;
1925 aRefCxt.mnRowDelta = nDy;
1926 aRefCxt.mnTabDelta = nDz;
1928 if ( bOldFormula )
1929 maOldCell.getFormula()->UpdateReference(aRefCxt);
1930 if ( bNewFormula )
1931 maNewCell.getFormula()->UpdateReference(aRefCxt);
1933 if ( aBigRange.aStart.IsValid( pTrack->GetDocument() ) )
1934 return;
1936 //FIXME:
1937 // UpdateReference cannot handle positions outside of the Document.
1938 // Therefore set everything to #REF!
1939 //TODO: Remove the need for this hack! This means big changes to ScAddress etc.!
1940 const ScBigAddress& rPos = aBigRange.aStart;
1941 if ( bOldFormula )
1943 formula::FormulaToken* t;
1944 ScTokenArray* pArr = maOldCell.getFormula()->GetCode();
1945 formula::FormulaTokenArrayPlainIterator aIter(*pArr);
1946 while ( ( t = aIter.GetNextReference() ) != nullptr )
1947 lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
1948 aIter.Reset();
1949 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
1950 lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
1952 if ( bNewFormula )
1954 formula::FormulaToken* t;
1955 ScTokenArray* pArr = maNewCell.getFormula()->GetCode();
1956 formula::FormulaTokenArrayPlainIterator aIter(*pArr);
1957 while ( ( t = aIter.GetNextReference() ) != nullptr )
1958 lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
1959 aIter.Reset();
1960 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
1961 lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
1965 bool ScChangeActionContent::IsMatrixOrigin() const
1967 return GetContentCellType(GetNewCell()) == SC_CACCT_MATORG;
1970 bool ScChangeActionContent::IsOldMatrixReference() const
1972 return GetContentCellType(GetOldCell()) == SC_CACCT_MATREF;
1975 // ScChangeActionReject
1976 ScChangeActionReject::ScChangeActionReject(
1977 const sal_uLong nActionNumber, const ScChangeActionState eStateP,
1978 const sal_uLong nRejectingNumber,
1979 const ScBigRange& aBigRangeP, const OUString& aUserP,
1980 const DateTime& aDateTimeP, const OUString& sComment) :
1981 ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment)
1985 bool ScChangeActionReject::Reject(ScDocument& /*rDoc*/)
1987 return false;
1990 SCSIZE ScChangeTrack::ComputeContentSlot( sal_Int32 nRow ) const
1992 if ( nRow < 0 || nRow > rDoc.GetSheetLimits().mnMaxRow )
1993 return mnContentSlots - 1;
1994 return static_cast< SCSIZE >( nRow / mnContentRowsPerSlot );
1997 SCROW ScChangeTrack::InitContentRowsPerSlot()
1999 const SCSIZE nMaxSlots = 0xffe0 / sizeof( ScChangeActionContent* ) - 2;
2000 SCROW nRowsPerSlot = rDoc.GetMaxRowCount() / nMaxSlots;
2001 if ( nRowsPerSlot * nMaxSlots < sal::static_int_cast<SCSIZE>(rDoc.GetMaxRowCount()) )
2002 ++nRowsPerSlot;
2003 return nRowsPerSlot;
2006 ScChangeTrack::ScChangeTrack( ScDocument& rDocP ) :
2007 aFixDateTime( DateTime::SYSTEM ),
2008 rDoc( rDocP )
2010 Init();
2011 SC_MOD()->GetUserOptions().AddListener(this);
2013 ppContentSlots.reset( new ScChangeActionContent* [ mnContentSlots ] );
2014 memset( ppContentSlots.get(), 0, mnContentSlots * sizeof( ScChangeActionContent* ) );
2017 ScChangeTrack::ScChangeTrack( ScDocument& rDocP, std::set<OUString>&& aTempUserCollection) :
2018 maUserCollection(std::move(aTempUserCollection)),
2019 aFixDateTime( DateTime::SYSTEM ),
2020 rDoc( rDocP )
2022 Init();
2023 SC_MOD()->GetUserOptions().AddListener(this);
2024 ppContentSlots.reset( new ScChangeActionContent* [ mnContentSlots ] );
2025 memset( ppContentSlots.get(), 0, mnContentSlots * sizeof( ScChangeActionContent* ) );
2028 ScChangeTrack::~ScChangeTrack()
2030 SC_MOD()->GetUserOptions().RemoveListener(this);
2031 DtorClear();
2034 void ScChangeTrack::Init()
2036 mnContentRowsPerSlot = InitContentRowsPerSlot();
2037 mnContentSlots = rDoc.GetMaxRowCount() / InitContentRowsPerSlot() + 2;
2039 pFirst = nullptr;
2040 pLast = nullptr;
2041 pFirstGeneratedDelContent = nullptr;
2042 pLastCutMove = nullptr;
2043 pLinkInsertCol = nullptr;
2044 pLinkInsertRow = nullptr;
2045 pLinkInsertTab = nullptr;
2046 pLinkMove = nullptr;
2047 xBlockModifyMsg.reset();
2048 nActionMax = 0;
2049 nGeneratedMin = SC_CHGTRACK_GENERATED_START;
2050 nMarkLastSaved = 0;
2051 nStartLastCut = 0;
2052 nEndLastCut = 0;
2053 nLastMerge = 0;
2054 eMergeState = SC_CTMS_NONE;
2055 bInDelete = false;
2056 bInDeleteTop = false;
2057 bInDeleteUndo = false;
2058 bInPasteCut = false;
2059 bUseFixDateTime = false;
2060 bTimeNanoSeconds = true;
2062 CreateAuthorName();
2065 void ScChangeTrack::DtorClear()
2067 ScChangeAction* p;
2068 ScChangeAction* pNext;
2069 for ( p = GetFirst(); p; p = pNext )
2071 pNext = p->GetNext();
2072 delete p;
2074 for ( p = pFirstGeneratedDelContent; p; p = pNext )
2076 pNext = p->GetNext();
2077 delete p;
2079 for( const auto& rEntry : aPasteCutMap )
2081 delete rEntry.second;
2083 pLastCutMove.reset();
2084 ClearMsgQueue();
2087 void ScChangeTrack::ClearMsgQueue()
2089 xBlockModifyMsg.reset();
2090 aMsgStackTmp.clear();
2091 aMsgStackFinal.clear();
2092 aMsgQueue.clear();
2095 void ScChangeTrack::Clear()
2097 DtorClear();
2098 aMap.clear();
2099 aGeneratedMap.clear();
2100 aPasteCutMap.clear();
2101 maUserCollection.clear();
2102 maUser.clear();
2103 Init();
2106 bool ScChangeTrack::IsGenerated( sal_uLong nAction ) const
2108 return nAction >= nGeneratedMin;
2111 ScChangeAction* ScChangeTrack::GetAction( sal_uLong nAction ) const
2113 ScChangeActionMap::const_iterator it = aMap.find( nAction );
2114 if( it != aMap.end() )
2115 return it->second;
2116 else
2117 return nullptr;
2120 ScChangeAction* ScChangeTrack::GetGenerated( sal_uLong nGenerated ) const
2122 ScChangeActionMap::const_iterator it = aGeneratedMap.find( nGenerated );
2123 if( it != aGeneratedMap.end() )
2124 return it->second;
2125 else
2126 return nullptr;
2129 ScChangeAction* ScChangeTrack::GetActionOrGenerated( sal_uLong nAction ) const
2131 return IsGenerated( nAction ) ?
2132 GetGenerated( nAction ) :
2133 GetAction( nAction );
2135 sal_uLong ScChangeTrack::GetLastSavedActionNumber() const
2137 return nMarkLastSaved;
2140 void ScChangeTrack::SetLastSavedActionNumber(sal_uLong nNew)
2142 nMarkLastSaved = nNew;
2145 ScChangeAction* ScChangeTrack::GetLastSaved() const
2147 ScChangeActionMap::const_iterator it = aMap.find( nMarkLastSaved );
2148 if( it != aMap.end() )
2149 return it->second;
2150 else
2151 return nullptr;
2154 void ScChangeTrack::ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints )
2156 if ( rDoc.IsInDtorClear() )
2157 return;
2159 size_t nOldCount = maUserCollection.size();
2161 CreateAuthorName();
2163 if ( maUserCollection.size() != nOldCount )
2165 // New user in collection -> have to repaint because
2166 // colors may be different now (#106697#).
2167 // (Has to be done in the Notify handler, to be sure
2168 // the user collection has already been updated)
2170 ScDocShell* pDocSh = rDoc.GetDocumentShell();
2171 if (pDocSh)
2172 pDocSh->Broadcast( ScPaintHint( ScRange(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB), PaintPartFlags::Grid ) );
2176 void ScChangeTrack::CreateAuthorName()
2178 const SvtUserOptions& rUserOptions = SC_MOD()->GetUserOptions();
2179 OUString aFirstName(rUserOptions.GetFirstName());
2180 OUString aLastName(rUserOptions.GetLastName());
2181 if (aFirstName.isEmpty() && aLastName.isEmpty())
2182 SetUser(ScResId(STR_CHG_UNKNOWN_AUTHOR));
2183 else if(!aFirstName.isEmpty() && aLastName.isEmpty())
2184 SetUser(aFirstName);
2185 else if(aFirstName.isEmpty() && !aLastName.isEmpty())
2186 SetUser(aLastName);
2187 else
2188 SetUser(aFirstName + " " + aLastName);
2192 void ScChangeTrack::SetUser( const OUString& rUser )
2194 maUser = rUser;
2195 maUserCollection.insert(maUser);
2198 void ScChangeTrack::StartBlockModify( ScChangeTrackMsgType eMsgType,
2199 sal_uLong nStartAction )
2201 if ( aModifiedLink.IsSet() )
2203 if ( xBlockModifyMsg )
2204 aMsgStackTmp.push_back( *xBlockModifyMsg ); // Block in Block
2205 xBlockModifyMsg = ScChangeTrackMsgInfo();
2206 xBlockModifyMsg->eMsgType = eMsgType;
2207 xBlockModifyMsg->nStartAction = nStartAction;
2208 xBlockModifyMsg->nEndAction = 0;
2212 void ScChangeTrack::EndBlockModify( sal_uLong nEndAction )
2214 if ( !aModifiedLink.IsSet() )
2215 return;
2217 if ( xBlockModifyMsg )
2219 if ( xBlockModifyMsg->nStartAction <= nEndAction )
2221 xBlockModifyMsg->nEndAction = nEndAction;
2222 // Blocks dissolved in Blocks
2223 aMsgStackFinal.push_back( *xBlockModifyMsg );
2225 else
2226 xBlockModifyMsg.reset();
2227 if (aMsgStackTmp.empty())
2228 xBlockModifyMsg.reset();
2229 else
2231 xBlockModifyMsg = aMsgStackTmp.back(); // Maybe Block in Block
2232 aMsgStackTmp.pop_back();
2235 if ( !xBlockModifyMsg )
2237 bool bNew = !aMsgStackFinal.empty();
2238 aMsgQueue.reserve(aMsgQueue.size() + aMsgStackFinal.size());
2239 aMsgQueue.insert(aMsgQueue.end(), aMsgStackFinal.rbegin(), aMsgStackFinal.rend());
2240 aMsgStackFinal.clear();
2241 if ( bNew )
2242 aModifiedLink.Call( *this );
2246 ScChangeTrackMsgQueue& ScChangeTrack::GetMsgQueue()
2248 return aMsgQueue;
2251 void ScChangeTrack::NotifyModified( ScChangeTrackMsgType eMsgType,
2252 sal_uLong nStartAction, sal_uLong nEndAction )
2254 if ( aModifiedLink.IsSet() )
2256 if ( !xBlockModifyMsg || xBlockModifyMsg->eMsgType != eMsgType ||
2257 (IsGenerated( nStartAction ) &&
2258 (eMsgType == ScChangeTrackMsgType::Append || eMsgType == ScChangeTrackMsgType::Remove)) )
2259 { // Append within Append e.g. not
2260 StartBlockModify( eMsgType, nStartAction );
2261 EndBlockModify( nEndAction );
2266 void ScChangeTrack::MasterLinks( ScChangeAction* pAppend )
2268 ScChangeActionType eType = pAppend->GetType();
2270 if ( eType == SC_CAT_CONTENT )
2272 if ( !IsGenerated( pAppend->GetActionNumber() ) )
2274 SCSIZE nSlot = ComputeContentSlot(
2275 pAppend->GetBigRange().aStart.Row() );
2276 static_cast<ScChangeActionContent*>(pAppend)->InsertInSlot(
2277 &ppContentSlots[nSlot] );
2279 return ;
2282 if ( pAppend->IsRejecting() )
2283 return ; // Rejects do not have dependencies
2285 switch ( eType )
2287 case SC_CAT_INSERT_COLS :
2289 ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
2290 &pLinkInsertCol, pAppend );
2291 pAppend->AddLink( nullptr, pLink );
2293 break;
2294 case SC_CAT_INSERT_ROWS :
2296 ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
2297 &pLinkInsertRow, pAppend );
2298 pAppend->AddLink( nullptr, pLink );
2300 break;
2301 case SC_CAT_INSERT_TABS :
2303 ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
2304 &pLinkInsertTab, pAppend );
2305 pAppend->AddLink( nullptr, pLink );
2307 break;
2308 case SC_CAT_MOVE :
2310 ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
2311 &pLinkMove, pAppend );
2312 pAppend->AddLink( nullptr, pLink );
2314 break;
2315 default:
2317 // added to avoid warnings
2322 void ScChangeTrack::AppendLoaded( std::unique_ptr<ScChangeAction> pActionParam )
2324 ScChangeAction* pAppend = pActionParam.release();
2325 aMap.insert( ::std::make_pair( pAppend->GetActionNumber(), pAppend ) );
2326 if ( !pLast )
2327 pFirst = pLast = pAppend;
2328 else
2330 pLast->pNext = pAppend;
2331 pAppend->pPrev = pLast;
2332 pLast = pAppend;
2334 MasterLinks( pAppend );
2337 void ScChangeTrack::Append( ScChangeAction* pAppend, sal_uLong nAction )
2339 if ( nActionMax < nAction )
2340 nActionMax = nAction;
2341 pAppend->SetUser( maUser );
2342 if ( bUseFixDateTime )
2343 pAppend->SetDateTimeUTC( aFixDateTime );
2344 pAppend->SetActionNumber( nAction );
2345 aMap.insert( ::std::make_pair( nAction, pAppend ) );
2346 // UpdateReference Inserts before Dependencies.
2347 // Delete rejecting Insert which had UpdateReference with Delete Undo.
2348 // UpdateReference also with pLast==NULL, as pAppend can be a Delete,
2349 // which could have generated DelContents.
2350 if ( pAppend->IsInsertType() && !pAppend->IsRejecting() )
2351 UpdateReference( pAppend, false );
2352 if ( !pLast )
2353 pFirst = pLast = pAppend;
2354 else
2356 pLast->pNext = pAppend;
2357 pAppend->pPrev = pLast;
2358 pLast = pAppend;
2359 Dependencies( pAppend );
2361 // UpdateReference does not Insert() after Dependencies.
2362 // Move rejecting Move, which had UpdateReference with Move Undo.
2363 // Do not delete content in ToRange.
2364 if ( !pAppend->IsInsertType() &&
2365 !(pAppend->GetType() == SC_CAT_MOVE && pAppend->IsRejecting()) )
2366 UpdateReference( pAppend, false );
2367 MasterLinks( pAppend );
2369 if ( !aModifiedLink.IsSet() )
2370 return;
2372 NotifyModified( ScChangeTrackMsgType::Append, nAction, nAction );
2373 if ( pAppend->GetType() == SC_CAT_CONTENT )
2375 ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(pAppend);
2376 if ( ( pContent = pContent->GetPrevContent() ) != nullptr )
2378 sal_uLong nMod = pContent->GetActionNumber();
2379 NotifyModified( ScChangeTrackMsgType::Change, nMod, nMod );
2382 else
2383 NotifyModified( ScChangeTrackMsgType::Change, pFirst->GetActionNumber(),
2384 pLast->GetActionNumber() );
2387 void ScChangeTrack::Append( ScChangeAction* pAppend )
2389 Append( pAppend, ++nActionMax );
2392 void ScChangeTrack::AppendDeleteRange( const ScRange& rRange,
2393 ScDocument* pRefDoc, sal_uLong& nStartAction, sal_uLong& nEndAction, SCTAB nDz )
2395 nStartAction = GetActionMax() + 1;
2396 AppendDeleteRange( rRange, pRefDoc, nDz, 0 );
2397 nEndAction = GetActionMax();
2400 void ScChangeTrack::AppendDeleteRange( const ScRange& rRange,
2401 ScDocument* pRefDoc, SCTAB nDz, sal_uLong nRejectingInsert )
2403 SetInDeleteRange( rRange );
2404 StartBlockModify( ScChangeTrackMsgType::Append, GetActionMax() + 1 );
2405 SCCOL nCol1;
2406 SCROW nRow1;
2407 SCTAB nTab1;
2408 SCCOL nCol2;
2409 SCROW nRow2;
2410 SCTAB nTab2;
2411 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
2412 for ( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ )
2414 if ( !pRefDoc || nTab < pRefDoc->GetTableCount() )
2416 if ( nCol1 == 0 && nCol2 == rDoc.MaxCol() )
2417 { // Whole Row and/or Tables
2418 if ( nRow1 == 0 && nRow2 == rDoc.MaxRow() )
2419 { // Whole Table
2420 // TODO: Can't we do the whole Table as a whole?
2421 ScRange aRange( 0, 0, nTab, 0, rDoc.MaxRow(), nTab );
2422 for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
2423 { // Column by column is less than row by row
2424 aRange.aStart.SetCol( nCol );
2425 aRange.aEnd.SetCol( nCol );
2426 if ( nCol == nCol2 )
2427 SetInDeleteTop( true );
2428 AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0,
2429 nTab-nTab1 + nDz, nRejectingInsert );
2431 // Still InDeleteTop!
2432 AppendOneDeleteRange( rRange, pRefDoc, 0, 0,
2433 nTab-nTab1 + nDz, nRejectingInsert );
2435 else
2436 { // Whole rows
2437 ScRange aRange( 0, 0, nTab, rDoc.MaxCol(), 0, nTab );
2438 for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
2440 aRange.aStart.SetRow( nRow );
2441 aRange.aEnd.SetRow( nRow );
2442 if ( nRow == nRow2 )
2443 SetInDeleteTop( true );
2444 AppendOneDeleteRange( aRange, pRefDoc, 0, nRow-nRow1,
2445 0, nRejectingInsert );
2449 else if ( nRow1 == 0 && nRow2 == rDoc.MaxRow() )
2450 { // Whole columns
2451 ScRange aRange( 0, 0, nTab, 0, rDoc.MaxRow(), nTab );
2452 for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
2454 aRange.aStart.SetCol( nCol );
2455 aRange.aEnd.SetCol( nCol );
2456 if ( nCol == nCol2 )
2457 SetInDeleteTop( true );
2458 AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0,
2459 0, nRejectingInsert );
2462 else
2464 OSL_FAIL( "ScChangeTrack::AppendDeleteRange: Block not supported!" );
2466 SetInDeleteTop( false );
2469 EndBlockModify( GetActionMax() );
2472 void ScChangeTrack::AppendOneDeleteRange( const ScRange& rOrgRange,
2473 ScDocument* pRefDoc, SCCOL nDx, SCROW nDy, SCTAB nDz,
2474 sal_uLong nRejectingInsert )
2476 ScRange aTrackRange( rOrgRange );
2477 if ( nDx )
2479 aTrackRange.aStart.IncCol( -nDx );
2480 aTrackRange.aEnd.IncCol( -nDx );
2482 if ( nDy )
2484 aTrackRange.aStart.IncRow( -nDy );
2485 aTrackRange.aEnd.IncRow( -nDy );
2487 if ( nDz )
2489 aTrackRange.aStart.IncTab( -nDz );
2490 aTrackRange.aEnd.IncTab( -nDz );
2492 ScChangeActionDel* pAct = new ScChangeActionDel( &rDoc, aTrackRange, nDx, nDy,
2493 this );
2494 // TabDelete not Contents; they are in separate columns
2495 if ( !(rOrgRange.aStart.Col() == 0 && rOrgRange.aStart.Row() == 0 &&
2496 rOrgRange.aEnd.Col() == rDoc.MaxCol() && rOrgRange.aEnd.Row() == rDoc.MaxRow()) )
2497 LookUpContents( rOrgRange, pRefDoc, -nDx, -nDy, -nDz );
2498 if ( nRejectingInsert )
2500 pAct->SetRejectAction( nRejectingInsert );
2501 pAct->SetState( SC_CAS_ACCEPTED );
2503 Append( pAct );
2506 void ScChangeTrack::LookUpContents( const ScRange& rOrgRange,
2507 ScDocument* pRefDoc, SCCOL nDx, SCROW nDy, SCTAB nDz )
2509 if (!pRefDoc)
2510 return;
2512 ScAddress aPos;
2513 ScBigAddress aBigPos;
2514 ScCellIterator aIter( *pRefDoc, rOrgRange );
2515 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
2517 if (!ScChangeActionContent::GetContentCellType(aIter.getRefCellValue()))
2518 continue;
2520 aBigPos.Set( aIter.GetPos().Col() + nDx, aIter.GetPos().Row() + nDy,
2521 aIter.GetPos().Tab() + nDz );
2522 ScChangeActionContent* pContent = SearchContentAt( aBigPos, nullptr );
2523 if (pContent)
2524 continue;
2526 // Untracked Contents
2527 aPos.Set( aIter.GetPos().Col() + nDx, aIter.GetPos().Row() + nDy,
2528 aIter.GetPos().Tab() + nDz );
2530 GenerateDelContent(aPos, aIter.getCellValue(), pRefDoc);
2531 // The Content is _not_ added with AddContent here, but in UpdateReference.
2532 // We do this in order to e.g. handle intersecting Deletes correctly
2536 void ScChangeTrack::AppendMove( const ScRange& rFromRange,
2537 const ScRange& rToRange, ScDocument* pRefDoc )
2539 ScChangeActionMove* pAct = new ScChangeActionMove( rFromRange, rToRange, this );
2540 LookUpContents( rToRange, pRefDoc, 0, 0, 0 ); // Overwritten Contents
2541 Append( pAct );
2544 bool ScChangeTrack::IsMatrixFormulaRangeDifferent(
2545 const ScCellValue& rOldCell, const ScCellValue& rNewCell )
2547 SCCOL nC1, nC2;
2548 SCROW nR1, nR2;
2549 nC1 = nC2 = 0;
2550 nR1 = nR2 = 0;
2552 if (rOldCell.getType() == CELLTYPE_FORMULA && rOldCell.getFormula()->GetMatrixFlag() == ScMatrixMode::Formula)
2553 rOldCell.getFormula()->GetMatColsRows(nC1, nR1);
2555 if (rNewCell.getType() == CELLTYPE_FORMULA && rNewCell.getFormula()->GetMatrixFlag() == ScMatrixMode::Formula)
2556 rNewCell.getFormula()->GetMatColsRows(nC1, nR1);
2558 return nC1 != nC2 || nR1 != nR2;
2561 void ScChangeTrack::AppendContent(
2562 const ScAddress& rPos, const ScCellValue& rOldCell, sal_uLong nOldFormat, ScDocument* pRefDoc )
2564 if ( !pRefDoc )
2565 pRefDoc = &rDoc;
2567 OUString aOldValue = ScChangeActionContent::GetStringOfCell(rOldCell, pRefDoc, nOldFormat);
2569 ScCellValue aNewCell;
2570 aNewCell.assign(rDoc, rPos);
2571 OUString aNewValue = ScChangeActionContent::GetStringOfCell(aNewCell, &rDoc, rPos);
2573 if (aOldValue != aNewValue || IsMatrixFormulaRangeDifferent(rOldCell, aNewCell))
2574 { // Only track real changes
2575 ScRange aRange( rPos );
2576 ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
2577 pAct->SetOldValue(rOldCell, pRefDoc, &rDoc, nOldFormat);
2578 pAct->SetNewValue(aNewCell, &rDoc);
2579 Append( pAct );
2583 void ScChangeTrack::AppendContent( const ScAddress& rPos,
2584 const ScDocument* pRefDoc )
2586 ScCellValue aOldCell;
2587 aOldCell.assign(*pRefDoc, rPos);
2588 OUString aOldValue = ScChangeActionContent::GetStringOfCell(aOldCell, pRefDoc, rPos);
2590 ScCellValue aNewCell;
2591 aNewCell.assign(rDoc, rPos);
2592 OUString aNewValue = ScChangeActionContent::GetStringOfCell(aNewCell, &rDoc, rPos);
2594 if (aOldValue != aNewValue || IsMatrixFormulaRangeDifferent(aOldCell, aNewCell))
2595 { // Only track real changes
2596 ScRange aRange( rPos );
2597 ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
2598 pAct->SetOldValue(aOldCell, pRefDoc, &rDoc);
2599 pAct->SetNewValue(aNewCell, &rDoc);
2600 Append( pAct );
2604 void ScChangeTrack::AppendContent( const ScAddress& rPos, const ScCellValue& rOldCell )
2606 if (ScChangeActionContent::NeedsNumberFormat(rOldCell))
2607 AppendContent(rPos, rOldCell, rDoc.GetNumberFormat(rPos), &rDoc);
2608 else
2609 AppendContent(rPos, rOldCell, 0, &rDoc);
2612 void ScChangeTrack::SetLastCutMoveRange( const ScRange& rRange,
2613 ScDocument* pRefDoc )
2615 if ( !pLastCutMove )
2616 return;
2618 // Do not link ToRange with Deletes and don't change its size
2619 // This is actually unnecessary, as a delete triggers a ResetLastCut
2620 // in ScViewFunc::PasteFromClip before that
2621 ScBigRange& r = pLastCutMove->GetBigRange();
2622 r.aEnd.SetCol( -1 );
2623 r.aEnd.SetRow( -1 );
2624 r.aEnd.SetTab( -1 );
2625 r.aStart.SetCol( -1 - (rRange.aEnd.Col() - rRange.aStart.Col()) );
2626 r.aStart.SetRow( -1 - (rRange.aEnd.Row() - rRange.aStart.Row()) );
2627 r.aStart.SetTab( -1 - (rRange.aEnd.Tab() - rRange.aStart.Tab()) );
2628 // Contents in FromRange we should overwrite
2629 LookUpContents( rRange, pRefDoc, 0, 0, 0 );
2632 void ScChangeTrack::AppendContentRange( const ScRange& rRange,
2633 ScDocument* pRefDoc, sal_uLong& nStartAction, sal_uLong& nEndAction,
2634 ScChangeActionClipMode eClipMode )
2636 if ( eClipMode == SC_CACM_CUT )
2638 ResetLastCut();
2639 pLastCutMove.reset(new ScChangeActionMove( rRange, rRange, this ));
2640 SetLastCutMoveRange( rRange, pRefDoc );
2642 SCCOL nCol1;
2643 SCROW nRow1;
2644 SCTAB nTab1;
2645 SCCOL nCol2;
2646 SCROW nRow2;
2647 SCTAB nTab2;
2648 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
2649 bool bDoContents;
2650 if ( eClipMode == SC_CACM_PASTE && HasLastCut() )
2652 bDoContents = false;
2653 SetInPasteCut( true );
2654 // Adjust Paste and Cut; Paste can be larger a Range
2655 ScRange aRange( rRange );
2656 ScBigRange& r = pLastCutMove->GetBigRange();
2657 SCCOL nTmpCol;
2658 if ( (nTmpCol = static_cast<SCCOL>(r.aEnd.Col() - r.aStart.Col())) != (nCol2 - nCol1) )
2660 aRange.aEnd.SetCol( aRange.aStart.Col() + nTmpCol );
2661 nCol1 += nTmpCol + 1;
2662 bDoContents = true;
2664 SCROW nTmpRow;
2665 if ( (nTmpRow = static_cast<SCROW>(r.aEnd.Row() - r.aStart.Row())) != (nRow2 - nRow1) )
2667 aRange.aEnd.SetRow( aRange.aStart.Row() + nTmpRow );
2668 nRow1 += nTmpRow + 1;
2669 bDoContents = true;
2671 SCTAB nTmpTab;
2672 if ( (nTmpTab = static_cast<SCTAB>(r.aEnd.Tab() - r.aStart.Tab())) != (nTab2 - nTab1) )
2674 aRange.aEnd.SetTab( aRange.aStart.Tab() + nTmpTab );
2675 nTab1 += nTmpTab + 1;
2676 bDoContents = true;
2678 r = aRange;
2679 Undo( nStartLastCut, nEndLastCut ); // Remember Cuts here
2680 // StartAction only after Undo!
2681 nStartAction = GetActionMax() + 1;
2682 StartBlockModify( ScChangeTrackMsgType::Append, nStartAction );
2683 // Contents to overwrite in ToRange
2684 LookUpContents( aRange, pRefDoc, 0, 0, 0 );
2685 pLastCutMove->SetStartLastCut( nStartLastCut );
2686 pLastCutMove->SetEndLastCut( nEndLastCut );
2687 Append( pLastCutMove.release() );
2688 ResetLastCut();
2689 SetInPasteCut( false );
2691 else
2693 bDoContents = true;
2694 nStartAction = GetActionMax() + 1;
2695 StartBlockModify( ScChangeTrackMsgType::Append, nStartAction );
2697 if ( bDoContents )
2699 ScAddress aPos;
2700 for ( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ )
2702 aPos.SetTab( nTab );
2703 // AppendContent() is a no-op if both cells are empty.
2704 SCCOL lastCol = std::max( pRefDoc->ClampToAllocatedColumns( nTab, nCol2 ),
2705 rDoc.ClampToAllocatedColumns( nTab, nCol2 ));
2706 for ( SCCOL nCol = nCol1; nCol <= lastCol; nCol++ )
2708 aPos.SetCol( nCol );
2709 SCROW lastRow = std::max( pRefDoc->GetLastDataRow( nTab, nCol, nCol, nRow2 ),
2710 rDoc.GetLastDataRow( nTab, nCol, nCol, nRow2 ));
2711 for ( SCROW nRow = nRow1; nRow <= lastRow; nRow++ )
2713 aPos.SetRow( nRow );
2714 AppendContent( aPos, pRefDoc );
2719 nEndAction = GetActionMax();
2720 EndBlockModify( nEndAction );
2721 if ( eClipMode == SC_CACM_CUT )
2723 nStartLastCut = nStartAction;
2724 nEndLastCut = nEndAction;
2728 void ScChangeTrack::AppendContentsIfInRefDoc( ScDocument& rRefDoc,
2729 sal_uLong& nStartAction, sal_uLong& nEndAction )
2731 ScCellIterator aIter(rRefDoc, ScRange(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB));
2732 if (aIter.first())
2734 nStartAction = GetActionMax() + 1;
2735 StartBlockModify( ScChangeTrackMsgType::Append, nStartAction );
2736 SvNumberFormatter* pFormatter = rRefDoc.GetFormatTable();
2739 const ScAddress& rPos = aIter.GetPos();
2740 const ScPatternAttr* pPat = rRefDoc.GetPattern(rPos);
2741 AppendContent(
2742 rPos, aIter.getCellValue(), pPat->GetNumberFormat(pFormatter), &rRefDoc);
2744 while (aIter.next());
2746 nEndAction = GetActionMax();
2747 EndBlockModify( nEndAction );
2749 else
2750 nStartAction = nEndAction = 0;
2753 ScChangeActionContent* ScChangeTrack::AppendContentOnTheFly(
2754 const ScAddress& rPos, const ScCellValue& rOldCell, const ScCellValue& rNewCell,
2755 sal_uLong nOldFormat, sal_uLong nNewFormat )
2757 ScRange aRange( rPos );
2758 ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
2759 pAct->SetOldNewCells(rOldCell, nOldFormat, rNewCell, nNewFormat, &rDoc);
2760 Append( pAct );
2761 return pAct;
2764 void ScChangeTrack::AppendInsert( const ScRange& rRange, bool bEndOfList )
2766 ScChangeActionIns* pAct = new ScChangeActionIns(&rDoc, rRange, bEndOfList);
2767 Append( pAct );
2770 void ScChangeTrack::DeleteCellEntries( std::vector<ScChangeActionContent*>& rCellList,
2771 const ScChangeAction* pDeletor )
2773 for (ScChangeActionContent* pContent : rCellList)
2775 pContent->RemoveDeletedIn( pDeletor );
2776 if ( IsGenerated( pContent->GetActionNumber() ) &&
2777 !pContent->IsDeletedIn() )
2778 DeleteGeneratedDelContent( pContent );
2780 rCellList.clear();
2783 ScChangeActionContent* ScChangeTrack::GenerateDelContent(
2784 const ScAddress& rPos, const ScCellValue& rCell, const ScDocument* pFromDoc )
2786 ScChangeActionContent* pContent = new ScChangeActionContent(
2787 ScRange( rPos ) );
2788 pContent->SetActionNumber( --nGeneratedMin );
2789 // Only NewValue
2790 ScChangeActionContent::SetValue( pContent->maNewValue, pContent->maNewCell,
2791 rPos, rCell, pFromDoc, &rDoc );
2792 // pNextContent and pPrevContent are not set
2793 if ( pFirstGeneratedDelContent )
2794 { // Insert at front
2795 pFirstGeneratedDelContent->pPrev = pContent;
2796 pContent->pNext = pFirstGeneratedDelContent;
2798 pFirstGeneratedDelContent = pContent;
2799 aGeneratedMap.insert( std::make_pair( nGeneratedMin, pContent ) );
2800 NotifyModified( ScChangeTrackMsgType::Append, nGeneratedMin, nGeneratedMin );
2801 return pContent;
2804 void ScChangeTrack::DeleteGeneratedDelContent( ScChangeActionContent* pContent )
2806 sal_uLong nAct = pContent->GetActionNumber();
2807 aGeneratedMap.erase( nAct );
2808 if ( pFirstGeneratedDelContent == pContent )
2809 pFirstGeneratedDelContent = static_cast<ScChangeActionContent*>(pContent->pNext);
2810 if ( pContent->pNext )
2811 pContent->pNext->pPrev = pContent->pPrev;
2812 if ( pContent->pPrev )
2813 pContent->pPrev->pNext = pContent->pNext;
2814 delete pContent;
2815 NotifyModified( ScChangeTrackMsgType::Remove, nAct, nAct );
2816 if ( nAct == nGeneratedMin )
2817 ++nGeneratedMin; // Only after NotifyModified due to IsGenerated!
2820 ScChangeActionContent* ScChangeTrack::SearchContentAt(
2821 const ScBigAddress& rPos, const ScChangeAction* pButNotThis ) const
2823 SCSIZE nSlot = ComputeContentSlot( rPos.Row() );
2824 for ( ScChangeActionContent* p = ppContentSlots[nSlot]; p;
2825 p = p->GetNextInSlot() )
2827 if ( p != pButNotThis && !p->IsDeletedIn() &&
2828 p->GetBigRange().aStart == rPos )
2830 ScChangeActionContent* pContent = p->GetTopContent();
2831 if ( !pContent->IsDeletedIn() )
2832 return pContent;
2835 return nullptr;
2838 void ScChangeTrack::AddDependentWithNotify( ScChangeAction* pParent,
2839 ScChangeAction* pDependent )
2841 ScChangeActionLinkEntry* pLink = pParent->AddDependent( pDependent );
2842 pDependent->AddLink( pParent, pLink );
2843 if ( aModifiedLink.IsSet() )
2845 sal_uLong nMod = pParent->GetActionNumber();
2846 NotifyModified( ScChangeTrackMsgType::Parent, nMod, nMod );
2850 void ScChangeTrack::Dependencies( ScChangeAction* pAct )
2852 // Find the last dependency for Col/Row/Tab each
2853 // Concatenate Content at the same position
2854 // Move dependencies
2855 ScChangeActionType eActType = pAct->GetType();
2856 if ( eActType == SC_CAT_REJECT ||
2857 (eActType == SC_CAT_MOVE && pAct->IsRejecting()) )
2858 return ; // These Rejects are not dependent
2860 if ( eActType == SC_CAT_CONTENT )
2862 if ( !(static_cast<ScChangeActionContent*>(pAct)->GetNextContent() ||
2863 static_cast<ScChangeActionContent*>(pAct)->GetPrevContent()) )
2864 { // Concatenate Contents at same position
2865 ScChangeActionContent* pContent = SearchContentAt(
2866 pAct->GetBigRange().aStart, pAct );
2867 if ( pContent )
2869 pContent->SetNextContent( static_cast<ScChangeActionContent*>(pAct) );
2870 static_cast<ScChangeActionContent*>(pAct)->SetPrevContent( pContent );
2873 const ScCellValue& rCell = static_cast<ScChangeActionContent*>(pAct)->GetNewCell();
2874 if ( ScChangeActionContent::GetContentCellType(rCell) == SC_CACCT_MATREF )
2876 ScAddress aOrg;
2877 bool bOrgFound = rCell.getFormula()->GetMatrixOrigin(rDoc, aOrg);
2878 ScChangeActionContent* pContent = (bOrgFound ? SearchContentAt( aOrg, pAct ) : nullptr);
2879 if ( pContent && pContent->IsMatrixOrigin() )
2881 AddDependentWithNotify( pContent, pAct );
2883 else
2885 OSL_FAIL( "ScChangeTrack::Dependencies: MatOrg not found" );
2890 if ( !(pLinkInsertCol || pLinkInsertRow || pLinkInsertTab || pLinkMove) )
2891 return ; // No Dependencies
2892 if ( pAct->IsRejecting() )
2893 return ; // Except for Content no Dependencies
2895 // Insert in a corresponding Insert depends on it or else we would need
2896 // to split the preceding one.
2897 // Intersecting Inserts and Deletes are not dependent, everything else
2898 // is dependent.
2899 // The Insert last linked in is at the beginning of a chain, just the way we need it
2901 const ScBigRange& rRange = pAct->GetBigRange();
2902 bool bActNoInsert = !pAct->IsInsertType();
2903 bool bActColDel = ( eActType == SC_CAT_DELETE_COLS );
2904 bool bActRowDel = ( eActType == SC_CAT_DELETE_ROWS );
2905 bool bActTabDel = ( eActType == SC_CAT_DELETE_TABS );
2907 if ( pLinkInsertCol && (eActType == SC_CAT_INSERT_COLS ||
2908 (bActNoInsert && !bActRowDel && !bActTabDel)) )
2910 for ( ScChangeActionLinkEntry* pL = pLinkInsertCol; pL; pL = pL->GetNext() )
2912 ScChangeActionIns* pTest = static_cast<ScChangeActionIns*>(pL->GetAction());
2913 if ( !pTest->IsRejected() &&
2914 pTest->GetBigRange().Intersects( rRange ) )
2916 AddDependentWithNotify( pTest, pAct );
2917 break; // for
2921 if ( pLinkInsertRow && (eActType == SC_CAT_INSERT_ROWS ||
2922 (bActNoInsert && !bActColDel && !bActTabDel)) )
2924 for ( ScChangeActionLinkEntry* pL = pLinkInsertRow; pL; pL = pL->GetNext() )
2926 ScChangeActionIns* pTest = static_cast<ScChangeActionIns*>(pL->GetAction());
2927 if ( !pTest->IsRejected() &&
2928 pTest->GetBigRange().Intersects( rRange ) )
2930 AddDependentWithNotify( pTest, pAct );
2931 break; // for
2935 if ( pLinkInsertTab && (eActType == SC_CAT_INSERT_TABS ||
2936 (bActNoInsert && !bActColDel && !bActRowDel)) )
2938 for ( ScChangeActionLinkEntry* pL = pLinkInsertTab; pL; pL = pL->GetNext() )
2940 ScChangeActionIns* pTest = static_cast<ScChangeActionIns*>(pL->GetAction());
2941 if ( !pTest->IsRejected() &&
2942 pTest->GetBigRange().Intersects( rRange ) )
2944 AddDependentWithNotify( pTest, pAct );
2945 break; // for
2950 if ( !pLinkMove )
2951 return;
2953 if ( eActType == SC_CAT_CONTENT )
2954 { // Content is depending on FromRange
2955 const ScBigAddress& rPos = rRange.aStart;
2956 for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
2958 ScChangeActionMove* pTest = static_cast<ScChangeActionMove*>(pL->GetAction());
2959 if ( !pTest->IsRejected() &&
2960 pTest->GetFromRange().Contains( rPos ) )
2962 AddDependentWithNotify( pTest, pAct );
2966 else if ( eActType == SC_CAT_MOVE )
2967 { // Move FromRange is depending on ToRange
2968 const ScBigRange& rFromRange = static_cast<ScChangeActionMove*>(pAct)->GetFromRange();
2969 for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
2971 ScChangeActionMove* pTest = static_cast<ScChangeActionMove*>(pL->GetAction());
2972 if ( !pTest->IsRejected() &&
2973 pTest->GetBigRange().Intersects( rFromRange ) )
2975 AddDependentWithNotify( pTest, pAct );
2979 else
2980 { // Inserts and Deletes are depending as soon as they cross FromRange or
2981 // ToRange
2982 for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
2984 ScChangeActionMove* pTest = static_cast<ScChangeActionMove*>(pL->GetAction());
2985 if ( !pTest->IsRejected() &&
2986 (pTest->GetFromRange().Intersects( rRange ) ||
2987 pTest->GetBigRange().Intersects( rRange )) )
2989 AddDependentWithNotify( pTest, pAct );
2995 void ScChangeTrack::Remove( ScChangeAction* pRemove )
2997 // Remove from Track
2998 sal_uLong nAct = pRemove->GetActionNumber();
2999 aMap.erase( nAct );
3000 if ( nAct == nActionMax )
3001 --nActionMax;
3002 if ( pRemove == pLast )
3003 pLast = pRemove->pPrev;
3004 if ( pRemove == pFirst )
3005 pFirst = pRemove->pNext;
3006 if ( nAct == nMarkLastSaved )
3007 nMarkLastSaved =
3008 ( pRemove->pPrev ? pRemove->pPrev->GetActionNumber() : 0 );
3010 // Remove from global chain
3011 if ( pRemove->pNext )
3012 pRemove->pNext->pPrev = pRemove->pPrev;
3013 if ( pRemove->pPrev )
3014 pRemove->pPrev->pNext = pRemove->pNext;
3016 // Don't delete Dependencies
3017 // That happens automatically on delete by LinkEntry without traversing lists
3018 if ( aModifiedLink.IsSet() )
3020 NotifyModified( ScChangeTrackMsgType::Remove, nAct, nAct );
3021 if ( pRemove->GetType() == SC_CAT_CONTENT )
3023 ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(pRemove);
3024 if ( ( pContent = pContent->GetPrevContent() ) != nullptr )
3026 sal_uLong nMod = pContent->GetActionNumber();
3027 NotifyModified( ScChangeTrackMsgType::Change, nMod, nMod );
3030 else if ( pLast )
3031 NotifyModified( ScChangeTrackMsgType::Change, pFirst->GetActionNumber(),
3032 pLast->GetActionNumber() );
3035 if ( IsInPasteCut() && pRemove->GetType() == SC_CAT_CONTENT )
3036 { // Content is reused!
3037 ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(pRemove);
3038 pContent->RemoveAllLinks();
3039 pContent->ClearTrack();
3040 pContent->pNext = pContent->pPrev = nullptr;
3041 pContent->pNextContent = pContent->pPrevContent = nullptr;
3045 void ScChangeTrack::Undo( sal_uLong nStartAction, sal_uLong nEndAction, bool bMerge )
3047 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3048 if ( bMerge )
3050 SetMergeState( SC_CTMS_UNDO );
3053 if ( nStartAction == 0 )
3054 ++nStartAction;
3055 if ( nEndAction > nActionMax )
3056 nEndAction = nActionMax;
3057 if ( nEndAction && nStartAction <= nEndAction )
3059 if ( nStartAction == nStartLastCut && nEndAction == nEndLastCut &&
3060 !IsInPasteCut() )
3061 ResetLastCut();
3062 StartBlockModify( ScChangeTrackMsgType::Remove, nStartAction );
3063 for ( sal_uLong j = nEndAction; j >= nStartAction; --j )
3064 { // Traverse backwards to recycle nActionMax and for faster access via pLast
3065 // Deletes are in right order
3066 ScChangeAction* pAct = IsLastAction(j) ? pLast : GetAction(j);
3068 if (!pAct)
3069 continue;
3071 if ( pAct->IsDeleteType() )
3073 if (j == nEndAction || (pAct != pLast && static_cast<ScChangeActionDel*>(pAct)->IsTopDelete()))
3075 SetInDeleteTop( true );
3076 SetInDeleteRange( static_cast<ScChangeActionDel*>(pAct)->GetOverAllRange().MakeRange( rDoc ) );
3079 UpdateReference( pAct, true );
3080 SetInDeleteTop( false );
3081 Remove( pAct );
3082 if ( IsInPasteCut() )
3084 aPasteCutMap.insert( ::std::make_pair( pAct->GetActionNumber(), pAct ) );
3085 continue;
3088 if ( j == nStartAction && pAct->GetType() == SC_CAT_MOVE )
3090 ScChangeActionMove* pMove = static_cast<ScChangeActionMove*>(pAct);
3091 sal_uLong nStart = pMove->GetStartLastCut();
3092 sal_uLong nEnd = pMove->GetEndLastCut();
3093 if ( nStart && nStart <= nEnd )
3094 { // Recover LastCut
3095 // Break Links before Cut Append!
3096 pMove->RemoveAllLinks();
3097 StartBlockModify( ScChangeTrackMsgType::Append, nStart );
3098 for ( sal_uLong nCut = nStart; nCut <= nEnd; nCut++ )
3100 ScChangeActionMap::iterator itCut = aPasteCutMap.find( nCut );
3102 if ( itCut != aPasteCutMap.end() )
3104 OSL_ENSURE( aMap.find( nCut ) == aMap.end(), "ScChangeTrack::Undo: nCut dup" );
3105 Append( itCut->second, nCut );
3106 aPasteCutMap.erase( itCut );
3108 else
3110 OSL_FAIL( "ScChangeTrack::Undo: nCut not found" );
3113 EndBlockModify( nEnd );
3114 ResetLastCut();
3115 nStartLastCut = nStart;
3116 nEndLastCut = nEnd;
3117 pLastCutMove.reset(pMove);
3118 SetLastCutMoveRange(
3119 pMove->GetFromRange().MakeRange( rDoc ), &rDoc );
3121 else
3122 delete pMove;
3124 else
3125 delete pAct;
3127 EndBlockModify( nEndAction );
3130 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3131 if ( bMerge )
3133 SetMergeState( SC_CTMS_OTHER );
3137 bool ScChangeTrack::MergeIgnore( const ScChangeAction& rAction, sal_uLong nFirstMerge )
3139 if ( rAction.IsRejected() )
3140 return true; // There's still a suitable Reject Action coming
3142 if ( rAction.IsRejecting() && rAction.GetRejectAction() >= nFirstMerge )
3143 return true; // There it is
3145 return false; // Everything else
3148 void ScChangeTrack::MergePrepare( const ScChangeAction* pFirstMerge, bool bShared )
3150 SetMergeState( SC_CTMS_PREPARE );
3151 sal_uLong nFirstMerge = pFirstMerge->GetActionNumber();
3152 ScChangeAction* pAct = GetLast();
3153 if ( pAct )
3155 SetLastMerge( pAct->GetActionNumber() );
3156 while ( pAct )
3157 { // Traverse backwards; Deletes in right order
3158 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3159 if ( bShared || !ScChangeTrack::MergeIgnore( *pAct, nFirstMerge ) )
3161 if ( pAct->IsDeleteType() )
3163 if ( static_cast<ScChangeActionDel*>(pAct)->IsTopDelete() )
3165 SetInDeleteTop( true );
3166 SetInDeleteRange( static_cast<ScChangeActionDel*>(pAct)->
3167 GetOverAllRange().MakeRange( rDoc ) );
3170 UpdateReference( pAct, true );
3171 SetInDeleteTop( false );
3172 pAct->DeleteCellEntries(); // Else segfault in Track Clear()
3174 pAct = ( pAct == pFirstMerge ? nullptr : pAct->GetPrev() );
3177 SetMergeState( SC_CTMS_OTHER ); // Preceding by default MergeOther!
3180 void ScChangeTrack::MergeOwn( ScChangeAction* pAct, sal_uLong nFirstMerge, bool bShared )
3182 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3183 if ( !bShared && ScChangeTrack::MergeIgnore( *pAct, nFirstMerge ) )
3184 return;
3186 SetMergeState( SC_CTMS_OWN );
3187 if ( pAct->IsDeleteType() )
3189 if ( static_cast<ScChangeActionDel*>(pAct)->IsTopDelete() )
3191 SetInDeleteTop( true );
3192 SetInDeleteRange( static_cast<ScChangeActionDel*>(pAct)->
3193 GetOverAllRange().MakeRange( rDoc ) );
3196 UpdateReference( pAct, false );
3197 SetInDeleteTop( false );
3198 SetMergeState( SC_CTMS_OTHER ); // Preceding by default MergeOther!
3201 void ScChangeTrack::UpdateReference( ScChangeAction* pAct, bool bUndo )
3203 ScChangeActionType eActType = pAct->GetType();
3204 if ( eActType == SC_CAT_CONTENT || eActType == SC_CAT_REJECT )
3205 return ;
3207 // Formula cells are not in the Document!
3208 bool bOldAutoCalc = rDoc.GetAutoCalc();
3209 rDoc.SetAutoCalc( false );
3210 bool bOldNoListening = rDoc.GetNoListening();
3211 rDoc.SetNoListening( true );
3213 // Formula cells ExpandRefs synchronized to the ones in the Document!
3214 bool bOldExpandRefs = rDoc.IsExpandRefs();
3215 if ( (!bUndo && pAct->IsInsertType()) || (bUndo && pAct->IsDeleteType()) )
3216 rDoc.SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() );
3218 if ( pAct->IsDeleteType() )
3220 SetInDeleteUndo( bUndo );
3221 SetInDelete( true );
3223 else if ( GetMergeState() == SC_CTMS_OWN )
3225 // Recover references of formula cells
3226 // Previous MergePrepare behaved like a Delete when Inserting
3227 if ( pAct->IsInsertType() )
3228 SetInDeleteUndo( true );
3231 // First the generated ones, as if they were tracked previously!
3232 if ( pFirstGeneratedDelContent )
3233 UpdateReference( reinterpret_cast<ScChangeAction**>(&pFirstGeneratedDelContent), pAct,
3234 bUndo );
3235 UpdateReference( &pFirst, pAct, bUndo );
3237 SetInDelete( false );
3238 SetInDeleteUndo( false );
3240 rDoc.SetExpandRefs( bOldExpandRefs );
3241 rDoc.SetNoListening( bOldNoListening );
3242 rDoc.SetAutoCalc( bOldAutoCalc );
3245 void ScChangeTrack::UpdateReference( ScChangeAction** ppFirstAction,
3246 ScChangeAction* pAct, bool bUndo )
3248 ScChangeActionType eActType = pAct->GetType();
3249 bool bGeneratedDelContents =
3250 ( ppFirstAction == reinterpret_cast<ScChangeAction**>(&pFirstGeneratedDelContent) );
3251 const ScBigRange& rOrgRange = pAct->GetBigRange();
3252 ScBigRange aRange( rOrgRange );
3253 ScBigRange aDelRange( rOrgRange );
3254 sal_Int32 nDx, nDy, nDz;
3255 nDx = nDy = nDz = 0;
3256 UpdateRefMode eMode = URM_INSDEL;
3257 bool bDel = false;
3258 switch ( eActType )
3260 case SC_CAT_INSERT_COLS :
3261 aRange.aEnd.SetCol( ScBigRange::nRangeMax );
3262 nDx = rOrgRange.aEnd.Col() - rOrgRange.aStart.Col() + 1;
3263 break;
3264 case SC_CAT_INSERT_ROWS :
3265 aRange.aEnd.SetRow( ScBigRange::nRangeMax );
3266 nDy = rOrgRange.aEnd.Row() - rOrgRange.aStart.Row() + 1;
3267 break;
3268 case SC_CAT_INSERT_TABS :
3269 aRange.aEnd.SetTab( ScBigRange::nRangeMax );
3270 nDz = rOrgRange.aEnd.Tab() - rOrgRange.aStart.Tab() + 1;
3271 break;
3272 case SC_CAT_DELETE_COLS :
3273 aRange.aEnd.SetCol( ScBigRange::nRangeMax );
3274 nDx = -(rOrgRange.aEnd.Col() - rOrgRange.aStart.Col() + 1);
3275 aDelRange.aEnd.SetCol( aDelRange.aStart.Col() - nDx - 1 );
3276 bDel = true;
3277 break;
3278 case SC_CAT_DELETE_ROWS :
3279 aRange.aEnd.SetRow( ScBigRange::nRangeMax );
3280 nDy = -(rOrgRange.aEnd.Row() - rOrgRange.aStart.Row() + 1);
3281 aDelRange.aEnd.SetRow( aDelRange.aStart.Row() - nDy - 1 );
3282 bDel = true;
3283 break;
3284 case SC_CAT_DELETE_TABS :
3285 aRange.aEnd.SetTab( ScBigRange::nRangeMax );
3286 nDz = -(rOrgRange.aEnd.Tab() - rOrgRange.aStart.Tab() + 1);
3287 aDelRange.aEnd.SetTab( aDelRange.aStart.Tab() - nDz - 1 );
3288 bDel = true;
3289 break;
3290 case SC_CAT_MOVE :
3291 eMode = URM_MOVE;
3292 static_cast<ScChangeActionMove*>(pAct)->GetDelta( nDx, nDy, nDz );
3293 break;
3294 default:
3295 OSL_FAIL( "ScChangeTrack::UpdateReference: unknown Type" );
3297 if ( bUndo )
3299 nDx = -nDx;
3300 nDy = -nDy;
3301 nDz = -nDz;
3303 if ( bDel )
3304 { // For this mechanism we assume:
3305 // There's only a whole, simple deleted row/column
3306 ScChangeActionDel* pActDel = static_cast<ScChangeActionDel*>(pAct);
3307 if ( !bUndo )
3308 { // Delete
3309 ScChangeActionType eInsType = SC_CAT_NONE; // for Insert Undo "Deletes"
3310 switch ( eActType )
3312 case SC_CAT_DELETE_COLS :
3313 eInsType = SC_CAT_INSERT_COLS;
3314 break;
3315 case SC_CAT_DELETE_ROWS :
3316 eInsType = SC_CAT_INSERT_ROWS;
3317 break;
3318 case SC_CAT_DELETE_TABS :
3319 eInsType = SC_CAT_INSERT_TABS;
3320 break;
3321 default:
3323 // added to avoid warnings
3326 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3328 if ( p == pAct )
3329 continue; // for
3330 bool bUpdate = true;
3331 if ( GetMergeState() == SC_CTMS_OTHER &&
3332 p->GetActionNumber() <= GetLastMerge() )
3333 { // Delete in merged Document, Action in the one to be merged
3334 if ( p->IsInsertType() )
3336 // On Insert only adjust references if the Delete does
3337 // not intersect the Insert
3338 if ( !aDelRange.Intersects( p->GetBigRange() ) )
3339 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3340 bUpdate = false;
3342 else if ( p->GetType() == SC_CAT_CONTENT &&
3343 p->IsDeletedInDelType( eInsType ) )
3344 { // Content in Insert Undo "Delete"
3345 // Do not adjust if this Delete would be in the Insert "Delete" (was just moved)
3346 if ( aDelRange.Contains( p->GetBigRange().aStart ) )
3347 bUpdate = false;
3348 else
3350 const ScChangeActionLinkEntry* pLink = p->GetDeletedIn();
3351 while ( pLink && bUpdate )
3353 const ScChangeAction* pDel = pLink->GetAction();
3354 if ( pDel && pDel->GetType() == eInsType &&
3355 pDel->GetBigRange().Contains( aDelRange ) )
3356 bUpdate = false;
3357 pLink = pLink->GetNext();
3361 if ( !bUpdate )
3362 continue; // for
3364 if ( aDelRange.Contains( p->GetBigRange() ) )
3366 // Do not adjust within a just deleted range,
3367 // instead assign the range.
3368 // Stack up ranges that have been deleted multiple times.
3369 // Intersecting Deletes cause "multiple delete" to be set.
3370 if ( !p->IsDeletedInDelType( eActType ) )
3372 p->SetDeletedIn( pActDel );
3373 // Add GeneratedDelContent to the to-be-deleted list
3374 if ( bGeneratedDelContents )
3375 pActDel->AddContent( static_cast<ScChangeActionContent*>(p) );
3377 bUpdate = false;
3379 else
3381 // Cut off inserted ranges, if Start/End is within the Delete,
3382 // but the Insert is not completely within the Delete or
3383 // the Delete is not completely within the Insert.
3384 // The Delete remembers which Insert it has cut off from;
3385 // it can also just be a single Insert (because Delete has
3386 // a single column/is a single row).
3387 // There can be a lot of cut-off Moves.
3389 // ! A Delete is always a single column/a single row, therefore
3390 // ! 1 without calculating the intersection.
3391 switch ( p->GetType() )
3393 case SC_CAT_INSERT_COLS :
3394 if ( eActType == SC_CAT_DELETE_COLS )
3396 if ( aDelRange.Contains( p->GetBigRange().aStart ) )
3398 pActDel->SetCutOffInsert(
3399 static_cast<ScChangeActionIns*>(p), 1 );
3400 p->GetBigRange().aStart.IncCol();
3402 else if ( aDelRange.Contains( p->GetBigRange().aEnd ) )
3404 pActDel->SetCutOffInsert(
3405 static_cast<ScChangeActionIns*>(p), -1 );
3406 p->GetBigRange().aEnd.IncCol( -1 );
3409 break;
3410 case SC_CAT_INSERT_ROWS :
3411 if ( eActType == SC_CAT_DELETE_ROWS )
3413 if ( aDelRange.Contains( p->GetBigRange().aStart ) )
3415 pActDel->SetCutOffInsert(
3416 static_cast<ScChangeActionIns*>(p), 1 );
3417 p->GetBigRange().aStart.IncRow();
3419 else if ( aDelRange.Contains( p->GetBigRange().aEnd ) )
3421 pActDel->SetCutOffInsert(
3422 static_cast<ScChangeActionIns*>(p), -1 );
3423 p->GetBigRange().aEnd.IncRow( -1 );
3426 break;
3427 case SC_CAT_INSERT_TABS :
3428 if ( eActType == SC_CAT_DELETE_TABS )
3430 if ( aDelRange.Contains( p->GetBigRange().aStart ) )
3432 pActDel->SetCutOffInsert(
3433 static_cast<ScChangeActionIns*>(p), 1 );
3434 p->GetBigRange().aStart.IncTab();
3436 else if ( aDelRange.Contains( p->GetBigRange().aEnd ) )
3438 pActDel->SetCutOffInsert(
3439 static_cast<ScChangeActionIns*>(p), -1 );
3440 p->GetBigRange().aEnd.IncTab( -1 );
3443 break;
3444 case SC_CAT_MOVE :
3446 ScChangeActionMove* pMove = static_cast<ScChangeActionMove*>(p);
3447 short nFrom = 0;
3448 short nTo = 0;
3449 if ( aDelRange.Contains( pMove->GetBigRange().aStart ) )
3450 nTo = 1;
3451 else if ( aDelRange.Contains( pMove->GetBigRange().aEnd ) )
3452 nTo = -1;
3453 if ( aDelRange.Contains( pMove->GetFromRange().aStart ) )
3454 nFrom = 1;
3455 else if ( aDelRange.Contains( pMove->GetFromRange().aEnd ) )
3456 nFrom = -1;
3457 if ( nFrom )
3459 switch ( eActType )
3461 case SC_CAT_DELETE_COLS :
3462 if ( nFrom > 0 )
3463 pMove->GetFromRange().aStart.IncCol( nFrom );
3464 else
3465 pMove->GetFromRange().aEnd.IncCol( nFrom );
3466 break;
3467 case SC_CAT_DELETE_ROWS :
3468 if ( nFrom > 0 )
3469 pMove->GetFromRange().aStart.IncRow( nFrom );
3470 else
3471 pMove->GetFromRange().aEnd.IncRow( nFrom );
3472 break;
3473 case SC_CAT_DELETE_TABS :
3474 if ( nFrom > 0 )
3475 pMove->GetFromRange().aStart.IncTab( nFrom );
3476 else
3477 pMove->GetFromRange().aEnd.IncTab( nFrom );
3478 break;
3479 default:
3481 // added to avoid warnings
3485 if ( nTo )
3487 switch ( eActType )
3489 case SC_CAT_DELETE_COLS :
3490 if ( nTo > 0 )
3491 pMove->GetBigRange().aStart.IncCol( nTo );
3492 else
3493 pMove->GetBigRange().aEnd.IncCol( nTo );
3494 break;
3495 case SC_CAT_DELETE_ROWS :
3496 if ( nTo > 0 )
3497 pMove->GetBigRange().aStart.IncRow( nTo );
3498 else
3499 pMove->GetBigRange().aEnd.IncRow( nTo );
3500 break;
3501 case SC_CAT_DELETE_TABS :
3502 if ( nTo > 0 )
3503 pMove->GetBigRange().aStart.IncTab( nTo );
3504 else
3505 pMove->GetBigRange().aEnd.IncTab( nTo );
3506 break;
3507 default:
3509 // added to avoid warnings
3513 if ( nFrom || nTo )
3515 ScChangeActionDelMoveEntry* pLink =
3516 pActDel->AddCutOffMove( pMove, nFrom, nTo );
3517 pMove->AddLink( pActDel, pLink );
3520 break;
3521 default:
3523 // added to avoid warnings
3527 if ( bUpdate )
3529 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3530 if ( p->GetType() == eActType && !p->IsRejected() &&
3531 !pActDel->IsDeletedIn() &&
3532 p->GetBigRange().Contains( aDelRange ) )
3533 pActDel->SetDeletedIn( p ); // Slipped underneath it
3537 else
3538 { // Undo Delete
3539 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3541 if ( p == pAct )
3542 continue; // for
3543 bool bUpdate = true;
3544 if ( aDelRange.Contains( p->GetBigRange() ) )
3546 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3547 if ( GetMergeState() == SC_CTMS_UNDO && !p->IsDeletedIn( pAct ) && pAct->IsDeleteType() &&
3548 ( p->GetType() == SC_CAT_CONTENT ||
3549 p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
3550 p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) )
3552 p->SetDeletedIn( pAct );
3555 if ( p->IsDeletedInDelType( eActType ) )
3557 if ( p->IsDeletedIn( pActDel ) )
3559 if ( p->GetType() != SC_CAT_CONTENT ||
3560 static_cast<ScChangeActionContent*>(p)->IsTopContent() )
3561 { // First really remove the TopContent
3562 p->RemoveDeletedIn( pActDel );
3563 // Do NOT delete GeneratedDelContent from the list, we might need
3564 // it later on for Reject; we delete in DeleteCellEntries
3567 bUpdate = false;
3569 else if ( eActType != SC_CAT_DELETE_TABS &&
3570 p->IsDeletedInDelType( SC_CAT_DELETE_TABS ) )
3571 { // Do not update in deleted Tables except for when moving Tables
3572 bUpdate = false;
3574 if ( p->GetType() == eActType && pActDel->IsDeletedIn( p ) )
3576 pActDel->RemoveDeletedIn( p );// Slipped underneath
3577 bUpdate = true;
3580 if ( bUpdate )
3581 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3583 if ( !bGeneratedDelContents )
3584 { // These are else also needed for the real Undo
3585 pActDel->UndoCutOffInsert();
3586 pActDel->UndoCutOffMoves();
3590 else if ( eActType == SC_CAT_MOVE )
3592 ScChangeActionMove* pActMove = static_cast<ScChangeActionMove*>(pAct);
3593 bool bLastCutMove = ( pActMove == pLastCutMove.get() );
3594 const ScBigRange& rTo = pActMove->GetBigRange();
3595 const ScBigRange& rFrom = pActMove->GetFromRange();
3596 if ( !bUndo )
3597 { // Move
3598 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3600 if ( p == pAct )
3601 continue; // for
3602 if ( p->GetType() == SC_CAT_CONTENT )
3604 // Delete content in Target (Move Content to Source)
3605 if ( rTo.Contains( p->GetBigRange() ) )
3607 if ( !p->IsDeletedIn( pActMove ) )
3609 p->SetDeletedIn( pActMove );
3610 // Add GeneratedDelContent to the to-be-deleted list
3611 if ( bGeneratedDelContents )
3612 pActMove->AddContent( static_cast<ScChangeActionContent*>(p) );
3615 else if ( bLastCutMove &&
3616 p->GetActionNumber() > nEndLastCut &&
3617 rFrom.Contains( p->GetBigRange() ) )
3618 { // Paste Cut: insert new Content inserted after stays
3619 // Split up the ContentChain
3620 ScChangeActionContent *pHere, *pTmp;
3621 pHere = static_cast<ScChangeActionContent*>(p);
3622 for (;;)
3624 pTmp = pHere->GetPrevContent();
3625 if (!pTmp || pTmp->GetActionNumber() <= nEndLastCut)
3626 break;
3627 pHere = pTmp;
3629 if ( pTmp )
3630 { // Becomes TopContent of the Move
3631 pTmp->SetNextContent( nullptr );
3632 pHere->SetPrevContent( nullptr );
3635 { // Recover dependency from FromRange
3636 AddDependentWithNotify( pActMove, pHere );
3637 } while ( ( pHere = pHere->GetNextContent() ) != nullptr );
3639 // #i87003# [Collaboration] Move range and insert content in FromRange is not merged correctly
3640 else if ( ( GetMergeState() != SC_CTMS_PREPARE && GetMergeState() != SC_CTMS_OWN ) || p->GetActionNumber() <= pAct->GetActionNumber() )
3641 p->UpdateReference( this, eMode, rFrom, nDx, nDy, nDz );
3645 else
3646 { // Undo Move
3647 bool bActRejected = pActMove->IsRejected();
3648 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3650 if ( p == pAct )
3651 continue; // for
3652 if ( p->GetType() == SC_CAT_CONTENT )
3654 // Move Content into Target if not deleted else to delete (FIXME: What?)
3655 if ( p->IsDeletedIn( pActMove ) )
3657 if ( static_cast<ScChangeActionContent*>(p)->IsTopContent() )
3658 { // First really remove the TopContent
3659 p->RemoveDeletedIn( pActMove );
3660 // Do NOT delete GeneratedDelContent from the list, we might need
3661 // it later on for Reject; we delete in DeleteCellEntries
3664 // #i87003# [Collaboration] Move range and insert content in FromRange is not merged correctly
3665 else if ( ( GetMergeState() != SC_CTMS_PREPARE && GetMergeState() != SC_CTMS_OWN ) || p->GetActionNumber() <= pAct->GetActionNumber() )
3666 p->UpdateReference( this, eMode, rTo, nDx, nDy, nDz );
3667 if ( bActRejected &&
3668 static_cast<ScChangeActionContent*>(p)->IsTopContent() &&
3669 rFrom.Contains( p->GetBigRange() ) )
3670 { // Recover dependency to write Content
3671 ScChangeActionLinkEntry* pLink =
3672 pActMove->AddDependent( p );
3673 p->AddLink( pActMove, pLink );
3679 else
3680 { // Insert/Undo Insert
3681 switch ( GetMergeState() )
3683 case SC_CTMS_NONE :
3684 case SC_CTMS_OTHER :
3686 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3688 if ( p == pAct )
3689 continue; // for
3690 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3693 break;
3694 case SC_CTMS_PREPARE :
3696 // "Delete" in Insert-Undo
3697 const ScChangeActionLinkEntry* pLink = pAct->GetFirstDependentEntry();
3698 while ( pLink )
3700 ScChangeAction* p = const_cast<ScChangeAction*>(pLink->GetAction());
3701 if ( p )
3702 p->SetDeletedIn( pAct );
3703 pLink = pLink->GetNext();
3706 // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly
3707 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3709 if ( !p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
3710 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3711 ( p->GetType() == SC_CAT_CONTENT ||
3712 p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
3713 p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
3714 pAct->GetBigRange().Intersects( p->GetBigRange() ) )
3716 p->SetDeletedIn( pAct );
3720 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3722 if ( p == pAct )
3723 continue; // for
3724 if ( !p->IsDeletedIn( pAct )
3725 // #i95212# [Collaboration] Bad handling of row insertion in shared spreadsheet
3726 && p->GetActionNumber() <= pAct->GetActionNumber() )
3728 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3732 break;
3733 case SC_CTMS_OWN :
3735 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3737 if ( p == pAct )
3738 continue; // for
3739 if ( !p->IsDeletedIn( pAct )
3740 // #i95212# [Collaboration] Bad handling of row insertion in shared spreadsheet
3741 && p->GetActionNumber() <= pAct->GetActionNumber() )
3743 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3746 // Undo "Delete" in Insert-Undo
3747 const ScChangeActionLinkEntry* pLink = pAct->GetFirstDependentEntry();
3748 while ( pLink )
3750 ScChangeAction* p = const_cast<ScChangeAction*>(pLink->GetAction());
3751 if ( p )
3752 p->RemoveDeletedIn( pAct );
3753 pLink = pLink->GetNext();
3756 // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly
3757 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3759 if ( p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
3760 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3761 ( p->GetType() == SC_CAT_CONTENT ||
3762 p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
3763 p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
3764 pAct->GetBigRange().Intersects( p->GetBigRange() ) )
3766 p->RemoveDeletedIn( pAct );
3770 break;
3771 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3772 case SC_CTMS_UNDO :
3774 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3776 if ( !p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
3777 ( p->GetType() == SC_CAT_CONTENT ||
3778 p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
3779 p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
3780 pAct->GetBigRange().Intersects( p->GetBigRange() ) )
3782 p->SetDeletedIn( pAct );
3786 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3788 if ( p == pAct )
3790 continue;
3792 if ( !p->IsDeletedIn( pAct ) && p->GetActionNumber() <= pAct->GetActionNumber() )
3794 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3798 break;
3803 void ScChangeTrack::GetDependents( ScChangeAction* pAct,
3804 ScChangeActionMap& rMap, bool bListMasterDelete, bool bAllFlat ) const
3806 //TODO: bAllFlat==TRUE: called internally from Accept or Reject
3807 //TODO: => Generated will not be added
3808 bool bIsDelete = pAct->IsDeleteType();
3809 bool bIsMasterDelete = ( bListMasterDelete && pAct->IsMasterDelete() );
3811 const ScChangeAction* pCur = nullptr;
3812 ::std::stack<ScChangeAction*> cStack;
3813 cStack.push(pAct);
3815 while ( !cStack.empty() )
3817 pCur = cStack.top();
3818 cStack.pop();
3820 if ( pCur->IsInsertType() )
3822 const ScChangeActionLinkEntry* pL = pCur->GetFirstDependentEntry();
3823 while ( pL )
3825 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
3826 if ( p != pAct )
3828 if ( bAllFlat )
3830 sal_uLong n = p->GetActionNumber();
3831 if ( !IsGenerated( n ) && rMap.insert( ::std::make_pair( n, p ) ).second )
3832 if ( p->HasDependent() )
3833 cStack.push( p );
3835 else
3837 if ( p->GetType() == SC_CAT_CONTENT )
3839 if ( static_cast<ScChangeActionContent*>(p)->IsTopContent() )
3840 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3842 else
3843 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3846 pL = pL->GetNext();
3849 else if ( pCur->IsDeleteType() )
3851 if ( bIsDelete )
3852 { // Contents of deleted Ranges are only of interest on Delete
3853 ScChangeActionDel* pDel = const_cast<ScChangeActionDel*>(static_cast<const ScChangeActionDel*>(pCur));
3854 if ( !bAllFlat && bIsMasterDelete && pCur == pAct )
3856 // Corresponding Deletes to this Delete to the same level,
3857 // if this Delete is at the top of a Row
3858 ScChangeActionType eType = pDel->GetType();
3859 ScChangeAction* p = pDel;
3860 for (;;)
3862 p = p->GetPrev();
3863 if (!p || p->GetType() != eType ||
3864 static_cast<ScChangeActionDel*>(p)->IsTopDelete() )
3865 break;
3866 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3868 // delete this in the map too
3869 rMap.insert( ::std::make_pair( pAct->GetActionNumber(), pAct ) );
3871 else
3873 const ScChangeActionLinkEntry* pL = pCur->GetFirstDeletedEntry();
3874 while ( pL )
3876 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
3877 if ( p != pAct )
3879 if ( bAllFlat )
3881 // Only a TopContent of a chain is in LinkDeleted
3882 sal_uLong n = p->GetActionNumber();
3883 if ( !IsGenerated( n ) && rMap.insert( ::std::make_pair( n, p ) ).second )
3884 if ( p->HasDeleted() ||
3885 p->GetType() == SC_CAT_CONTENT )
3886 cStack.push( p );
3888 else
3890 if ( p->IsDeleteType() )
3891 { // Further TopDeletes to same level: it's not rejectable
3892 if ( static_cast<ScChangeActionDel*>(p)->IsTopDelete() )
3893 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3895 else
3896 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3899 pL = pL->GetNext();
3904 else if ( pCur->GetType() == SC_CAT_MOVE )
3906 // Deleted Contents in ToRange
3907 const ScChangeActionLinkEntry* pL = pCur->GetFirstDeletedEntry();
3908 while ( pL )
3910 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
3911 if ( p != pAct && rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) ).second )
3913 // Only one TopContent of a chain is in LinkDeleted
3914 if ( bAllFlat && (p->HasDeleted() ||
3915 p->GetType() == SC_CAT_CONTENT) )
3916 cStack.push( p );
3918 pL = pL->GetNext();
3920 // New Contents in FromRange or new FromRange in ToRange
3921 // or Inserts/Deletes in FromRange/ToRange
3922 pL = pCur->GetFirstDependentEntry();
3923 while ( pL )
3925 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
3926 if ( p != pAct )
3928 if ( bAllFlat )
3930 sal_uLong n = p->GetActionNumber();
3931 if ( !IsGenerated( n ) && rMap.insert( ::std::make_pair( n, p ) ).second )
3932 if ( p->HasDependent() || p->HasDeleted() )
3933 cStack.push( p );
3935 else
3937 if ( p->GetType() == SC_CAT_CONTENT )
3939 if ( static_cast<ScChangeActionContent*>(p)->IsTopContent() )
3940 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3942 else
3943 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3946 pL = pL->GetNext();
3949 else if ( pCur->GetType() == SC_CAT_CONTENT )
3950 { // All changes at same position
3951 ScChangeActionContent* pContent = const_cast<ScChangeActionContent*>(static_cast<const ScChangeActionContent*>(pCur));
3952 // All preceding ones
3953 while ( ( pContent = pContent->GetPrevContent() ) != nullptr )
3955 if ( !pContent->IsRejected() )
3956 rMap.insert( ::std::make_pair( pContent->GetActionNumber(), pContent ) );
3958 pContent = const_cast<ScChangeActionContent*>(static_cast<const ScChangeActionContent*>(pCur));
3959 // All succeeding ones
3960 while ( ( pContent = pContent->GetNextContent() ) != nullptr )
3962 if ( !pContent->IsRejected() )
3963 rMap.insert( ::std::make_pair( pContent->GetActionNumber(), pContent ) );
3965 // all MatrixReferences of a MatrixOrigin
3966 const ScChangeActionLinkEntry* pL = pCur->GetFirstDependentEntry();
3967 while ( pL )
3969 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
3970 if ( p != pAct )
3972 if ( bAllFlat )
3974 sal_uLong n = p->GetActionNumber();
3975 if ( !IsGenerated( n ) && rMap.insert( ::std::make_pair( n, p ) ).second )
3976 if ( p->HasDependent() )
3977 cStack.push( p );
3979 else
3980 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3982 pL = pL->GetNext();
3985 else if ( pCur->GetType() == SC_CAT_REJECT )
3987 if ( bAllFlat )
3989 ScChangeAction* p = GetAction(
3990 static_cast<const ScChangeActionReject*>(pCur)->GetRejectAction() );
3991 if (p != pAct && rMap.find( p->GetActionNumber() ) == rMap.end())
3992 cStack.push( p );
3998 bool ScChangeTrack::SelectContent( ScChangeAction* pAct, bool bOldest )
4000 if ( pAct->GetType() != SC_CAT_CONTENT )
4001 return false;
4003 ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(pAct);
4004 if ( bOldest )
4006 pContent = pContent->GetTopContent();
4007 for (;;)
4009 ScChangeActionContent* pPrevContent = pContent->GetPrevContent();
4010 if ( !pPrevContent || !pPrevContent->IsVirgin() )
4011 break;
4012 pContent = pPrevContent;
4016 if ( !pContent->IsClickable() )
4017 return false;
4019 ScBigRange aBigRange( pContent->GetBigRange() );
4020 const ScCellValue& rCell = (bOldest ? pContent->GetOldCell() : pContent->GetNewCell());
4021 if ( ScChangeActionContent::GetContentCellType(rCell) == SC_CACCT_MATORG )
4023 SCCOL nC;
4024 SCROW nR;
4025 rCell.getFormula()->GetMatColsRows(nC, nR);
4026 aBigRange.aEnd.IncCol( nC-1 );
4027 aBigRange.aEnd.IncRow( nR-1 );
4030 if ( !aBigRange.IsValid( rDoc ) )
4031 return false;
4033 ScRange aRange( aBigRange.MakeRange( rDoc ) );
4034 if ( !rDoc.IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(),
4035 aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) )
4036 return false;
4038 if ( pContent->HasDependent() )
4040 bool bOk = true;
4041 ::std::stack<ScChangeActionContent*> aRejectActions;
4042 const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry();
4043 while ( pL )
4045 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
4046 if ( p != pContent )
4048 if ( p->GetType() == SC_CAT_CONTENT )
4050 // we don't need no recursion here, do we?
4051 bOk &= static_cast<ScChangeActionContent*>(p)->Select( rDoc, this,
4052 bOldest, &aRejectActions );
4054 else
4056 OSL_FAIL( "ScChangeTrack::SelectContent: content dependent no content" );
4059 pL = pL->GetNext();
4062 bOk &= pContent->Select( rDoc, this, bOldest, nullptr );
4063 // now the matrix is inserted and new content values are ready
4065 while ( !aRejectActions.empty() )
4067 ScChangeActionContent* pNew = aRejectActions.top();
4068 aRejectActions.pop();
4069 ScAddress aPos( pNew->GetBigRange().aStart.MakeAddress( rDoc ) );
4070 ScCellValue aCell;
4071 aCell.assign(rDoc, aPos);
4072 pNew->SetNewValue(aCell, &rDoc);
4073 Append( pNew );
4075 return bOk;
4077 else
4078 return pContent->Select( rDoc, this, bOldest, nullptr );
4081 void ScChangeTrack::AcceptAll()
4083 for ( ScChangeAction* p = GetFirst(); p; p = p->GetNext() )
4085 p->Accept();
4089 bool ScChangeTrack::Accept( ScChangeAction* pAct )
4091 if ( !pAct->IsClickable() )
4092 return false;
4094 if ( pAct->IsDeleteType() || pAct->GetType() == SC_CAT_CONTENT )
4096 ScChangeActionMap aActionMap;
4098 GetDependents( pAct, aActionMap, false, true );
4100 for( auto& rEntry : aActionMap )
4102 rEntry.second->Accept();
4105 pAct->Accept();
4106 return true;
4109 bool ScChangeTrack::RejectAll()
4111 bool bOk = true;
4112 for ( ScChangeAction* p = GetLast(); p && bOk; p = p->GetPrev() )
4113 { //TODO: Traverse backwards as dependencies attached to RejectActions
4114 if ( p->IsInternalRejectable() )
4115 bOk = Reject( p );
4117 return bOk;
4120 bool ScChangeTrack::Reject( ScChangeAction* pAct, bool bShared )
4122 // #i100895# When collaboration changes are reversed, it must be possible
4123 // to reject a deleted row above another deleted row.
4124 if ( bShared && pAct->IsDeletedIn() )
4125 pAct->RemoveAllDeletedIn();
4127 if ( !pAct->IsRejectable() )
4128 return false;
4130 std::unique_ptr<ScChangeActionMap> pMap;
4131 if ( pAct->HasDependent() )
4133 pMap.reset(new ScChangeActionMap);
4134 GetDependents( pAct, *pMap, false, true );
4136 bool bRejected = Reject( pAct, pMap.get(), false );
4137 return bRejected;
4140 bool ScChangeTrack::Reject(
4141 ScChangeAction* pAct, ScChangeActionMap* pMap, bool bRecursion )
4143 if ( !pAct->IsInternalRejectable() )
4144 return false;
4146 bool bOk = true;
4147 bool bRejected = false;
4148 if ( pAct->IsInsertType() )
4150 if ( pAct->HasDependent() && !bRecursion )
4152 OSL_ENSURE( pMap, "ScChangeTrack::Reject: Insert without map" );
4153 ScChangeActionMap::reverse_iterator itChangeAction;
4154 for (itChangeAction = pMap->rbegin();
4155 itChangeAction != pMap->rend() && bOk; ++itChangeAction)
4157 // Do not restore Contents which would end up being deleted anyways
4158 if ( itChangeAction->second->GetType() == SC_CAT_CONTENT )
4159 itChangeAction->second->SetRejected();
4160 else if ( itChangeAction->second->IsDeleteType() )
4161 itChangeAction->second->Accept(); // Deleted to Nirvana
4162 else
4163 bOk = Reject( itChangeAction->second, nullptr, true ); // Recursion!
4166 if ( bOk )
4168 bRejected = pAct->Reject( rDoc );
4169 if ( bRejected )
4171 // pRefDoc NULL := Do not save deleted Cells
4172 AppendDeleteRange( pAct->GetBigRange().MakeRange( rDoc ), nullptr, short(0),
4173 pAct->GetActionNumber() );
4177 else if ( pAct->IsDeleteType() )
4179 OSL_ENSURE( !pMap, "ScChangeTrack::Reject: Delete with map" );
4180 ScBigRange aDelRange;
4181 sal_uLong nRejectAction = pAct->GetActionNumber();
4182 bool bTabDel, bTabDelOk;
4183 if ( pAct->GetType() == SC_CAT_DELETE_TABS )
4185 bTabDel = true;
4186 aDelRange = pAct->GetBigRange();
4187 bTabDelOk = pAct->Reject( rDoc );
4188 bOk = bTabDelOk;
4189 if ( bOk )
4191 pAct = pAct->GetPrev();
4192 bOk = ( pAct && pAct->GetType() == SC_CAT_DELETE_COLS );
4195 else
4196 bTabDel = bTabDelOk = false;
4197 ScChangeActionDel* pDel = static_cast<ScChangeActionDel*>(pAct);
4198 if ( bOk )
4200 aDelRange = pDel->GetOverAllRange();
4201 bOk = aDelRange.IsValid( rDoc );
4203 bool bOneOk = false;
4204 if ( bOk )
4206 ScChangeActionType eActType = pAct->GetType();
4207 switch ( eActType )
4209 case SC_CAT_DELETE_COLS :
4210 aDelRange.aStart.SetCol( aDelRange.aEnd.Col() );
4211 break;
4212 case SC_CAT_DELETE_ROWS :
4213 aDelRange.aStart.SetRow( aDelRange.aEnd.Row() );
4214 break;
4215 case SC_CAT_DELETE_TABS :
4216 aDelRange.aStart.SetTab( aDelRange.aEnd.Tab() );
4217 break;
4218 default:
4220 // added to avoid warnings
4223 ScChangeAction* p = pAct;
4224 bool bLoop = true;
4227 pDel = static_cast<ScChangeActionDel*>(p);
4228 bOk = pDel->Reject( rDoc );
4229 if ( bOk )
4231 if ( bOneOk )
4233 switch ( pDel->GetType() )
4235 case SC_CAT_DELETE_COLS :
4236 aDelRange.aStart.IncCol( -1 );
4237 break;
4238 case SC_CAT_DELETE_ROWS :
4239 aDelRange.aStart.IncRow( -1 );
4240 break;
4241 case SC_CAT_DELETE_TABS :
4242 aDelRange.aStart.IncTab( -1 );
4243 break;
4244 default:
4246 // added to avoid warnings
4250 else
4251 bOneOk = true;
4253 if ( pDel->IsBaseDelete() )
4254 bLoop = false;
4255 else
4256 p = p->GetPrev();
4257 } while ( bOk && bLoop && p && p->GetType() == eActType &&
4258 !static_cast<ScChangeActionDel*>(p)->IsTopDelete() );
4260 bRejected = bOk;
4261 if ( bOneOk || (bTabDel && bTabDelOk) )
4263 // Delete Reject made UpdateReference Undo
4264 ScChangeActionIns* pReject = new ScChangeActionIns( &rDoc,
4265 aDelRange.MakeRange( rDoc ) );
4266 pReject->SetRejectAction( nRejectAction );
4267 pReject->SetState( SC_CAS_ACCEPTED );
4268 Append( pReject );
4271 else if ( pAct->GetType() == SC_CAT_MOVE )
4273 if ( pAct->HasDependent() && !bRecursion )
4275 OSL_ENSURE( pMap, "ScChangeTrack::Reject: Move without Map" );
4276 ScChangeActionMap::reverse_iterator itChangeAction;
4278 for( itChangeAction = pMap->rbegin(); itChangeAction != pMap->rend() && bOk; ++itChangeAction )
4280 bOk = Reject( itChangeAction->second, nullptr, true ); // Recursion!
4283 if ( bOk )
4285 bRejected = pAct->Reject( rDoc );
4286 if ( bRejected )
4288 ScChangeActionMove* pReject = new ScChangeActionMove(
4289 pAct->GetBigRange().MakeRange( rDoc ),
4290 static_cast<ScChangeActionMove*>(pAct)->GetFromRange().MakeRange( rDoc ), this );
4291 pReject->SetRejectAction( pAct->GetActionNumber() );
4292 pReject->SetState( SC_CAS_ACCEPTED );
4293 Append( pReject );
4297 else if ( pAct->GetType() == SC_CAT_CONTENT )
4299 ScRange aRange;
4300 ScChangeActionContent* pReject;
4301 if ( bRecursion )
4302 pReject = nullptr;
4303 else
4305 aRange = pAct->GetBigRange().aStart.MakeAddress( rDoc );
4306 pReject = new ScChangeActionContent( aRange );
4307 ScCellValue aCell;
4308 aCell.assign(rDoc, aRange.aStart);
4309 pReject->SetOldValue(aCell, &rDoc, &rDoc);
4311 bRejected = pAct->Reject( rDoc );
4312 if ( bRejected && !bRecursion )
4314 ScCellValue aCell;
4315 aCell.assign(rDoc, aRange.aStart);
4316 pReject->SetNewValue(aCell, &rDoc);
4317 pReject->SetRejectAction( pAct->GetActionNumber() );
4318 pReject->SetState( SC_CAS_ACCEPTED );
4319 Append( pReject );
4321 else
4322 delete pReject;
4324 else
4326 OSL_FAIL( "ScChangeTrack::Reject: say what?" );
4329 return bRejected;
4332 bool ScChangeTrack::IsLastAction( sal_uLong nNum ) const
4334 return nNum == nActionMax && pLast && pLast->GetActionNumber() == nNum;
4337 sal_uLong ScChangeTrack::AddLoadedGenerated(
4338 const ScCellValue& rNewCell, const ScBigRange& aBigRange, const OUString& sNewValue )
4340 ScChangeActionContent* pAct = new ScChangeActionContent( --nGeneratedMin, rNewCell, aBigRange, &rDoc, sNewValue );
4341 if ( pFirstGeneratedDelContent )
4342 pFirstGeneratedDelContent->pPrev = pAct;
4343 pAct->pNext = pFirstGeneratedDelContent;
4344 pFirstGeneratedDelContent = pAct;
4345 aGeneratedMap.insert( ::std::make_pair( pAct->GetActionNumber(), pAct ) );
4346 return pAct->GetActionNumber();
4349 void ScChangeTrack::AppendCloned( ScChangeAction* pAppend )
4351 aMap.insert( ::std::make_pair( pAppend->GetActionNumber(), pAppend ) );
4352 if ( !pLast )
4353 pFirst = pLast = pAppend;
4354 else
4356 pLast->pNext = pAppend;
4357 pAppend->pPrev = pLast;
4358 pLast = pAppend;
4362 ScChangeTrack* ScChangeTrack::Clone( ScDocument* pDocument ) const
4364 if ( !pDocument )
4366 return nullptr;
4369 std::unique_ptr<ScChangeTrack> pClonedTrack(new ScChangeTrack( *pDocument ));
4370 pClonedTrack->SetTimeNanoSeconds( IsTimeNanoSeconds() );
4372 // clone generated actions
4373 ::std::stack< const ScChangeAction* > aGeneratedStack;
4374 const ScChangeAction* pGenerated = GetFirstGenerated();
4375 while ( pGenerated )
4377 aGeneratedStack.push( pGenerated );
4378 pGenerated = pGenerated->GetNext();
4380 while ( !aGeneratedStack.empty() )
4382 pGenerated = aGeneratedStack.top();
4383 aGeneratedStack.pop();
4384 const ScChangeActionContent& rContent = dynamic_cast<const ScChangeActionContent&>(*pGenerated);
4385 const ScCellValue& rNewCell = rContent.GetNewCell();
4386 if (!rNewCell.isEmpty())
4388 ScCellValue aClonedNewCell;
4389 aClonedNewCell.assign(rNewCell, *pDocument);
4390 OUString aNewValue = rContent.GetNewString( pDocument );
4391 pClonedTrack->nGeneratedMin = pGenerated->GetActionNumber() + 1;
4392 pClonedTrack->AddLoadedGenerated(aClonedNewCell, pGenerated->GetBigRange(), aNewValue);
4396 // clone actions
4397 const ScChangeAction* pAction = GetFirst();
4398 while ( pAction )
4400 ScChangeAction* pClonedAction = nullptr;
4402 switch ( pAction->GetType() )
4404 case SC_CAT_INSERT_COLS:
4405 case SC_CAT_INSERT_ROWS:
4406 case SC_CAT_INSERT_TABS:
4408 bool bEndOfList = static_cast<const ScChangeActionIns*>(pAction)->IsEndOfList();
4409 pClonedAction = new ScChangeActionIns(
4410 pAction->GetActionNumber(),
4411 pAction->GetState(),
4412 pAction->GetRejectAction(),
4413 pAction->GetBigRange(),
4414 pAction->GetUser(),
4415 pAction->GetDateTimeUTC(),
4416 pAction->GetComment(),
4417 pAction->GetType(),
4418 bEndOfList );
4420 break;
4421 case SC_CAT_DELETE_COLS:
4422 case SC_CAT_DELETE_ROWS:
4423 case SC_CAT_DELETE_TABS:
4425 const ScChangeActionDel& rDelete = dynamic_cast<const ScChangeActionDel&>(*pAction);
4427 SCCOLROW nD = 0;
4428 ScChangeActionType eType = pAction->GetType();
4429 if ( eType == SC_CAT_DELETE_COLS )
4431 nD = static_cast< SCCOLROW >( rDelete.GetDx() );
4433 else if ( eType == SC_CAT_DELETE_ROWS )
4435 nD = static_cast< SCCOLROW >( rDelete.GetDy() );
4438 pClonedAction = new ScChangeActionDel(
4439 pAction->GetActionNumber(),
4440 pAction->GetState(),
4441 pAction->GetRejectAction(),
4442 pAction->GetBigRange(),
4443 pAction->GetUser(),
4444 pAction->GetDateTimeUTC(),
4445 pAction->GetComment(),
4446 eType,
4448 pClonedTrack.get() );
4450 break;
4451 case SC_CAT_MOVE:
4453 auto pMove = dynamic_cast<const ScChangeActionMove*>(pAction);
4454 assert(pMove && "ScChangeTrack::Clone: pMove is null!");
4456 pClonedAction = new ScChangeActionMove(
4457 pAction->GetActionNumber(),
4458 pAction->GetState(),
4459 pAction->GetRejectAction(),
4460 pAction->GetBigRange(),
4461 pAction->GetUser(),
4462 pAction->GetDateTimeUTC(),
4463 pAction->GetComment(),
4464 pMove->GetFromRange(),
4465 pClonedTrack.get() );
4467 break;
4468 case SC_CAT_CONTENT:
4470 const ScChangeActionContent& rContent = dynamic_cast<const ScChangeActionContent&>(*pAction);
4471 const ScCellValue& rOldCell = rContent.GetOldCell();
4472 ScCellValue aClonedOldCell;
4473 aClonedOldCell.assign(rOldCell, *pDocument);
4474 OUString aOldValue = rContent.GetOldString( pDocument );
4476 ScChangeActionContent* pClonedContent = new ScChangeActionContent(
4477 pAction->GetActionNumber(),
4478 pAction->GetState(),
4479 pAction->GetRejectAction(),
4480 pAction->GetBigRange(),
4481 pAction->GetUser(),
4482 pAction->GetDateTimeUTC(),
4483 pAction->GetComment(),
4484 std::move(aClonedOldCell),
4485 pDocument,
4486 aOldValue );
4488 const ScCellValue& rNewCell = rContent.GetNewCell();
4489 if (!rNewCell.isEmpty())
4491 ScCellValue aClonedNewCell;
4492 aClonedNewCell.assign(rNewCell, *pDocument);
4493 pClonedContent->SetNewValue(aClonedNewCell, pDocument);
4496 pClonedAction = pClonedContent;
4498 break;
4499 case SC_CAT_REJECT:
4501 pClonedAction = new ScChangeActionReject(
4502 pAction->GetActionNumber(),
4503 pAction->GetState(),
4504 pAction->GetRejectAction(),
4505 pAction->GetBigRange(),
4506 pAction->GetUser(),
4507 pAction->GetDateTimeUTC(),
4508 pAction->GetComment() );
4510 break;
4511 default:
4514 break;
4517 if ( pClonedAction )
4519 pClonedTrack->AppendCloned( pClonedAction );
4522 pAction = pAction->GetNext();
4525 if ( pClonedTrack->GetLast() )
4527 pClonedTrack->SetActionMax( pClonedTrack->GetLast()->GetActionNumber() );
4530 // set dependencies for Deleted/DeletedIn
4531 pAction = GetFirst();
4532 while ( pAction )
4534 if ( pAction->HasDeleted() )
4536 ::std::stack< sal_uLong > aStack;
4537 const ScChangeActionLinkEntry* pL = pAction->GetFirstDeletedEntry();
4538 while ( pL )
4540 const ScChangeAction* pDeleted = pL->GetAction();
4541 if ( pDeleted )
4543 aStack.push( pDeleted->GetActionNumber() );
4545 pL = pL->GetNext();
4547 ScChangeAction* pClonedAction = pClonedTrack->GetAction( pAction->GetActionNumber() );
4548 if ( pClonedAction )
4550 while ( !aStack.empty() )
4552 ScChangeAction* pClonedDeleted = pClonedTrack->GetActionOrGenerated( aStack.top() );
4553 aStack.pop();
4554 if ( pClonedDeleted )
4556 pClonedDeleted->SetDeletedIn( pClonedAction );
4561 pAction = pAction->GetNext();
4564 // set dependencies for Dependent/Any
4565 pAction = GetLast();
4566 while ( pAction )
4568 if ( pAction->HasDependent() )
4570 ::std::stack< sal_uLong > aStack;
4571 const ScChangeActionLinkEntry* pL = pAction->GetFirstDependentEntry();
4572 while ( pL )
4574 const ScChangeAction* pDependent = pL->GetAction();
4575 if ( pDependent )
4577 aStack.push( pDependent->GetActionNumber() );
4579 pL = pL->GetNext();
4581 ScChangeAction* pClonedAction = pClonedTrack->GetAction( pAction->GetActionNumber() );
4582 if ( pClonedAction )
4584 while ( !aStack.empty() )
4586 ScChangeAction* pClonedDependent = pClonedTrack->GetActionOrGenerated( aStack.top() );
4587 aStack.pop();
4588 if ( pClonedDependent )
4590 ScChangeActionLinkEntry* pLink = pClonedAction->AddDependent( pClonedDependent );
4591 pClonedDependent->AddLink( pClonedAction, pLink );
4596 pAction = pAction->GetPrev();
4599 // masterlinks
4600 ScChangeAction* pClonedAction = pClonedTrack->GetFirst();
4601 while ( pClonedAction )
4603 pClonedTrack->MasterLinks( pClonedAction );
4604 pClonedAction = pClonedAction->GetNext();
4607 if ( IsProtected() )
4609 pClonedTrack->SetProtection( GetProtection() );
4612 if ( pClonedTrack->GetLast() )
4614 pClonedTrack->SetLastSavedActionNumber( pClonedTrack->GetLast()->GetActionNumber() );
4617 auto tmp = pClonedTrack.get();
4618 pDocument->SetChangeTrack( std::move(pClonedTrack) );
4620 return tmp;
4623 void ScChangeTrack::MergeActionState( ScChangeAction* pAct, const ScChangeAction* pOtherAct )
4625 if ( !pAct->IsVirgin() )
4626 return;
4628 if ( pOtherAct->IsAccepted() )
4630 pAct->Accept();
4631 if ( pOtherAct->IsRejecting() )
4633 pAct->SetRejectAction( pOtherAct->GetRejectAction() );
4636 else if ( pOtherAct->IsRejected() )
4638 pAct->SetRejected();
4642 /// Get info about a single ScChangeAction element.
4643 static void lcl_getTrackedChange(ScDocument& rDoc, int nIndex, const ScChangeAction* pAction, tools::JsonWriter& rRedlines)
4645 if (pAction->GetType() != SC_CAT_CONTENT)
4646 return;
4648 auto redlinesNode = rRedlines.startStruct();
4649 rRedlines.put("index", static_cast<sal_Int64>(nIndex));
4651 rRedlines.put("author", pAction->GetUser());
4653 rRedlines.put("type", "Modify");
4655 rRedlines.put("comment", pAction->GetComment());
4657 OUString aDescription = pAction->GetDescription(rDoc, true);
4658 rRedlines.put("description", aDescription);
4660 OUString sDateTime = utl::toISO8601(pAction->GetDateTimeUTC().GetUNODateTime());
4661 rRedlines.put("dateTime", sDateTime);
4664 void ScChangeTrack::GetChangeTrackInfo(tools::JsonWriter& aRedlines)
4666 auto redlinesNode = aRedlines.startArray("redlines");
4668 ScChangeAction* pAction = GetFirst();
4669 if (pAction)
4671 int i = 0;
4672 lcl_getTrackedChange(rDoc, i++, pAction, aRedlines);
4673 ScChangeAction* pLastAction = GetLast();
4674 while (pAction != pLastAction)
4676 pAction = pAction->GetNext();
4677 lcl_getTrackedChange(rDoc, i++, pAction, aRedlines);
4682 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */