merge the formfield patch from ooo-build
[ooovba.git] / sc / source / core / tool / chgtrack.cxx
blobcba879a86ee9339a0433753ce1cc1fc1fdbb25ab
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: chgtrack.cxx,v $
10 * $Revision: 1.32.100.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
35 #include <tools/debug.hxx>
36 #include <tools/shl.hxx> // SHL_CALC
37 #include <tools/stack.hxx>
38 #include <tools/rtti.hxx>
39 #include <svtools/zforlist.hxx>
40 #include <svtools/itemset.hxx>
41 #include <svtools/isethint.hxx>
42 #include <svtools/itempool.hxx>
43 #include <sfx2/app.hxx>
44 #include <svtools/useroptions.hxx>
45 #include <sfx2/sfxsids.hrc>
47 #include "cell.hxx"
48 #include "document.hxx"
49 #include "dociter.hxx"
50 #include "global.hxx"
51 #include "rechead.hxx"
52 #include "scerrors.hxx"
53 #include "scmod.hxx" // SC_MOD
54 #include "inputopt.hxx" // GetExpandRefs
55 #include "patattr.hxx"
56 #include "hints.hxx"
58 #include "globstr.hrc"
60 #include <stack>
62 #define SC_CHGTRACK_CXX
63 #include "chgtrack.hxx"
65 DECLARE_STACK( ScChangeActionStack, ScChangeAction* )
67 const USHORT nMemPoolChangeActionCellListEntry = (0x2000 - 64) / sizeof(ScChangeActionCellListEntry);
68 IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeActionCellListEntry, nMemPoolChangeActionCellListEntry, nMemPoolChangeActionCellListEntry )
70 const USHORT nMemPoolChangeActionLinkEntry = (0x8000 - 64) / sizeof(ScChangeActionLinkEntry);
71 IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeActionLinkEntry, nMemPoolChangeActionLinkEntry, nMemPoolChangeActionLinkEntry )
73 // loaded MSB > eigenes => inkompatibel
74 #define SC_CHGTRACK_FILEFORMAT_FIRST 0x0001
75 #define SC_CHGTRACK_FILEFORMAT 0x0001
77 // --- ScChangeActionLinkEntry ---------------------------------------------
79 #if DEBUG_CHANGETRACK
80 String ScChangeActionLinkEntry::ToString() const
82 String aReturn;
83 if ( pAction )
85 aReturn = String::CreateFromInt64( static_cast< sal_Int64 >( pAction->GetActionNumber() ) );
87 else if ( pLink && pLink->pAction )
89 aReturn = String::CreateFromAscii( "*" );
90 aReturn += String::CreateFromInt64( static_cast< sal_Int64 >( pLink->pAction->GetActionNumber() ) );
92 else
94 aReturn = String::CreateFromAscii( "-" );
97 return aReturn;
99 #endif // DEBUG_CHANGETRACK
101 // --- ScChangeAction ------------------------------------------------------
103 ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScRange& rRange )
105 aBigRange( rRange ),
106 pNext( NULL ),
107 pPrev( NULL ),
108 pLinkAny( NULL ),
109 pLinkDeletedIn( NULL ),
110 pLinkDeleted( NULL ),
111 pLinkDependent( NULL ),
112 nAction( 0 ),
113 nRejectAction( 0 ),
114 eType( eTypeP ),
115 eState( SC_CAS_VIRGIN )
117 aDateTime.ConvertToUTC();
120 ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScBigRange& rRange,
121 const ULONG nTempAction, const ULONG nTempRejectAction,
122 const ScChangeActionState eTempState, const DateTime& aTempDateTime,
123 const String& aTempUser, const String& aTempComment)
125 aBigRange( rRange ),
126 aDateTime( aTempDateTime ),
127 aUser( aTempUser ),
128 aComment( aTempComment ),
129 pNext( NULL ),
130 pPrev( NULL ),
131 pLinkAny( NULL ),
132 pLinkDeletedIn( NULL ),
133 pLinkDeleted( NULL ),
134 pLinkDependent( NULL ),
135 nAction( nTempAction ),
136 nRejectAction( nTempRejectAction ),
137 eType( eTypeP ),
138 eState( eTempState )
142 ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScBigRange& rRange,
143 const ULONG nTempAction)
145 aBigRange( rRange ),
146 pNext( NULL ),
147 pPrev( NULL ),
148 pLinkAny( NULL ),
149 pLinkDeletedIn( NULL ),
150 pLinkDeleted( NULL ),
151 pLinkDependent( NULL ),
152 nAction( nTempAction ),
153 nRejectAction( 0 ),
154 eType( eTypeP ),
155 eState( SC_CAS_VIRGIN )
157 aDateTime.ConvertToUTC();
161 ScChangeAction::~ScChangeAction()
163 RemoveAllLinks();
167 BOOL ScChangeAction::IsVisible() const
169 //! sequence order of execution is significant
170 if ( IsRejected() || GetType() == SC_CAT_DELETE_TABS || IsDeletedIn() )
171 return FALSE;
172 if ( GetType() == SC_CAT_CONTENT )
173 return ((ScChangeActionContent*)this)->IsTopContent();
174 return TRUE;
178 BOOL ScChangeAction::IsTouchable() const
180 //! sequence order of execution is significant
181 if ( IsRejected() || GetType() == SC_CAT_REJECT || IsDeletedIn() )
182 return FALSE;
183 // content may reject and be touchable if on top
184 if ( GetType() == SC_CAT_CONTENT )
185 return ((ScChangeActionContent*)this)->IsTopContent();
186 if ( IsRejecting() )
187 return FALSE;
188 return TRUE;
192 BOOL ScChangeAction::IsClickable() const
194 //! sequence order of execution is significant
195 if ( !IsVirgin() )
196 return FALSE;
197 if ( IsDeletedIn() )
198 return FALSE;
199 if ( GetType() == SC_CAT_CONTENT )
201 ScChangeActionContentCellType eCCT =
202 ScChangeActionContent::GetContentCellType(
203 ((ScChangeActionContent*)this)->GetNewCell() );
204 if ( eCCT == SC_CACCT_MATREF )
205 return FALSE;
206 if ( eCCT == SC_CACCT_MATORG )
207 { // no Accept-Select if one of the references is in a deleted col/row
208 const ScChangeActionLinkEntry* pL =
209 ((ScChangeActionContent*)this)->GetFirstDependentEntry();
210 while ( pL )
212 ScChangeAction* p = (ScChangeAction*) pL->GetAction();
213 if ( p && p->IsDeletedIn() )
214 return FALSE;
215 pL = pL->GetNext();
218 return TRUE; // for Select() a content doesn't have to be touchable
220 return IsTouchable(); // Accept()/Reject() only on touchables
224 BOOL ScChangeAction::IsRejectable() const
226 //! sequence order of execution is significant
227 if ( !IsClickable() )
228 return FALSE;
229 if ( GetType() == SC_CAT_CONTENT )
231 if ( ((ScChangeActionContent*)this)->IsOldMatrixReference() )
232 return FALSE;
233 ScChangeActionContent* pNextContent =
234 ((ScChangeActionContent*)this)->GetNextContent();
235 if ( pNextContent == NULL )
236 return TRUE; // *this is TopContent
237 return pNextContent->IsRejected(); // *this is next rejectable
239 return IsTouchable();
243 BOOL ScChangeAction::IsInternalRejectable() const
245 //! sequence order of execution is significant
246 if ( !IsVirgin() )
247 return FALSE;
248 if ( IsDeletedIn() )
249 return FALSE;
250 if ( GetType() == SC_CAT_CONTENT )
252 ScChangeActionContent* pNextContent =
253 ((ScChangeActionContent*)this)->GetNextContent();
254 if ( pNextContent == NULL )
255 return TRUE; // *this is TopContent
256 return pNextContent->IsRejected(); // *this is next rejectable
258 return IsTouchable();
262 BOOL ScChangeAction::IsDialogRoot() const
264 return IsInternalRejectable(); // only rejectables in root
268 BOOL ScChangeAction::IsDialogParent() const
270 //! sequence order of execution is significant
271 if ( GetType() == SC_CAT_CONTENT )
273 if ( !IsDialogRoot() )
274 return FALSE;
275 if ( ((ScChangeActionContent*)this)->IsMatrixOrigin() && HasDependent() )
276 return TRUE;
277 ScChangeActionContent* pPrevContent =
278 ((ScChangeActionContent*)this)->GetPrevContent();
279 return pPrevContent && pPrevContent->IsVirgin();
281 if ( HasDependent() )
282 return IsDeleteType() ? TRUE : !IsDeletedIn();
283 if ( HasDeleted() )
285 if ( IsDeleteType() )
287 if ( IsDialogRoot() )
288 return TRUE;
289 ScChangeActionLinkEntry* pL = pLinkDeleted;
290 while ( pL )
292 ScChangeAction* p = pL->GetAction();
293 if ( p && p->GetType() != eType )
294 return TRUE;
295 pL = pL->GetNext();
298 else
299 return TRUE;
301 return FALSE;
305 BOOL ScChangeAction::IsMasterDelete() const
307 if ( !IsDeleteType() )
308 return FALSE;
309 ScChangeActionDel* pDel = (ScChangeActionDel*) this;
310 return pDel->IsMultiDelete() && (pDel->IsTopDelete() || pDel->IsRejectable());
314 void ScChangeAction::RemoveAllLinks()
316 RemoveAllAnyLinks();
317 RemoveAllDeletedIn();
318 RemoveAllDeleted();
319 RemoveAllDependent();
323 void ScChangeAction::RemoveAllAnyLinks()
325 while ( pLinkAny )
326 delete pLinkAny; // rueckt sich selbst hoch
330 BOOL ScChangeAction::RemoveDeletedIn( const ScChangeAction* p )
332 BOOL bRemoved = FALSE;
333 ScChangeActionLinkEntry* pL = GetDeletedIn();
334 while ( pL )
336 ScChangeActionLinkEntry* pNextLink = pL->GetNext();
337 if ( pL->GetAction() == p )
339 delete pL;
340 bRemoved = TRUE;
342 pL = pNextLink;
344 return bRemoved;
348 BOOL ScChangeAction::IsDeletedIn( const ScChangeAction* p ) const
350 ScChangeActionLinkEntry* pL = GetDeletedIn();
351 while ( pL )
353 if ( pL->GetAction() == p )
354 return TRUE;
355 pL = pL->GetNext();
357 return FALSE;
361 void ScChangeAction::RemoveAllDeletedIn()
363 //! nicht vom evtl. TopContent sondern wirklich dieser
364 while ( pLinkDeletedIn )
365 delete pLinkDeletedIn; // rueckt sich selbst hoch
369 BOOL ScChangeAction::IsDeletedInDelType( ScChangeActionType eDelType ) const
371 ScChangeAction* p;
372 ScChangeActionLinkEntry* pL = GetDeletedIn();
373 if ( pL )
375 // InsertType fuer MergePrepare/MergeOwn
376 ScChangeActionType eInsType;
377 switch ( eDelType )
379 case SC_CAT_DELETE_COLS :
380 eInsType = SC_CAT_INSERT_COLS;
381 break;
382 case SC_CAT_DELETE_ROWS :
383 eInsType = SC_CAT_INSERT_ROWS;
384 break;
385 case SC_CAT_DELETE_TABS :
386 eInsType = SC_CAT_INSERT_TABS;
387 break;
388 default:
389 eInsType = SC_CAT_NONE;
391 while ( pL )
393 if ( (p = pL->GetAction()) != NULL &&
394 (p->GetType() == eDelType || p->GetType() == eInsType) )
395 return TRUE;
396 pL = pL->GetNext();
399 return FALSE;
403 void ScChangeAction::SetDeletedIn( ScChangeAction* p )
405 ScChangeActionLinkEntry* pLink1 = AddDeletedIn( p );
406 ScChangeActionLinkEntry* pLink2;
407 if ( GetType() == SC_CAT_CONTENT )
408 pLink2 = p->AddDeleted( ((ScChangeActionContent*)this)->GetTopContent() );
409 else
410 pLink2 = p->AddDeleted( this );
411 pLink1->SetLink( pLink2 );
415 void ScChangeAction::RemoveAllDeleted()
417 while ( pLinkDeleted )
418 delete pLinkDeleted; // rueckt sich selbst hoch
422 void ScChangeAction::RemoveAllDependent()
424 while ( pLinkDependent )
425 delete pLinkDependent; // rueckt sich selbst hoch
429 DateTime ScChangeAction::GetDateTime() const
431 DateTime aDT( aDateTime );
432 aDT.ConvertToLocalTime();
433 return aDT;
437 void ScChangeAction::UpdateReference( const ScChangeTrack* /* pTrack */,
438 UpdateRefMode eMode, const ScBigRange& rRange,
439 INT32 nDx, INT32 nDy, INT32 nDz )
441 ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() );
445 void ScChangeAction::GetDescription( String& rStr, ScDocument* /* pDoc */,
446 BOOL /* bSplitRange */, bool bWarning ) const
448 if ( IsRejecting() && bWarning )
450 // #112261# Add comment if rejection may have resulted in references
451 // not properly restored in formulas. See specification at
452 // http://specs.openoffice.org/calc/ease-of-use/redlining_comment.sxw
453 if (GetType() == SC_CAT_MOVE)
455 rStr += ScGlobal::GetRscString(
456 STR_CHANGED_MOVE_REJECTION_WARNING);
457 rStr += ' ';
459 else if (IsInsertType())
461 rStr += ScGlobal::GetRscString(
462 STR_CHANGED_DELETE_REJECTION_WARNING);
463 rStr += ' ';
465 else
467 const ScChangeTrack* pCT = GetChangeTrack();
468 if (pCT)
470 ScChangeAction* pReject = pCT->GetActionOrGenerated(
471 GetRejectAction());
472 if (pReject)
474 if (pReject->GetType() == SC_CAT_MOVE)
476 rStr += ScGlobal::GetRscString(
477 STR_CHANGED_MOVE_REJECTION_WARNING);
478 rStr += ' ';
480 else if (pReject->IsDeleteType())
482 rStr += ScGlobal::GetRscString(
483 STR_CHANGED_DELETE_REJECTION_WARNING);
484 rStr += ' ';
486 else if (pReject->HasDependent())
488 ScChangeActionTable aTable;
489 pCT->GetDependents( pReject, aTable, FALSE, TRUE );
490 for ( const ScChangeAction* p = aTable.First(); p;
491 p = aTable.Next() )
493 if (p->GetType() == SC_CAT_MOVE)
495 rStr += ScGlobal::GetRscString(
496 STR_CHANGED_MOVE_REJECTION_WARNING);
497 rStr += ' ';
498 break; // for
500 else if (pReject->IsDeleteType())
502 rStr += ScGlobal::GetRscString(
503 STR_CHANGED_DELETE_REJECTION_WARNING);
504 rStr += ' ';
505 break; // for
516 String ScChangeAction::GetRefString( const ScBigRange& rRange,
517 ScDocument* pDoc, BOOL bFlag3D ) const
519 String aStr;
520 USHORT nFlags = ( rRange.IsValid( pDoc ) ? SCA_VALID : 0 );
521 if ( !nFlags )
522 aStr = ScGlobal::GetRscString( STR_NOREF_STR );
523 else
525 ScRange aTmpRange( rRange.MakeRange() );
526 switch ( GetType() )
528 case SC_CAT_INSERT_COLS :
529 case SC_CAT_DELETE_COLS :
530 if ( bFlag3D )
532 pDoc->GetName( aTmpRange.aStart.Tab(), aStr );
533 aStr += '.';
535 aStr += ::ScColToAlpha( aTmpRange.aStart.Col() );
536 aStr += ':';
537 aStr += ::ScColToAlpha( aTmpRange.aEnd.Col() );
538 break;
539 case SC_CAT_INSERT_ROWS :
540 case SC_CAT_DELETE_ROWS :
541 if ( bFlag3D )
543 pDoc->GetName( aTmpRange.aStart.Tab(), aStr );
544 aStr += '.';
546 aStr += String::CreateFromInt32( aTmpRange.aStart.Row() + 1 );
547 aStr += ':';
548 aStr += String::CreateFromInt32( aTmpRange.aEnd.Row() + 1 );
549 break;
550 default:
551 if ( bFlag3D || GetType() == SC_CAT_INSERT_TABS )
552 nFlags |= SCA_TAB_3D;
553 aTmpRange.Format( aStr, nFlags, pDoc, pDoc->GetAddressConvention() );
555 if ( (bFlag3D && IsDeleteType()) || IsDeletedIn() )
557 aStr.Insert( '(', 0 );
558 aStr += ')';
561 return aStr;
565 void ScChangeAction::GetRefString( String& rStr, ScDocument* pDoc,
566 BOOL bFlag3D ) const
568 rStr = GetRefString( GetBigRange(), pDoc, bFlag3D );
572 void ScChangeAction::Accept()
574 if ( IsVirgin() )
576 SetState( SC_CAS_ACCEPTED );
577 DeleteCellEntries();
582 void ScChangeAction::SetRejected()
584 if ( IsVirgin() )
586 SetState( SC_CAS_REJECTED );
587 RemoveAllLinks();
588 DeleteCellEntries();
593 void ScChangeAction::RejectRestoreContents( ScChangeTrack* pTrack,
594 SCsCOL nDx, SCsROW nDy )
596 // Liste der Contents aufbauen
597 ScChangeActionCellListEntry* pListContents = NULL;
598 for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() )
600 ScChangeAction* p = pL->GetAction();
601 if ( p && p->GetType() == SC_CAT_CONTENT )
603 ScChangeActionCellListEntry* pE = new ScChangeActionCellListEntry(
604 (ScChangeActionContent*) p, pListContents );
605 pListContents = pE;
608 SetState( SC_CAS_REJECTED ); // vor UpdateReference fuer Move
609 pTrack->UpdateReference( this, TRUE ); // LinkDeleted freigeben
610 DBG_ASSERT( !pLinkDeleted, "ScChangeAction::RejectRestoreContents: pLinkDeleted != NULL" );
611 // Liste der Contents abarbeiten und loeschen
612 ScDocument* pDoc = pTrack->GetDocument();
613 ScChangeActionCellListEntry* pE = pListContents;
614 while ( pE )
616 if ( !pE->pContent->IsDeletedIn() &&
617 pE->pContent->GetBigRange().aStart.IsValid( pDoc ) )
618 pE->pContent->PutNewValueToDoc( pDoc, nDx, nDy );
619 ScChangeActionCellListEntry* pNextEntry;
620 pNextEntry = pE->pNext;
621 delete pE;
622 pE = pNextEntry;
624 DeleteCellEntries(); // weg mit den generierten
628 void ScChangeAction::SetDeletedInThis( ULONG nActionNumber,
629 const ScChangeTrack* pTrack )
631 if ( nActionNumber )
633 ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber );
634 DBG_ASSERT( pAct, "ScChangeAction::SetDeletedInThis: missing Action" );
635 if ( pAct )
636 pAct->SetDeletedIn( this );
641 void ScChangeAction::AddDependent( ULONG nActionNumber,
642 const ScChangeTrack* pTrack )
644 if ( nActionNumber )
646 ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber );
647 DBG_ASSERT( pAct, "ScChangeAction::AddDependent: missing Action" );
648 if ( pAct )
650 ScChangeActionLinkEntry* pLink = AddDependent( pAct );
651 pAct->AddLink( this, pLink );
657 #if DEBUG_CHANGETRACK
658 String ScChangeAction::ToString( ScDocument* pDoc ) const
660 String aReturn;
662 String aNumber = String::CreateFromInt64( static_cast< sal_Int64 >( GetActionNumber() ) );
664 String aActionState;
665 ScChangeActionState eActionState = GetState();
666 switch ( eActionState )
668 case SC_CAS_VIRGIN:
670 aActionState = String::CreateFromAscii( " " );
672 break;
673 case SC_CAS_ACCEPTED:
675 aActionState = String::CreateFromAscii( "+" );
677 break;
678 case SC_CAS_REJECTED:
680 aActionState = String::CreateFromAscii( "-" );
682 break;
685 String aRejectAction;
686 if ( IsRejecting() )
688 aRejectAction += 'r';
689 aRejectAction += String::CreateFromInt64( static_cast< sal_Int64 >( GetRejectAction() ) );
692 String aReference;
693 GetRefString( aReference, pDoc, TRUE );
695 String aAuthor = GetUser();
697 DateTime aDT = GetDateTime();
698 String aDate = ScGlobal::pLocaleData->getDate( aDT );
699 aDate += ' ';
700 aDate += ScGlobal::pLocaleData->getTime( aDT, FALSE, FALSE );
702 String aDescription;
703 GetDescription( aDescription, pDoc );
705 String aLinkAny;
706 const ScChangeActionLinkEntry* pLinkA = pLinkAny;
707 while ( pLinkA )
709 if ( !aLinkAny.Len() )
711 aLinkAny = String::CreateFromAscii( "(Any:" );
713 aLinkAny += String::CreateFromAscii( " ->" );
714 aLinkAny += pLinkA->ToString();
715 pLinkA = pLinkA->GetNext();
717 if ( aLinkAny.Len() )
719 aLinkAny += ')';
722 String aLinkDeletedIn;
723 const ScChangeActionLinkEntry* pLinkDI = pLinkDeletedIn;
724 while ( pLinkDI )
726 if ( !aLinkDeletedIn.Len() )
728 aLinkDeletedIn = String::CreateFromAscii( "(DeletedIn:" );
730 aLinkDeletedIn += String::CreateFromAscii( " ->" );
731 aLinkDeletedIn += pLinkDI->ToString();
732 pLinkDI = pLinkDI->GetNext();
734 if ( aLinkDeletedIn.Len() )
736 aLinkDeletedIn += ')';
739 String aLinkDeleted;
740 const ScChangeActionLinkEntry* pLinkD = pLinkDeleted;
741 while ( pLinkD )
743 if ( !aLinkDeleted.Len() )
745 aLinkDeleted = String::CreateFromAscii( "(Deleted:" );
747 aLinkDeleted += String::CreateFromAscii( " ->" );
748 aLinkDeleted += pLinkD->ToString();
749 pLinkD = pLinkD->GetNext();
751 if ( aLinkDeleted.Len() )
753 aLinkDeleted += ')';
756 String aLinkDependent;
757 const ScChangeActionLinkEntry* pLinkDp = pLinkDependent;
758 while ( pLinkDp )
760 if ( !aLinkDependent.Len() )
762 aLinkDependent = String::CreateFromAscii( "(Dependent:" );
764 aLinkDependent += String::CreateFromAscii( " ->" );
765 aLinkDependent += pLinkDp->ToString();
766 pLinkDp = pLinkDp->GetNext();
768 if ( aLinkDependent.Len() )
770 aLinkDependent += ')';
773 aReturn += aNumber;
774 aReturn += aActionState;
775 aReturn += aRejectAction;
776 aReturn += String::CreateFromAscii( ": " );
777 aReturn += aReference;
778 aReturn += ' ';
779 aReturn += aAuthor;
780 aReturn += ' ';
781 aReturn += aDate;
782 aReturn += ' ';
783 aReturn += aDescription;
784 aReturn += ' ';
785 aReturn += aLinkAny;
786 aReturn += ' ';
787 aReturn += aLinkDeletedIn;
788 aReturn += ' ';
789 aReturn += aLinkDeleted;
790 aReturn += ' ';
791 aReturn += aLinkDependent;
793 return aReturn;
795 #endif // DEBUG_CHANGETRACK
798 // --- ScChangeActionIns ---------------------------------------------------
800 ScChangeActionIns::ScChangeActionIns( const ScRange& rRange )
801 : ScChangeAction( SC_CAT_NONE, rRange )
803 if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == MAXCOL )
805 aBigRange.aStart.SetCol( nInt32Min );
806 aBigRange.aEnd.SetCol( nInt32Max );
807 if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW )
809 SetType( SC_CAT_INSERT_TABS );
810 aBigRange.aStart.SetRow( nInt32Min );
811 aBigRange.aEnd.SetRow( nInt32Max );
813 else
814 SetType( SC_CAT_INSERT_ROWS );
816 else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW )
818 SetType( SC_CAT_INSERT_COLS );
819 aBigRange.aStart.SetRow( nInt32Min );
820 aBigRange.aEnd.SetRow( nInt32Max );
822 else
824 DBG_ERROR( "ScChangeActionIns: Block not supported!" );
829 ScChangeActionIns::ScChangeActionIns(const ULONG nActionNumber, const ScChangeActionState eStateP, const ULONG nRejectingNumber,
830 const ScBigRange& aBigRangeP, const String& aUserP, const DateTime& aDateTimeP, const String& sComment,
831 const ScChangeActionType eTypeP)
833 ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment)
837 ScChangeActionIns::~ScChangeActionIns()
842 void ScChangeActionIns::GetDescription( String& rStr, ScDocument* pDoc,
843 BOOL bSplitRange, bool bWarning ) const
845 ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning );
847 USHORT nWhatId;
848 switch ( GetType() )
850 case SC_CAT_INSERT_COLS :
851 nWhatId = STR_COLUMN;
852 break;
853 case SC_CAT_INSERT_ROWS :
854 nWhatId = STR_ROW;
855 break;
856 default:
857 nWhatId = STR_AREA;
860 String aRsc( ScGlobal::GetRscString( STR_CHANGED_INSERT ) );
861 xub_StrLen nPos = aRsc.SearchAscii( "#1" );
862 rStr += aRsc.Copy( 0, nPos );
863 rStr += ScGlobal::GetRscString( nWhatId );
864 rStr += ' ';
865 rStr += GetRefString( GetBigRange(), pDoc );
866 rStr += aRsc.Copy( nPos+2 );
870 BOOL ScChangeActionIns::Reject( ScDocument* pDoc )
872 if ( !aBigRange.IsValid( pDoc ) )
873 return FALSE;
875 ScRange aRange( aBigRange.MakeRange() );
876 if ( !pDoc->IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(),
877 aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) )
878 return FALSE;
880 switch ( GetType() )
882 case SC_CAT_INSERT_COLS :
883 pDoc->DeleteCol( aRange );
884 break;
885 case SC_CAT_INSERT_ROWS :
886 pDoc->DeleteRow( aRange );
887 break;
888 case SC_CAT_INSERT_TABS :
889 pDoc->DeleteTab( aRange.aStart.Tab() );
890 break;
891 default:
893 // added to avoid warnings
896 SetState( SC_CAS_REJECTED );
897 RemoveAllLinks();
898 return TRUE;
902 // --- ScChangeActionDel ---------------------------------------------------
904 ScChangeActionDel::ScChangeActionDel( const ScRange& rRange,
905 SCsCOL nDxP, SCsROW nDyP, ScChangeTrack* pTrackP )
907 ScChangeAction( SC_CAT_NONE, rRange ),
908 pTrack( pTrackP ),
909 pFirstCell( NULL ),
910 pCutOff( NULL ),
911 nCutOff( 0 ),
912 pLinkMove( NULL ),
913 nDx( nDxP ),
914 nDy( nDyP )
916 if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == MAXCOL )
918 aBigRange.aStart.SetCol( nInt32Min );
919 aBigRange.aEnd.SetCol( nInt32Max );
920 if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW )
922 SetType( SC_CAT_DELETE_TABS );
923 aBigRange.aStart.SetRow( nInt32Min );
924 aBigRange.aEnd.SetRow( nInt32Max );
926 else
927 SetType( SC_CAT_DELETE_ROWS );
929 else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW )
931 SetType( SC_CAT_DELETE_COLS );
932 aBigRange.aStart.SetRow( nInt32Min );
933 aBigRange.aEnd.SetRow( nInt32Max );
935 else
937 DBG_ERROR( "ScChangeActionDel: Block not supported!" );
942 ScChangeActionDel::ScChangeActionDel(const ULONG nActionNumber, const ScChangeActionState eStateP, const ULONG nRejectingNumber,
943 const ScBigRange& aBigRangeP, const String& aUserP, const DateTime& aDateTimeP, const String &sComment,
944 const ScChangeActionType eTypeP, const SCsCOLROW nD, ScChangeTrack* pTrackP) // wich of nDx and nDy is set is depend on the type
946 ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
947 pTrack( pTrackP ),
948 pFirstCell( NULL ),
949 pCutOff( NULL ),
950 nCutOff( 0 ),
951 pLinkMove( NULL ),
952 nDx( 0 ),
953 nDy( 0 )
955 if (eType == SC_CAT_DELETE_COLS)
956 nDx = static_cast<SCsCOL>(nD);
957 else if (eType == SC_CAT_DELETE_ROWS)
958 nDy = static_cast<SCsROW>(nD);
961 ScChangeActionDel::~ScChangeActionDel()
963 DeleteCellEntries();
964 while ( pLinkMove )
965 delete pLinkMove;
968 void ScChangeActionDel::AddContent( ScChangeActionContent* pContent )
970 ScChangeActionCellListEntry* pE = new ScChangeActionCellListEntry(
971 pContent, pFirstCell );
972 pFirstCell = pE;
976 void ScChangeActionDel::DeleteCellEntries()
978 pTrack->DeleteCellEntries( pFirstCell, this );
982 BOOL ScChangeActionDel::IsBaseDelete() const
984 return !GetDx() && !GetDy();
988 BOOL ScChangeActionDel::IsTopDelete() const
990 const ScChangeAction* p = GetNext();
991 if ( !p || p->GetType() != GetType() )
992 return TRUE;
993 return ((ScChangeActionDel*)p)->IsBaseDelete();
997 BOOL ScChangeActionDel::IsMultiDelete() const
999 if ( GetDx() || GetDy() )
1000 return TRUE;
1001 const ScChangeAction* p = GetNext();
1002 if ( !p || p->GetType() != GetType() )
1003 return FALSE;
1004 const ScChangeActionDel* pDel = (const ScChangeActionDel*) p;
1005 if ( (pDel->GetDx() > GetDx() || pDel->GetDy() > GetDy()) &&
1006 pDel->GetBigRange() == aBigRange )
1007 return TRUE;
1008 return FALSE;
1012 BOOL ScChangeActionDel::IsTabDeleteCol() const
1014 if ( GetType() != SC_CAT_DELETE_COLS )
1015 return FALSE;
1016 const ScChangeAction* p = this;
1017 while ( p && p->GetType() == SC_CAT_DELETE_COLS &&
1018 !((const ScChangeActionDel*)p)->IsTopDelete() )
1019 p = p->GetNext();
1020 return p && p->GetType() == SC_CAT_DELETE_TABS;
1024 void ScChangeActionDel::UpdateReference( const ScChangeTrack* /* pTrack */,
1025 UpdateRefMode eMode, const ScBigRange& rRange,
1026 INT32 nDxP, INT32 nDyP, INT32 nDz )
1028 ScRefUpdate::Update( eMode, rRange, nDxP, nDyP, nDz, GetBigRange() );
1029 if ( !IsDeletedIn() )
1030 return ;
1031 // evtl. in "druntergerutschten" anpassen
1032 for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() )
1034 ScChangeAction* p = pL->GetAction();
1035 if ( p && p->GetType() == SC_CAT_CONTENT &&
1036 !GetBigRange().In( p->GetBigRange() ) )
1038 switch ( GetType() )
1040 case SC_CAT_DELETE_COLS :
1041 p->GetBigRange().aStart.SetCol( GetBigRange().aStart.Col() );
1042 p->GetBigRange().aEnd.SetCol( GetBigRange().aStart.Col() );
1043 break;
1044 case SC_CAT_DELETE_ROWS :
1045 p->GetBigRange().aStart.SetRow( GetBigRange().aStart.Row() );
1046 p->GetBigRange().aEnd.SetRow( GetBigRange().aStart.Row() );
1047 break;
1048 case SC_CAT_DELETE_TABS :
1049 p->GetBigRange().aStart.SetTab( GetBigRange().aStart.Tab() );
1050 p->GetBigRange().aEnd.SetTab( GetBigRange().aStart.Tab() );
1051 break;
1052 default:
1054 // added to avoid warnings
1062 ScBigRange ScChangeActionDel::GetOverAllRange() const
1064 ScBigRange aTmpRange( GetBigRange() );
1065 aTmpRange.aEnd.SetCol( aTmpRange.aEnd.Col() + GetDx() );
1066 aTmpRange.aEnd.SetRow( aTmpRange.aEnd.Row() + GetDy() );
1067 return aTmpRange;
1071 void ScChangeActionDel::GetDescription( String& rStr, ScDocument* pDoc,
1072 BOOL bSplitRange, bool bWarning ) const
1074 ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning );
1076 USHORT nWhatId;
1077 switch ( GetType() )
1079 case SC_CAT_DELETE_COLS :
1080 nWhatId = STR_COLUMN;
1081 break;
1082 case SC_CAT_DELETE_ROWS :
1083 nWhatId = STR_ROW;
1084 break;
1085 default:
1086 nWhatId = STR_AREA;
1089 ScBigRange aTmpRange( GetBigRange() );
1090 if ( !IsRejected() )
1092 if ( bSplitRange )
1094 aTmpRange.aStart.SetCol( aTmpRange.aStart.Col() + GetDx() );
1095 aTmpRange.aStart.SetRow( aTmpRange.aStart.Row() + GetDy() );
1097 aTmpRange.aEnd.SetCol( aTmpRange.aEnd.Col() + GetDx() );
1098 aTmpRange.aEnd.SetRow( aTmpRange.aEnd.Row() + GetDy() );
1101 String aRsc( ScGlobal::GetRscString( STR_CHANGED_DELETE ) );
1102 xub_StrLen nPos = aRsc.SearchAscii( "#1" );
1103 rStr += aRsc.Copy( 0, nPos );
1104 rStr += ScGlobal::GetRscString( nWhatId );
1105 rStr += ' ';
1106 rStr += GetRefString( aTmpRange, pDoc );
1107 rStr += aRsc.Copy( nPos+2 );
1111 BOOL ScChangeActionDel::Reject( ScDocument* pDoc )
1113 if ( !aBigRange.IsValid( pDoc ) && GetType() != SC_CAT_DELETE_TABS )
1114 return FALSE;
1116 BOOL bOk = TRUE;
1118 if ( IsTopDelete() )
1119 { // den kompletten Bereich in einem Rutsch restaurieren
1120 ScBigRange aTmpRange( GetOverAllRange() );
1121 if ( !aTmpRange.IsValid( pDoc ) )
1123 if ( GetType() == SC_CAT_DELETE_TABS )
1124 { // wird Tab angehaengt?
1125 if ( aTmpRange.aStart.Tab() > pDoc->GetMaxTableNumber() )
1126 bOk = FALSE;
1128 else
1129 bOk = FALSE;
1131 if ( bOk )
1133 ScRange aRange( aTmpRange.MakeRange() );
1134 // InDelete... fuer Formel UpdateReference in Document
1135 pTrack->SetInDeleteRange( aRange );
1136 pTrack->SetInDeleteTop( TRUE );
1137 pTrack->SetInDeleteUndo( TRUE );
1138 pTrack->SetInDelete( TRUE );
1139 switch ( GetType() )
1141 case SC_CAT_DELETE_COLS :
1142 if ( !(aRange.aStart.Col() == 0 && aRange.aEnd.Col() == MAXCOL) )
1143 { // nur wenn nicht TabDelete
1144 if ( ( bOk = pDoc->CanInsertCol( aRange ) ) != FALSE )
1145 bOk = pDoc->InsertCol( aRange );
1147 break;
1148 case SC_CAT_DELETE_ROWS :
1149 if ( ( bOk = pDoc->CanInsertRow( aRange ) ) != FALSE )
1150 bOk = pDoc->InsertRow( aRange );
1151 break;
1152 case SC_CAT_DELETE_TABS :
1154 //2do: Tabellennamen merken?
1155 String aName;
1156 pDoc->CreateValidTabName( aName );
1157 if ( ( bOk = pDoc->ValidNewTabName( aName ) ) != FALSE )
1158 bOk = pDoc->InsertTab( aRange.aStart.Tab(), aName );
1160 break;
1161 default:
1163 // added to avoid warnings
1166 pTrack->SetInDelete( FALSE );
1167 pTrack->SetInDeleteUndo( FALSE );
1169 if ( !bOk )
1171 pTrack->SetInDeleteTop( FALSE );
1172 return FALSE;
1174 // InDeleteTop fuer UpdateReference-Undo behalten
1177 // setzt rejected und ruft UpdateReference-Undo und DeleteCellEntries
1178 RejectRestoreContents( pTrack, GetDx(), GetDy() );
1180 pTrack->SetInDeleteTop( FALSE );
1181 RemoveAllLinks();
1182 return TRUE;
1186 void ScChangeActionDel::UndoCutOffMoves()
1187 { // abgeschnittene Moves wiederherstellen, Entries/Links deleten
1188 while ( pLinkMove )
1190 ScChangeActionMove* pMove = pLinkMove->GetMove();
1191 short nFrom = pLinkMove->GetCutOffFrom();
1192 short nTo = pLinkMove->GetCutOffTo();
1193 switch ( GetType() )
1195 case SC_CAT_DELETE_COLS :
1196 if ( nFrom > 0 )
1197 pMove->GetFromRange().aStart.IncCol( -nFrom );
1198 else if ( nFrom < 0 )
1199 pMove->GetFromRange().aEnd.IncCol( -nFrom );
1200 if ( nTo > 0 )
1201 pMove->GetBigRange().aStart.IncCol( -nTo );
1202 else if ( nTo < 0 )
1203 pMove->GetBigRange().aEnd.IncCol( -nTo );
1204 break;
1205 case SC_CAT_DELETE_ROWS :
1206 if ( nFrom > 0 )
1207 pMove->GetFromRange().aStart.IncRow( -nFrom );
1208 else if ( nFrom < 0 )
1209 pMove->GetFromRange().aEnd.IncRow( -nFrom );
1210 if ( nTo > 0 )
1211 pMove->GetBigRange().aStart.IncRow( -nTo );
1212 else if ( nTo < 0 )
1213 pMove->GetBigRange().aEnd.IncRow( -nTo );
1214 break;
1215 case SC_CAT_DELETE_TABS :
1216 if ( nFrom > 0 )
1217 pMove->GetFromRange().aStart.IncTab( -nFrom );
1218 else if ( nFrom < 0 )
1219 pMove->GetFromRange().aEnd.IncTab( -nFrom );
1220 if ( nTo > 0 )
1221 pMove->GetBigRange().aStart.IncTab( -nTo );
1222 else if ( nTo < 0 )
1223 pMove->GetBigRange().aEnd.IncTab( -nTo );
1224 break;
1225 default:
1227 // added to avoid warnings
1230 delete pLinkMove; // rueckt sich selbst hoch
1234 void ScChangeActionDel::UndoCutOffInsert()
1235 { // abgeschnittenes Insert wiederherstellen
1236 if ( pCutOff )
1238 switch ( pCutOff->GetType() )
1240 case SC_CAT_INSERT_COLS :
1241 if ( nCutOff < 0 )
1242 pCutOff->GetBigRange().aEnd.IncCol( -nCutOff );
1243 else
1244 pCutOff->GetBigRange().aStart.IncCol( -nCutOff );
1245 break;
1246 case SC_CAT_INSERT_ROWS :
1247 if ( nCutOff < 0 )
1248 pCutOff->GetBigRange().aEnd.IncRow( -nCutOff );
1249 else
1250 pCutOff->GetBigRange().aStart.IncRow( -nCutOff );
1251 break;
1252 case SC_CAT_INSERT_TABS :
1253 if ( nCutOff < 0 )
1254 pCutOff->GetBigRange().aEnd.IncTab( -nCutOff );
1255 else
1256 pCutOff->GetBigRange().aStart.IncTab( -nCutOff );
1257 break;
1258 default:
1260 // added to avoid warnings
1263 SetCutOffInsert( NULL, 0 );
1268 // --- ScChangeActionMove --------------------------------------------------
1270 ScChangeActionMove::ScChangeActionMove(const ULONG nActionNumber, const ScChangeActionState eStateP, const ULONG nRejectingNumber,
1271 const ScBigRange& aToBigRange, const String& aUserP, const DateTime& aDateTimeP, const String &sComment,
1272 const ScBigRange& aFromBigRange, ScChangeTrack* pTrackP) // wich of nDx and nDy is set is depend on the type
1274 ScChangeAction(SC_CAT_MOVE, aToBigRange, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
1275 aFromRange(aFromBigRange),
1276 pTrack( pTrackP ),
1277 pFirstCell( NULL ),
1278 nStartLastCut(0),
1279 nEndLastCut(0)
1283 ScChangeActionMove::~ScChangeActionMove()
1285 DeleteCellEntries();
1289 void ScChangeActionMove::AddContent( ScChangeActionContent* pContent )
1291 ScChangeActionCellListEntry* pE = new ScChangeActionCellListEntry(
1292 pContent, pFirstCell );
1293 pFirstCell = pE;
1297 void ScChangeActionMove::DeleteCellEntries()
1299 pTrack->DeleteCellEntries( pFirstCell, this );
1303 void ScChangeActionMove::UpdateReference( const ScChangeTrack* /* pTrack */,
1304 UpdateRefMode eMode, const ScBigRange& rRange,
1305 INT32 nDx, INT32 nDy, INT32 nDz )
1307 ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aFromRange );
1308 ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() );
1312 void ScChangeActionMove::GetDelta( INT32& nDx, INT32& nDy, INT32& nDz ) const
1314 const ScBigAddress& rToPos = GetBigRange().aStart;
1315 const ScBigAddress& rFromPos = GetFromRange().aStart;
1316 nDx = rToPos.Col() - rFromPos.Col();
1317 nDy = rToPos.Row() - rFromPos.Row();
1318 nDz = rToPos.Tab() - rFromPos.Tab();
1322 void ScChangeActionMove::GetDescription( String& rStr, ScDocument* pDoc,
1323 BOOL bSplitRange, bool bWarning ) const
1325 ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning );
1327 BOOL bFlag3D = ( GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab() );
1329 String aRsc( ScGlobal::GetRscString( STR_CHANGED_MOVE ) );
1331 xub_StrLen nPos = 0;
1332 String aTmpStr = ScChangeAction::GetRefString( GetFromRange(), pDoc, bFlag3D );
1333 nPos = aRsc.SearchAscii( "#1", nPos );
1334 aRsc.Erase( nPos, 2 );
1335 aRsc.Insert( aTmpStr, nPos );
1336 nPos = sal::static_int_cast<xub_StrLen>( nPos + aTmpStr.Len() );
1338 aTmpStr = ScChangeAction::GetRefString( GetBigRange(), pDoc, bFlag3D );
1339 nPos = aRsc.SearchAscii( "#2", nPos );
1340 aRsc.Erase( nPos, 2 );
1341 aRsc.Insert( aTmpStr, nPos );
1342 nPos = sal::static_int_cast<xub_StrLen>( nPos + aTmpStr.Len() );
1344 rStr += aRsc;
1348 void ScChangeActionMove::GetRefString( String& rStr, ScDocument* pDoc,
1349 BOOL bFlag3D ) const
1351 if ( !bFlag3D )
1352 bFlag3D = ( GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab() );
1353 rStr = ScChangeAction::GetRefString( GetFromRange(), pDoc, bFlag3D );
1354 rStr += ',';
1355 rStr += ' ';
1356 rStr += ScChangeAction::GetRefString( GetBigRange(), pDoc, bFlag3D );
1360 BOOL ScChangeActionMove::Reject( ScDocument* pDoc )
1362 if ( !(aBigRange.IsValid( pDoc ) && aFromRange.IsValid( pDoc )) )
1363 return FALSE;
1365 ScRange aToRange( aBigRange.MakeRange() );
1366 ScRange aFrmRange( aFromRange.MakeRange() );
1368 BOOL bOk = pDoc->IsBlockEditable( aToRange.aStart.Tab(),
1369 aToRange.aStart.Col(), aToRange.aStart.Row(),
1370 aToRange.aEnd.Col(), aToRange.aEnd.Row() );
1371 if ( bOk )
1372 bOk = pDoc->IsBlockEditable( aFrmRange.aStart.Tab(),
1373 aFrmRange.aStart.Col(), aFrmRange.aStart.Row(),
1374 aFrmRange.aEnd.Col(), aFrmRange.aEnd.Row() );
1375 if ( !bOk )
1376 return FALSE;
1378 pTrack->LookUpContents( aToRange, pDoc, 0, 0, 0 ); // zu movende Contents
1380 pDoc->DeleteAreaTab( aToRange, IDF_ALL );
1381 pDoc->DeleteAreaTab( aFrmRange, IDF_ALL );
1382 // Formeln im Dokument anpassen
1383 pDoc->UpdateReference( URM_MOVE,
1384 aFrmRange.aStart.Col(), aFrmRange.aStart.Row(), aFrmRange.aStart.Tab(),
1385 aFrmRange.aEnd.Col(), aFrmRange.aEnd.Row(), aFrmRange.aEnd.Tab(),
1386 (SCsCOL) aFrmRange.aStart.Col() - aToRange.aStart.Col(),
1387 (SCsROW) aFrmRange.aStart.Row() - aToRange.aStart.Row(),
1388 (SCsTAB) aFrmRange.aStart.Tab() - aToRange.aStart.Tab(), NULL );
1390 // LinkDependent freigeben, nachfolgendes UpdateReference-Undo setzt
1391 // ToRange->FromRange Dependents
1392 RemoveAllDependent();
1394 // setzt rejected und ruft UpdateReference-Undo und DeleteCellEntries
1395 RejectRestoreContents( pTrack, 0, 0 );
1397 while ( pLinkDependent )
1399 ScChangeAction* p = pLinkDependent->GetAction();
1400 if ( p && p->GetType() == SC_CAT_CONTENT )
1402 ScChangeActionContent* pContent = (ScChangeActionContent*) p;
1403 if ( !pContent->IsDeletedIn() &&
1404 pContent->GetBigRange().aStart.IsValid( pDoc ) )
1405 pContent->PutNewValueToDoc( pDoc, 0, 0 );
1406 // in LookUpContents generierte loeschen
1407 if ( pTrack->IsGenerated( pContent->GetActionNumber() ) &&
1408 !pContent->IsDeletedIn() )
1410 pLinkDependent->UnLink(); //! sonst wird der mitgeloescht
1411 pTrack->DeleteGeneratedDelContent( pContent );
1414 delete pLinkDependent;
1417 RemoveAllLinks();
1418 return TRUE;
1422 // --- ScChangeActionContent -----------------------------------------------
1424 const USHORT nMemPoolChangeActionContent = (0x8000 - 64) / sizeof(ScChangeActionContent);
1425 IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeActionContent, nMemPoolChangeActionContent, nMemPoolChangeActionContent )
1427 ScChangeActionContent::ScChangeActionContent( const ULONG nActionNumber,
1428 const ScChangeActionState eStateP, const ULONG nRejectingNumber,
1429 const ScBigRange& aBigRangeP, const String& aUserP,
1430 const DateTime& aDateTimeP, const String& sComment,
1431 ScBaseCell* pTempOldCell, ScDocument* pDoc, const String& sOldValue )
1433 ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
1434 aOldValue(sOldValue),
1435 pOldCell(pTempOldCell),
1436 pNewCell(NULL),
1437 pNextContent(NULL),
1438 pPrevContent(NULL),
1439 pNextInSlot(NULL),
1440 ppPrevInSlot(NULL)
1443 if (pOldCell)
1444 ScChangeActionContent::SetCell( aOldValue, pOldCell, 0, pDoc );
1445 if ( sOldValue.Len() ) // #i40704# don't overwrite SetCell result with empty string
1446 aOldValue = sOldValue; // set again, because SetCell removes it
1449 ScChangeActionContent::ScChangeActionContent( const ULONG nActionNumber,
1450 ScBaseCell* pTempNewCell, const ScBigRange& aBigRangeP,
1451 ScDocument* pDoc, const String& sNewValue )
1453 ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber),
1454 aNewValue(sNewValue),
1455 pOldCell(NULL),
1456 pNewCell(pTempNewCell),
1457 pNextContent(NULL),
1458 pPrevContent(NULL),
1459 pNextInSlot(NULL),
1460 ppPrevInSlot(NULL)
1462 if (pNewCell)
1463 ScChangeActionContent::SetCell( aNewValue, pNewCell, 0, pDoc );
1464 if ( sNewValue.Len() ) // #i40704# don't overwrite SetCell result with empty string
1465 aNewValue = sNewValue; // set again, because SetCell removes it
1468 ScChangeActionContent::~ScChangeActionContent()
1470 ClearTrack();
1474 void ScChangeActionContent::ClearTrack()
1476 RemoveFromSlot();
1477 if ( pPrevContent )
1478 pPrevContent->pNextContent = pNextContent;
1479 if ( pNextContent )
1480 pNextContent->pPrevContent = pPrevContent;
1484 ScChangeActionContent* ScChangeActionContent::GetTopContent() const
1486 if ( pNextContent )
1488 ScChangeActionContent* pContent = pNextContent;
1489 while ( pContent->pNextContent && pContent != pContent->pNextContent )
1490 pContent = pContent->pNextContent;
1491 return pContent;
1493 return (ScChangeActionContent*) this;
1497 ScChangeActionLinkEntry* ScChangeActionContent::GetDeletedIn() const
1499 if ( pNextContent )
1500 return GetTopContent()->pLinkDeletedIn;
1501 return pLinkDeletedIn;
1505 ScChangeActionLinkEntry** ScChangeActionContent::GetDeletedInAddress()
1507 if ( pNextContent )
1508 return GetTopContent()->GetDeletedInAddress();
1509 return &pLinkDeletedIn;
1513 void ScChangeActionContent::SetOldValue( const ScBaseCell* pCell,
1514 const ScDocument* pFromDoc, ScDocument* pToDoc, ULONG nFormat )
1516 ScChangeActionContent::SetValue( aOldValue, pOldCell,
1517 nFormat, pCell, pFromDoc, pToDoc );
1521 void ScChangeActionContent::SetOldValue( const ScBaseCell* pCell,
1522 const ScDocument* pFromDoc, ScDocument* pToDoc )
1524 ScChangeActionContent::SetValue( aOldValue, pOldCell,
1525 aBigRange.aStart.MakeAddress(), pCell, pFromDoc, pToDoc );
1529 void ScChangeActionContent::SetNewValue( const ScBaseCell* pCell,
1530 ScDocument* pDoc )
1532 ScChangeActionContent::SetValue( aNewValue, pNewCell,
1533 aBigRange.aStart.MakeAddress(), pCell, pDoc, pDoc );
1537 void ScChangeActionContent::SetOldNewCells( ScBaseCell* pOldCellP,
1538 ULONG nOldFormat, ScBaseCell* pNewCellP,
1539 ULONG nNewFormat, ScDocument* pDoc )
1541 pOldCell = pOldCellP;
1542 pNewCell = pNewCellP;
1543 ScChangeActionContent::SetCell( aOldValue, pOldCell, nOldFormat, pDoc );
1544 ScChangeActionContent::SetCell( aNewValue, pNewCell, nNewFormat, pDoc );
1547 void ScChangeActionContent::SetNewCell( ScBaseCell* pCell, ScDocument* pDoc, const String& rFormatted )
1549 DBG_ASSERT( !pNewCell, "ScChangeActionContent::SetNewCell: overwriting existing cell" );
1550 pNewCell = pCell;
1551 ScChangeActionContent::SetCell( aNewValue, pNewCell, 0, pDoc );
1553 // #i40704# allow to set formatted text here - don't call SetNewValue with String from XML filter
1554 if ( rFormatted.Len() )
1555 aNewValue = rFormatted;
1558 void ScChangeActionContent::SetValueString( String& rValue, ScBaseCell*& pCell,
1559 const String& rStr, ScDocument* pDoc )
1561 if ( pCell )
1563 pCell->Delete();
1564 pCell = NULL;
1566 if ( rStr.Len() > 1 && rStr.GetChar(0) == '=' )
1568 rValue.Erase();
1569 pCell = new ScFormulaCell(
1570 pDoc, aBigRange.aStart.MakeAddress(), rStr, formula::FormulaGrammar::GRAM_DEFAULT, formula::FormulaGrammar::CONV_OOO );
1571 ((ScFormulaCell*)pCell)->SetInChangeTrack( TRUE );
1573 else
1574 rValue = rStr;
1578 void ScChangeActionContent::SetOldValue( const String& rOld, ScDocument* pDoc )
1580 SetValueString( aOldValue, pOldCell, rOld, pDoc );
1584 void ScChangeActionContent::SetNewValue( const String& rNew, ScDocument* pDoc )
1586 SetValueString( aNewValue, pNewCell, rNew, pDoc );
1590 void ScChangeActionContent::GetOldString( String& rStr ) const
1592 GetValueString( rStr, aOldValue, pOldCell );
1596 void ScChangeActionContent::GetNewString( String& rStr ) const
1598 GetValueString( rStr, aNewValue, pNewCell );
1602 void ScChangeActionContent::GetDescription( String& rStr, ScDocument* pDoc,
1603 BOOL bSplitRange, bool bWarning ) const
1605 ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning );
1607 String aRsc( ScGlobal::GetRscString( STR_CHANGED_CELL ) );
1609 String aTmpStr;
1610 GetRefString( aTmpStr, pDoc );
1612 xub_StrLen nPos = 0;
1613 nPos = aRsc.SearchAscii( "#1", nPos );
1614 aRsc.Erase( nPos, 2 );
1615 aRsc.Insert( aTmpStr, nPos );
1616 nPos = sal::static_int_cast<xub_StrLen>( nPos + aTmpStr.Len() );
1618 GetOldString( aTmpStr );
1619 if ( !aTmpStr.Len() )
1620 aTmpStr = ScGlobal::GetRscString( STR_CHANGED_BLANK );
1621 nPos = aRsc.SearchAscii( "#2", nPos );
1622 aRsc.Erase( nPos, 2 );
1623 aRsc.Insert( aTmpStr, nPos );
1624 nPos = sal::static_int_cast<xub_StrLen>( nPos + aTmpStr.Len() );
1626 GetNewString( aTmpStr );
1627 if ( !aTmpStr.Len() )
1628 aTmpStr = ScGlobal::GetRscString( STR_CHANGED_BLANK );
1629 nPos = aRsc.SearchAscii( "#3", nPos );
1630 aRsc.Erase( nPos, 2 );
1631 aRsc.Insert( aTmpStr, nPos );
1633 rStr += aRsc;
1637 void ScChangeActionContent::GetRefString( String& rStr, ScDocument* pDoc,
1638 BOOL bFlag3D ) const
1640 USHORT nFlags = ( GetBigRange().IsValid( pDoc ) ? SCA_VALID : 0 );
1641 if ( nFlags )
1643 const ScBaseCell* pCell = GetNewCell();
1644 if ( ScChangeActionContent::GetContentCellType( pCell ) == SC_CACCT_MATORG )
1646 ScBigRange aLocalBigRange( GetBigRange() );
1647 SCCOL nC;
1648 SCROW nR;
1649 ((const ScFormulaCell*)pCell)->GetMatColsRows( nC, nR );
1650 aLocalBigRange.aEnd.IncCol( nC-1 );
1651 aLocalBigRange.aEnd.IncRow( nR-1 );
1652 rStr = ScChangeAction::GetRefString( aLocalBigRange, pDoc, bFlag3D );
1654 return ;
1657 ScAddress aTmpAddress( GetBigRange().aStart.MakeAddress() );
1658 if ( bFlag3D )
1659 nFlags |= SCA_TAB_3D;
1660 aTmpAddress.Format( rStr, nFlags, pDoc, pDoc->GetAddressConvention() );
1661 if ( IsDeletedIn() )
1663 rStr.Insert( '(', 0 );
1664 rStr += ')';
1667 else
1668 rStr = ScGlobal::GetRscString( STR_NOREF_STR );
1672 BOOL ScChangeActionContent::Reject( ScDocument* pDoc )
1674 if ( !aBigRange.IsValid( pDoc ) )
1675 return FALSE;
1677 PutOldValueToDoc( pDoc, 0, 0 );
1679 SetState( SC_CAS_REJECTED );
1680 RemoveAllLinks();
1682 return TRUE;
1686 BOOL ScChangeActionContent::Select( ScDocument* pDoc, ScChangeTrack* pTrack,
1687 BOOL bOldest, Stack* pRejectActions )
1689 if ( !aBigRange.IsValid( pDoc ) )
1690 return FALSE;
1692 ScChangeActionContent* pContent = this;
1693 // accept previous contents
1694 while ( ( pContent = pContent->pPrevContent ) != NULL )
1696 if ( pContent->IsVirgin() )
1697 pContent->SetState( SC_CAS_ACCEPTED );
1699 ScChangeActionContent* pEnd = pContent = this;
1700 // reject subsequent contents
1701 while ( ( pContent = pContent->pNextContent ) != NULL )
1703 // MatrixOrigin may have dependents, no dependency recursion needed
1704 const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry();
1705 while ( pL )
1707 ScChangeAction* p = (ScChangeAction*) pL->GetAction();
1708 if ( p )
1709 p->SetRejected();
1710 pL = pL->GetNext();
1712 pContent->SetRejected();
1713 pEnd = pContent;
1716 if ( bOldest || pEnd != this )
1717 { // wenn nicht aeltester: ist es ueberhaupt ein anderer als der letzte?
1718 ScRange aRange( aBigRange.aStart.MakeAddress() );
1719 const ScAddress& rPos = aRange.aStart;
1721 ScChangeActionContent* pNew = new ScChangeActionContent( aRange );
1722 pNew->SetOldValue( pDoc->GetCell( rPos ), pDoc, pDoc );
1724 if ( bOldest )
1725 PutOldValueToDoc( pDoc, 0, 0 );
1726 else
1727 PutNewValueToDoc( pDoc, 0, 0 );
1729 pNew->SetRejectAction( bOldest ? GetActionNumber() : pEnd->GetActionNumber() );
1730 pNew->SetState( SC_CAS_ACCEPTED );
1731 if ( pRejectActions )
1732 pRejectActions->Push( pNew );
1733 else
1735 pNew->SetNewValue( pDoc->GetCell( rPos ), pDoc );
1736 pTrack->Append( pNew );
1740 if ( bOldest )
1741 SetRejected();
1742 else
1743 SetState( SC_CAS_ACCEPTED );
1745 return TRUE;
1749 // static
1750 void ScChangeActionContent::GetStringOfCell( String& rStr,
1751 const ScBaseCell* pCell, const ScDocument* pDoc, const ScAddress& rPos )
1753 if ( pCell )
1755 if ( ScChangeActionContent::NeedsNumberFormat( pCell ) )
1756 GetStringOfCell( rStr, pCell, pDoc, pDoc->GetNumberFormat( rPos ) );
1757 else
1758 GetStringOfCell( rStr, pCell, pDoc, 0 );
1760 else
1761 rStr.Erase();
1765 // static
1766 void ScChangeActionContent::GetStringOfCell( String& rStr,
1767 const ScBaseCell* pCell, const ScDocument* pDoc, ULONG nFormat )
1769 if ( ScChangeActionContent::GetContentCellType( pCell ) )
1771 switch ( pCell->GetCellType() )
1773 case CELLTYPE_VALUE :
1775 double nValue = ((ScValueCell*)pCell)->GetValue();
1776 pDoc->GetFormatTable()->GetInputLineString( nValue, nFormat,
1777 rStr );
1779 break;
1780 case CELLTYPE_STRING :
1781 ((ScStringCell*)pCell)->GetString( rStr );
1782 break;
1783 case CELLTYPE_EDIT :
1784 ((ScEditCell*)pCell)->GetString( rStr );
1785 break;
1786 case CELLTYPE_FORMULA :
1787 ((ScFormulaCell*)pCell)->GetFormula( rStr );
1788 break;
1789 default:
1790 rStr.Erase();
1793 else
1794 rStr.Erase();
1798 // static
1799 ScChangeActionContentCellType ScChangeActionContent::GetContentCellType( const ScBaseCell* pCell )
1801 if ( pCell )
1803 switch ( pCell->GetCellType() )
1805 case CELLTYPE_VALUE :
1806 case CELLTYPE_STRING :
1807 case CELLTYPE_EDIT :
1808 return SC_CACCT_NORMAL;
1809 //break;
1810 case CELLTYPE_FORMULA :
1811 switch ( ((const ScFormulaCell*)pCell)->GetMatrixFlag() )
1813 case MM_NONE :
1814 return SC_CACCT_NORMAL;
1815 //break;
1816 case MM_FORMULA :
1817 case MM_FAKE :
1818 return SC_CACCT_MATORG;
1819 //break;
1820 case MM_REFERENCE :
1821 return SC_CACCT_MATREF;
1822 //break;
1824 return SC_CACCT_NORMAL;
1825 //break;
1826 default:
1827 return SC_CACCT_NONE;
1830 return SC_CACCT_NONE;
1834 // static
1835 BOOL ScChangeActionContent::NeedsNumberFormat( const ScBaseCell* pCell )
1837 return pCell && pCell->GetCellType() == CELLTYPE_VALUE;
1841 // static
1842 void ScChangeActionContent::SetValue( String& rStr, ScBaseCell*& pCell,
1843 const ScAddress& rPos, const ScBaseCell* pOrgCell,
1844 const ScDocument* pFromDoc, ScDocument* pToDoc )
1846 ULONG nFormat = NeedsNumberFormat( pOrgCell ) ? pFromDoc->GetNumberFormat( rPos ) : 0;
1847 SetValue( rStr, pCell, nFormat, pOrgCell, pFromDoc, pToDoc );
1851 // static
1852 void ScChangeActionContent::SetValue( String& rStr, ScBaseCell*& pCell,
1853 ULONG nFormat, const ScBaseCell* pOrgCell,
1854 const ScDocument* pFromDoc, ScDocument* pToDoc )
1856 rStr.Erase();
1857 if ( pCell )
1858 pCell->Delete();
1859 if ( ScChangeActionContent::GetContentCellType( pOrgCell ) )
1861 pCell = pOrgCell->CloneWithoutNote( *pToDoc );
1862 switch ( pOrgCell->GetCellType() )
1864 case CELLTYPE_VALUE :
1865 { // z.B. Datum auch als solches merken
1866 double nValue = ((ScValueCell*)pOrgCell)->GetValue();
1867 pFromDoc->GetFormatTable()->GetInputLineString( nValue,
1868 nFormat, rStr );
1870 break;
1871 case CELLTYPE_FORMULA :
1872 ((ScFormulaCell*)pCell)->SetInChangeTrack( TRUE );
1873 break;
1874 default:
1876 // added to avoid warnings
1880 else
1881 pCell = NULL;
1885 // static
1886 void ScChangeActionContent::SetCell( String& rStr, ScBaseCell* pCell,
1887 ULONG nFormat, const ScDocument* pDoc )
1889 rStr.Erase();
1890 if ( pCell )
1892 switch ( pCell->GetCellType() )
1894 case CELLTYPE_VALUE :
1895 { // e.g. remember date as date string
1896 double nValue = ((ScValueCell*)pCell)->GetValue();
1897 pDoc->GetFormatTable()->GetInputLineString( nValue,
1898 nFormat, rStr );
1900 break;
1901 case CELLTYPE_FORMULA :
1902 ((ScFormulaCell*)pCell)->SetInChangeTrack( TRUE );
1903 break;
1904 default:
1906 // added to avoid warnings
1913 void ScChangeActionContent::GetValueString( String& rStr,
1914 const String& rValue, const ScBaseCell* pCell ) const
1916 if ( !rValue.Len() )
1918 if ( pCell )
1920 switch ( pCell->GetCellType() )
1922 case CELLTYPE_STRING :
1923 ((ScStringCell*)pCell)->GetString( rStr );
1924 break;
1925 case CELLTYPE_EDIT :
1926 ((ScEditCell*)pCell)->GetString( rStr );
1927 break;
1928 case CELLTYPE_VALUE : // ist immer in rValue
1929 rStr = rValue;
1930 break;
1931 case CELLTYPE_FORMULA :
1932 GetFormulaString( rStr, (ScFormulaCell*) pCell );
1933 break;
1934 default:
1936 // added to avoid warnings
1940 else
1941 rStr.Erase();
1943 else
1944 rStr = rValue;
1948 void ScChangeActionContent::GetFormulaString( String& rStr,
1949 const ScFormulaCell* pCell ) const
1951 ScAddress aPos( aBigRange.aStart.MakeAddress() );
1952 if ( aPos == pCell->aPos || IsDeletedIn() )
1953 pCell->GetFormula( rStr );
1954 else
1956 DBG_ERROR( "ScChangeActionContent::GetFormulaString: aPos != pCell->aPos" );
1957 ScFormulaCell* pNew = new ScFormulaCell( *pCell, *pCell->GetDocument(), aPos );
1958 pNew->GetFormula( rStr );
1959 delete pNew;
1964 void ScChangeActionContent::PutOldValueToDoc( ScDocument* pDoc,
1965 SCsCOL nDx, SCsROW nDy ) const
1967 PutValueToDoc( pOldCell, aOldValue, pDoc, nDx, nDy );
1971 void ScChangeActionContent::PutNewValueToDoc( ScDocument* pDoc,
1972 SCsCOL nDx, SCsROW nDy ) const
1974 PutValueToDoc( pNewCell, aNewValue, pDoc, nDx, nDy );
1978 void ScChangeActionContent::PutValueToDoc( ScBaseCell* pCell,
1979 const String& rValue, ScDocument* pDoc, SCsCOL nDx, SCsROW nDy ) const
1981 ScAddress aPos( aBigRange.aStart.MakeAddress() );
1982 if ( nDx )
1983 aPos.IncCol( nDx );
1984 if ( nDy )
1985 aPos.IncRow( nDy );
1986 if ( !rValue.Len() )
1988 if ( pCell )
1990 switch ( pCell->GetCellType() )
1992 case CELLTYPE_VALUE : // ist immer in rValue
1993 pDoc->SetString( aPos.Col(), aPos.Row(), aPos.Tab(), rValue );
1994 break;
1995 default:
1996 switch ( ScChangeActionContent::GetContentCellType( pCell ) )
1998 case SC_CACCT_MATORG :
2000 SCCOL nC;
2001 SCROW nR;
2002 ((const ScFormulaCell*)pCell)->GetMatColsRows( nC, nR );
2003 DBG_ASSERT( nC>0 && nR>0, "ScChangeActionContent::PutValueToDoc: MatColsRows?" );
2004 ScRange aRange( aPos );
2005 if ( nC > 1 )
2006 aRange.aEnd.IncCol( nC-1 );
2007 if ( nR > 1 )
2008 aRange.aEnd.IncRow( nR-1 );
2009 ScMarkData aDestMark;
2010 aDestMark.SelectOneTable( aPos.Tab() );
2011 aDestMark.SetMarkArea( aRange );
2012 pDoc->InsertMatrixFormula( aPos.Col(), aPos.Row(),
2013 aRange.aEnd.Col(), aRange.aEnd.Row(),
2014 aDestMark, EMPTY_STRING,
2015 ((const ScFormulaCell*)pCell)->GetCode() );
2017 break;
2018 case SC_CACCT_MATREF :
2019 // nothing
2020 break;
2021 default:
2022 pDoc->PutCell( aPos, pCell->CloneWithoutNote( *pDoc ) );
2026 else
2027 pDoc->PutCell( aPos, NULL );
2029 else
2030 pDoc->SetString( aPos.Col(), aPos.Row(), aPos.Tab(), rValue );
2034 void lcl_InvalidateReference( ScToken& rTok, const ScBigAddress& rPos )
2036 ScSingleRefData& rRef1 = rTok.GetSingleRef();
2037 if ( rPos.Col() < 0 || MAXCOL < rPos.Col() )
2039 rRef1.nCol = SCCOL_MAX;
2040 rRef1.nRelCol = SCCOL_MAX;
2041 rRef1.SetColDeleted( TRUE );
2043 if ( rPos.Row() < 0 || MAXROW < rPos.Row() )
2045 rRef1.nRow = SCROW_MAX;
2046 rRef1.nRelRow = SCROW_MAX;
2047 rRef1.SetRowDeleted( TRUE );
2049 if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() )
2051 rRef1.nTab = SCTAB_MAX;
2052 rRef1.nRelTab = SCTAB_MAX;
2053 rRef1.SetTabDeleted( TRUE );
2055 if ( rTok.GetType() == formula::svDoubleRef )
2057 ScSingleRefData& rRef2 = rTok.GetDoubleRef().Ref2;
2058 if ( rPos.Col() < 0 || MAXCOL < rPos.Col() )
2060 rRef2.nCol = SCCOL_MAX;
2061 rRef2.nRelCol = SCCOL_MAX;
2062 rRef2.SetColDeleted( TRUE );
2064 if ( rPos.Row() < 0 || MAXROW < rPos.Row() )
2066 rRef2.nRow = SCROW_MAX;
2067 rRef2.nRelRow = SCROW_MAX;
2068 rRef2.SetRowDeleted( TRUE );
2070 if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() )
2072 rRef2.nTab = SCTAB_MAX;
2073 rRef2.nRelTab = SCTAB_MAX;
2074 rRef2.SetTabDeleted( TRUE );
2080 void ScChangeActionContent::UpdateReference( const ScChangeTrack* pTrack,
2081 UpdateRefMode eMode, const ScBigRange& rRange,
2082 INT32 nDx, INT32 nDy, INT32 nDz )
2084 SCSIZE nOldSlot = ScChangeTrack::ComputeContentSlot( aBigRange.aStart.Row() );
2085 ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aBigRange );
2086 SCSIZE nNewSlot = ScChangeTrack::ComputeContentSlot( aBigRange.aStart.Row() );
2087 if ( nNewSlot != nOldSlot )
2089 RemoveFromSlot();
2090 InsertInSlot( &(pTrack->GetContentSlots()[nNewSlot]) );
2093 if ( pTrack->IsInDelete() && !pTrack->IsInDeleteTop() )
2094 return ; // Formeln nur kompletten Bereich updaten
2096 BOOL bOldFormula = ( pOldCell && pOldCell->GetCellType() == CELLTYPE_FORMULA );
2097 BOOL bNewFormula = ( pNewCell && pNewCell->GetCellType() == CELLTYPE_FORMULA );
2098 if ( bOldFormula || bNewFormula )
2099 { // via ScFormulaCell UpdateReference anpassen (dort)
2100 if ( pTrack->IsInDelete() )
2102 const ScRange& rDelRange = pTrack->GetInDeleteRange();
2103 if ( nDx > 0 )
2104 nDx = rDelRange.aEnd.Col() - rDelRange.aStart.Col() + 1;
2105 else if ( nDx < 0 )
2106 nDx = -(rDelRange.aEnd.Col() - rDelRange.aStart.Col() + 1);
2107 if ( nDy > 0 )
2108 nDy = rDelRange.aEnd.Row() - rDelRange.aStart.Row() + 1;
2109 else if ( nDy < 0 )
2110 nDy = -(rDelRange.aEnd.Row() - rDelRange.aStart.Row() + 1);
2111 if ( nDz > 0 )
2112 nDz = rDelRange.aEnd.Tab() - rDelRange.aStart.Tab() + 1;
2113 else if ( nDz < 0 )
2114 nDz = -(rDelRange.aEnd.Tab() - rDelRange.aStart.Tab() + 1);
2116 ScBigRange aTmpRange( rRange );
2117 switch ( eMode )
2119 case URM_INSDEL :
2120 if ( nDx < 0 || nDy < 0 || nDz < 0 )
2121 { // Delete startet dort hinter geloeschtem Bereich,
2122 // Position wird dort angepasst.
2123 if ( nDx )
2124 aTmpRange.aStart.IncCol( -nDx );
2125 if ( nDy )
2126 aTmpRange.aStart.IncRow( -nDy );
2127 if ( nDz )
2128 aTmpRange.aStart.IncTab( -nDz );
2130 break;
2131 case URM_MOVE :
2132 // Move ist hier Quelle, dort Ziel,
2133 // Position muss vorher angepasst sein.
2134 if ( bOldFormula )
2135 ((ScFormulaCell*)pOldCell)->aPos = aBigRange.aStart.MakeAddress();
2136 if ( bNewFormula )
2137 ((ScFormulaCell*)pNewCell)->aPos = aBigRange.aStart.MakeAddress();
2138 if ( nDx )
2140 aTmpRange.aStart.IncCol( nDx );
2141 aTmpRange.aEnd.IncCol( nDx );
2143 if ( nDy )
2145 aTmpRange.aStart.IncRow( nDy );
2146 aTmpRange.aEnd.IncRow( nDy );
2148 if ( nDz )
2150 aTmpRange.aStart.IncTab( nDz );
2151 aTmpRange.aEnd.IncTab( nDz );
2153 break;
2154 default:
2156 // added to avoid warnings
2159 ScRange aRange( aTmpRange.MakeRange() );
2160 if ( bOldFormula )
2161 ((ScFormulaCell*)pOldCell)->UpdateReference( eMode, aRange,
2162 (SCsCOL) nDx, (SCsROW) nDy, (SCsTAB) nDz, NULL );
2163 if ( bNewFormula )
2164 ((ScFormulaCell*)pNewCell)->UpdateReference( eMode, aRange,
2165 (SCsCOL) nDx, (SCsROW) nDy, (SCsTAB) nDz, NULL );
2166 if ( !aBigRange.aStart.IsValid( pTrack->GetDocument() ) )
2167 { //! HACK!
2168 //! UpdateReference kann nicht mit Positionen ausserhalb des
2169 //! Dokuments umgehen, deswegen alles auf #REF! setzen
2170 //2do: make it possible! das bedeutet grossen Umbau von ScAddress etc.!
2171 const ScBigAddress& rPos = aBigRange.aStart;
2172 if ( bOldFormula )
2174 ScToken* t;
2175 ScTokenArray* pArr = ((ScFormulaCell*)pOldCell)->GetCode();
2176 pArr->Reset();
2177 while ( ( t = static_cast<ScToken*>(pArr->GetNextReference()) ) != NULL )
2178 lcl_InvalidateReference( *t, rPos );
2179 pArr->Reset();
2180 while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
2181 lcl_InvalidateReference( *t, rPos );
2183 if ( bNewFormula )
2185 ScToken* t;
2186 ScTokenArray* pArr = ((ScFormulaCell*)pNewCell)->GetCode();
2187 pArr->Reset();
2188 while ( ( t = static_cast<ScToken*>(pArr->GetNextReference()) ) != NULL )
2189 lcl_InvalidateReference( *t, rPos );
2190 pArr->Reset();
2191 while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
2192 lcl_InvalidateReference( *t, rPos );
2199 // --- ScChangeActionReject ------------------------------------------------
2201 ScChangeActionReject::ScChangeActionReject(const ULONG nActionNumber, const ScChangeActionState eStateP, const ULONG nRejectingNumber,
2202 const ScBigRange& aBigRangeP, const String& aUserP, const DateTime& aDateTimeP, const String& sComment)
2204 ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment)
2209 // --- ScChangeTrack -------------------------------------------------------
2211 IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeTrackMsgInfo, 16, 16 )
2213 const SCROW ScChangeTrack::nContentRowsPerSlot = InitContentRowsPerSlot();
2214 const SCSIZE ScChangeTrack::nContentSlots =
2215 (MAXROWCOUNT) / InitContentRowsPerSlot() + 2;
2217 // static
2218 SCROW ScChangeTrack::InitContentRowsPerSlot()
2220 const SCSIZE nMaxSlots = 0xffe0 / sizeof( ScChangeActionContent* ) - 2;
2221 SCROW nRowsPerSlot = (MAXROWCOUNT) / nMaxSlots;
2222 if ( nRowsPerSlot * nMaxSlots < sal::static_int_cast<SCSIZE>(MAXROWCOUNT) )
2223 ++nRowsPerSlot;
2224 return nRowsPerSlot;
2228 ScChangeTrack::ScChangeTrack( ScDocument* pDocP ) :
2229 pDoc( pDocP )
2231 Init();
2232 StartListening(SC_MOD()->GetUserOptions());
2234 ppContentSlots = new ScChangeActionContent* [ nContentSlots ];
2235 memset( ppContentSlots, 0, nContentSlots * sizeof( ScChangeActionContent* ) );
2238 ScChangeTrack::ScChangeTrack( ScDocument* pDocP, const ScStrCollection& aTempUserCollection) :
2239 aUserCollection(aTempUserCollection),
2240 pDoc( pDocP )
2242 Init();
2243 StartListening(SC_MOD()->GetUserOptions());
2244 ppContentSlots = new ScChangeActionContent* [ nContentSlots ];
2245 memset( ppContentSlots, 0, nContentSlots * sizeof( ScChangeActionContent* ) );
2248 ScChangeTrack::~ScChangeTrack()
2250 DtorClear();
2251 delete [] ppContentSlots;
2255 void ScChangeTrack::Init()
2257 pFirst = NULL;
2258 pLast = NULL;
2259 pFirstGeneratedDelContent = NULL;
2260 pLastCutMove = NULL;
2261 pLinkInsertCol = NULL;
2262 pLinkInsertRow = NULL;
2263 pLinkInsertTab = NULL;
2264 pLinkMove = NULL;
2265 pBlockModifyMsg = NULL;
2266 nActionMax = 0;
2267 nGeneratedMin = SC_CHGTRACK_GENERATED_START;
2268 nMarkLastSaved = 0;
2269 nStartLastCut = 0;
2270 nEndLastCut = 0;
2271 nLastMerge = 0;
2272 eMergeState = SC_CTMS_NONE;
2273 nLoadedFileFormatVersion = SC_CHGTRACK_FILEFORMAT;
2274 bLoadSave = FALSE;
2275 bInDelete = FALSE;
2276 bInDeleteTop = FALSE;
2277 bInDeleteUndo = FALSE;
2278 bInPasteCut = FALSE;
2279 bUseFixDateTime = FALSE;
2280 bTime100thSeconds = TRUE;
2282 const SvtUserOptions& rUserOpt = SC_MOD()->GetUserOptions();
2283 aUser = rUserOpt.GetFirstName();
2284 aUser += ' ';
2285 aUser += (String)rUserOpt.GetLastName();
2286 aUserCollection.Insert( new StrData( aUser ) );
2290 void ScChangeTrack::DtorClear()
2292 ScChangeAction* p;
2293 ScChangeAction* pNext;
2294 for ( p = GetFirst(); p; p = pNext )
2296 pNext = p->GetNext();
2297 delete p;
2299 for ( p = pFirstGeneratedDelContent; p; p = pNext )
2301 pNext = p->GetNext();
2302 delete p;
2304 for ( p = aPasteCutTable.First(); p; p = aPasteCutTable.Next() )
2306 delete p;
2308 delete pLastCutMove;
2309 ClearMsgQueue();
2313 void ScChangeTrack::ClearMsgQueue()
2315 if ( pBlockModifyMsg )
2317 delete pBlockModifyMsg;
2318 pBlockModifyMsg = NULL;
2320 ScChangeTrackMsgInfo* pMsgInfo;
2321 while ( ( pMsgInfo = aMsgStackTmp.Pop() ) != NULL )
2322 delete pMsgInfo;
2323 while ( ( pMsgInfo = aMsgStackFinal.Pop() ) != NULL )
2324 delete pMsgInfo;
2325 while ( ( pMsgInfo = aMsgQueue.Get() ) != NULL )
2326 delete pMsgInfo;
2330 void ScChangeTrack::Clear()
2332 DtorClear();
2333 aTable.Clear();
2334 aGeneratedTable.Clear();
2335 aPasteCutTable.Clear();
2336 aUserCollection.FreeAll();
2337 aUser.Erase();
2338 Init();
2342 void __EXPORT ScChangeTrack::Notify( SfxBroadcaster&, const SfxHint& rHint )
2344 if ( !pDoc->IsInDtorClear() &&
2345 rHint.ISA(SfxSimpleHint) &&
2346 ((SfxSimpleHint&)rHint).GetId() == SFX_HINT_USER_OPTIONS_CHANGED )
2348 const SvtUserOptions& rUserOptions = SC_MOD()->GetUserOptions();
2349 USHORT nOldCount = aUserCollection.GetCount();
2351 String aStr( rUserOptions.GetFirstName() );
2352 aStr += ' ';
2353 aStr += (String)rUserOptions.GetLastName();
2354 SetUser( aStr );
2356 if ( aUserCollection.GetCount() != nOldCount )
2358 // New user in collection -> have to repaint because
2359 // colors may be different now (#106697#).
2360 // (Has to be done in the Notify handler, to be sure
2361 // the user collection has already been updated)
2363 SfxObjectShell* pDocSh = pDoc->GetDocumentShell();
2364 if (pDocSh)
2365 pDocSh->Broadcast( ScPaintHint( ScRange(0,0,0,MAXCOL,MAXROW,MAXTAB), PAINT_GRID ) );
2371 void ScChangeTrack::SetUser( const String& rUser )
2373 if ( IsLoadSave() )
2374 return ; // nicht die Collection zerschiessen
2376 aUser = rUser;
2377 StrData* pStrData = new StrData( aUser );
2378 if ( !aUserCollection.Insert( pStrData ) )
2379 delete pStrData;
2383 void ScChangeTrack::StartBlockModify( ScChangeTrackMsgType eMsgType,
2384 ULONG nStartAction )
2386 if ( aModifiedLink.IsSet() )
2388 if ( pBlockModifyMsg )
2389 aMsgStackTmp.Push( pBlockModifyMsg ); // Block im Block
2390 pBlockModifyMsg = new ScChangeTrackMsgInfo;
2391 pBlockModifyMsg->eMsgType = eMsgType;
2392 pBlockModifyMsg->nStartAction = nStartAction;
2397 void ScChangeTrack::EndBlockModify( ULONG nEndAction )
2399 if ( aModifiedLink.IsSet() )
2401 if ( pBlockModifyMsg )
2403 if ( pBlockModifyMsg->nStartAction <= nEndAction )
2405 pBlockModifyMsg->nEndAction = nEndAction;
2406 // Blocks in Blocks aufgeloest
2407 aMsgStackFinal.Push( pBlockModifyMsg );
2409 else
2410 delete pBlockModifyMsg;
2411 pBlockModifyMsg = aMsgStackTmp.Pop(); // evtl. Block im Block
2413 if ( !pBlockModifyMsg )
2415 BOOL bNew = FALSE;
2416 ScChangeTrackMsgInfo* pMsg;
2417 while ( ( pMsg = aMsgStackFinal.Pop() ) != NULL )
2419 aMsgQueue.Put( pMsg );
2420 bNew = TRUE;
2422 if ( bNew )
2423 aModifiedLink.Call( this );
2429 void ScChangeTrack::NotifyModified( ScChangeTrackMsgType eMsgType,
2430 ULONG nStartAction, ULONG nEndAction )
2432 if ( aModifiedLink.IsSet() )
2434 if ( !pBlockModifyMsg || pBlockModifyMsg->eMsgType != eMsgType ||
2435 (IsGenerated( nStartAction ) &&
2436 (eMsgType == SC_CTM_APPEND || eMsgType == SC_CTM_REMOVE)) )
2437 { // Append innerhalb von Append z.B. nicht
2438 StartBlockModify( eMsgType, nStartAction );
2439 EndBlockModify( nEndAction );
2445 void ScChangeTrack::MasterLinks( ScChangeAction* pAppend )
2447 ScChangeActionType eType = pAppend->GetType();
2449 if ( eType == SC_CAT_CONTENT )
2451 if ( !IsGenerated( pAppend->GetActionNumber() ) )
2453 SCSIZE nSlot = ComputeContentSlot(
2454 pAppend->GetBigRange().aStart.Row() );
2455 ((ScChangeActionContent*)pAppend)->InsertInSlot(
2456 &ppContentSlots[nSlot] );
2458 return ;
2461 if ( pAppend->IsRejecting() )
2462 return ; // Rejects haben keine Abhaengigkeiten
2464 switch ( eType )
2466 case SC_CAT_INSERT_COLS :
2468 ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
2469 &pLinkInsertCol, pAppend );
2470 pAppend->AddLink( NULL, pLink );
2472 break;
2473 case SC_CAT_INSERT_ROWS :
2475 ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
2476 &pLinkInsertRow, pAppend );
2477 pAppend->AddLink( NULL, pLink );
2479 break;
2480 case SC_CAT_INSERT_TABS :
2482 ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
2483 &pLinkInsertTab, pAppend );
2484 pAppend->AddLink( NULL, pLink );
2486 break;
2487 case SC_CAT_MOVE :
2489 ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
2490 &pLinkMove, pAppend );
2491 pAppend->AddLink( NULL, pLink );
2493 break;
2494 default:
2496 // added to avoid warnings
2502 void ScChangeTrack::AppendLoaded( ScChangeAction* pAppend )
2504 aTable.Insert( pAppend->GetActionNumber(), pAppend );
2505 if ( !pLast )
2506 pFirst = pLast = pAppend;
2507 else
2509 pLast->pNext = pAppend;
2510 pAppend->pPrev = pLast;
2511 pLast = pAppend;
2513 MasterLinks( pAppend );
2517 void ScChangeTrack::Append( ScChangeAction* pAppend, ULONG nAction )
2519 if ( nActionMax < nAction )
2520 nActionMax = nAction;
2521 pAppend->SetUser( aUser );
2522 if ( bUseFixDateTime )
2523 pAppend->SetDateTimeUTC( aFixDateTime );
2524 pAppend->SetActionNumber( nAction );
2525 aTable.Insert( nAction, pAppend );
2526 // UpdateReference Inserts vor Dependencies.
2527 // Delete rejectendes Insert hatte UpdateReference mit Delete-Undo.
2528 // UpdateReference auch wenn pLast==NULL, weil pAppend ein Delete sein
2529 // kann, dass DelContents generiert haben kann
2530 if ( pAppend->IsInsertType() && !pAppend->IsRejecting() )
2531 UpdateReference( pAppend, FALSE );
2532 if ( !pLast )
2533 pFirst = pLast = pAppend;
2534 else
2536 pLast->pNext = pAppend;
2537 pAppend->pPrev = pLast;
2538 pLast = pAppend;
2539 Dependencies( pAppend );
2541 // UpdateReference Inserts nicht nach Dependencies.
2542 // Move rejectendes Move hatte UpdateReference mit Move-Undo, Inhalt in
2543 // ToRange nicht deleten.
2544 if ( !pAppend->IsInsertType() &&
2545 !(pAppend->GetType() == SC_CAT_MOVE && pAppend->IsRejecting()) )
2546 UpdateReference( pAppend, FALSE );
2547 MasterLinks( pAppend );
2549 if ( aModifiedLink.IsSet() )
2551 NotifyModified( SC_CTM_APPEND, nAction, nAction );
2552 if ( pAppend->GetType() == SC_CAT_CONTENT )
2554 ScChangeActionContent* pContent = (ScChangeActionContent*) pAppend;
2555 if ( ( pContent = pContent->GetPrevContent() ) != NULL )
2557 ULONG nMod = pContent->GetActionNumber();
2558 NotifyModified( SC_CTM_CHANGE, nMod, nMod );
2561 else
2562 NotifyModified( SC_CTM_CHANGE, pFirst->GetActionNumber(),
2563 pLast->GetActionNumber() );
2568 void ScChangeTrack::Append( ScChangeAction* pAppend )
2570 Append( pAppend, ++nActionMax );
2574 void ScChangeTrack::AppendDeleteRange( const ScRange& rRange,
2575 ScDocument* pRefDoc, ULONG& nStartAction, ULONG& nEndAction, SCsTAB nDz )
2577 nStartAction = GetActionMax() + 1;
2578 AppendDeleteRange( rRange, pRefDoc, nDz, 0 );
2579 nEndAction = GetActionMax();
2583 void ScChangeTrack::AppendDeleteRange( const ScRange& rRange,
2584 ScDocument* pRefDoc, SCsTAB nDz, ULONG nRejectingInsert )
2586 SetInDeleteRange( rRange );
2587 StartBlockModify( SC_CTM_APPEND, GetActionMax() + 1 );
2588 SCCOL nCol1;
2589 SCROW nRow1;
2590 SCTAB nTab1;
2591 SCCOL nCol2;
2592 SCROW nRow2;
2593 SCTAB nTab2;
2594 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
2595 for ( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ )
2597 if ( !pRefDoc || nTab < pRefDoc->GetTableCount() )
2599 if ( nCol1 == 0 && nCol2 == MAXCOL )
2600 { // ganze Zeilen und/oder Tabellen
2601 if ( nRow1 == 0 && nRow2 == MAXROW )
2602 { // ganze Tabellen
2603 //2do: geht nicht auch komplette Tabelle als ganzes?
2604 ScRange aRange( 0, 0, nTab, 0, MAXROW, nTab );
2605 for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
2606 { // spaltenweise ist weniger als zeilenweise
2607 aRange.aStart.SetCol( nCol );
2608 aRange.aEnd.SetCol( nCol );
2609 if ( nCol == nCol2 )
2610 SetInDeleteTop( TRUE );
2611 AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0,
2612 nTab-nTab1 + nDz, nRejectingInsert );
2614 //! immer noch InDeleteTop
2615 AppendOneDeleteRange( rRange, pRefDoc, 0, 0,
2616 nTab-nTab1 + nDz, nRejectingInsert );
2618 else
2619 { // ganze Zeilen
2620 ScRange aRange( 0, 0, nTab, MAXCOL, 0, nTab );
2621 for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
2623 aRange.aStart.SetRow( nRow );
2624 aRange.aEnd.SetRow( nRow );
2625 if ( nRow == nRow2 )
2626 SetInDeleteTop( TRUE );
2627 AppendOneDeleteRange( aRange, pRefDoc, 0, nRow-nRow1,
2628 0, nRejectingInsert );
2632 else if ( nRow1 == 0 && nRow2 == MAXROW )
2633 { // ganze Spalten
2634 ScRange aRange( 0, 0, nTab, 0, MAXROW, nTab );
2635 for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
2637 aRange.aStart.SetCol( nCol );
2638 aRange.aEnd.SetCol( nCol );
2639 if ( nCol == nCol2 )
2640 SetInDeleteTop( TRUE );
2641 AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0,
2642 0, nRejectingInsert );
2645 else
2647 DBG_ERROR( "ScChangeTrack::AppendDeleteRange: Block not supported!" );
2649 SetInDeleteTop( FALSE );
2652 EndBlockModify( GetActionMax() );
2656 void ScChangeTrack::AppendOneDeleteRange( const ScRange& rOrgRange,
2657 ScDocument* pRefDoc, SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
2658 ULONG nRejectingInsert )
2660 ScRange aTrackRange( rOrgRange );
2661 if ( nDx )
2663 aTrackRange.aStart.IncCol( -nDx );
2664 aTrackRange.aEnd.IncCol( -nDx );
2666 if ( nDy )
2668 aTrackRange.aStart.IncRow( -nDy );
2669 aTrackRange.aEnd.IncRow( -nDy );
2671 if ( nDz )
2673 aTrackRange.aStart.IncTab( -nDz );
2674 aTrackRange.aEnd.IncTab( -nDz );
2676 ScChangeActionDel* pAct = new ScChangeActionDel( aTrackRange, nDx, nDy,
2677 this );
2678 // TabDelete keine Contents, sind in einzelnen Spalten
2679 if ( !(rOrgRange.aStart.Col() == 0 && rOrgRange.aStart.Row() == 0 &&
2680 rOrgRange.aEnd.Col() == MAXCOL && rOrgRange.aEnd.Row() == MAXROW) )
2681 LookUpContents( rOrgRange, pRefDoc, -nDx, -nDy, -nDz );
2682 if ( nRejectingInsert )
2684 pAct->SetRejectAction( nRejectingInsert );
2685 pAct->SetState( SC_CAS_ACCEPTED );
2687 Append( pAct );
2691 void ScChangeTrack::LookUpContents( const ScRange& rOrgRange,
2692 ScDocument* pRefDoc, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
2694 if ( pRefDoc )
2696 ScAddress aPos;
2697 ScBigAddress aBigPos;
2698 ScCellIterator aIter( pRefDoc, rOrgRange );
2699 ScBaseCell* pCell = aIter.GetFirst();
2700 while ( pCell )
2702 if ( ScChangeActionContent::GetContentCellType( pCell ) )
2704 aBigPos.Set( aIter.GetCol() + nDx, aIter.GetRow() + nDy,
2705 aIter.GetTab() + nDz );
2706 ScChangeActionContent* pContent = SearchContentAt( aBigPos, NULL );
2707 if ( !pContent )
2708 { // nicht getrackte Contents
2709 aPos.Set( aIter.GetCol() + nDx, aIter.GetRow() + nDy,
2710 aIter.GetTab() + nDz );
2711 GenerateDelContent( aPos, pCell, pRefDoc );
2712 //! der Content wird hier _nicht_ per AddContent hinzugefuegt,
2713 //! sondern in UpdateReference, um z.B. auch kreuzende Deletes
2714 //! korrekt zu erfassen
2717 pCell = aIter.GetNext();
2723 void ScChangeTrack::AppendMove( const ScRange& rFromRange,
2724 const ScRange& rToRange, ScDocument* pRefDoc )
2726 ScChangeActionMove* pAct = new ScChangeActionMove( rFromRange, rToRange, this );
2727 LookUpContents( rToRange, pRefDoc, 0, 0, 0 ); // ueberschriebene Contents
2728 Append( pAct );
2732 // static
2733 BOOL ScChangeTrack::IsMatrixFormulaRangeDifferent( const ScBaseCell* pOldCell,
2734 const ScBaseCell* pNewCell )
2736 SCCOL nC1, nC2;
2737 SCROW nR1, nR2;
2738 nC1 = nC2 = 0;
2739 nR1 = nR2 = 0;
2740 if ( pOldCell && (pOldCell->GetCellType() == CELLTYPE_FORMULA) &&
2741 ((const ScFormulaCell*)pOldCell)->GetMatrixFlag() == MM_FORMULA )
2742 ((const ScFormulaCell*)pOldCell)->GetMatColsRows( nC1, nR1 );
2743 if ( pNewCell && (pNewCell->GetCellType() == CELLTYPE_FORMULA) &&
2744 ((const ScFormulaCell*)pNewCell)->GetMatrixFlag() == MM_FORMULA )
2745 ((const ScFormulaCell*)pNewCell)->GetMatColsRows( nC1, nR1 );
2746 return nC1 != nC2 || nR1 != nR2;
2750 void ScChangeTrack::AppendContent( const ScAddress& rPos,
2751 const String& rNewValue, ScBaseCell* pOldCell )
2753 String aOldValue;
2754 ScChangeActionContent::GetStringOfCell( aOldValue, pOldCell, pDoc, rPos );
2755 if ( aOldValue != rNewValue ||
2756 IsMatrixFormulaRangeDifferent( pOldCell, NULL ) )
2757 { // nur wirkliche Aenderung tracken
2758 ScRange aRange( rPos );
2759 ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
2760 pAct->SetOldValue( pOldCell, pDoc, pDoc );
2761 pAct->SetNewValue( rNewValue, pDoc );
2762 Append( pAct );
2767 void ScChangeTrack::AppendContent( const ScAddress& rPos,
2768 const ScBaseCell* pOldCell, ULONG nOldFormat, ScDocument* pRefDoc )
2770 if ( !pRefDoc )
2771 pRefDoc = pDoc;
2772 String aOldValue;
2773 ScChangeActionContent::GetStringOfCell( aOldValue, pOldCell, pRefDoc, nOldFormat );
2774 String aNewValue;
2775 ScBaseCell* pNewCell = pDoc->GetCell( rPos );
2776 ScChangeActionContent::GetStringOfCell( aNewValue, pNewCell, pDoc, rPos );
2777 if ( aOldValue != aNewValue ||
2778 IsMatrixFormulaRangeDifferent( pOldCell, pNewCell ) )
2779 { // nur wirkliche Aenderung tracken
2780 ScRange aRange( rPos );
2781 ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
2782 pAct->SetOldValue( pOldCell, pRefDoc, pDoc, nOldFormat );
2783 pAct->SetNewValue( pNewCell, pDoc );
2784 Append( pAct );
2789 void ScChangeTrack::AppendContent( const ScAddress& rPos,
2790 ScDocument* pRefDoc )
2792 String aOldValue;
2793 ScBaseCell* pOldCell = pRefDoc->GetCell( rPos );
2794 ScChangeActionContent::GetStringOfCell( aOldValue, pOldCell, pRefDoc, rPos );
2795 String aNewValue;
2796 ScBaseCell* pNewCell = pDoc->GetCell( rPos );
2797 ScChangeActionContent::GetStringOfCell( aNewValue, pNewCell, pDoc, rPos );
2798 if ( aOldValue != aNewValue ||
2799 IsMatrixFormulaRangeDifferent( pOldCell, pNewCell ) )
2800 { // nur wirkliche Aenderung tracken
2801 ScRange aRange( rPos );
2802 ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
2803 pAct->SetOldValue( pOldCell, pRefDoc, pDoc );
2804 pAct->SetNewValue( pNewCell, pDoc );
2805 Append( pAct );
2810 void ScChangeTrack::AppendContent( const ScAddress& rPos,
2811 const ScBaseCell* pOldCell )
2813 if ( ScChangeActionContent::NeedsNumberFormat( pOldCell ) )
2814 AppendContent( rPos, pOldCell, pDoc->GetNumberFormat( rPos ), pDoc );
2815 else
2816 AppendContent( rPos, pOldCell, 0, pDoc );
2820 void ScChangeTrack::SetLastCutMoveRange( const ScRange& rRange,
2821 ScDocument* pRefDoc )
2823 if ( pLastCutMove )
2825 // ToRange nicht mit Deletes linken und nicht in der Groesse aendern,
2826 // eigentlich unnoetig, da ein Delete vorher in
2827 // ScViewFunc::PasteFromClip ein ResetLastCut ausloest
2828 ScBigRange& r = pLastCutMove->GetBigRange();
2829 r.aEnd.SetCol( -1 );
2830 r.aEnd.SetRow( -1 );
2831 r.aEnd.SetTab( -1 );
2832 r.aStart.SetCol( -1 - (rRange.aEnd.Col() - rRange.aStart.Col()) );
2833 r.aStart.SetRow( -1 - (rRange.aEnd.Row() - rRange.aStart.Row()) );
2834 r.aStart.SetTab( -1 - (rRange.aEnd.Tab() - rRange.aStart.Tab()) );
2835 // zu ueberschreibende Contents im FromRange
2836 LookUpContents( rRange, pRefDoc, 0, 0, 0 );
2841 void ScChangeTrack::AppendContentRange( const ScRange& rRange,
2842 ScDocument* pRefDoc, ULONG& nStartAction, ULONG& nEndAction,
2843 ScChangeActionClipMode eClipMode )
2845 if ( eClipMode == SC_CACM_CUT )
2847 ResetLastCut();
2848 pLastCutMove = new ScChangeActionMove( rRange, rRange, this );
2849 SetLastCutMoveRange( rRange, pRefDoc );
2851 SCCOL nCol1;
2852 SCROW nRow1;
2853 SCTAB nTab1;
2854 SCCOL nCol2;
2855 SCROW nRow2;
2856 SCTAB nTab2;
2857 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
2858 BOOL bDoContents;
2859 if ( eClipMode == SC_CACM_PASTE && HasLastCut() )
2861 bDoContents = FALSE;
2862 SetInPasteCut( TRUE );
2863 // Paste und Cut abstimmen, Paste kann groesserer Range sein
2864 ScRange aRange( rRange );
2865 ScBigRange& r = pLastCutMove->GetBigRange();
2866 SCCOL nTmpCol;
2867 if ( (nTmpCol = (SCCOL) (r.aEnd.Col() - r.aStart.Col())) != (nCol2 - nCol1) )
2869 aRange.aEnd.SetCol( aRange.aStart.Col() + nTmpCol );
2870 nCol1 += nTmpCol + 1;
2871 bDoContents = TRUE;
2873 SCROW nTmpRow;
2874 if ( (nTmpRow = (SCROW) (r.aEnd.Row() - r.aStart.Row())) != (nRow2 - nRow1) )
2876 aRange.aEnd.SetRow( aRange.aStart.Row() + nTmpRow );
2877 nRow1 += nTmpRow + 1;
2878 bDoContents = TRUE;
2880 SCTAB nTmpTab;
2881 if ( (nTmpTab = (SCTAB) (r.aEnd.Tab() - r.aStart.Tab())) != (nTab2 - nTab1) )
2883 aRange.aEnd.SetTab( aRange.aStart.Tab() + nTmpTab );
2884 nTab1 += nTmpTab + 1;
2885 bDoContents = TRUE;
2887 r = aRange;
2888 Undo( nStartLastCut, nEndLastCut ); // hier werden sich die Cuts gemerkt
2889 //! StartAction erst nach Undo
2890 nStartAction = GetActionMax() + 1;
2891 StartBlockModify( SC_CTM_APPEND, nStartAction );
2892 // zu ueberschreibende Contents im ToRange
2893 LookUpContents( aRange, pRefDoc, 0, 0, 0 );
2894 pLastCutMove->SetStartLastCut( nStartLastCut );
2895 pLastCutMove->SetEndLastCut( nEndLastCut );
2896 Append( pLastCutMove );
2897 pLastCutMove = NULL;
2898 ResetLastCut();
2899 SetInPasteCut( FALSE );
2901 else
2903 bDoContents = TRUE;
2904 nStartAction = GetActionMax() + 1;
2905 StartBlockModify( SC_CTM_APPEND, nStartAction );
2907 if ( bDoContents )
2909 ScAddress aPos;
2910 for ( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ )
2912 aPos.SetTab( nTab );
2913 for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
2915 aPos.SetCol( nCol );
2916 for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
2918 aPos.SetRow( nRow );
2919 AppendContent( aPos, pRefDoc );
2924 nEndAction = GetActionMax();
2925 EndBlockModify( nEndAction );
2926 if ( eClipMode == SC_CACM_CUT )
2928 nStartLastCut = nStartAction;
2929 nEndLastCut = nEndAction;
2934 void ScChangeTrack::AppendContentsIfInRefDoc( ScDocument* pRefDoc,
2935 ULONG& nStartAction, ULONG& nEndAction )
2937 ScDocumentIterator aIter( pRefDoc, 0, MAXTAB );
2938 if ( aIter.GetFirst() )
2940 nStartAction = GetActionMax() + 1;
2941 StartBlockModify( SC_CTM_APPEND, nStartAction );
2942 SvNumberFormatter* pFormatter = pRefDoc->GetFormatTable();
2945 SCCOL nCol;
2946 SCROW nRow;
2947 SCTAB nTab;
2948 aIter.GetPos( nCol, nRow, nTab );
2949 ScAddress aPos( nCol, nRow, nTab );
2950 AppendContent( aPos, aIter.GetCell(),
2951 aIter.GetPattern()->GetNumberFormat( pFormatter ), pRefDoc );
2952 } while ( aIter.GetNext() );
2953 nEndAction = GetActionMax();
2954 EndBlockModify( nEndAction );
2956 else
2957 nStartAction = nEndAction = 0;
2961 ScChangeActionContent* ScChangeTrack::AppendContentOnTheFly(
2962 const ScAddress& rPos, ScBaseCell* pOldCell, ScBaseCell* pNewCell,
2963 ULONG nOldFormat, ULONG nNewFormat )
2965 ScRange aRange( rPos );
2966 ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
2967 pAct->SetOldNewCells( pOldCell, nOldFormat, pNewCell, nNewFormat, pDoc );
2968 Append( pAct );
2969 return pAct;
2973 void ScChangeTrack::AppendInsert( const ScRange& rRange )
2975 ScChangeActionIns* pAct = new ScChangeActionIns( rRange );
2976 Append( pAct );
2980 void ScChangeTrack::DeleteCellEntries( ScChangeActionCellListEntry*& pCellList,
2981 ScChangeAction* pDeletor )
2983 ScChangeActionCellListEntry* pE = pCellList;
2984 while ( pE )
2986 ScChangeActionCellListEntry* pNext = pE->pNext;
2987 pE->pContent->RemoveDeletedIn( pDeletor );
2988 if ( IsGenerated( pE->pContent->GetActionNumber() ) &&
2989 !pE->pContent->IsDeletedIn() )
2990 DeleteGeneratedDelContent( pE->pContent );
2991 delete pE;
2992 pE = pNext;
2994 pCellList = NULL;
2998 ScChangeActionContent* ScChangeTrack::GenerateDelContent(
2999 const ScAddress& rPos, const ScBaseCell* pCell,
3000 const ScDocument* pFromDoc )
3002 ScChangeActionContent* pContent = new ScChangeActionContent(
3003 ScRange( rPos ) );
3004 pContent->SetActionNumber( --nGeneratedMin );
3005 // nur NewValue
3006 ScChangeActionContent::SetValue( pContent->aNewValue, pContent->pNewCell,
3007 rPos, pCell, pFromDoc, pDoc );
3008 // pNextContent und pPrevContent werden nicht gesetzt
3009 if ( pFirstGeneratedDelContent )
3010 { // vorne reinhaengen
3011 pFirstGeneratedDelContent->pPrev = pContent;
3012 pContent->pNext = pFirstGeneratedDelContent;
3014 pFirstGeneratedDelContent = pContent;
3015 aGeneratedTable.Insert( nGeneratedMin, pContent );
3016 NotifyModified( SC_CTM_APPEND, nGeneratedMin, nGeneratedMin );
3017 return pContent;
3021 void ScChangeTrack::DeleteGeneratedDelContent( ScChangeActionContent* pContent )
3023 ULONG nAct = pContent->GetActionNumber();
3024 aGeneratedTable.Remove( nAct );
3025 if ( pFirstGeneratedDelContent == pContent )
3026 pFirstGeneratedDelContent = (ScChangeActionContent*) pContent->pNext;
3027 if ( pContent->pNext )
3028 pContent->pNext->pPrev = pContent->pPrev;
3029 if ( pContent->pPrev )
3030 pContent->pPrev->pNext = pContent->pNext;
3031 delete pContent;
3032 NotifyModified( SC_CTM_REMOVE, nAct, nAct );
3033 if ( nAct == nGeneratedMin )
3034 ++nGeneratedMin; //! erst nach NotifyModified wg. IsGenerated
3038 ScChangeActionContent* ScChangeTrack::SearchContentAt(
3039 const ScBigAddress& rPos, ScChangeAction* pButNotThis ) const
3041 SCSIZE nSlot = ComputeContentSlot( rPos.Row() );
3042 for ( ScChangeActionContent* p = ppContentSlots[nSlot]; p;
3043 p = p->GetNextInSlot() )
3045 if ( p != pButNotThis && !p->IsDeletedIn() &&
3046 p->GetBigRange().aStart == rPos )
3048 ScChangeActionContent* pContent = p->GetTopContent();
3049 if ( !pContent->IsDeletedIn() )
3050 return pContent;
3053 return NULL;
3057 void ScChangeTrack::AddDependentWithNotify( ScChangeAction* pParent,
3058 ScChangeAction* pDependent )
3060 ScChangeActionLinkEntry* pLink = pParent->AddDependent( pDependent );
3061 pDependent->AddLink( pParent, pLink );
3062 if ( aModifiedLink.IsSet() )
3064 ULONG nMod = pParent->GetActionNumber();
3065 NotifyModified( SC_CTM_PARENT, nMod, nMod );
3070 void ScChangeTrack::Dependencies( ScChangeAction* pAct )
3072 // Finde die letzte Abhaengigkeit fuer jeweils Col/Row/Tab.
3073 // Content an gleicher Position verketten.
3074 // Move Abhaengigkeiten.
3075 ScChangeActionType eActType = pAct->GetType();
3076 if ( eActType == SC_CAT_REJECT ||
3077 (eActType == SC_CAT_MOVE && pAct->IsRejecting()) )
3078 return ; // diese Rejects sind nicht abhaengig
3080 if ( eActType == SC_CAT_CONTENT )
3082 if ( !(((ScChangeActionContent*)pAct)->GetNextContent() ||
3083 ((ScChangeActionContent*)pAct)->GetPrevContent()) )
3084 { // Contents an gleicher Position verketten
3085 ScChangeActionContent* pContent = SearchContentAt(
3086 pAct->GetBigRange().aStart, pAct );
3087 if ( pContent )
3089 pContent->SetNextContent( (ScChangeActionContent*) pAct );
3090 ((ScChangeActionContent*)pAct)->SetPrevContent( pContent );
3093 const ScBaseCell* pCell = ((ScChangeActionContent*)pAct)->GetNewCell();
3094 if ( ScChangeActionContent::GetContentCellType( pCell ) == SC_CACCT_MATREF )
3096 ScAddress aOrg;
3097 ((const ScFormulaCell*)pCell)->GetMatrixOrigin( aOrg );
3098 ScChangeActionContent* pContent = SearchContentAt( aOrg, pAct );
3099 if ( pContent && pContent->IsMatrixOrigin() )
3101 AddDependentWithNotify( pContent, pAct );
3103 else
3105 DBG_ERRORFILE( "ScChangeTrack::Dependencies: MatOrg not found" );
3110 if ( !(pLinkInsertCol || pLinkInsertRow || pLinkInsertTab || pLinkMove) )
3111 return ; // keine Dependencies
3112 if ( pAct->IsRejecting() )
3113 return ; // ausser Content keine Dependencies
3115 // Insert in einem entsprechenden Insert haengt davon ab, sonst muesste
3116 // der vorherige Insert gesplittet werden.
3117 // Sich kreuzende Inserts und Deletes sind nicht abhaengig.
3118 // Alles andere ist abhaengig.
3120 // Der zuletzt eingelinkte Insert steht am Anfang einer Kette,
3121 // also genau richtig
3123 const ScBigRange& rRange = pAct->GetBigRange();
3124 BOOL bActNoInsert = !pAct->IsInsertType();
3125 BOOL bActColDel = ( eActType == SC_CAT_DELETE_COLS );
3126 BOOL bActRowDel = ( eActType == SC_CAT_DELETE_ROWS );
3127 BOOL bActTabDel = ( eActType == SC_CAT_DELETE_TABS );
3129 if ( pLinkInsertCol && (eActType == SC_CAT_INSERT_COLS ||
3130 (bActNoInsert && !bActRowDel && !bActTabDel)) )
3132 for ( ScChangeActionLinkEntry* pL = pLinkInsertCol; pL; pL = pL->GetNext() )
3134 ScChangeActionIns* pTest = (ScChangeActionIns*) pL->GetAction();
3135 if ( !pTest->IsRejected() &&
3136 pTest->GetBigRange().Intersects( rRange ) )
3138 AddDependentWithNotify( pTest, pAct );
3139 break; // for
3143 if ( pLinkInsertRow && (eActType == SC_CAT_INSERT_ROWS ||
3144 (bActNoInsert && !bActColDel && !bActTabDel)) )
3146 for ( ScChangeActionLinkEntry* pL = pLinkInsertRow; pL; pL = pL->GetNext() )
3148 ScChangeActionIns* pTest = (ScChangeActionIns*) pL->GetAction();
3149 if ( !pTest->IsRejected() &&
3150 pTest->GetBigRange().Intersects( rRange ) )
3152 AddDependentWithNotify( pTest, pAct );
3153 break; // for
3157 if ( pLinkInsertTab && (eActType == SC_CAT_INSERT_TABS ||
3158 (bActNoInsert && !bActColDel && !bActRowDel)) )
3160 for ( ScChangeActionLinkEntry* pL = pLinkInsertTab; pL; pL = pL->GetNext() )
3162 ScChangeActionIns* pTest = (ScChangeActionIns*) pL->GetAction();
3163 if ( !pTest->IsRejected() &&
3164 pTest->GetBigRange().Intersects( rRange ) )
3166 AddDependentWithNotify( pTest, pAct );
3167 break; // for
3172 if ( pLinkMove )
3174 if ( eActType == SC_CAT_CONTENT )
3175 { // Content ist von FromRange abhaengig
3176 const ScBigAddress& rPos = rRange.aStart;
3177 for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
3179 ScChangeActionMove* pTest = (ScChangeActionMove*) pL->GetAction();
3180 if ( !pTest->IsRejected() &&
3181 pTest->GetFromRange().In( rPos ) )
3183 AddDependentWithNotify( pTest, pAct );
3187 else if ( eActType == SC_CAT_MOVE )
3188 { // Move FromRange ist von ToRange abhaengig
3189 const ScBigRange& rFromRange = ((ScChangeActionMove*)pAct)->GetFromRange();
3190 for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
3192 ScChangeActionMove* pTest = (ScChangeActionMove*) pL->GetAction();
3193 if ( !pTest->IsRejected() &&
3194 pTest->GetBigRange().Intersects( rFromRange ) )
3196 AddDependentWithNotify( pTest, pAct );
3200 else
3201 { // Inserts und Deletes sind abhaengig, sobald sie FromRange oder
3202 // ToRange kreuzen
3203 for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
3205 ScChangeActionMove* pTest = (ScChangeActionMove*) pL->GetAction();
3206 if ( !pTest->IsRejected() &&
3207 (pTest->GetFromRange().Intersects( rRange ) ||
3208 pTest->GetBigRange().Intersects( rRange )) )
3210 AddDependentWithNotify( pTest, pAct );
3218 void ScChangeTrack::Remove( ScChangeAction* pRemove )
3220 // aus Track ausklinken
3221 ULONG nAct = pRemove->GetActionNumber();
3222 aTable.Remove( nAct );
3223 if ( nAct == nActionMax )
3224 --nActionMax;
3225 if ( pRemove == pLast )
3226 pLast = pRemove->pPrev;
3227 if ( pRemove == pFirst )
3228 pFirst = pRemove->pNext;
3229 if ( nAct == nMarkLastSaved )
3230 nMarkLastSaved =
3231 ( pRemove->pPrev ? pRemove->pPrev->GetActionNumber() : 0 );
3233 // aus der globalen Kette ausklinken
3234 if ( pRemove->pNext )
3235 pRemove->pNext->pPrev = pRemove->pPrev;
3236 if ( pRemove->pPrev )
3237 pRemove->pPrev->pNext = pRemove->pNext;
3239 // Dependencies nicht loeschen, passiert on delete automatisch durch
3240 // LinkEntry, ohne Listen abzuklappern
3242 if ( aModifiedLink.IsSet() )
3244 NotifyModified( SC_CTM_REMOVE, nAct, nAct );
3245 if ( pRemove->GetType() == SC_CAT_CONTENT )
3247 ScChangeActionContent* pContent = (ScChangeActionContent*) pRemove;
3248 if ( ( pContent = pContent->GetPrevContent() ) != NULL )
3250 ULONG nMod = pContent->GetActionNumber();
3251 NotifyModified( SC_CTM_CHANGE, nMod, nMod );
3254 else if ( pLast )
3255 NotifyModified( SC_CTM_CHANGE, pFirst->GetActionNumber(),
3256 pLast->GetActionNumber() );
3259 if ( IsInPasteCut() && pRemove->GetType() == SC_CAT_CONTENT )
3260 { //! Content wird wiederverwertet
3261 ScChangeActionContent* pContent = (ScChangeActionContent*) pRemove;
3262 pContent->RemoveAllLinks();
3263 pContent->ClearTrack();
3264 pContent->pNext = pContent->pPrev = NULL;
3265 pContent->pNextContent = pContent->pPrevContent = NULL;
3270 void ScChangeTrack::Undo( ULONG nStartAction, ULONG nEndAction, bool bMerge )
3272 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3273 if ( bMerge )
3275 SetMergeState( SC_CTMS_UNDO );
3278 if ( nStartAction == 0 )
3279 ++nStartAction;
3280 if ( nEndAction > nActionMax )
3281 nEndAction = nActionMax;
3282 if ( nEndAction && nStartAction <= nEndAction )
3284 if ( nStartAction == nStartLastCut && nEndAction == nEndLastCut &&
3285 !IsInPasteCut() )
3286 ResetLastCut();
3287 StartBlockModify( SC_CTM_REMOVE, nStartAction );
3288 for ( ULONG j = nEndAction; j >= nStartAction; --j )
3289 { // rueckwaerts um evtl. nActionMax zu recyclen und schnelleren
3290 // Zugriff via pLast, Deletes in richtiger Reihenfolge
3291 ScChangeAction* pAct = ( (j == nActionMax && pLast &&
3292 pLast->GetActionNumber() == j) ? pLast : GetAction( j ) );
3293 if ( pAct )
3295 if ( pAct->IsDeleteType() )
3297 if ( j == nEndAction || (pAct != pLast &&
3298 ((ScChangeActionDel*)pAct)->IsTopDelete()) )
3300 SetInDeleteTop( TRUE );
3301 SetInDeleteRange( ((ScChangeActionDel*)pAct)->
3302 GetOverAllRange().MakeRange() );
3305 UpdateReference( pAct, TRUE );
3306 SetInDeleteTop( FALSE );
3307 Remove( pAct );
3308 if ( IsInPasteCut() )
3309 aPasteCutTable.Insert( pAct->GetActionNumber(), pAct );
3310 else
3312 if ( j == nStartAction && pAct->GetType() == SC_CAT_MOVE )
3314 ScChangeActionMove* pMove = (ScChangeActionMove*) pAct;
3315 ULONG nStart = pMove->GetStartLastCut();
3316 ULONG nEnd = pMove->GetEndLastCut();
3317 if ( nStart && nStart <= nEnd )
3318 { // LastCut wiederherstellen
3319 //! Links vor Cut-Append aufloesen
3320 pMove->RemoveAllLinks();
3321 StartBlockModify( SC_CTM_APPEND, nStart );
3322 for ( ULONG nCut = nStart; nCut <= nEnd; nCut++ )
3324 ScChangeAction* pCut = aPasteCutTable.Remove( nCut );
3325 if ( pCut )
3327 DBG_ASSERT( !aTable.Get( nCut ), "ScChangeTrack::Undo: nCut dup" );
3328 Append( pCut, nCut );
3330 else
3332 DBG_ERROR( "ScChangeTrack::Undo: nCut not found" );
3335 EndBlockModify( nEnd );
3336 ResetLastCut();
3337 nStartLastCut = nStart;
3338 nEndLastCut = nEnd;
3339 pLastCutMove = pMove;
3340 SetLastCutMoveRange(
3341 pMove->GetFromRange().MakeRange(), pDoc );
3343 else
3344 delete pMove;
3346 else
3347 delete pAct;
3351 EndBlockModify( nEndAction );
3354 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3355 if ( bMerge )
3357 SetMergeState( SC_CTMS_OTHER );
3362 // static
3363 BOOL ScChangeTrack::MergeIgnore( const ScChangeAction& rAction, ULONG nFirstMerge )
3365 if ( rAction.IsRejected() )
3366 return TRUE; // da kommt noch eine passende Reject-Action
3368 if ( rAction.IsRejecting() && rAction.GetRejectAction() >= nFirstMerge )
3369 return TRUE; // da ist sie
3371 return FALSE; // alles andere
3375 void ScChangeTrack::MergePrepare( ScChangeAction* pFirstMerge, bool bShared )
3377 SetMergeState( SC_CTMS_PREPARE );
3378 ULONG nFirstMerge = pFirstMerge->GetActionNumber();
3379 ScChangeAction* pAct = GetLast();
3380 if ( pAct )
3382 SetLastMerge( pAct->GetActionNumber() );
3383 while ( pAct )
3384 { // rueckwaerts, Deletes in richtiger Reihenfolge
3385 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3386 if ( bShared || !ScChangeTrack::MergeIgnore( *pAct, nFirstMerge ) )
3388 if ( pAct->IsDeleteType() )
3390 if ( ((ScChangeActionDel*)pAct)->IsTopDelete() )
3392 SetInDeleteTop( TRUE );
3393 SetInDeleteRange( ((ScChangeActionDel*)pAct)->
3394 GetOverAllRange().MakeRange() );
3397 UpdateReference( pAct, TRUE );
3398 SetInDeleteTop( FALSE );
3399 pAct->DeleteCellEntries(); // sonst GPF bei Track Clear()
3401 pAct = ( pAct == pFirstMerge ? NULL : pAct->GetPrev() );
3404 SetMergeState( SC_CTMS_OTHER ); //! nachfolgende per default MergeOther
3408 void ScChangeTrack::MergeOwn( ScChangeAction* pAct, ULONG nFirstMerge, bool bShared )
3410 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3411 if ( bShared || !ScChangeTrack::MergeIgnore( *pAct, nFirstMerge ) )
3413 SetMergeState( SC_CTMS_OWN );
3414 if ( pAct->IsDeleteType() )
3416 if ( ((ScChangeActionDel*)pAct)->IsTopDelete() )
3418 SetInDeleteTop( TRUE );
3419 SetInDeleteRange( ((ScChangeActionDel*)pAct)->
3420 GetOverAllRange().MakeRange() );
3423 UpdateReference( pAct, FALSE );
3424 SetInDeleteTop( FALSE );
3425 SetMergeState( SC_CTMS_OTHER ); //! nachfolgende per default MergeOther
3430 void ScChangeTrack::UpdateReference( ScChangeAction* pAct, BOOL bUndo )
3432 ScChangeActionType eActType = pAct->GetType();
3433 if ( eActType == SC_CAT_CONTENT || eActType == SC_CAT_REJECT )
3434 return ;
3436 //! Formelzellen haengen nicht im Dokument
3437 BOOL bOldAutoCalc = pDoc->GetAutoCalc();
3438 pDoc->SetAutoCalc( FALSE );
3439 BOOL bOldNoListening = pDoc->GetNoListening();
3440 pDoc->SetNoListening( TRUE );
3441 //! Formelzellen ExpandRefs synchronisiert zu denen im Dokument
3442 BOOL bOldExpandRefs = pDoc->IsExpandRefs();
3443 if ( (!bUndo && pAct->IsInsertType()) || (bUndo && pAct->IsDeleteType()) )
3444 pDoc->SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() );
3446 if ( pAct->IsDeleteType() )
3448 SetInDeleteUndo( bUndo );
3449 SetInDelete( TRUE );
3451 else if ( GetMergeState() == SC_CTMS_OWN )
3453 // Referenzen von Formelzellen wiederherstellen,
3454 // vorheriges MergePrepare war bei einem Insert wie ein Delete
3455 if ( pAct->IsInsertType() )
3456 SetInDeleteUndo( TRUE );
3459 //! erst die generated, als waeren sie vorher getrackt worden
3460 if ( pFirstGeneratedDelContent )
3461 UpdateReference( (ScChangeAction**)&pFirstGeneratedDelContent, pAct,
3462 bUndo );
3463 UpdateReference( &pFirst, pAct, bUndo );
3465 SetInDelete( FALSE );
3466 SetInDeleteUndo( FALSE );
3468 pDoc->SetExpandRefs( bOldExpandRefs );
3469 pDoc->SetNoListening( bOldNoListening );
3470 pDoc->SetAutoCalc( bOldAutoCalc );
3474 void ScChangeTrack::UpdateReference( ScChangeAction** ppFirstAction,
3475 ScChangeAction* pAct, BOOL bUndo )
3477 ScChangeActionType eActType = pAct->GetType();
3478 BOOL bGeneratedDelContents =
3479 ( ppFirstAction == (ScChangeAction**)&pFirstGeneratedDelContent );
3480 const ScBigRange& rOrgRange = pAct->GetBigRange();
3481 ScBigRange aRange( rOrgRange );
3482 ScBigRange aDelRange( rOrgRange );
3483 INT32 nDx, nDy, nDz;
3484 nDx = nDy = nDz = 0;
3485 UpdateRefMode eMode = URM_INSDEL;
3486 BOOL bDel = FALSE;
3487 switch ( eActType )
3489 case SC_CAT_INSERT_COLS :
3490 aRange.aEnd.SetCol( nInt32Max );
3491 nDx = rOrgRange.aEnd.Col() - rOrgRange.aStart.Col() + 1;
3492 break;
3493 case SC_CAT_INSERT_ROWS :
3494 aRange.aEnd.SetRow( nInt32Max );
3495 nDy = rOrgRange.aEnd.Row() - rOrgRange.aStart.Row() + 1;
3496 break;
3497 case SC_CAT_INSERT_TABS :
3498 aRange.aEnd.SetTab( nInt32Max );
3499 nDz = rOrgRange.aEnd.Tab() - rOrgRange.aStart.Tab() + 1;
3500 break;
3501 case SC_CAT_DELETE_COLS :
3502 aRange.aEnd.SetCol( nInt32Max );
3503 nDx = -(rOrgRange.aEnd.Col() - rOrgRange.aStart.Col() + 1);
3504 aDelRange.aEnd.SetCol( aDelRange.aStart.Col() - nDx - 1 );
3505 bDel = TRUE;
3506 break;
3507 case SC_CAT_DELETE_ROWS :
3508 aRange.aEnd.SetRow( nInt32Max );
3509 nDy = -(rOrgRange.aEnd.Row() - rOrgRange.aStart.Row() + 1);
3510 aDelRange.aEnd.SetRow( aDelRange.aStart.Row() - nDy - 1 );
3511 bDel = TRUE;
3512 break;
3513 case SC_CAT_DELETE_TABS :
3514 aRange.aEnd.SetTab( nInt32Max );
3515 nDz = -(rOrgRange.aEnd.Tab() - rOrgRange.aStart.Tab() + 1);
3516 aDelRange.aEnd.SetTab( aDelRange.aStart.Tab() - nDz - 1 );
3517 bDel = TRUE;
3518 break;
3519 case SC_CAT_MOVE :
3520 eMode = URM_MOVE;
3521 ((ScChangeActionMove*)pAct)->GetDelta( nDx, nDy, nDz );
3522 break;
3523 default:
3524 DBG_ERROR( "ScChangeTrack::UpdateReference: unknown Type" );
3526 if ( bUndo )
3528 nDx = -nDx;
3529 nDy = -nDy;
3530 nDz = -nDz;
3532 if ( bDel )
3533 { //! fuer diesen Mechanismus gilt:
3534 //! es gibt nur ganze, einfache geloeschte Spalten/Zeilen
3535 ScChangeActionDel* pActDel = (ScChangeActionDel*) pAct;
3536 if ( !bUndo )
3537 { // Delete
3538 ScChangeActionType eInsType = SC_CAT_NONE; // for Insert-Undo-"Deletes"
3539 switch ( eActType )
3541 case SC_CAT_DELETE_COLS :
3542 eInsType = SC_CAT_INSERT_COLS;
3543 break;
3544 case SC_CAT_DELETE_ROWS :
3545 eInsType = SC_CAT_INSERT_ROWS;
3546 break;
3547 case SC_CAT_DELETE_TABS :
3548 eInsType = SC_CAT_INSERT_TABS;
3549 break;
3550 default:
3552 // added to avoid warnings
3555 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3557 if ( p == pAct )
3558 continue; // for
3559 BOOL bUpdate = TRUE;
3560 if ( GetMergeState() == SC_CTMS_OTHER &&
3561 p->GetActionNumber() <= GetLastMerge() )
3562 { // Delete in mergendem Dokument, Action im zu mergenden
3563 if ( p->IsInsertType() )
3565 // Bei Insert Referenzen nur anpassen, wenn das Delete
3566 // das Insert nicht schneidet.
3567 if ( !aDelRange.Intersects( p->GetBigRange() ) )
3568 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3569 bUpdate = FALSE;
3571 else if ( p->GetType() == SC_CAT_CONTENT &&
3572 p->IsDeletedInDelType( eInsType ) )
3573 { // Content in Insert-Undo-"Delete"
3574 // Nicht anpassen, wenn dieses Delete in dem
3575 // Insert-"Delete" sein wuerde (ist nur verschoben).
3576 if ( aDelRange.In( p->GetBigRange().aStart ) )
3577 bUpdate = FALSE;
3578 else
3580 const ScChangeActionLinkEntry* pLink = p->GetDeletedIn();
3581 while ( pLink && bUpdate )
3583 const ScChangeAction* pDel = pLink->GetAction();
3584 if ( pDel && pDel->GetType() == eInsType &&
3585 pDel->GetBigRange().In( aDelRange ) )
3586 bUpdate = FALSE;
3587 pLink = pLink->GetNext();
3591 if ( !bUpdate )
3592 continue; // for
3594 if ( aDelRange.In( p->GetBigRange() ) )
3596 // Innerhalb eines gerade geloeschten Bereiches nicht
3597 // anpassen, stattdessen dem Bereich zuordnen.
3598 // Mehrfache geloeschte Bereiche "stapeln".
3599 // Kreuzende Deletes setzen mehrfach geloescht.
3600 if ( !p->IsDeletedInDelType( eActType ) )
3602 p->SetDeletedIn( pActDel );
3603 // GeneratedDelContent in zu loeschende Liste aufnehmen
3604 if ( bGeneratedDelContents )
3605 pActDel->AddContent( (ScChangeActionContent*) p );
3607 bUpdate = FALSE;
3609 else
3611 // Eingefuegte Bereiche abschneiden, wenn Start/End im
3612 // Delete liegt, aber das Insert nicht komplett innerhalb
3613 // des Delete liegt bzw. das Delete nicht komplett im
3614 // Insert. Das Delete merkt sich, welchem Insert es was
3615 // abgeschnitten hat, es kann auch nur ein einziges Insert
3616 // sein (weil Delete einspaltig/einzeilig ist).
3617 // Abgeschnittene Moves kann es viele geben.
3618 //! Ein Delete ist immer einspaltig/einzeilig, deswegen 1
3619 //! ohne die Ueberlappung auszurechnen.
3620 switch ( p->GetType() )
3622 case SC_CAT_INSERT_COLS :
3623 if ( eActType == SC_CAT_DELETE_COLS )
3625 if ( aDelRange.In( p->GetBigRange().aStart ) )
3627 pActDel->SetCutOffInsert(
3628 (ScChangeActionIns*) p, 1 );
3629 p->GetBigRange().aStart.IncCol( 1 );
3631 else if ( aDelRange.In( p->GetBigRange().aEnd ) )
3633 pActDel->SetCutOffInsert(
3634 (ScChangeActionIns*) p, -1 );
3635 p->GetBigRange().aEnd.IncCol( -1 );
3638 break;
3639 case SC_CAT_INSERT_ROWS :
3640 if ( eActType == SC_CAT_DELETE_ROWS )
3642 if ( aDelRange.In( p->GetBigRange().aStart ) )
3644 pActDel->SetCutOffInsert(
3645 (ScChangeActionIns*) p, 1 );
3646 p->GetBigRange().aStart.IncRow( 1 );
3648 else if ( aDelRange.In( p->GetBigRange().aEnd ) )
3650 pActDel->SetCutOffInsert(
3651 (ScChangeActionIns*) p, -1 );
3652 p->GetBigRange().aEnd.IncRow( -1 );
3655 break;
3656 case SC_CAT_INSERT_TABS :
3657 if ( eActType == SC_CAT_DELETE_TABS )
3659 if ( aDelRange.In( p->GetBigRange().aStart ) )
3661 pActDel->SetCutOffInsert(
3662 (ScChangeActionIns*) p, 1 );
3663 p->GetBigRange().aStart.IncTab( 1 );
3665 else if ( aDelRange.In( p->GetBigRange().aEnd ) )
3667 pActDel->SetCutOffInsert(
3668 (ScChangeActionIns*) p, -1 );
3669 p->GetBigRange().aEnd.IncTab( -1 );
3672 break;
3673 case SC_CAT_MOVE :
3675 ScChangeActionMove* pMove = (ScChangeActionMove*) p;
3676 short nFrom = 0;
3677 short nTo = 0;
3678 if ( aDelRange.In( pMove->GetBigRange().aStart ) )
3679 nTo = 1;
3680 else if ( aDelRange.In( pMove->GetBigRange().aEnd ) )
3681 nTo = -1;
3682 if ( aDelRange.In( pMove->GetFromRange().aStart ) )
3683 nFrom = 1;
3684 else if ( aDelRange.In( pMove->GetFromRange().aEnd ) )
3685 nFrom = -1;
3686 if ( nFrom )
3688 switch ( eActType )
3690 case SC_CAT_DELETE_COLS :
3691 if ( nFrom > 0 )
3692 pMove->GetFromRange().aStart.IncCol( nFrom );
3693 else
3694 pMove->GetFromRange().aEnd.IncCol( nFrom );
3695 break;
3696 case SC_CAT_DELETE_ROWS :
3697 if ( nFrom > 0 )
3698 pMove->GetFromRange().aStart.IncRow( nFrom );
3699 else
3700 pMove->GetFromRange().aEnd.IncRow( nFrom );
3701 break;
3702 case SC_CAT_DELETE_TABS :
3703 if ( nFrom > 0 )
3704 pMove->GetFromRange().aStart.IncTab( nFrom );
3705 else
3706 pMove->GetFromRange().aEnd.IncTab( nFrom );
3707 break;
3708 default:
3710 // added to avoid warnings
3714 if ( nTo )
3716 switch ( eActType )
3718 case SC_CAT_DELETE_COLS :
3719 if ( nTo > 0 )
3720 pMove->GetBigRange().aStart.IncCol( nTo );
3721 else
3722 pMove->GetBigRange().aEnd.IncCol( nTo );
3723 break;
3724 case SC_CAT_DELETE_ROWS :
3725 if ( nTo > 0 )
3726 pMove->GetBigRange().aStart.IncRow( nTo );
3727 else
3728 pMove->GetBigRange().aEnd.IncRow( nTo );
3729 break;
3730 case SC_CAT_DELETE_TABS :
3731 if ( nTo > 0 )
3732 pMove->GetBigRange().aStart.IncTab( nTo );
3733 else
3734 pMove->GetBigRange().aEnd.IncTab( nTo );
3735 break;
3736 default:
3738 // added to avoid warnings
3742 if ( nFrom || nTo )
3744 ScChangeActionDelMoveEntry* pLink =
3745 pActDel->AddCutOffMove( pMove, nFrom, nTo );
3746 pMove->AddLink( pActDel, pLink );
3749 break;
3750 default:
3752 // added to avoid warnings
3756 if ( bUpdate )
3758 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3759 if ( p->GetType() == eActType && !p->IsRejected() &&
3760 !pActDel->IsDeletedIn() &&
3761 p->GetBigRange().In( aDelRange ) )
3762 pActDel->SetDeletedIn( p ); // "druntergerutscht"
3766 else
3767 { // Undo Delete
3768 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3770 if ( p == pAct )
3771 continue; // for
3772 BOOL bUpdate = TRUE;
3773 if ( aDelRange.In( p->GetBigRange() ) )
3775 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3776 if ( GetMergeState() == SC_CTMS_UNDO && !p->IsDeletedIn( pAct ) && pAct->IsDeleteType() &&
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 ) )
3781 p->SetDeletedIn( pAct );
3784 if ( p->IsDeletedInDelType( eActType ) )
3786 if ( p->IsDeletedIn( pActDel ) )
3788 if ( p->GetType() != SC_CAT_CONTENT ||
3789 ((ScChangeActionContent*)p)->IsTopContent() )
3790 { // erst der TopContent wird wirklich entfernt
3791 p->RemoveDeletedIn( pActDel );
3792 // GeneratedDelContent _nicht_ aus Liste loeschen,
3793 // wir brauchen ihn evtl. noch fuer Reject,
3794 // geloescht wird in DeleteCellEntries
3797 bUpdate = FALSE;
3799 else if ( eActType != SC_CAT_DELETE_TABS &&
3800 p->IsDeletedInDelType( SC_CAT_DELETE_TABS ) )
3801 { // in geloeschten Tabellen nicht updaten,
3802 // ausser wenn Tabelle verschoben wird
3803 bUpdate = FALSE;
3805 if ( p->GetType() == eActType && pActDel->IsDeletedIn( p ) )
3807 pActDel->RemoveDeletedIn( p ); // "druntergerutscht"
3808 bUpdate = TRUE;
3811 if ( bUpdate )
3812 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3814 if ( !bGeneratedDelContents )
3815 { // die werden sonst noch fuer das echte Undo gebraucht
3816 pActDel->UndoCutOffInsert();
3817 pActDel->UndoCutOffMoves();
3821 else if ( eActType == SC_CAT_MOVE )
3823 ScChangeActionMove* pActMove = (ScChangeActionMove*) pAct;
3824 BOOL bLastCutMove = ( pActMove == pLastCutMove );
3825 const ScBigRange& rTo = pActMove->GetBigRange();
3826 const ScBigRange& rFrom = pActMove->GetFromRange();
3827 if ( !bUndo )
3828 { // Move
3829 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3831 if ( p == pAct )
3832 continue; // for
3833 if ( p->GetType() == SC_CAT_CONTENT )
3835 // Inhalt in Ziel deleten (Inhalt in Quelle moven)
3836 if ( rTo.In( p->GetBigRange() ) )
3838 if ( !p->IsDeletedIn( pActMove ) )
3840 p->SetDeletedIn( pActMove );
3841 // GeneratedDelContent in zu loeschende Liste aufnehmen
3842 if ( bGeneratedDelContents )
3843 pActMove->AddContent( (ScChangeActionContent*) p );
3846 else if ( bLastCutMove &&
3847 p->GetActionNumber() > nEndLastCut &&
3848 rFrom.In( p->GetBigRange() ) )
3849 { // Paste Cut: neuer Content nach Cut eingefuegt, bleibt.
3850 // Aufsplitten der ContentChain
3851 ScChangeActionContent *pHere, *pTmp;
3852 pHere = (ScChangeActionContent*) p;
3853 while ( (pTmp = pHere->GetPrevContent()) != NULL &&
3854 pTmp->GetActionNumber() > nEndLastCut )
3855 pHere = pTmp;
3856 if ( pTmp )
3857 { // wird TopContent des Move
3858 pTmp->SetNextContent( NULL );
3859 pHere->SetPrevContent( NULL );
3862 { // Abhaengigkeit vom FromRange herstellen
3863 AddDependentWithNotify( pActMove, pHere );
3864 } while ( ( pHere = pHere->GetNextContent() ) != NULL );
3866 // #i87003# [Collaboration] Move range and insert content in FromRange is not merged correctly
3867 else if ( ( GetMergeState() != SC_CTMS_PREPARE && GetMergeState() != SC_CTMS_OWN ) || p->GetActionNumber() <= pAct->GetActionNumber() )
3868 p->UpdateReference( this, eMode, rFrom, nDx, nDy, nDz );
3872 else
3873 { // Undo Move
3874 BOOL bActRejected = pActMove->IsRejected();
3875 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3877 if ( p == pAct )
3878 continue; // for
3879 if ( p->GetType() == SC_CAT_CONTENT )
3881 // Inhalt in Ziel moven, wenn nicht deleted, sonst undelete
3882 if ( p->IsDeletedIn( pActMove ) )
3884 if ( ((ScChangeActionContent*)p)->IsTopContent() )
3885 { // erst der TopContent wird wirklich entfernt
3886 p->RemoveDeletedIn( pActMove );
3887 // GeneratedDelContent _nicht_ aus Liste loeschen,
3888 // wir brauchen ihn evtl. noch fuer Reject,
3889 // geloescht wird in DeleteCellEntries
3892 // #i87003# [Collaboration] Move range and insert content in FromRange is not merged correctly
3893 else if ( ( GetMergeState() != SC_CTMS_PREPARE && GetMergeState() != SC_CTMS_OWN ) || p->GetActionNumber() <= pAct->GetActionNumber() )
3894 p->UpdateReference( this, eMode, rTo, nDx, nDy, nDz );
3895 if ( bActRejected &&
3896 ((ScChangeActionContent*)p)->IsTopContent() &&
3897 rFrom.In( p->GetBigRange() ) )
3898 { // Abhaengigkeit herstellen, um Content zu schreiben
3899 ScChangeActionLinkEntry* pLink =
3900 pActMove->AddDependent( p );
3901 p->AddLink( pActMove, pLink );
3907 else
3908 { // Insert / Undo Insert
3909 switch ( GetMergeState() )
3911 case SC_CTMS_NONE :
3912 case SC_CTMS_OTHER :
3914 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3916 if ( p == pAct )
3917 continue; // for
3918 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3921 break;
3922 case SC_CTMS_PREPARE :
3924 // in Insert-Undo "Deleten"
3925 const ScChangeActionLinkEntry* pLink = pAct->GetFirstDependentEntry();
3926 while ( pLink )
3928 ScChangeAction* p = (ScChangeAction*) pLink->GetAction();
3929 if ( p )
3930 p->SetDeletedIn( pAct );
3931 pLink = pLink->GetNext();
3934 // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly
3935 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3937 if ( !p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
3938 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3939 ( p->GetType() == SC_CAT_CONTENT ||
3940 p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
3941 p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
3942 pAct->GetBigRange().Intersects( p->GetBigRange() ) )
3944 p->SetDeletedIn( pAct );
3948 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3950 if ( p == pAct )
3951 continue; // for
3952 if ( !p->IsDeletedIn( pAct )
3953 // #i95212# [Collaboration] Bad handling of row insertion in shared spreadsheet
3954 && p->GetActionNumber() <= pAct->GetActionNumber() )
3956 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3960 break;
3961 case SC_CTMS_OWN :
3963 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3965 if ( p == pAct )
3966 continue; // for
3967 if ( !p->IsDeletedIn( pAct )
3968 // #i95212# [Collaboration] Bad handling of row insertion in shared spreadsheet
3969 && p->GetActionNumber() <= pAct->GetActionNumber() )
3971 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3974 // in Insert-Undo "Delete" rueckgaengig
3975 const ScChangeActionLinkEntry* pLink = pAct->GetFirstDependentEntry();
3976 while ( pLink )
3978 ScChangeAction* p = (ScChangeAction*) pLink->GetAction();
3979 if ( p )
3980 p->RemoveDeletedIn( pAct );
3981 pLink = pLink->GetNext();
3984 // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly
3985 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3987 if ( p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
3988 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3989 ( p->GetType() == SC_CAT_CONTENT ||
3990 p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
3991 p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
3992 pAct->GetBigRange().Intersects( p->GetBigRange() ) )
3994 p->RemoveDeletedIn( pAct );
3998 break;
3999 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
4000 case SC_CTMS_UNDO :
4002 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
4004 if ( !p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
4005 ( p->GetType() == SC_CAT_CONTENT ||
4006 p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
4007 p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
4008 pAct->GetBigRange().Intersects( p->GetBigRange() ) )
4010 p->SetDeletedIn( pAct );
4014 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
4016 if ( p == pAct )
4018 continue;
4020 if ( !p->IsDeletedIn( pAct ) && p->GetActionNumber() <= pAct->GetActionNumber() )
4022 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
4026 break;
4032 void ScChangeTrack::GetDependents( ScChangeAction* pAct,
4033 ScChangeActionTable& rTable, BOOL bListMasterDelete, BOOL bAllFlat ) const
4035 //! bAllFlat==TRUE: intern aus Accept oder Reject gerufen,
4036 //! => Generated werden nicht aufgenommen
4038 BOOL bIsDelete = pAct->IsDeleteType();
4039 BOOL bIsMasterDelete = ( bListMasterDelete && pAct->IsMasterDelete() );
4041 const ScChangeAction* pCur = pAct;
4042 ScChangeActionStack* pStack = new ScChangeActionStack;
4045 if ( pCur->IsInsertType() )
4047 const ScChangeActionLinkEntry* pL = pCur->GetFirstDependentEntry();
4048 while ( pL )
4050 ScChangeAction* p = (ScChangeAction*) pL->GetAction();
4051 if ( p != pAct )
4053 if ( bAllFlat )
4055 ULONG n = p->GetActionNumber();
4056 if ( !IsGenerated( n ) && rTable.Insert( n, p ) )
4057 if ( p->HasDependent() )
4058 pStack->Push( p );
4060 else
4062 if ( p->GetType() == SC_CAT_CONTENT )
4064 if ( ((ScChangeActionContent*)p)->IsTopContent() )
4065 rTable.Insert( p->GetActionNumber(), p );
4067 else
4068 rTable.Insert( p->GetActionNumber(), p );
4071 pL = pL->GetNext();
4074 else if ( pCur->IsDeleteType() )
4076 if ( bIsDelete )
4077 { // Inhalte geloeschter Bereiche interessieren nur bei Delete
4078 ScChangeActionDel* pDel = (ScChangeActionDel*) pCur;
4079 if ( !bAllFlat && bIsMasterDelete && pCur == pAct )
4081 // zu diesem Delete gehoerende Deletes in gleiche Ebene,
4082 // wenn dieses Delete das momentan oberste einer Reihe ist,
4083 ScChangeActionType eType = pDel->GetType();
4084 ScChangeAction* p = pDel;
4085 while ( (p = p->GetPrev()) != NULL && p->GetType() == eType &&
4086 !((ScChangeActionDel*)p)->IsTopDelete() )
4087 rTable.Insert( p->GetActionNumber(), p );
4088 // dieses Delete auch in Table!
4089 rTable.Insert( pAct->GetActionNumber(), pAct );
4091 else
4093 const ScChangeActionLinkEntry* pL = pCur->GetFirstDeletedEntry();
4094 while ( pL )
4096 ScChangeAction* p = (ScChangeAction*) pL->GetAction();
4097 if ( p != pAct )
4099 if ( bAllFlat )
4101 // nur ein TopContent einer Kette ist in LinkDeleted
4102 ULONG n = p->GetActionNumber();
4103 if ( !IsGenerated( n ) && rTable.Insert( n, p ) )
4104 if ( p->HasDeleted() ||
4105 p->GetType() == SC_CAT_CONTENT )
4106 pStack->Push( p );
4108 else
4110 if ( p->IsDeleteType() )
4111 { // weiteres TopDelete in gleiche Ebene,
4112 // es ist nicht rejectable
4113 if ( ((ScChangeActionDel*)p)->IsTopDelete() )
4114 rTable.Insert( p->GetActionNumber(), p );
4116 else
4117 rTable.Insert( p->GetActionNumber(), p );
4120 pL = pL->GetNext();
4125 else if ( pCur->GetType() == SC_CAT_MOVE )
4127 // geloeschte Contents im ToRange
4128 const ScChangeActionLinkEntry* pL = pCur->GetFirstDeletedEntry();
4129 while ( pL )
4131 ScChangeAction* p = (ScChangeAction*) pL->GetAction();
4132 if ( p != pAct && rTable.Insert( p->GetActionNumber(), p ) )
4134 // nur ein TopContent einer Kette ist in LinkDeleted
4135 if ( bAllFlat && (p->HasDeleted() ||
4136 p->GetType() == SC_CAT_CONTENT) )
4137 pStack->Push( p );
4139 pL = pL->GetNext();
4141 // neue Contents im FromRange oder neuer FromRange im ToRange
4142 // oder Inserts/Deletes in FromRange/ToRange
4143 pL = pCur->GetFirstDependentEntry();
4144 while ( pL )
4146 ScChangeAction* p = (ScChangeAction*) pL->GetAction();
4147 if ( p != pAct )
4149 if ( bAllFlat )
4151 ULONG n = p->GetActionNumber();
4152 if ( !IsGenerated( n ) && rTable.Insert( n, p ) )
4153 if ( p->HasDependent() || p->HasDeleted() )
4154 pStack->Push( p );
4156 else
4158 if ( p->GetType() == SC_CAT_CONTENT )
4160 if ( ((ScChangeActionContent*)p)->IsTopContent() )
4161 rTable.Insert( p->GetActionNumber(), p );
4163 else
4164 rTable.Insert( p->GetActionNumber(), p );
4167 pL = pL->GetNext();
4170 else if ( pCur->GetType() == SC_CAT_CONTENT )
4171 { // alle Aenderungen an gleicher Position
4172 ScChangeActionContent* pContent = (ScChangeActionContent*) pCur;
4173 // alle vorherigen
4174 while ( ( pContent = pContent->GetPrevContent() ) != NULL )
4176 if ( !pContent->IsRejected() )
4177 rTable.Insert( pContent->GetActionNumber(), pContent );
4179 pContent = (ScChangeActionContent*) pCur;
4180 // alle nachfolgenden
4181 while ( ( pContent = pContent->GetNextContent() ) != NULL )
4183 if ( !pContent->IsRejected() )
4184 rTable.Insert( pContent->GetActionNumber(), pContent );
4186 // all MatrixReferences of a MatrixOrigin
4187 const ScChangeActionLinkEntry* pL = pCur->GetFirstDependentEntry();
4188 while ( pL )
4190 ScChangeAction* p = (ScChangeAction*) pL->GetAction();
4191 if ( p != pAct )
4193 if ( bAllFlat )
4195 ULONG n = p->GetActionNumber();
4196 if ( !IsGenerated( n ) && rTable.Insert( n, p ) )
4197 if ( p->HasDependent() )
4198 pStack->Push( p );
4200 else
4201 rTable.Insert( p->GetActionNumber(), p );
4203 pL = pL->GetNext();
4206 else if ( pCur->GetType() == SC_CAT_REJECT )
4208 if ( bAllFlat )
4210 ScChangeAction* p = GetAction(
4211 ((ScChangeActionReject*)pCur)->GetRejectAction() );
4212 if ( p != pAct && !rTable.Get( p->GetActionNumber() ) )
4213 pStack->Push( p );
4216 } while ( ( pCur = pStack->Pop() ) != NULL );
4217 delete pStack;
4221 BOOL ScChangeTrack::SelectContent( ScChangeAction* pAct, BOOL bOldest )
4223 if ( pAct->GetType() != SC_CAT_CONTENT )
4224 return FALSE;
4226 ScChangeActionContent* pContent = (ScChangeActionContent*) pAct;
4227 if ( bOldest )
4229 pContent = pContent->GetTopContent();
4230 ScChangeActionContent* pPrevContent;
4231 while ( (pPrevContent = pContent->GetPrevContent()) != NULL &&
4232 pPrevContent->IsVirgin() )
4233 pContent = pPrevContent;
4236 if ( !pContent->IsClickable() )
4237 return FALSE;
4239 ScBigRange aBigRange( pContent->GetBigRange() );
4240 const ScBaseCell* pCell = (bOldest ? pContent->GetOldCell() :
4241 pContent->GetNewCell());
4242 if ( ScChangeActionContent::GetContentCellType( pCell ) == SC_CACCT_MATORG )
4244 SCCOL nC;
4245 SCROW nR;
4246 ((const ScFormulaCell*)pCell)->GetMatColsRows( nC, nR );
4247 aBigRange.aEnd.IncCol( nC-1 );
4248 aBigRange.aEnd.IncRow( nR-1 );
4251 if ( !aBigRange.IsValid( pDoc ) )
4252 return FALSE;
4254 ScRange aRange( aBigRange.MakeRange() );
4255 if ( !pDoc->IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(),
4256 aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) )
4257 return FALSE;
4259 if ( pContent->HasDependent() )
4261 BOOL bOk = TRUE;
4262 Stack aRejectActions;
4263 const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry();
4264 while ( pL )
4266 ScChangeAction* p = (ScChangeAction*) pL->GetAction();
4267 if ( p != pContent )
4269 if ( p->GetType() == SC_CAT_CONTENT )
4271 // we don't need no recursion here, do we?
4272 bOk &= ((ScChangeActionContent*)p)->Select( pDoc, this,
4273 bOldest, &aRejectActions );
4275 else
4277 DBG_ERRORFILE( "ScChangeTrack::SelectContent: content dependent no content" );
4280 pL = pL->GetNext();
4283 bOk &= pContent->Select( pDoc, this, bOldest, NULL );
4284 // now the matrix is inserted and new content values are ready
4286 ScChangeActionContent* pNew;
4287 while ( ( pNew = (ScChangeActionContent*) aRejectActions.Pop() ) != NULL )
4289 ScAddress aPos( pNew->GetBigRange().aStart.MakeAddress() );
4290 pNew->SetNewValue( pDoc->GetCell( aPos ), pDoc );
4291 Append( pNew );
4293 return bOk;
4295 else
4296 return pContent->Select( pDoc, this, bOldest, NULL );
4300 void ScChangeTrack::AcceptAll()
4302 for ( ScChangeAction* p = GetFirst(); p; p = p->GetNext() )
4304 p->Accept();
4309 BOOL ScChangeTrack::Accept( ScChangeAction* pAct )
4311 if ( !pAct->IsClickable() )
4312 return FALSE;
4314 if ( pAct->IsDeleteType() || pAct->GetType() == SC_CAT_CONTENT )
4316 ScChangeActionTable aActionTable;
4317 GetDependents( pAct, aActionTable, FALSE, TRUE );
4318 for ( ScChangeAction* p = aActionTable.First(); p; p = aActionTable.Next() )
4320 p->Accept();
4323 pAct->Accept();
4324 return TRUE;
4328 BOOL ScChangeTrack::RejectAll()
4330 BOOL bOk = TRUE;
4331 for ( ScChangeAction* p = GetLast(); p && bOk; p = p->GetPrev() )
4332 { //! rueckwaerts, weil abhaengige hinten und RejectActions angehaengt
4333 if ( p->IsInternalRejectable() )
4334 bOk = Reject( p );
4336 return bOk;
4340 BOOL ScChangeTrack::Reject( ScChangeAction* pAct, bool bShared )
4342 // #i100895# When collaboration changes are reversed, it must be possible
4343 // to reject a deleted row above another deleted row.
4344 if ( bShared && pAct->IsDeletedIn() )
4345 pAct->RemoveAllDeletedIn();
4347 if ( !pAct->IsRejectable() )
4348 return FALSE;
4350 ScChangeActionTable* pTable = NULL;
4351 if ( pAct->HasDependent() )
4353 pTable = new ScChangeActionTable;
4354 GetDependents( pAct, *pTable, FALSE, TRUE );
4356 BOOL bRejected = Reject( pAct, pTable, FALSE );
4357 if ( pTable )
4358 delete pTable;
4359 return bRejected;
4363 BOOL ScChangeTrack::Reject( ScChangeAction* pAct, ScChangeActionTable* pTable,
4364 BOOL bRecursion )
4366 if ( !pAct->IsInternalRejectable() )
4367 return FALSE;
4369 BOOL bOk = TRUE;
4370 BOOL bRejected = FALSE;
4371 if ( pAct->IsInsertType() )
4373 if ( pAct->HasDependent() && !bRecursion )
4375 DBG_ASSERT( pTable, "ScChangeTrack::Reject: Insert ohne Table" );
4376 for ( ScChangeAction* p = pTable->Last(); p && bOk; p = pTable->Prev() )
4378 // keine Contents restoren, die eh geloescht werden wuerden
4379 if ( p->GetType() == SC_CAT_CONTENT )
4380 p->SetRejected();
4381 else if ( p->IsDeleteType() )
4382 p->Accept(); // geloeschtes ins Nirvana
4383 else
4384 bOk = Reject( p, NULL, TRUE ); //! rekursiv
4387 if ( bOk && (bRejected = pAct->Reject( pDoc )) != FALSE )
4389 // pRefDoc NULL := geloeschte Zellen nicht speichern
4390 AppendDeleteRange( pAct->GetBigRange().MakeRange(), NULL, (short) 0,
4391 pAct->GetActionNumber() );
4394 else if ( pAct->IsDeleteType() )
4396 DBG_ASSERT( !pTable, "ScChangeTrack::Reject: Delete mit Table" );
4397 ScBigRange aDelRange;
4398 ULONG nRejectAction = pAct->GetActionNumber();
4399 BOOL bTabDel, bTabDelOk;
4400 if ( pAct->GetType() == SC_CAT_DELETE_TABS )
4402 bTabDel = TRUE;
4403 aDelRange = pAct->GetBigRange();
4404 bOk = bTabDelOk = pAct->Reject( pDoc );
4405 if ( bOk )
4407 pAct = pAct->GetPrev();
4408 bOk = ( pAct && pAct->GetType() == SC_CAT_DELETE_COLS );
4411 else
4412 bTabDel = bTabDelOk = FALSE;
4413 ScChangeActionDel* pDel = (ScChangeActionDel*) pAct;
4414 if ( bOk )
4416 aDelRange = pDel->GetOverAllRange();
4417 bOk = aDelRange.IsValid( pDoc );
4419 BOOL bOneOk = FALSE;
4420 if ( bOk )
4422 ScChangeActionType eActType = pAct->GetType();
4423 switch ( eActType )
4425 case SC_CAT_DELETE_COLS :
4426 aDelRange.aStart.SetCol( aDelRange.aEnd.Col() );
4427 break;
4428 case SC_CAT_DELETE_ROWS :
4429 aDelRange.aStart.SetRow( aDelRange.aEnd.Row() );
4430 break;
4431 case SC_CAT_DELETE_TABS :
4432 aDelRange.aStart.SetTab( aDelRange.aEnd.Tab() );
4433 break;
4434 default:
4436 // added to avoid warnings
4439 ScChangeAction* p = pAct;
4440 BOOL bLoop = TRUE;
4443 pDel = (ScChangeActionDel*) p;
4444 bOk = pDel->Reject( pDoc );
4445 if ( bOk )
4447 if ( bOneOk )
4449 switch ( pDel->GetType() )
4451 case SC_CAT_DELETE_COLS :
4452 aDelRange.aStart.IncCol( -1 );
4453 break;
4454 case SC_CAT_DELETE_ROWS :
4455 aDelRange.aStart.IncRow( -1 );
4456 break;
4457 case SC_CAT_DELETE_TABS :
4458 aDelRange.aStart.IncTab( -1 );
4459 break;
4460 default:
4462 // added to avoid warnings
4466 else
4467 bOneOk = TRUE;
4469 if ( pDel->IsBaseDelete() )
4470 bLoop = FALSE;
4471 else
4472 p = p->GetPrev();
4473 } while ( bOk && bLoop && p && p->GetType() == eActType &&
4474 !((ScChangeActionDel*)p)->IsTopDelete() );
4476 bRejected = bOk;
4477 if ( bOneOk || (bTabDel && bTabDelOk) )
4479 // Delete-Reject machte UpdateReference Undo
4480 ScChangeActionIns* pReject = new ScChangeActionIns(
4481 aDelRange.MakeRange() );
4482 pReject->SetRejectAction( nRejectAction );
4483 pReject->SetState( SC_CAS_ACCEPTED );
4484 Append( pReject );
4487 else if ( pAct->GetType() == SC_CAT_MOVE )
4489 if ( pAct->HasDependent() && !bRecursion )
4491 DBG_ASSERT( pTable, "ScChangeTrack::Reject: Move ohne Table" );
4492 for ( ScChangeAction* p = pTable->Last(); p && bOk; p = pTable->Prev() )
4494 bOk = Reject( p, NULL, TRUE ); //! rekursiv
4497 if ( bOk && (bRejected = pAct->Reject( pDoc )) != FALSE )
4499 ScChangeActionMove* pReject = new ScChangeActionMove(
4500 pAct->GetBigRange().MakeRange(),
4501 ((ScChangeActionMove*)pAct)->GetFromRange().MakeRange(), this );
4502 pReject->SetRejectAction( pAct->GetActionNumber() );
4503 pReject->SetState( SC_CAS_ACCEPTED );
4504 Append( pReject );
4507 else if ( pAct->GetType() == SC_CAT_CONTENT )
4509 ScRange aRange;
4510 ScChangeActionContent* pReject;
4511 if ( bRecursion )
4512 pReject = NULL;
4513 else
4515 aRange = pAct->GetBigRange().aStart.MakeAddress();
4516 pReject = new ScChangeActionContent( aRange );
4517 pReject->SetOldValue( pDoc->GetCell( aRange.aStart ), pDoc, pDoc );
4519 if ( (bRejected = pAct->Reject( pDoc )) != FALSE && !bRecursion )
4521 pReject->SetNewValue( pDoc->GetCell( aRange.aStart ), pDoc );
4522 pReject->SetRejectAction( pAct->GetActionNumber() );
4523 pReject->SetState( SC_CAS_ACCEPTED );
4524 Append( pReject );
4526 else if ( pReject )
4527 delete pReject;
4529 else
4531 DBG_ERROR( "ScChangeTrack::Reject: say what?" );
4534 return bRejected;
4538 ULONG ScChangeTrack::AddLoadedGenerated(ScBaseCell* pNewCell, const ScBigRange& aBigRange, const String& sNewValue )
4540 ScChangeActionContent* pAct = new ScChangeActionContent( --nGeneratedMin, pNewCell, aBigRange, pDoc, sNewValue );
4541 if ( pAct )
4543 if ( pFirstGeneratedDelContent )
4544 pFirstGeneratedDelContent->pPrev = pAct;
4545 pAct->pNext = pFirstGeneratedDelContent;
4546 pFirstGeneratedDelContent = pAct;
4547 aGeneratedTable.Insert( pAct->GetActionNumber(), pAct );
4548 return pAct->GetActionNumber();
4550 return 0;
4553 void ScChangeTrack::AppendCloned( ScChangeAction* pAppend )
4555 aTable.Insert( pAppend->GetActionNumber(), pAppend );
4556 if ( !pLast )
4557 pFirst = pLast = pAppend;
4558 else
4560 pLast->pNext = pAppend;
4561 pAppend->pPrev = pLast;
4562 pLast = pAppend;
4566 ScChangeTrack* ScChangeTrack::Clone( ScDocument* pDocument ) const
4568 if ( !pDocument )
4570 return NULL;
4573 ScChangeTrack* pClonedTrack = new ScChangeTrack( pDocument );
4574 pClonedTrack->SetTime100thSeconds( IsTime100thSeconds() );
4576 // clone generated actions
4577 ::std::stack< const ScChangeAction* > aGeneratedStack;
4578 const ScChangeAction* pGenerated = GetFirstGenerated();
4579 while ( pGenerated )
4581 aGeneratedStack.push( pGenerated );
4582 pGenerated = pGenerated->GetNext();
4584 while ( !aGeneratedStack.empty() )
4586 pGenerated = aGeneratedStack.top();
4587 aGeneratedStack.pop();
4588 const ScChangeActionContent* pContent = dynamic_cast< const ScChangeActionContent* >( pGenerated );
4589 DBG_ASSERT( pContent, "ScChangeTrack::Clone: pContent is null!" );
4590 const ScBaseCell* pNewCell = pContent->GetNewCell();
4591 if ( pNewCell )
4593 ScBaseCell* pClonedNewCell = pNewCell->CloneWithoutNote( *pDocument );
4594 String aNewValue;
4595 pContent->GetNewString( aNewValue );
4596 pClonedTrack->nGeneratedMin = pGenerated->GetActionNumber() + 1;
4597 pClonedTrack->AddLoadedGenerated( pClonedNewCell, pGenerated->GetBigRange(), aNewValue );
4601 // clone actions
4602 const ScChangeAction* pAction = GetFirst();
4603 while ( pAction )
4605 ScChangeAction* pClonedAction = NULL;
4607 switch ( pAction->GetType() )
4609 case SC_CAT_INSERT_COLS:
4610 case SC_CAT_INSERT_ROWS:
4611 case SC_CAT_INSERT_TABS:
4613 pClonedAction = new ScChangeActionIns(
4614 pAction->GetActionNumber(),
4615 pAction->GetState(),
4616 pAction->GetRejectAction(),
4617 pAction->GetBigRange(),
4618 pAction->GetUser(),
4619 pAction->GetDateTimeUTC(),
4620 pAction->GetComment(),
4621 pAction->GetType() );
4623 break;
4624 case SC_CAT_DELETE_COLS:
4625 case SC_CAT_DELETE_ROWS:
4626 case SC_CAT_DELETE_TABS:
4628 const ScChangeActionDel* pDelete = dynamic_cast< const ScChangeActionDel* >( pAction );
4629 DBG_ASSERT( pDelete, "ScChangeTrack::Clone: pDelete is null!" );
4631 SCsCOLROW nD = 0;
4632 ScChangeActionType eType = pAction->GetType();
4633 if ( eType == SC_CAT_DELETE_COLS )
4635 nD = static_cast< SCsCOLROW >( pDelete->GetDx() );
4637 else if ( eType == SC_CAT_DELETE_ROWS )
4639 nD = static_cast< SCsCOLROW >( pDelete->GetDy() );
4642 pClonedAction = new ScChangeActionDel(
4643 pAction->GetActionNumber(),
4644 pAction->GetState(),
4645 pAction->GetRejectAction(),
4646 pAction->GetBigRange(),
4647 pAction->GetUser(),
4648 pAction->GetDateTimeUTC(),
4649 pAction->GetComment(),
4650 eType,
4652 pClonedTrack );
4654 break;
4655 case SC_CAT_MOVE:
4657 const ScChangeActionMove* pMove = dynamic_cast< const ScChangeActionMove* >( pAction );
4658 DBG_ASSERT( pMove, "ScChangeTrack::Clone: pMove is null!" );
4660 pClonedAction = new ScChangeActionMove(
4661 pAction->GetActionNumber(),
4662 pAction->GetState(),
4663 pAction->GetRejectAction(),
4664 pAction->GetBigRange(),
4665 pAction->GetUser(),
4666 pAction->GetDateTimeUTC(),
4667 pAction->GetComment(),
4668 pMove->GetFromRange(),
4669 pClonedTrack );
4671 break;
4672 case SC_CAT_CONTENT:
4674 const ScChangeActionContent* pContent = dynamic_cast< const ScChangeActionContent* >( pAction );
4675 DBG_ASSERT( pContent, "ScChangeTrack::Clone: pContent is null!" );
4676 const ScBaseCell* pOldCell = pContent->GetOldCell();
4677 ScBaseCell* pClonedOldCell = pOldCell ? pOldCell->CloneWithoutNote( *pDocument ) : 0;
4678 String aOldValue;
4679 pContent->GetOldString( aOldValue );
4681 ScChangeActionContent* pClonedContent = new ScChangeActionContent(
4682 pAction->GetActionNumber(),
4683 pAction->GetState(),
4684 pAction->GetRejectAction(),
4685 pAction->GetBigRange(),
4686 pAction->GetUser(),
4687 pAction->GetDateTimeUTC(),
4688 pAction->GetComment(),
4689 pClonedOldCell,
4690 pDocument,
4691 aOldValue );
4693 const ScBaseCell* pNewCell = pContent->GetNewCell();
4694 if ( pNewCell )
4696 ScBaseCell* pClonedNewCell = pNewCell->CloneWithoutNote( *pDocument );
4697 pClonedContent->SetNewValue( pClonedNewCell, pDocument );
4700 pClonedAction = pClonedContent;
4702 break;
4703 case SC_CAT_REJECT:
4705 pClonedAction = new ScChangeActionReject(
4706 pAction->GetActionNumber(),
4707 pAction->GetState(),
4708 pAction->GetRejectAction(),
4709 pAction->GetBigRange(),
4710 pAction->GetUser(),
4711 pAction->GetDateTimeUTC(),
4712 pAction->GetComment() );
4714 break;
4715 default:
4718 break;
4721 if ( pClonedAction )
4723 pClonedTrack->AppendCloned( pClonedAction );
4726 pAction = pAction->GetNext();
4729 if ( pClonedTrack->GetLast() )
4731 pClonedTrack->SetActionMax( pClonedTrack->GetLast()->GetActionNumber() );
4734 // set dependencies for Deleted/DeletedIn
4735 pAction = GetFirst();
4736 while ( pAction )
4738 if ( pAction->HasDeleted() )
4740 ::std::stack< ULONG > aStack;
4741 const ScChangeActionLinkEntry* pL = pAction->GetFirstDeletedEntry();
4742 while ( pL )
4744 const ScChangeAction* pDeleted = pL->GetAction();
4745 if ( pDeleted )
4747 aStack.push( pDeleted->GetActionNumber() );
4749 pL = pL->GetNext();
4751 ScChangeAction* pClonedAction = pClonedTrack->GetAction( pAction->GetActionNumber() );
4752 if ( pClonedAction )
4754 while ( !aStack.empty() )
4756 ScChangeAction* pClonedDeleted = pClonedTrack->GetActionOrGenerated( aStack.top() );
4757 aStack.pop();
4758 if ( pClonedDeleted )
4760 pClonedDeleted->SetDeletedIn( pClonedAction );
4765 pAction = pAction->GetNext();
4768 // set dependencies for Dependent/Any
4769 pAction = GetLast();
4770 while ( pAction )
4772 if ( pAction->HasDependent() )
4774 ::std::stack< ULONG > aStack;
4775 const ScChangeActionLinkEntry* pL = pAction->GetFirstDependentEntry();
4776 while ( pL )
4778 const ScChangeAction* pDependent = pL->GetAction();
4779 if ( pDependent )
4781 aStack.push( pDependent->GetActionNumber() );
4783 pL = pL->GetNext();
4785 ScChangeAction* pClonedAction = pClonedTrack->GetAction( pAction->GetActionNumber() );
4786 if ( pClonedAction )
4788 while ( !aStack.empty() )
4790 ScChangeAction* pClonedDependent = pClonedTrack->GetActionOrGenerated( aStack.top() );
4791 aStack.pop();
4792 if ( pClonedDependent )
4794 ScChangeActionLinkEntry* pLink = pClonedAction->AddDependent( pClonedDependent );
4795 pClonedDependent->AddLink( pClonedAction, pLink );
4800 pAction = pAction->GetPrev();
4803 // masterlinks
4804 ScChangeAction* pClonedAction = pClonedTrack->GetFirst();
4805 while ( pClonedAction )
4807 pClonedTrack->MasterLinks( pClonedAction );
4808 pClonedAction = pClonedAction->GetNext();
4811 if ( IsProtected() )
4813 pClonedTrack->SetProtection( GetProtection() );
4816 if ( pClonedTrack->GetLast() )
4818 pClonedTrack->SetLastSavedActionNumber( pClonedTrack->GetLast()->GetActionNumber() );
4821 pDocument->SetChangeTrack( pClonedTrack );
4823 return pClonedTrack;
4826 void ScChangeTrack::MergeActionState( ScChangeAction* pAct, const ScChangeAction* pOtherAct )
4828 if ( pAct->IsVirgin() )
4830 if ( pOtherAct->IsAccepted() )
4832 pAct->Accept();
4833 if ( pOtherAct->IsRejecting() )
4835 pAct->SetRejectAction( pOtherAct->GetRejectAction() );
4838 else if ( pOtherAct->IsRejected() )
4840 pAct->SetRejected();
4845 #if DEBUG_CHANGETRACK
4846 String ScChangeTrack::ToString() const
4848 String aReturn;
4850 aReturn += String::CreateFromAscii( "============================================================\n" );
4852 const ScChangeAction* pGenerated = GetFirstGenerated();
4853 while ( pGenerated )
4855 aReturn += pGenerated->ToString( pDoc );
4856 aReturn += '\n';
4857 pGenerated = pGenerated->GetNext();
4860 aReturn += String::CreateFromAscii( "------------------------------------------------------------\n" );
4862 const ScChangeAction* pAction = GetFirst();
4863 while ( pAction )
4865 aReturn += pAction->ToString( pDoc );
4866 aReturn += '\n';
4867 pAction = pAction->GetNext();
4869 aReturn += String::CreateFromAscii( "============================================================\n" );
4871 return aReturn;
4873 #endif // DEBUG_CHANGETRACK