Bug Summary

File:home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx
Warning:line 347, column 16
Attempt to delete released memory

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name chgtrack.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -isystem /usr/include/libxml2 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D SC_DLLIMPLEMENTATION -D SC_INFO_OSVERSION="LINUX" -D SYSTEM_LIBXML -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/liborcus/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/mdds/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/i18n -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/common -I /home/maarten/src/libreoffice/core/external/clew/source/include -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/sc/source/core/inc -I /home/maarten/src/libreoffice/core/sc/source/filter/inc -I /home/maarten/src/libreoffice/core/sc/source/ui/inc -I /home/maarten/src/libreoffice/core/sc/inc -I /home/maarten/src/libreoffice/core/workdir/SdiTarget/sc/sdi -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/officecfg/registry -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/oovbaapi/normal -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <chgtrack.hxx>
21#include <compiler.hxx>
22#include <formulacell.hxx>
23#include <document.hxx>
24#include <dociter.hxx>
25#include <global.hxx>
26#include <rechead.hxx>
27#include <scerrors.hxx>
28#include <scmod.hxx>
29#include <inputopt.hxx>
30#include <patattr.hxx>
31#include <hints.hxx>
32#include <markdata.hxx>
33#include <globstr.hrc>
34#include <scresid.hxx>
35#include <editutil.hxx>
36#include <tokenarray.hxx>
37#include <refupdatecontext.hxx>
38#include <refupdat.hxx>
39
40#include <osl/diagnose.h>
41#include <svl/zforlist.hxx>
42#include <svl/itemset.hxx>
43#include <svl/isethint.hxx>
44#include <svl/itempool.hxx>
45#include <sfx2/app.hxx>
46#include <sfx2/objsh.hxx>
47#include <unotools/useroptions.hxx>
48#include <unotools/datetime.hxx>
49#include <sfx2/sfxsids.hrc>
50#include <tools/json_writer.hxx>
51#include <algorithm>
52#include <memory>
53#include <boost/property_tree/json_parser.hpp>
54
55ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScRange& rRange )
56 :
57 aBigRange( rRange ),
58 aDateTime( DateTime::SYSTEM ),
59 pNext( nullptr ),
60 pPrev( nullptr ),
61 pLinkAny( nullptr ),
62 pLinkDeletedIn( nullptr ),
63 pLinkDeleted( nullptr ),
64 pLinkDependent( nullptr ),
65 nAction( 0 ),
66 nRejectAction( 0 ),
67 eType( eTypeP ),
68 eState( SC_CAS_VIRGIN )
69{
70 aDateTime.ConvertToUTC();
71}
72
73ScChangeAction::ScChangeAction(
74 ScChangeActionType eTypeP, const ScBigRange& rRange,
75 const sal_uLong nTempAction, const sal_uLong nTempRejectAction,
76 const ScChangeActionState eTempState, const DateTime& aTempDateTime,
77 const OUString& aTempUser, const OUString& aTempComment) :
78 aBigRange( rRange ),
79 aDateTime( aTempDateTime ),
80 aUser( aTempUser ),
81 aComment( aTempComment ),
82 pNext( nullptr ),
83 pPrev( nullptr ),
84 pLinkAny( nullptr ),
85 pLinkDeletedIn( nullptr ),
86 pLinkDeleted( nullptr ),
87 pLinkDependent( nullptr ),
88 nAction( nTempAction ),
89 nRejectAction( nTempRejectAction ),
90 eType( eTypeP ),
91 eState( eTempState )
92{
93}
94
95ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScBigRange& rRange,
96 const sal_uLong nTempAction)
97 :
98 aBigRange( rRange ),
99 aDateTime( DateTime::SYSTEM ),
100 pNext( nullptr ),
101 pPrev( nullptr ),
102 pLinkAny( nullptr ),
103 pLinkDeletedIn( nullptr ),
104 pLinkDeleted( nullptr ),
105 pLinkDependent( nullptr ),
106 nAction( nTempAction ),
107 nRejectAction( 0 ),
108 eType( eTypeP ),
109 eState( SC_CAS_VIRGIN )
110{
111 aDateTime.ConvertToUTC();
112}
113
114ScChangeAction::~ScChangeAction()
115{
116 RemoveAllLinks();
2
Calling 'ScChangeAction::RemoveAllLinks'
117}
118
119bool ScChangeAction::IsInsertType() const
120{
121 return eType == SC_CAT_INSERT_COLS || eType == SC_CAT_INSERT_ROWS || eType == SC_CAT_INSERT_TABS;
122}
123
124bool ScChangeAction::IsDeleteType() const
125{
126 return eType == SC_CAT_DELETE_COLS || eType == SC_CAT_DELETE_ROWS || eType == SC_CAT_DELETE_TABS;
127}
128
129bool ScChangeAction::IsVirgin() const
130{
131 return eState == SC_CAS_VIRGIN;
132}
133
134bool ScChangeAction::IsAccepted() const
135{
136 return eState == SC_CAS_ACCEPTED;
137}
138
139bool ScChangeAction::IsRejected() const
140{
141 return eState == SC_CAS_REJECTED;
142}
143
144bool ScChangeAction::IsRejecting() const
145{
146 return nRejectAction != 0;
147}
148
149bool ScChangeAction::IsVisible() const
150{
151 // sequence order of execution is significant!
152 if ( IsRejected() || GetType() == SC_CAT_DELETE_TABS || IsDeletedIn() )
153 return false;
154 if ( GetType() == SC_CAT_CONTENT )
155 return static_cast<const ScChangeActionContent*>(this)->IsTopContent();
156 return true;
157}
158
159bool ScChangeAction::IsTouchable() const
160{
161 // sequence order of execution is significant!
162 if ( IsRejected() || GetType() == SC_CAT_REJECT || IsDeletedIn() )
163 return false;
164 // content may reject and be touchable if on top
165 if ( GetType() == SC_CAT_CONTENT )
166 return static_cast<const ScChangeActionContent*>(this)->IsTopContent();
167 if ( IsRejecting() )
168 return false;
169 return true;
170}
171
172bool ScChangeAction::IsClickable() const
173{
174 // sequence order of execution is significant!
175 if ( !IsVirgin() )
176 return false;
177 if ( IsDeletedIn() )
178 return false;
179 if ( GetType() == SC_CAT_CONTENT )
180 {
181 ScChangeActionContentCellType eCCT =
182 ScChangeActionContent::GetContentCellType(
183 static_cast<const ScChangeActionContent*>(this)->GetNewCell() );
184 if ( eCCT == SC_CACCT_MATREF )
185 return false;
186 if ( eCCT == SC_CACCT_MATORG )
187 { // no Accept-Select if one of the references is in a deleted col/row
188 const ScChangeActionLinkEntry* pL =
189 static_cast<const ScChangeActionContent*>(this)->GetFirstDependentEntry();
190 while ( pL )
191 {
192 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
193 if ( p && p->IsDeletedIn() )
194 return false;
195 pL = pL->GetNext();
196 }
197 }
198 return true; // for Select() a content doesn't have to be touchable
199 }
200 return IsTouchable(); // Accept()/Reject() only on touchables
201}
202
203bool ScChangeAction::IsRejectable() const
204{
205 // sequence order of execution is significant!
206 if ( !IsClickable() )
207 return false;
208 if ( GetType() == SC_CAT_CONTENT )
209 {
210 if ( static_cast<const ScChangeActionContent*>(this)->IsOldMatrixReference() )
211 return false;
212 ScChangeActionContent* pNextContent =
213 static_cast<const ScChangeActionContent*>(this)->GetNextContent();
214 if ( pNextContent == nullptr )
215 return true; // *this is TopContent
216 return pNextContent->IsRejected(); // *this is next rejectable
217 }
218 return IsTouchable();
219}
220
221bool ScChangeAction::IsInternalRejectable() const
222{
223 // sequence order of execution is significant!
224 if ( !IsVirgin() )
225 return false;
226 if ( IsDeletedIn() )
227 return false;
228 if ( GetType() == SC_CAT_CONTENT )
229 {
230 ScChangeActionContent* pNextContent =
231 static_cast<const ScChangeActionContent*>(this)->GetNextContent();
232 if ( pNextContent == nullptr )
233 return true; // *this is TopContent
234 return pNextContent->IsRejected(); // *this is next rejectable
235 }
236 return IsTouchable();
237}
238
239bool ScChangeAction::IsDialogRoot() const
240{
241 return IsInternalRejectable(); // only rejectables in root
242}
243
244bool ScChangeAction::IsDialogParent() const
245{
246 // sequence order of execution is significant!
247 if ( GetType() == SC_CAT_CONTENT )
248 {
249 if ( !IsDialogRoot() )
250 return false;
251 if ( static_cast<const ScChangeActionContent*>(this)->IsMatrixOrigin() && HasDependent() )
252 return true;
253 ScChangeActionContent* pPrevContent =
254 static_cast<const ScChangeActionContent*>(this)->GetPrevContent();
255 return pPrevContent && pPrevContent->IsVirgin();
256 }
257 if ( HasDependent() )
258 return IsDeleteType() || !IsDeletedIn();
259 if ( HasDeleted() )
260 {
261 if ( IsDeleteType() )
262 {
263 if ( IsDialogRoot() )
264 return true;
265 ScChangeActionLinkEntry* pL = pLinkDeleted;
266 while ( pL )
267 {
268 ScChangeAction* p = pL->GetAction();
269 if ( p && p->GetType() != eType )
270 return true;
271 pL = pL->GetNext();
272 }
273 }
274 else
275 return true;
276 }
277 return false;
278}
279
280bool ScChangeAction::IsMasterDelete() const
281{
282 if ( !IsDeleteType() )
283 return false;
284 const ScChangeActionDel* pDel = static_cast<const ScChangeActionDel*>(this);
285 return pDel->IsMultiDelete() && (pDel->IsTopDelete() || pDel->IsRejectable());
286}
287
288void ScChangeAction::RemoveAllLinks()
289{
290 while (pLinkAny)
3
Loop condition is false. Execution continues on line 296
291 {
292 // coverity[use_after_free] - Moves up by itself
293 delete pLinkAny;
294 }
295
296 RemoveAllDeletedIn();
4
Calling 'ScChangeAction::RemoveAllDeletedIn'
297
298 while (pLinkDeleted)
299 {
300 // coverity[use_after_free] - Moves up by itself
301 delete pLinkDeleted;
302 }
303
304 RemoveAllDependent();
305}
306
307bool ScChangeAction::RemoveDeletedIn( const ScChangeAction* p )
308{
309 bool bRemoved = false;
310 ScChangeActionLinkEntry* pL = GetDeletedIn();
311 while ( pL )
312 {
313 ScChangeActionLinkEntry* pNextLink = pL->GetNext();
314 if ( pL->GetAction() == p )
315 {
316 delete pL;
317 bRemoved = true;
318 }
319 pL = pNextLink;
320 }
321 return bRemoved;
322}
323
324bool ScChangeAction::IsDeletedIn() const
325{
326 return GetDeletedIn() != nullptr;
327}
328
329bool ScChangeAction::IsDeletedIn( const ScChangeAction* p ) const
330{
331 ScChangeActionLinkEntry* pL = GetDeletedIn();
332 while ( pL )
333 {
334 if ( pL->GetAction() == p )
335 return true;
336 pL = pL->GetNext();
337 }
338 return false;
339}
340
341void ScChangeAction::RemoveAllDeletedIn()
342{
343 //TODO: Not from TopContent, but really this one
344 while (pLinkDeletedIn)
5
Loop condition is true. Entering loop body
7
Loop condition is true. Entering loop body
345 {
346 // coverity[use_after_free] - Moves up by itself
347 delete pLinkDeletedIn;
6
Memory is released
8
Attempt to delete released memory
348 }
349}
350
351bool ScChangeAction::IsDeletedInDelType( ScChangeActionType eDelType ) const
352{
353 ScChangeActionLinkEntry* pL = GetDeletedIn();
354 if ( pL )
355 {
356 // InsertType for MergePrepare/MergeOwn
357 ScChangeActionType eInsType;
358 switch ( eDelType )
359 {
360 case SC_CAT_DELETE_COLS :
361 eInsType = SC_CAT_INSERT_COLS;
362 break;
363 case SC_CAT_DELETE_ROWS :
364 eInsType = SC_CAT_INSERT_ROWS;
365 break;
366 case SC_CAT_DELETE_TABS :
367 eInsType = SC_CAT_INSERT_TABS;
368 break;
369 default:
370 eInsType = SC_CAT_NONE;
371 }
372 while ( pL )
373 {
374 ScChangeAction* p = pL->GetAction();
375 if ( p != nullptr && (p->GetType() == eDelType || p->GetType() == eInsType) )
376 return true;
377 pL = pL->GetNext();
378 }
379 }
380 return false;
381}
382
383bool ScChangeAction::HasDependent() const
384{
385 return pLinkDependent != nullptr;
386}
387
388bool ScChangeAction::HasDeleted() const
389{
390 return pLinkDeleted != nullptr;
391}
392
393void ScChangeAction::SetDeletedIn( ScChangeAction* p )
394{
395 ScChangeActionLinkEntry* pLink1 = new ScChangeActionLinkEntry( GetDeletedInAddress(), p );
396 ScChangeActionLinkEntry* pLink2;
397 if ( GetType() == SC_CAT_CONTENT )
398 pLink2 = p->AddDeleted( static_cast<ScChangeActionContent*>(this)->GetTopContent() );
399 else
400 pLink2 = p->AddDeleted( this );
401 pLink1->SetLink( pLink2 );
402}
403
404void ScChangeAction::RemoveAllDependent()
405{
406 while (pLinkDependent)
407 {
408 // coverity[use_after_free] - Moves up by itself
409 delete pLinkDependent;
410 }
411}
412
413DateTime ScChangeAction::GetDateTime() const
414{
415 DateTime aDT( aDateTime );
416 aDT.ConvertToLocalTime();
417 return aDT;
418}
419
420void ScChangeAction::UpdateReference( const ScChangeTrack* /* pTrack */,
421 UpdateRefMode eMode, const ScBigRange& rRange,
422 sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz )
423{
424 ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() );
425}
426
427void ScChangeAction::GetDescription(
428 OUString& rStr, ScDocument& /* rDoc */, bool /* bSplitRange */, bool bWarning ) const
429{
430 if (!IsRejecting() || !bWarning)
431 return;
432
433 // Add comment if rejection may have resulted in references
434 // not properly restored in formulas. See specification at
435 // http://specs.openoffice.org/calc/ease-of-use/redlining_comment.sxw
436
437 OUStringBuffer aBuf(rStr); // Take the original string.
438 if (GetType() == SC_CAT_MOVE)
439 {
440 aBuf.append(
441 ScResId(STR_CHANGED_MOVE_REJECTION_WARNINGreinterpret_cast<char const *>("STR_CHANGED_MOVE_REJECTION_WARNING"
"\004" u8"WARNING: This action may have resulted in unintended changes to cell references in formulas."
)
)).append(" ");
442 rStr = aBuf.makeStringAndClear();
443 return;
444 }
445
446 if (IsInsertType())
447 {
448 aBuf.append(
449 ScResId(STR_CHANGED_DELETE_REJECTION_WARNINGreinterpret_cast<char const *>("STR_CHANGED_DELETE_REJECTION_WARNING"
"\004" u8"WARNING: This action may have resulted in references to the deleted area not being restored."
)
)).append(" ");
450 rStr = aBuf.makeStringAndClear();
451 return;
452 }
453
454 const ScChangeTrack* pCT = GetChangeTrack();
455 if (!pCT)
456 return;
457
458 ScChangeAction* pReject = pCT->GetActionOrGenerated(GetRejectAction());
459
460 if (!pReject)
461 return;
462
463 if (pReject->GetType() == SC_CAT_MOVE)
464 {
465 aBuf.append(
466 ScResId(STR_CHANGED_MOVE_REJECTION_WARNINGreinterpret_cast<char const *>("STR_CHANGED_MOVE_REJECTION_WARNING"
"\004" u8"WARNING: This action may have resulted in unintended changes to cell references in formulas."
)
));
467 aBuf.append(' ');
468 rStr = aBuf.makeStringAndClear();
469 return;
470 }
471
472 if (pReject->IsDeleteType())
473 {
474 aBuf.append(
475 ScResId(STR_CHANGED_DELETE_REJECTION_WARNINGreinterpret_cast<char const *>("STR_CHANGED_DELETE_REJECTION_WARNING"
"\004" u8"WARNING: This action may have resulted in references to the deleted area not being restored."
)
));
476 aBuf.append(' ');
477 rStr = aBuf.makeStringAndClear();
478 return;
479 }
480
481 if (!pReject->HasDependent())
482 return;
483
484 ScChangeActionMap aMap;
485 pCT->GetDependents( pReject, aMap, false, true );
486 ScChangeActionMap::iterator itChangeAction = std::find_if(aMap.begin(), aMap.end(),
487 [&pReject](const ScChangeActionMap::value_type& rEntry) {
488 return rEntry.second->GetType() == SC_CAT_MOVE || pReject->IsDeleteType(); });
489 if (itChangeAction == aMap.end())
490 return;
491
492 if( itChangeAction->second->GetType() == SC_CAT_MOVE)
493 aBuf.append(
494 ScResId(STR_CHANGED_MOVE_REJECTION_WARNINGreinterpret_cast<char const *>("STR_CHANGED_MOVE_REJECTION_WARNING"
"\004" u8"WARNING: This action may have resulted in unintended changes to cell references in formulas."
)
));
495 else
496 aBuf.append(
497 ScResId(STR_CHANGED_DELETE_REJECTION_WARNINGreinterpret_cast<char const *>("STR_CHANGED_DELETE_REJECTION_WARNING"
"\004" u8"WARNING: This action may have resulted in references to the deleted area not being restored."
)
));
498
499 aBuf.append(' ');
500 rStr = aBuf.makeStringAndClear();
501 return;
502}
503
504OUString ScChangeAction::GetRefString(
505 const ScBigRange& rRange, const ScDocument& rDoc, bool bFlag3D ) const
506{
507 OUStringBuffer aBuf;
508 ScRefFlags nFlags = ( rRange.IsValid( rDoc ) ? ScRefFlags::VALID : ScRefFlags::ZERO );
509 if ( nFlags == ScRefFlags::ZERO )
510 aBuf.append(ScCompiler::GetNativeSymbol(ocErrRef));
511 else
512 {
513 ScRange aTmpRange( rRange.MakeRange() );
514 switch ( GetType() )
515 {
516 case SC_CAT_INSERT_COLS :
517 case SC_CAT_DELETE_COLS :
518 if ( bFlag3D )
519 {
520 OUString aTmp;
521 rDoc.GetName( aTmpRange.aStart.Tab(), aTmp );
522 aBuf.append(aTmp);
523 aBuf.append('.');
524 }
525 aBuf.append(ScColToAlpha(aTmpRange.aStart.Col()));
526 aBuf.append(':');
527 aBuf.append(ScColToAlpha(aTmpRange.aEnd.Col()));
528 break;
529 case SC_CAT_INSERT_ROWS :
530 case SC_CAT_DELETE_ROWS :
531 if ( bFlag3D )
532 {
533 OUString aTmp;
534 rDoc.GetName( aTmpRange.aStart.Tab(), aTmp );
535 aBuf.append(aTmp);
536 aBuf.append('.');
537 }
538 aBuf.append(static_cast<sal_Int32>(aTmpRange.aStart.Row()+1));
539 aBuf.append(':');
540 aBuf.append(static_cast<sal_Int32>(aTmpRange.aEnd.Row()+1));
541 break;
542 default:
543 {
544 if ( bFlag3D || GetType() == SC_CAT_INSERT_TABS )
545 nFlags |= ScRefFlags::TAB_3D;
546
547 aBuf.append(aTmpRange.Format(rDoc, nFlags, rDoc.GetAddressConvention()));
548 }
549 }
550 if ( (bFlag3D && IsDeleteType()) || IsDeletedIn() )
551 {
552 aBuf.insert(0, '(');
553 aBuf.append(')');
554 }
555 }
556 return aBuf.makeStringAndClear();
557}
558
559void ScChangeAction::SetUser( const OUString& r )
560{
561 aUser = r;
562}
563
564void ScChangeAction::SetComment( const OUString& rStr )
565{
566 aComment = rStr;
567}
568
569void ScChangeAction::GetRefString(
570 OUString& rStr, ScDocument& rDoc, bool bFlag3D ) const
571{
572 rStr = GetRefString( GetBigRange(), rDoc, bFlag3D );
573}
574
575void ScChangeAction::Accept()
576{
577 if ( IsVirgin() )
578 {
579 SetState( SC_CAS_ACCEPTED );
580 DeleteCellEntries();
581 }
582}
583
584void ScChangeAction::SetRejected()
585{
586 if ( IsVirgin() )
587 {
588 SetState( SC_CAS_REJECTED );
589 RemoveAllLinks();
590 DeleteCellEntries();
591 }
592}
593
594void ScChangeAction::RejectRestoreContents( ScChangeTrack* pTrack,
595 SCCOL nDx, SCROW nDy )
596{
597 // Construct list of Contents
598 std::vector<ScChangeActionContent*> aContentsList;
599 for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() )
600 {
601 ScChangeAction* p = pL->GetAction();
602 if ( p && p->GetType() == SC_CAT_CONTENT )
603 {
604 aContentsList.push_back(static_cast<ScChangeActionContent*>(p) );
605 }
606 }
607 SetState( SC_CAS_REJECTED ); // Before UpdateReference for Move
608 pTrack->UpdateReference( this, true ); // Free LinkDeleted
609 OSL_ENSURE( !pLinkDeleted, "ScChangeAction::RejectRestoreContents: pLinkDeleted != NULL" )do { if (true && (!(!pLinkDeleted))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "609" ": "), "%s", "ScChangeAction::RejectRestoreContents: pLinkDeleted != NULL"
); } } while (false)
;
610
611 // Work through list of Contents and delete
612 ScDocument& rDoc = pTrack->GetDocument();
613 for (ScChangeActionContent* pContent : aContentsList)
614 {
615 if ( !pContent->IsDeletedIn() &&
616 pContent->GetBigRange().aStart.IsValid( rDoc ) )
617 pContent->PutNewValueToDoc( &rDoc, nDx, nDy );
618 }
619 DeleteCellEntries(); // Remove generated ones
620}
621
622void ScChangeAction::SetDeletedInThis( sal_uLong nActionNumber,
623 const ScChangeTrack* pTrack )
624{
625 if ( nActionNumber )
626 {
627 ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber );
628 OSL_ENSURE( pAct, "ScChangeAction::SetDeletedInThis: missing Action" )do { if (true && (!(pAct))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN
), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "628" ": "), "%s", "ScChangeAction::SetDeletedInThis: missing Action"
); } } while (false)
;
629 if ( pAct )
630 pAct->SetDeletedIn( this );
631 }
632}
633
634void ScChangeAction::AddDependent( sal_uLong nActionNumber,
635 const ScChangeTrack* pTrack )
636{
637 if ( nActionNumber )
638 {
639 ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber );
640 OSL_ENSURE( pAct, "ScChangeAction::AddDependent: missing Action" )do { if (true && (!(pAct))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN
), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "640" ": "), "%s", "ScChangeAction::AddDependent: missing Action"
); } } while (false)
;
641 if ( pAct )
642 {
643 ScChangeActionLinkEntry* pLink = AddDependent( pAct );
644 pAct->AddLink( this, pLink );
645 }
646 }
647}
648
649// ScChangeActionIns
650ScChangeActionIns::ScChangeActionIns( const ScDocument* pDoc, const ScRange& rRange, bool bEndOfList ) :
651 ScChangeAction(SC_CAT_NONE, rRange),
652 mbEndOfList(bEndOfList)
653{
654 if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == pDoc->MaxCol() )
655 {
656 aBigRange.aStart.SetCol( nInt32Min );
657 aBigRange.aEnd.SetCol( nInt32Max );
658 if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
659 {
660 SetType( SC_CAT_INSERT_TABS );
661 aBigRange.aStart.SetRow( nInt32Min );
662 aBigRange.aEnd.SetRow( nInt32Max );
663 }
664 else
665 SetType( SC_CAT_INSERT_ROWS );
666 }
667 else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
668 {
669 SetType( SC_CAT_INSERT_COLS );
670 aBigRange.aStart.SetRow( nInt32Min );
671 aBigRange.aEnd.SetRow( nInt32Max );
672 }
673 else
674 {
675 OSL_FAIL( "ScChangeActionIns: Block not supported!" )do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "675" ": "), "%s", "ScChangeActionIns: Block not supported!"
); } } while (false)
;
676 }
677}
678
679ScChangeActionIns::ScChangeActionIns(
680 const sal_uLong nActionNumber, const ScChangeActionState eStateP,
681 const sal_uLong nRejectingNumber, const ScBigRange& aBigRangeP,
682 const OUString& aUserP, const DateTime& aDateTimeP,
683 const OUString& sComment, const ScChangeActionType eTypeP,
684 bool bEndOfList ) :
685 ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
686 mbEndOfList(bEndOfList)
687{
688}
689
690ScChangeActionIns::~ScChangeActionIns()
691{
692}
1
Calling '~ScChangeAction'
693
694void ScChangeActionIns::GetDescription(
695 OUString& rStr, ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
696{
697 ScChangeAction::GetDescription( rStr, rDoc, bSplitRange, bWarning );
698
699 const char* pWhatId;
700 switch ( GetType() )
701 {
702 case SC_CAT_INSERT_COLS :
703 pWhatId = STR_COLUMNreinterpret_cast<char const *>("STR_COLUMN" "\004" u8"Column"
)
;
704 break;
705 case SC_CAT_INSERT_ROWS :
706 pWhatId = STR_ROWreinterpret_cast<char const *>("STR_ROW" "\004" u8"Row"
)
;
707 break;
708 default:
709 pWhatId = STR_AREAreinterpret_cast<char const *>("STR_AREA" "\004" u8"Range"
)
;
710 }
711
712 OUString aRsc = ScResId(STR_CHANGED_INSERTreinterpret_cast<char const *>("STR_CHANGED_INSERT" "\004"
u8"#1 inserted")
);
713 sal_Int32 nPos = aRsc.indexOf("#1");
714 if (nPos < 0)
715 return;
716
717 // Construct a range string to replace '#1' first.
718 OUStringBuffer aBuf(ScResId(pWhatId));
719 aBuf.append(' ');
720 aBuf.append(GetRefString(GetBigRange(), rDoc));
721 OUString aRangeStr = aBuf.makeStringAndClear();
722
723 aRsc = aRsc.replaceAt(nPos, 2, aRangeStr); // replace '#1' with the range string.
724
725 aBuf.append(rStr).append(aRsc);
726 rStr = aBuf.makeStringAndClear();
727}
728
729bool ScChangeActionIns::IsEndOfList() const
730{
731 return mbEndOfList;
732}
733
734bool ScChangeActionIns::Reject( ScDocument& rDoc )
735{
736 if ( !aBigRange.IsValid( rDoc ) )
737 return false;
738
739 ScRange aRange( aBigRange.MakeRange() );
740 if ( !rDoc.IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(),
741 aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) )
742 return false;
743
744 switch ( GetType() )
745 {
746 case SC_CAT_INSERT_COLS :
747 rDoc.DeleteCol( aRange );
748 break;
749 case SC_CAT_INSERT_ROWS :
750 rDoc.DeleteRow( aRange );
751 break;
752 case SC_CAT_INSERT_TABS :
753 rDoc.DeleteTab( aRange.aStart.Tab() );
754 break;
755 default:
756 {
757 // added to avoid warnings
758 }
759 }
760 SetState( SC_CAS_REJECTED );
761 RemoveAllLinks();
762 return true;
763}
764
765// ScChangeActionDel
766ScChangeActionDel::ScChangeActionDel( const ScDocument* pDoc, const ScRange& rRange,
767 SCCOL nDxP, SCROW nDyP, ScChangeTrack* pTrackP )
768 :
769 ScChangeAction( SC_CAT_NONE, rRange ),
770 pTrack( pTrackP ),
771 pCutOff( nullptr ),
772 nCutOff( 0 ),
773 pLinkMove( nullptr ),
774 nDx( nDxP ),
775 nDy( nDyP )
776{
777 if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == pDoc->MaxCol() )
778 {
779 aBigRange.aStart.SetCol( nInt32Min );
780 aBigRange.aEnd.SetCol( nInt32Max );
781 if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
782 {
783 SetType( SC_CAT_DELETE_TABS );
784 aBigRange.aStart.SetRow( nInt32Min );
785 aBigRange.aEnd.SetRow( nInt32Max );
786 }
787 else
788 SetType( SC_CAT_DELETE_ROWS );
789 }
790 else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
791 {
792 SetType( SC_CAT_DELETE_COLS );
793 aBigRange.aStart.SetRow( nInt32Min );
794 aBigRange.aEnd.SetRow( nInt32Max );
795 }
796 else
797 {
798 OSL_FAIL( "ScChangeActionDel: Block not supported!" )do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "798" ": "), "%s", "ScChangeActionDel: Block not supported!"
); } } while (false)
;
799 }
800}
801
802ScChangeActionDel::ScChangeActionDel(
803 const sal_uLong nActionNumber, const ScChangeActionState eStateP,
804 const sal_uLong nRejectingNumber, const ScBigRange& aBigRangeP,
805 const OUString& aUserP, const DateTime& aDateTimeP, const OUString &sComment,
806 const ScChangeActionType eTypeP, const SCCOLROW nD, ScChangeTrack* pTrackP) : // which of nDx and nDy is set depends on the type
807 ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
808 pTrack( pTrackP ),
809 pCutOff( nullptr ),
810 nCutOff( 0 ),
811 pLinkMove( nullptr ),
812 nDx( 0 ),
813 nDy( 0 )
814{
815 if (eType == SC_CAT_DELETE_COLS)
816 nDx = static_cast<SCCOL>(nD);
817 else if (eType == SC_CAT_DELETE_ROWS)
818 nDy = static_cast<SCROW>(nD);
819}
820
821ScChangeActionDel::~ScChangeActionDel()
822{
823 DeleteCellEntries();
824 while (pLinkMove)
825 {
826 // coverity[use_after_free] - Moves up by itself
827 delete pLinkMove;
828 }
829}
830
831void ScChangeActionDel::AddContent( ScChangeActionContent* pContent )
832{
833 mvCells.push_back(pContent);
834}
835
836void ScChangeActionDel::DeleteCellEntries()
837{
838 pTrack->DeleteCellEntries( mvCells, this );
839}
840
841bool ScChangeActionDel::IsBaseDelete() const
842{
843 return !GetDx() && !GetDy();
844}
845
846bool ScChangeActionDel::IsTopDelete() const
847{
848 const ScChangeAction* p = GetNext();
849 if ( !p || p->GetType() != GetType() )
850 return true;
851 return static_cast<const ScChangeActionDel*>(p)->IsBaseDelete();
852}
853
854bool ScChangeActionDel::IsMultiDelete() const
855{
856 if ( GetDx() || GetDy() )
857 return true;
858 const ScChangeAction* p = GetNext();
859 if ( !p || p->GetType() != GetType() )
860 return false;
861 const ScChangeActionDel* pDel = static_cast<const ScChangeActionDel*>(p);
862 return (pDel->GetDx() > GetDx() || pDel->GetDy() > GetDy()) &&
863 pDel->GetBigRange() == aBigRange;
864}
865
866bool ScChangeActionDel::IsTabDeleteCol() const
867{
868 if ( GetType() != SC_CAT_DELETE_COLS )
869 return false;
870 const ScChangeAction* p = this;
871 while ( p && p->GetType() == SC_CAT_DELETE_COLS &&
872 !static_cast<const ScChangeActionDel*>(p)->IsTopDelete() )
873 p = p->GetNext();
874 return p && p->GetType() == SC_CAT_DELETE_TABS;
875}
876
877ScChangeActionDelMoveEntry* ScChangeActionDel::AddCutOffMove(
878 ScChangeActionMove* pMove, short nFrom, short nTo )
879{
880 return new ScChangeActionDelMoveEntry(&pLinkMove, pMove, nFrom, nTo);
881}
882
883void ScChangeActionDel::UpdateReference( const ScChangeTrack* /* pTrack */,
884 UpdateRefMode eMode, const ScBigRange& rRange,
885 sal_Int32 nDxP, sal_Int32 nDyP, sal_Int32 nDz )
886{
887 ScRefUpdate::Update( eMode, rRange, nDxP, nDyP, nDz, GetBigRange() );
888
889 if ( !IsDeletedIn() )
890 return ;
891
892 // Correct in the ones who slipped through
893 for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() )
894 {
895 ScChangeAction* p = pL->GetAction();
896 if ( p && p->GetType() == SC_CAT_CONTENT &&
897 !GetBigRange().In( p->GetBigRange() ) )
898 {
899 switch ( GetType() )
900 {
901 case SC_CAT_DELETE_COLS :
902 p->GetBigRange().aStart.SetCol( GetBigRange().aStart.Col() );
903 p->GetBigRange().aEnd.SetCol( GetBigRange().aStart.Col() );
904 break;
905 case SC_CAT_DELETE_ROWS :
906 p->GetBigRange().aStart.SetRow( GetBigRange().aStart.Row() );
907 p->GetBigRange().aEnd.SetRow( GetBigRange().aStart.Row() );
908 break;
909 case SC_CAT_DELETE_TABS :
910 p->GetBigRange().aStart.SetTab( GetBigRange().aStart.Tab() );
911 p->GetBigRange().aEnd.SetTab( GetBigRange().aStart.Tab() );
912 break;
913 default:
914 {
915 // added to avoid warnings
916 }
917 }
918 }
919 }
920}
921
922ScBigRange ScChangeActionDel::GetOverAllRange() const
923{
924 ScBigRange aTmpRange( GetBigRange() );
925 aTmpRange.aEnd.SetCol( aTmpRange.aEnd.Col() + GetDx() );
926 aTmpRange.aEnd.SetRow( aTmpRange.aEnd.Row() + GetDy() );
927 return aTmpRange;
928}
929
930void ScChangeActionDel::GetDescription(
931 OUString& rStr, ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
932{
933 ScChangeAction::GetDescription( rStr, rDoc, bSplitRange, bWarning );
934
935 const char* pWhatId;
936 switch ( GetType() )
937 {
938 case SC_CAT_DELETE_COLS :
939 pWhatId = STR_COLUMNreinterpret_cast<char const *>("STR_COLUMN" "\004" u8"Column"
)
;
940 break;
941 case SC_CAT_DELETE_ROWS :
942 pWhatId = STR_ROWreinterpret_cast<char const *>("STR_ROW" "\004" u8"Row"
)
;
943 break;
944 default:
945 pWhatId = STR_AREAreinterpret_cast<char const *>("STR_AREA" "\004" u8"Range"
)
;
946 }
947
948 ScBigRange aTmpRange( GetBigRange() );
949 if ( !IsRejected() )
950 {
951 if ( bSplitRange )
952 {
953 aTmpRange.aStart.SetCol( aTmpRange.aStart.Col() + GetDx() );
954 aTmpRange.aStart.SetRow( aTmpRange.aStart.Row() + GetDy() );
955 }
956 aTmpRange.aEnd.SetCol( aTmpRange.aEnd.Col() + GetDx() );
957 aTmpRange.aEnd.SetRow( aTmpRange.aEnd.Row() + GetDy() );
958 }
959
960 OUString aRsc = ScResId(STR_CHANGED_DELETEreinterpret_cast<char const *>("STR_CHANGED_DELETE" "\004"
u8"#1 deleted")
);
961 sal_Int32 nPos = aRsc.indexOf("#1");
962 if (nPos < 0)
963 return;
964
965 // Build a string to replace with.
966 OUStringBuffer aBuf;
967 aBuf.append(ScResId(pWhatId));
968 aBuf.append(' ');
969 aBuf.append(GetRefString(aTmpRange, rDoc));
970 OUString aRangeStr = aBuf.makeStringAndClear();
971 aRsc = aRsc.replaceAt(nPos, 2, aRangeStr); // replace '#1' with the string.
972
973 aBuf.append(rStr).append(aRsc);
974 rStr = aBuf.makeStringAndClear(); // append to the original.
975}
976
977bool ScChangeActionDel::Reject( ScDocument& rDoc )
978{
979 if ( !aBigRange.IsValid( rDoc ) && GetType() != SC_CAT_DELETE_TABS )
980 return false;
981
982 if ( IsTopDelete() )
983 { // Restore whole section in one go
984 bool bOk = true;
985 ScBigRange aTmpRange( GetOverAllRange() );
986 if ( !aTmpRange.IsValid( rDoc ) )
987 {
988 if ( GetType() == SC_CAT_DELETE_TABS )
989 { // Do we attach a Tab?
990 if ( aTmpRange.aStart.Tab() > rDoc.GetMaxTableNumber() )
991 bOk = false;
992 }
993 else
994 bOk = false;
995 }
996 if ( bOk )
997 {
998 ScRange aRange( aTmpRange.MakeRange() );
999 // InDelete... for formula UpdateReference in Document
1000 pTrack->SetInDeleteRange( aRange );
1001 pTrack->SetInDeleteTop( true );
1002 pTrack->SetInDeleteUndo( true );
1003 pTrack->SetInDelete( true );
1004 switch ( GetType() )
1005 {
1006 case SC_CAT_DELETE_COLS :
1007 if ( aRange.aStart.Col() != 0 || aRange.aEnd.Col() != rDoc.MaxCol() )
1008 { // Only if not TabDelete
1009 bOk = rDoc.CanInsertCol( aRange ) && rDoc.InsertCol( aRange );
1010 }
1011 break;
1012 case SC_CAT_DELETE_ROWS :
1013 bOk = rDoc.CanInsertRow( aRange ) && rDoc.InsertRow( aRange );
1014 break;
1015 case SC_CAT_DELETE_TABS :
1016 {
1017 //TODO: Remember table names?
1018 OUString aName;
1019 rDoc.CreateValidTabName( aName );
1020 bOk = rDoc.ValidNewTabName( aName ) && rDoc.InsertTab( aRange.aStart.Tab(), aName );
1021 }
1022 break;
1023 default:
1024 {
1025 // added to avoid warnings
1026 }
1027 }
1028 pTrack->SetInDelete( false );
1029 pTrack->SetInDeleteUndo( false );
1030 }
1031 if ( !bOk )
1032 {
1033 pTrack->SetInDeleteTop( false );
1034 return false;
1035 }
1036 // Keep InDeleteTop for UpdateReference Undo
1037 }
1038
1039 // Sets rejected and calls UpdateReference-Undo and DeleteCellEntries
1040 RejectRestoreContents( pTrack, GetDx(), GetDy() );
1041
1042 pTrack->SetInDeleteTop( false );
1043 RemoveAllLinks();
1044 return true;
1045}
1046
1047void ScChangeActionDel::UndoCutOffMoves()
1048{ // Restore cut off Moves; delete Entries/Links
1049 while ( pLinkMove )
1050 {
1051 // coverity[deref_arg] - the call on delete pLinkMove at the block end Moves a new entry into pLinkMode by itself
1052 ScChangeActionMove* pMove = pLinkMove->GetMove();
1053 short nFrom = pLinkMove->GetCutOffFrom();
1054 short nTo = pLinkMove->GetCutOffTo();
1055 switch ( GetType() )
1056 {
1057 case SC_CAT_DELETE_COLS :
1058 if ( nFrom > 0 )
1059 pMove->GetFromRange().aStart.IncCol( -nFrom );
1060 else if ( nFrom < 0 )
1061 pMove->GetFromRange().aEnd.IncCol( -nFrom );
1062 if ( nTo > 0 )
1063 pMove->GetBigRange().aStart.IncCol( -nTo );
1064 else if ( nTo < 0 )
1065 pMove->GetBigRange().aEnd.IncCol( -nTo );
1066 break;
1067 case SC_CAT_DELETE_ROWS :
1068 if ( nFrom > 0 )
1069 pMove->GetFromRange().aStart.IncRow( -nFrom );
1070 else if ( nFrom < 0 )
1071 pMove->GetFromRange().aEnd.IncRow( -nFrom );
1072 if ( nTo > 0 )
1073 pMove->GetBigRange().aStart.IncRow( -nTo );
1074 else if ( nTo < 0 )
1075 pMove->GetBigRange().aEnd.IncRow( -nTo );
1076 break;
1077 case SC_CAT_DELETE_TABS :
1078 if ( nFrom > 0 )
1079 pMove->GetFromRange().aStart.IncTab( -nFrom );
1080 else if ( nFrom < 0 )
1081 pMove->GetFromRange().aEnd.IncTab( -nFrom );
1082 if ( nTo > 0 )
1083 pMove->GetBigRange().aStart.IncTab( -nTo );
1084 else if ( nTo < 0 )
1085 pMove->GetBigRange().aEnd.IncTab( -nTo );
1086 break;
1087 default:
1088 {
1089 // added to avoid warnings
1090 }
1091 }
1092 delete pLinkMove; // Moves up by itself
1093 }
1094}
1095
1096void ScChangeActionDel::UndoCutOffInsert()
1097{ //Restore cut off Insert
1098 if ( !pCutOff )
1099 return;
1100
1101 switch ( pCutOff->GetType() )
1102 {
1103 case SC_CAT_INSERT_COLS :
1104 if ( nCutOff < 0 )
1105 pCutOff->GetBigRange().aEnd.IncCol( -nCutOff );
1106 else
1107 pCutOff->GetBigRange().aStart.IncCol( -nCutOff );
1108 break;
1109 case SC_CAT_INSERT_ROWS :
1110 if ( nCutOff < 0 )
1111 pCutOff->GetBigRange().aEnd.IncRow( -nCutOff );
1112 else
1113 pCutOff->GetBigRange().aStart.IncRow( -nCutOff );
1114 break;
1115 case SC_CAT_INSERT_TABS :
1116 if ( nCutOff < 0 )
1117 pCutOff->GetBigRange().aEnd.IncTab( -nCutOff );
1118 else
1119 pCutOff->GetBigRange().aStart.IncTab( -nCutOff );
1120 break;
1121 default:
1122 {
1123 // added to avoid warnings
1124 }
1125 }
1126 SetCutOffInsert( nullptr, 0 );
1127}
1128
1129// ScChangeActionMove
1130ScChangeActionMove::ScChangeActionMove(
1131 const sal_uLong nActionNumber, const ScChangeActionState eStateP,
1132 const sal_uLong nRejectingNumber, const ScBigRange& aToBigRange,
1133 const OUString& aUserP, const DateTime& aDateTimeP,
1134 const OUString &sComment, const ScBigRange& aFromBigRange,
1135 ScChangeTrack* pTrackP) : // which of nDx and nDy is set depends on the type
1136 ScChangeAction(SC_CAT_MOVE, aToBigRange, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
1137 aFromRange(aFromBigRange),
1138 pTrack( pTrackP ),
1139 nStartLastCut(0),
1140 nEndLastCut(0)
1141{
1142}
1143
1144ScChangeActionMove::~ScChangeActionMove()
1145{
1146 DeleteCellEntries();
1147}
1148
1149void ScChangeActionMove::AddContent( ScChangeActionContent* pContent )
1150{
1151 mvCells.push_back(pContent);
1152}
1153
1154void ScChangeActionMove::DeleteCellEntries()
1155{
1156 pTrack->DeleteCellEntries( mvCells, this );
1157}
1158
1159void ScChangeActionMove::UpdateReference( const ScChangeTrack* /* pTrack */,
1160 UpdateRefMode eMode, const ScBigRange& rRange,
1161 sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz )
1162{
1163 ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aFromRange );
1164 ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() );
1165}
1166
1167void ScChangeActionMove::GetDelta( sal_Int32& nDx, sal_Int32& nDy, sal_Int32& nDz ) const
1168{
1169 const ScBigAddress& rToPos = GetBigRange().aStart;
1170 const ScBigAddress& rFromPos = GetFromRange().aStart;
1171 nDx = rToPos.Col() - rFromPos.Col();
1172 nDy = rToPos.Row() - rFromPos.Row();
1173 nDz = rToPos.Tab() - rFromPos.Tab();
1174}
1175
1176void ScChangeActionMove::GetDescription(
1177 OUString& rStr, ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
1178{
1179 ScChangeAction::GetDescription( rStr, rDoc, bSplitRange, bWarning );
1180
1181 bool bFlag3D = GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab();
1182
1183 OUString aRsc = ScResId(STR_CHANGED_MOVEreinterpret_cast<char const *>("STR_CHANGED_MOVE" "\004"
u8"Range moved from #1 to #2")
);
1184
1185 OUString aTmpStr = ScChangeAction::GetRefString(GetFromRange(), rDoc, bFlag3D);
1186 sal_Int32 nPos = aRsc.indexOf("#1");
1187 if (nPos >= 0)
1188 {
1189 aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
1190 nPos += aTmpStr.getLength();
1191 }
1192
1193 aTmpStr = ScChangeAction::GetRefString(GetBigRange(), rDoc, bFlag3D);
1194 nPos = nPos >= 0 ? aRsc.indexOf("#2", nPos) : -1;
1195 if (nPos >= 0)
1196 {
1197 aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
1198 }
1199
1200 rStr += aRsc; // append to the original string.
1201}
1202
1203void ScChangeActionMove::GetRefString(
1204 OUString& rStr, ScDocument& rDoc, bool bFlag3D ) const
1205{
1206 if ( !bFlag3D )
1207 bFlag3D = ( GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab() );
1208
1209 // overwrite existing string value.
1210 rStr = ScChangeAction::GetRefString(GetFromRange(), rDoc, bFlag3D)
1211 + ", "
1212 + ScChangeAction::GetRefString(GetBigRange(), rDoc, bFlag3D);
1213}
1214
1215bool ScChangeActionMove::Reject( ScDocument& rDoc )
1216{
1217 if ( !(aBigRange.IsValid( rDoc ) && aFromRange.IsValid( rDoc )) )
1218 return false;
1219
1220 ScRange aToRange( aBigRange.MakeRange() );
1221 ScRange aFrmRange( aFromRange.MakeRange() );
1222
1223 bool bOk = rDoc.IsBlockEditable( aToRange.aStart.Tab(),
1224 aToRange.aStart.Col(), aToRange.aStart.Row(),
1225 aToRange.aEnd.Col(), aToRange.aEnd.Row() );
1226 if ( bOk )
1227 bOk = rDoc.IsBlockEditable( aFrmRange.aStart.Tab(),
1228 aFrmRange.aStart.Col(), aFrmRange.aStart.Row(),
1229 aFrmRange.aEnd.Col(), aFrmRange.aEnd.Row() );
1230 if ( !bOk )
1231 return false;
1232
1233 pTrack->LookUpContents( aToRange, &rDoc, 0, 0, 0 ); // Contents to be moved
1234
1235 rDoc.DeleteAreaTab( aToRange, InsertDeleteFlags::ALL );
1236 rDoc.DeleteAreaTab( aFrmRange, InsertDeleteFlags::ALL );
1237 // Adjust formula in the Document
1238 sc::RefUpdateContext aCxt(rDoc);
1239 aCxt.meMode = URM_MOVE;
1240 aCxt.maRange = aFrmRange;
1241 aCxt.mnColDelta = aFrmRange.aStart.Col() - aToRange.aStart.Col();
1242 aCxt.mnRowDelta = aFrmRange.aStart.Row() - aToRange.aStart.Row();
1243 aCxt.mnTabDelta = aFrmRange.aStart.Tab() - aToRange.aStart.Tab();
1244 rDoc.UpdateReference(aCxt);
1245
1246 // Free LinkDependent, set succeeding UpdateReference Undo
1247 // ToRange->FromRange Dependents
1248 RemoveAllDependent();
1249
1250 // Sets rejected and calls UpdateReference Undo and DeleteCellEntries
1251 RejectRestoreContents( pTrack, 0, 0 );
1252
1253 while ( pLinkDependent )
1254 {
1255 ScChangeAction* p = pLinkDependent->GetAction();
1256 if ( p && p->GetType() == SC_CAT_CONTENT )
1257 {
1258 ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(p);
1259 if ( !pContent->IsDeletedIn() &&
1260 pContent->GetBigRange().aStart.IsValid( rDoc ) )
1261 pContent->PutNewValueToDoc( &rDoc, 0, 0 );
1262 // Delete the ones created in LookUpContents
1263 if ( pTrack->IsGenerated( pContent->GetActionNumber() ) &&
1264 !pContent->IsDeletedIn() )
1265 {
1266 pLinkDependent->UnLink(); // Else this one is also deleted!
1267 pTrack->DeleteGeneratedDelContent( pContent );
1268 }
1269 }
1270 delete pLinkDependent;
1271 }
1272
1273 RemoveAllLinks();
1274 return true;
1275}
1276
1277ScChangeActionContent::ScChangeActionContent( const ScRange& rRange ) :
1278 ScChangeAction(SC_CAT_CONTENT, rRange),
1279 pNextContent(nullptr),
1280 pPrevContent(nullptr),
1281 pNextInSlot(nullptr),
1282 ppPrevInSlot(nullptr)
1283{}
1284
1285ScChangeActionContent::ScChangeActionContent( const sal_uLong nActionNumber,
1286 const ScChangeActionState eStateP, const sal_uLong nRejectingNumber,
1287 const ScBigRange& aBigRangeP, const OUString& aUserP,
1288 const DateTime& aDateTimeP, const OUString& sComment,
1289 const ScCellValue& rOldCell, const ScDocument* pDoc, const OUString& sOldValue ) :
1290 ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
1291 maOldCell(rOldCell),
1292 maOldValue(sOldValue),
1293 pNextContent(nullptr),
1294 pPrevContent(nullptr),
1295 pNextInSlot(nullptr),
1296 ppPrevInSlot(nullptr)
1297{
1298 if (!maOldCell.isEmpty())
1299 SetCell(maOldValue, maOldCell, 0, pDoc);
1300
1301 if (!sOldValue.isEmpty()) // #i40704# don't overwrite SetCell result with empty string
1302 maOldValue = sOldValue; // set again, because SetCell removes it
1303}
1304
1305ScChangeActionContent::ScChangeActionContent( const sal_uLong nActionNumber,
1306 const ScCellValue& rNewCell, const ScBigRange& aBigRangeP,
1307 const ScDocument* pDoc, const OUString& sNewValue ) :
1308 ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber),
1309 maNewCell(rNewCell),
1310 maNewValue(sNewValue),
1311 pNextContent(nullptr),
1312 pPrevContent(nullptr),
1313 pNextInSlot(nullptr),
1314 ppPrevInSlot(nullptr)
1315{
1316 if (!maNewCell.isEmpty())
1317 SetCell(maNewValue, maNewCell, 0, pDoc);
1318
1319 if (!sNewValue.isEmpty()) // #i40704# don't overwrite SetCell result with empty string
1320 maNewValue = sNewValue; // set again, because SetCell removes it
1321}
1322
1323ScChangeActionContent::~ScChangeActionContent()
1324{
1325 ClearTrack();
1326}
1327
1328void ScChangeActionContent::ClearTrack()
1329{
1330 RemoveFromSlot();
1331 if ( pPrevContent )
1332 pPrevContent->pNextContent = pNextContent;
1333 if ( pNextContent )
1334 pNextContent->pPrevContent = pPrevContent;
1335}
1336
1337ScChangeActionContent* ScChangeActionContent::GetTopContent() const
1338{
1339 if ( pNextContent )
1340 {
1341 ScChangeActionContent* pContent = pNextContent;
1342 while ( pContent->pNextContent && pContent != pContent->pNextContent )
1343 pContent = pContent->pNextContent;
1344 return pContent;
1345 }
1346 return const_cast<ScChangeActionContent*>(this);
1347}
1348
1349ScChangeActionLinkEntry* ScChangeActionContent::GetDeletedIn() const
1350{
1351 if ( pNextContent )
1352 return GetTopContent()->pLinkDeletedIn;
1353 return pLinkDeletedIn;
1354}
1355
1356ScChangeActionLinkEntry** ScChangeActionContent::GetDeletedInAddress()
1357{
1358 if ( pNextContent )
1359 return GetTopContent()->GetDeletedInAddress();
1360 return &pLinkDeletedIn;
1361}
1362
1363void ScChangeActionContent::SetOldValue(
1364 const ScCellValue& rCell, const ScDocument* pFromDoc, ScDocument* pToDoc, sal_uLong nFormat )
1365{
1366 SetValue(maOldValue, maOldCell, nFormat, rCell, pFromDoc, pToDoc);
1367}
1368
1369void ScChangeActionContent::SetOldValue(
1370 const ScCellValue& rCell, const ScDocument* pFromDoc, ScDocument* pToDoc )
1371{
1372 SetValue(maOldValue, maOldCell, aBigRange.aStart.MakeAddress(), rCell, pFromDoc, pToDoc);
1373}
1374
1375void ScChangeActionContent::SetNewValue( const ScCellValue& rCell, ScDocument* pDoc )
1376{
1377 SetValue(maNewValue, maNewCell, aBigRange.aStart.MakeAddress(), rCell, pDoc, pDoc);
1378}
1379
1380void ScChangeActionContent::SetOldNewCells(
1381 const ScCellValue& rOldCell, sal_uLong nOldFormat, const ScCellValue& rNewCell,
1382 sal_uLong nNewFormat, const ScDocument* pDoc )
1383{
1384 maOldCell = rOldCell;
1385 maNewCell = rNewCell;
1386 SetCell(maOldValue, maOldCell, nOldFormat, pDoc);
1387 SetCell(maNewValue, maNewCell, nNewFormat, pDoc);
1388}
1389
1390void ScChangeActionContent::SetNewCell(
1391 const ScCellValue& rCell, const ScDocument* pDoc, const OUString& rFormatted )
1392{
1393 maNewCell = rCell;
1394 SetCell(maNewValue, maNewCell, 0, pDoc);
1395
1396 // #i40704# allow to set formatted text here - don't call SetNewValue with string from XML filter
1397 if (!rFormatted.isEmpty())
1398 maNewValue = rFormatted;
1399}
1400
1401void ScChangeActionContent::SetValueString(
1402 OUString& rValue, ScCellValue& rCell, const OUString& rStr, ScDocument* pDoc )
1403{
1404 rCell.clear();
1405 if ( rStr.getLength() > 1 && rStr[0] == '=' )
1406 {
1407 rValue = EMPTY_OUSTRINGScGlobal::GetEmptyOUString();
1408 rCell.meType = CELLTYPE_FORMULA;
1409 rCell.mpFormula = new ScFormulaCell(
1410 *pDoc, aBigRange.aStart.MakeAddress(), rStr,
1411 pDoc->GetGrammar() );
1412 rCell.mpFormula->SetInChangeTrack(true);
1413 }
1414 else
1415 rValue = rStr;
1416}
1417
1418void ScChangeActionContent::SetOldValue( const OUString& rOld, ScDocument* pDoc )
1419{
1420 SetValueString(maOldValue, maOldCell, rOld, pDoc);
1421}
1422
1423void ScChangeActionContent::GetOldString( OUString& rStr, const ScDocument* pDoc ) const
1424{
1425 GetValueString(rStr, maOldValue, maOldCell, pDoc);
1426}
1427
1428void ScChangeActionContent::GetNewString( OUString& rStr, const ScDocument* pDoc ) const
1429{
1430 GetValueString(rStr, maNewValue, maNewCell, pDoc);
1431}
1432
1433void ScChangeActionContent::GetDescription(
1434 OUString& rStr, ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
1435{
1436 ScChangeAction::GetDescription( rStr, rDoc, bSplitRange, bWarning );
1437
1438 OUString aRsc = ScResId(STR_CHANGED_CELLreinterpret_cast<char const *>("STR_CHANGED_CELL" "\004"
u8"Cell #1 changed from '#2' to '#3'")
);
1439
1440 OUString aTmpStr;
1441 GetRefString(aTmpStr, rDoc);
1442
1443 sal_Int32 nPos = aRsc.indexOf("#1", 0);
1444 if (nPos >= 0)
1445 {
1446 aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
1447 nPos += aTmpStr.getLength();
1448 }
1449
1450 GetOldString( aTmpStr, &rDoc );
1451 if (aTmpStr.isEmpty())
1452 aTmpStr = ScResId( STR_CHANGED_BLANKreinterpret_cast<char const *>("STR_CHANGED_BLANK" "\004"
u8"<empty>")
);
1453
1454 nPos = nPos >= 0 ? aRsc.indexOf("#2", nPos) : -1;
1455 if (nPos >= 0)
1456 {
1457 aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
1458 nPos += aTmpStr.getLength();
1459 }
1460
1461 GetNewString( aTmpStr, &rDoc );
1462 if (aTmpStr.isEmpty())
1463 aTmpStr = ScResId( STR_CHANGED_BLANKreinterpret_cast<char const *>("STR_CHANGED_BLANK" "\004"
u8"<empty>")
);
1464
1465 nPos = nPos >= 0 ? aRsc.indexOf("#3", nPos) : -1;
1466 if (nPos >= 0)
1467 {
1468 aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
1469 }
1470
1471 rStr += aRsc; // append to the original string.
1472}
1473
1474void ScChangeActionContent::GetRefString(
1475 OUString& rStr, ScDocument& rDoc, bool bFlag3D ) const
1476{
1477 ScRefFlags nFlags = ( GetBigRange().IsValid( rDoc ) ? ScRefFlags::VALID : ScRefFlags::ZERO );
1478 if ( nFlags != ScRefFlags::ZERO )
1479 {
1480 const ScCellValue& rCell = GetNewCell();
1481 if ( GetContentCellType(rCell) == SC_CACCT_MATORG )
1482 {
1483 ScBigRange aLocalBigRange( GetBigRange() );
1484 SCCOL nC;
1485 SCROW nR;
1486 rCell.mpFormula->GetMatColsRows( nC, nR );
1487 aLocalBigRange.aEnd.IncCol( nC-1 );
1488 aLocalBigRange.aEnd.IncRow( nR-1 );
1489 rStr = ScChangeAction::GetRefString( aLocalBigRange, rDoc, bFlag3D );
1490
1491 return ;
1492 }
1493
1494 ScAddress aTmpAddress( GetBigRange().aStart.MakeAddress() );
1495 if ( bFlag3D )
1496 nFlags |= ScRefFlags::TAB_3D;
1497 rStr = aTmpAddress.Format(nFlags, &rDoc, rDoc.GetAddressConvention());
1498 if ( IsDeletedIn() )
1499 {
1500 // Insert the parentheses.
1501 rStr = "(" + rStr + ")";
1502 }
1503 }
1504 else
1505 rStr = ScCompiler::GetNativeSymbol(ocErrRef);
1506}
1507
1508bool ScChangeActionContent::Reject( ScDocument& rDoc )
1509{
1510 if ( !aBigRange.IsValid( rDoc ) )
1511 return false;
1512
1513 PutOldValueToDoc( &rDoc, 0, 0 );
1514
1515 SetState( SC_CAS_REJECTED );
1516 RemoveAllLinks();
1517
1518 return true;
1519}
1520
1521bool ScChangeActionContent::Select( ScDocument& rDoc, ScChangeTrack* pTrack,
1522 bool bOldest, ::std::stack<ScChangeActionContent*>* pRejectActions )
1523{
1524 if ( !aBigRange.IsValid( rDoc ) )
1525 return false;
1526
1527 ScChangeActionContent* pContent = this;
1528 // accept previous contents
1529 while ( ( pContent = pContent->pPrevContent ) != nullptr )
1530 {
1531 if ( pContent->IsVirgin() )
1532 pContent->SetState( SC_CAS_ACCEPTED );
1533 }
1534 ScChangeActionContent* pEnd = pContent = this;
1535 // reject subsequent contents
1536 while ( ( pContent = pContent->pNextContent ) != nullptr )
1537 {
1538 // MatrixOrigin may have dependents, no dependency recursion needed
1539 const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry();
1540 while ( pL )
1541 {
1542 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
1543 if ( p )
1544 p->SetRejected();
1545 pL = pL->GetNext();
1546 }
1547 pContent->SetRejected();
1548 pEnd = pContent;
1549 }
1550
1551 // If not oldest: Is it anyone else than the last one?
1552 if ( bOldest || pEnd != this )
1553 { ScRange aRange( aBigRange.aStart.MakeAddress() );
1554 const ScAddress& rPos = aRange.aStart;
1555
1556 ScChangeActionContent* pNew = new ScChangeActionContent( aRange );
1557 ScCellValue aCell;
1558 aCell.assign(rDoc, rPos);
1559 pNew->SetOldValue(aCell, &rDoc, &rDoc);
1560
1561 if ( bOldest )
1562 PutOldValueToDoc( &rDoc, 0, 0 );
1563 else
1564 PutNewValueToDoc( &rDoc, 0, 0 );
1565
1566 pNew->SetRejectAction( bOldest ? GetActionNumber() : pEnd->GetActionNumber() );
1567 pNew->SetState( SC_CAS_ACCEPTED );
1568 if ( pRejectActions )
1569 pRejectActions->push( pNew );
1570 else
1571 {
1572 aCell.assign(rDoc, rPos);
1573 pNew->SetNewValue(aCell, &rDoc);
1574 pTrack->Append( pNew );
1575 }
1576 }
1577
1578 if ( bOldest )
1579 SetRejected();
1580 else
1581 SetState( SC_CAS_ACCEPTED );
1582
1583 return true;
1584}
1585
1586void ScChangeActionContent::GetStringOfCell(
1587 OUString& rStr, const ScCellValue& rCell, const ScDocument* pDoc, const ScAddress& rPos )
1588{
1589 if (NeedsNumberFormat(rCell))
1590 GetStringOfCell(rStr, rCell, pDoc, pDoc->GetNumberFormat(rPos));
1591 else
1592 GetStringOfCell(rStr, rCell, pDoc, 0);
1593}
1594
1595void ScChangeActionContent::GetStringOfCell(
1596 OUString& rStr, const ScCellValue& rCell, const ScDocument* pDoc, sal_uLong nFormat )
1597{
1598 rStr = EMPTY_OUSTRINGScGlobal::GetEmptyOUString();
1599
1600 if (!GetContentCellType(rCell))
1601 return;
1602
1603 switch (rCell.meType)
1604 {
1605 case CELLTYPE_VALUE:
1606 pDoc->GetFormatTable()->GetInputLineString(rCell.mfValue, nFormat, rStr);
1607 break;
1608 case CELLTYPE_STRING:
1609 rStr = rCell.mpString->getString();
1610 break;
1611 case CELLTYPE_EDIT:
1612 if (rCell.mpEditText)
1613 rStr = ScEditUtil::GetString(*rCell.mpEditText, pDoc);
1614 break;
1615 case CELLTYPE_FORMULA:
1616 rCell.mpFormula->GetFormula(rStr);
1617 break;
1618 default:
1619 ;
1620 }
1621}
1622
1623ScChangeActionContentCellType ScChangeActionContent::GetContentCellType( const ScCellValue& rCell )
1624{
1625 switch (rCell.meType)
1626 {
1627 case CELLTYPE_VALUE :
1628 case CELLTYPE_STRING :
1629 case CELLTYPE_EDIT :
1630 return SC_CACCT_NORMAL;
1631 case CELLTYPE_FORMULA :
1632 switch (rCell.mpFormula->GetMatrixFlag())
1633 {
1634 case ScMatrixMode::NONE :
1635 return SC_CACCT_NORMAL;
1636 case ScMatrixMode::Formula :
1637 return SC_CACCT_MATORG;
1638 case ScMatrixMode::Reference :
1639 return SC_CACCT_MATREF;
1640 }
1641 return SC_CACCT_NORMAL;
1642 default:
1643 return SC_CACCT_NONE;
1644 }
1645}
1646
1647ScChangeActionContentCellType ScChangeActionContent::GetContentCellType( const ScRefCellValue& rCell )
1648{
1649 switch (rCell.meType)
1650 {
1651 case CELLTYPE_VALUE:
1652 case CELLTYPE_STRING:
1653 case CELLTYPE_EDIT:
1654 return SC_CACCT_NORMAL;
1655 case CELLTYPE_FORMULA:
1656 {
1657 const ScFormulaCell* pCell = rCell.mpFormula;
1658 switch (pCell->GetMatrixFlag())
1659 {
1660 case ScMatrixMode::NONE :
1661 return SC_CACCT_NORMAL;
1662 case ScMatrixMode::Formula :
1663 return SC_CACCT_MATORG;
1664 case ScMatrixMode::Reference :
1665 return SC_CACCT_MATREF;
1666 }
1667 return SC_CACCT_NORMAL;
1668 }
1669 default:
1670 ;
1671 }
1672
1673 return SC_CACCT_NONE;
1674}
1675
1676bool ScChangeActionContent::NeedsNumberFormat( const ScCellValue& rVal )
1677{
1678 return rVal.meType == CELLTYPE_VALUE;
1679}
1680
1681void ScChangeActionContent::SetValue(
1682 OUString& rStr, ScCellValue& rCell, const ScAddress& rPos, const ScCellValue& rOrgCell,
1683 const ScDocument* pFromDoc, ScDocument* pToDoc )
1684{
1685 sal_uInt32 nFormat = NeedsNumberFormat(rOrgCell) ? pFromDoc->GetNumberFormat(rPos) : 0;
1686 SetValue(rStr, rCell, nFormat, rOrgCell, pFromDoc, pToDoc);
1687}
1688
1689void ScChangeActionContent::SetValue(
1690 OUString& rStr, ScCellValue& rCell, sal_uLong nFormat, const ScCellValue& rOrgCell,
1691 const ScDocument* pFromDoc, ScDocument* pToDoc )
1692{
1693 rStr.clear();
1694
1695 if (GetContentCellType(rOrgCell))
1696 {
1697 rCell.assign(rOrgCell, *pToDoc);
1698 switch (rOrgCell.meType)
1699 {
1700 case CELLTYPE_VALUE :
1701 { // E.g.: Remember date as such
1702 pFromDoc->GetFormatTable()->GetInputLineString(
1703 rOrgCell.mfValue, nFormat, rStr);
1704 }
1705 break;
1706 case CELLTYPE_FORMULA :
1707 rCell.mpFormula->SetInChangeTrack(true);
1708 break;
1709 default:
1710 {
1711 // added to avoid warnings
1712 }
1713 }
1714 }
1715 else
1716 rCell.clear();
1717}
1718
1719void ScChangeActionContent::SetCell( OUString& rStr, ScCellValue& rCell, sal_uLong nFormat, const ScDocument* pDoc )
1720{
1721 rStr.clear();
1722 if (rCell.isEmpty())
1723 return;
1724
1725 switch (rCell.meType)
1726 {
1727 case CELLTYPE_VALUE :
1728 // e.g. remember date as date string
1729 pDoc->GetFormatTable()->GetInputLineString(rCell.mfValue, nFormat, rStr);
1730 break;
1731 case CELLTYPE_FORMULA :
1732 rCell.mpFormula->SetInChangeTrack(true);
1733 break;
1734 default:
1735 {
1736 // added to avoid warnings
1737 }
1738 }
1739}
1740
1741void ScChangeActionContent::GetValueString(
1742 OUString& rStr, const OUString& rValue, const ScCellValue& rCell, const ScDocument* pDoc ) const
1743{
1744 if (!rValue.isEmpty())
1745 {
1746 rStr = rValue;
1747 return;
1748 }
1749
1750 switch (rCell.meType)
1751 {
1752 case CELLTYPE_STRING :
1753 rStr = rCell.mpString->getString();
1754 break;
1755 case CELLTYPE_EDIT :
1756 if (rCell.mpEditText)
1757 rStr = ScEditUtil::GetString(*rCell.mpEditText, pDoc);
1758 break;
1759 case CELLTYPE_VALUE : // Is always in rValue
1760 rStr = rValue;
1761 break;
1762 case CELLTYPE_FORMULA :
1763 GetFormulaString(rStr, rCell.mpFormula);
1764 break;
1765 case CELLTYPE_NONE:
1766 default:
1767 rStr.clear();
1768 }
1769}
1770
1771void ScChangeActionContent::GetFormulaString(
1772 OUString& rStr, const ScFormulaCell* pCell ) const
1773{
1774 ScAddress aPos( aBigRange.aStart.MakeAddress() );
1775 if ( aPos == pCell->aPos || IsDeletedIn() )
1776 pCell->GetFormula( rStr );
1777 else
1778 {
1779 OSL_FAIL( "ScChangeActionContent::GetFormulaString: aPos != pCell->aPos" )do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "1779" ": "), "%s", "ScChangeActionContent::GetFormulaString: aPos != pCell->aPos"
); } } while (false)
;
1780 std::unique_ptr<ScFormulaCell> pNew(new ScFormulaCell( *pCell, pCell->GetDocument(), aPos ));
1781 pNew->GetFormula( rStr );
1782 }
1783}
1784
1785void ScChangeActionContent::PutOldValueToDoc( ScDocument* pDoc,
1786 SCCOL nDx, SCROW nDy ) const
1787{
1788 PutValueToDoc(maOldCell, maOldValue, pDoc, nDx, nDy);
1789}
1790
1791void ScChangeActionContent::PutNewValueToDoc( ScDocument* pDoc,
1792 SCCOL nDx, SCROW nDy ) const
1793{
1794 PutValueToDoc(maNewCell, maNewValue, pDoc, nDx, nDy);
1795}
1796
1797void ScChangeActionContent::PutValueToDoc(
1798 const ScCellValue& rCell, const OUString& rValue, ScDocument* pDoc,
1799 SCCOL nDx, SCROW nDy ) const
1800{
1801 ScAddress aPos( aBigRange.aStart.MakeAddress() );
1802 if ( nDx )
1803 aPos.IncCol( nDx );
1804 if ( nDy )
1805 aPos.IncRow( nDy );
1806
1807 if (!rValue.isEmpty())
1808 {
1809 pDoc->SetString(aPos, rValue);
1810 return;
1811 }
1812
1813 if (rCell.isEmpty())
1814 {
1815 pDoc->SetEmptyCell(aPos);
1816 return;
1817 }
1818
1819 if (rCell.meType == CELLTYPE_VALUE)
1820 {
1821 pDoc->SetString( aPos.Col(), aPos.Row(), aPos.Tab(), rValue );
1822 return;
1823 }
1824
1825 switch (GetContentCellType(rCell))
1826 {
1827 case SC_CACCT_MATORG :
1828 {
1829 SCCOL nC;
1830 SCROW nR;
1831 rCell.mpFormula->GetMatColsRows(nC, nR);
1832 OSL_ENSURE( nC>0 && nR>0, "ScChangeActionContent::PutValueToDoc: MatColsRows?" )do { if (true && (!(nC>0 && nR>0))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "1832" ": "), "%s", "ScChangeActionContent::PutValueToDoc: MatColsRows?"
); } } while (false)
;
1833 ScRange aRange( aPos );
1834 if ( nC > 1 )
1835 aRange.aEnd.IncCol( nC-1 );
1836 if ( nR > 1 )
1837 aRange.aEnd.IncRow( nR-1 );
1838 ScMarkData aDestMark(pDoc->GetSheetLimits());
1839 aDestMark.SelectOneTable( aPos.Tab() );
1840 aDestMark.SetMarkArea( aRange );
1841 pDoc->InsertMatrixFormula( aPos.Col(), aPos.Row(),
1842 aRange.aEnd.Col(), aRange.aEnd.Row(),
1843 aDestMark, EMPTY_OUSTRINGScGlobal::GetEmptyOUString(), rCell.mpFormula->GetCode());
1844 }
1845 break;
1846 case SC_CACCT_MATREF :
1847 // nothing
1848 break;
1849 default:
1850 rCell.commit(*pDoc, aPos);
1851 }
1852}
1853
1854static void lcl_InvalidateReference( const ScDocument& rDoc, formula::FormulaToken& rTok, const ScBigAddress& rPos )
1855{
1856 ScSingleRefData& rRef1 = *rTok.GetSingleRef();
1857 if ( rPos.Col() < 0 || rDoc.MaxCol() < rPos.Col() )
1858 {
1859 rRef1.SetColDeleted( true );
1860 }
1861 if ( rPos.Row() < 0 || rDoc.MaxRow() < rPos.Row() )
1862 {
1863 rRef1.SetRowDeleted( true );
1864 }
1865 if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() )
1866 {
1867 rRef1.SetTabDeleted( true );
1868 }
1869 if ( rTok.GetType() != formula::svDoubleRef )
1870 return;
1871
1872 ScSingleRefData& rRef2 = rTok.GetDoubleRef()->Ref2;
1873 if ( rPos.Col() < 0 || rDoc.MaxCol() < rPos.Col() )
1874 {
1875 rRef2.SetColDeleted( true );
1876 }
1877 if ( rPos.Row() < 0 || rDoc.MaxRow() < rPos.Row() )
1878 {
1879 rRef2.SetRowDeleted( true );
1880 }
1881 if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() )
1882 {
1883 rRef2.SetTabDeleted( true );
1884 }
1885}
1886
1887void ScChangeActionContent::UpdateReference( const ScChangeTrack* pTrack,
1888 UpdateRefMode eMode, const ScBigRange& rRange,
1889 sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz )
1890{
1891 SCSIZE nOldSlot = pTrack->ComputeContentSlot( aBigRange.aStart.Row() );
1892 ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aBigRange );
1893 SCSIZE nNewSlot = pTrack->ComputeContentSlot( aBigRange.aStart.Row() );
1894 if ( nNewSlot != nOldSlot )
1895 {
1896 RemoveFromSlot();
1897 InsertInSlot( &(pTrack->GetContentSlots()[nNewSlot]) );
1898 }
1899
1900 if ( pTrack->IsInDelete() && !pTrack->IsInDeleteTop() )
1901 return ; // Formula only update whole range
1902
1903 bool bOldFormula = maOldCell.meType == CELLTYPE_FORMULA;
1904 bool bNewFormula = maNewCell.meType == CELLTYPE_FORMULA;
1905 if ( !(bOldFormula || bNewFormula) )
1906 return;
1907
1908// Adjust UpdateReference via ScFormulaCell (there)
1909 if ( pTrack->IsInDelete() )
1910 {
1911 const ScRange& rDelRange = pTrack->GetInDeleteRange();
1912 if ( nDx > 0 )
1913 nDx = rDelRange.aEnd.Col() - rDelRange.aStart.Col() + 1;
1914 else if ( nDx < 0 )
1915 nDx = -(rDelRange.aEnd.Col() - rDelRange.aStart.Col() + 1);
1916 if ( nDy > 0 )
1917 nDy = rDelRange.aEnd.Row() - rDelRange.aStart.Row() + 1;
1918 else if ( nDy < 0 )
1919 nDy = -(rDelRange.aEnd.Row() - rDelRange.aStart.Row() + 1);
1920 if ( nDz > 0 )
1921 nDz = rDelRange.aEnd.Tab() - rDelRange.aStart.Tab() + 1;
1922 else if ( nDz < 0 )
1923 nDz = -(rDelRange.aEnd.Tab() - rDelRange.aStart.Tab() + 1);
1924 }
1925 ScBigRange aTmpRange( rRange );
1926 switch ( eMode )
1927 {
1928 case URM_INSDEL :
1929 if ( nDx < 0 || nDy < 0 || nDz < 0 )
1930 { // Delete starts there after removed range
1931 // Position is changed there
1932 if ( nDx )
1933 aTmpRange.aStart.IncCol( -nDx );
1934 if ( nDy )
1935 aTmpRange.aStart.IncRow( -nDy );
1936 if ( nDz )
1937 aTmpRange.aStart.IncTab( -nDz );
1938 }
1939 break;
1940 case URM_MOVE :
1941 // Move is Source here and Target there
1942 // Position needs to be adjusted before that
1943 if ( bOldFormula )
1944 maOldCell.mpFormula->aPos = aBigRange.aStart.MakeAddress();
1945 if ( bNewFormula )
1946 maNewCell.mpFormula->aPos = aBigRange.aStart.MakeAddress();
1947 if ( nDx )
1948 {
1949 aTmpRange.aStart.IncCol( nDx );
1950 aTmpRange.aEnd.IncCol( nDx );
1951 }
1952 if ( nDy )
1953 {
1954 aTmpRange.aStart.IncRow( nDy );
1955 aTmpRange.aEnd.IncRow( nDy );
1956 }
1957 if ( nDz )
1958 {
1959 aTmpRange.aStart.IncTab( nDz );
1960 aTmpRange.aEnd.IncTab( nDz );
1961 }
1962 break;
1963 default:
1964 {
1965 // added to avoid warnings
1966 }
1967 }
1968 ScRange aRange( aTmpRange.MakeRange() );
1969
1970 sc::RefUpdateContext aRefCxt(pTrack->GetDocument());
1971 aRefCxt.meMode = eMode;
1972 aRefCxt.maRange = aRange;
1973 aRefCxt.mnColDelta = nDx;
1974 aRefCxt.mnRowDelta = nDy;
1975 aRefCxt.mnTabDelta = nDz;
1976
1977 if ( bOldFormula )
1978 maOldCell.mpFormula->UpdateReference(aRefCxt);
1979 if ( bNewFormula )
1980 maNewCell.mpFormula->UpdateReference(aRefCxt);
1981
1982 if ( aBigRange.aStart.IsValid( pTrack->GetDocument() ) )
1983 return;
1984
1985//FIXME:
1986 // UpdateReference cannot handle positions outside of the Document.
1987 // Therefore set everything to #REF!
1988 //TODO: Remove the need for this hack! This means big changes to ScAddress etc.!
1989 const ScBigAddress& rPos = aBigRange.aStart;
1990 if ( bOldFormula )
1991 {
1992 formula::FormulaToken* t;
1993 ScTokenArray* pArr = maOldCell.mpFormula->GetCode();
1994 formula::FormulaTokenArrayPlainIterator aIter(*pArr);
1995 while ( ( t = aIter.GetNextReference() ) != nullptr )
1996 lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
1997 aIter.Reset();
1998 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
1999 lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
2000 }
2001 if ( bNewFormula )
2002 {
2003 formula::FormulaToken* t;
2004 ScTokenArray* pArr = maNewCell.mpFormula->GetCode();
2005 formula::FormulaTokenArrayPlainIterator aIter(*pArr);
2006 while ( ( t = aIter.GetNextReference() ) != nullptr )
2007 lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
2008 aIter.Reset();
2009 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
2010 lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
2011 }
2012}
2013
2014bool ScChangeActionContent::IsMatrixOrigin() const
2015{
2016 return GetContentCellType(GetNewCell()) == SC_CACCT_MATORG;
2017}
2018
2019bool ScChangeActionContent::IsOldMatrixReference() const
2020{
2021 return GetContentCellType(GetOldCell()) == SC_CACCT_MATREF;
2022}
2023
2024// ScChangeActionReject
2025ScChangeActionReject::ScChangeActionReject(
2026 const sal_uLong nActionNumber, const ScChangeActionState eStateP,
2027 const sal_uLong nRejectingNumber,
2028 const ScBigRange& aBigRangeP, const OUString& aUserP,
2029 const DateTime& aDateTimeP, const OUString& sComment) :
2030 ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment)
2031{
2032}
2033
2034bool ScChangeActionReject::Reject(ScDocument& /*rDoc*/)
2035{
2036 return false;
2037}
2038
2039SCSIZE ScChangeTrack::ComputeContentSlot( sal_Int32 nRow ) const
2040{
2041 if ( nRow < 0 || nRow > rDoc.GetSheetLimits().mnMaxRow )
2042 return mnContentSlots - 1;
2043 return static_cast< SCSIZE >( nRow / mnContentRowsPerSlot );
2044}
2045
2046SCROW ScChangeTrack::InitContentRowsPerSlot()
2047{
2048 const SCSIZE nMaxSlots = 0xffe0 / sizeof( ScChangeActionContent* ) - 2;
2049 SCROW nRowsPerSlot = rDoc.GetSheetLimits().GetMaxRowCount() / nMaxSlots;
2050 if ( nRowsPerSlot * nMaxSlots < sal::static_int_cast<SCSIZE>(rDoc.GetSheetLimits().GetMaxRowCount()) )
2051 ++nRowsPerSlot;
2052 return nRowsPerSlot;
2053}
2054
2055ScChangeTrack::ScChangeTrack( ScDocument& rDocP ) :
2056 aFixDateTime( DateTime::SYSTEM ),
2057 rDoc( rDocP )
2058{
2059 Init();
2060 SC_MOD()( static_cast<ScModule*>(SfxApplication::GetModule(SfxToolsModule
::Calc)) )
->GetUserOptions().AddListener(this);
2061
2062 ppContentSlots.reset( new ScChangeActionContent* [ mnContentSlots ] );
2063 memset( ppContentSlots.get(), 0, mnContentSlots * sizeof( ScChangeActionContent* ) );
2064}
2065
2066ScChangeTrack::ScChangeTrack( ScDocument& rDocP, const std::set<OUString>& aTempUserCollection) :
2067 maUserCollection(aTempUserCollection),
2068 aFixDateTime( DateTime::SYSTEM ),
2069 rDoc( rDocP )
2070{
2071 Init();
2072 SC_MOD()( static_cast<ScModule*>(SfxApplication::GetModule(SfxToolsModule
::Calc)) )
->GetUserOptions().AddListener(this);
2073 ppContentSlots.reset( new ScChangeActionContent* [ mnContentSlots ] );
2074 memset( ppContentSlots.get(), 0, mnContentSlots * sizeof( ScChangeActionContent* ) );
2075}
2076
2077ScChangeTrack::~ScChangeTrack()
2078{
2079 SC_MOD()( static_cast<ScModule*>(SfxApplication::GetModule(SfxToolsModule
::Calc)) )
->GetUserOptions().RemoveListener(this);
2080 DtorClear();
2081}
2082
2083void ScChangeTrack::Init()
2084{
2085 mnContentRowsPerSlot = InitContentRowsPerSlot();
2086 mnContentSlots = rDoc.GetSheetLimits().GetMaxRowCount() / InitContentRowsPerSlot() + 2;
2087
2088 pFirst = nullptr;
2089 pLast = nullptr;
2090 pFirstGeneratedDelContent = nullptr;
2091 pLastCutMove = nullptr;
2092 pLinkInsertCol = nullptr;
2093 pLinkInsertRow = nullptr;
2094 pLinkInsertTab = nullptr;
2095 pLinkMove = nullptr;
2096 xBlockModifyMsg.reset();
2097 nActionMax = 0;
2098 nGeneratedMin = SC_CHGTRACK_GENERATED_START(sal_uInt32(0xfffffff0));
2099 nMarkLastSaved = 0;
2100 nStartLastCut = 0;
2101 nEndLastCut = 0;
2102 nLastMerge = 0;
2103 eMergeState = SC_CTMS_NONE;
2104 bInDelete = false;
2105 bInDeleteTop = false;
2106 bInDeleteUndo = false;
2107 bInPasteCut = false;
2108 bUseFixDateTime = false;
2109 bTimeNanoSeconds = true;
2110
2111 const SvtUserOptions& rUserOpt = SC_MOD()( static_cast<ScModule*>(SfxApplication::GetModule(SfxToolsModule
::Calc)) )
->GetUserOptions();
2112 OUStringBuffer aBuf;
2113 aBuf.append(rUserOpt.GetFirstName());
2114 aBuf.append(' ');
2115 aBuf.append(rUserOpt.GetLastName());
2116 maUser = aBuf.makeStringAndClear();
2117 maUserCollection.insert(maUser);
2118}
2119
2120void ScChangeTrack::DtorClear()
2121{
2122 ScChangeAction* p;
2123 ScChangeAction* pNext;
2124 for ( p = GetFirst(); p; p = pNext )
2125 {
2126 pNext = p->GetNext();
2127 delete p;
2128 }
2129 for ( p = pFirstGeneratedDelContent; p; p = pNext )
2130 {
2131 pNext = p->GetNext();
2132 delete p;
2133 }
2134 for( const auto& rEntry : aPasteCutMap )
2135 {
2136 delete rEntry.second;
2137 }
2138 pLastCutMove.reset();
2139 ClearMsgQueue();
2140}
2141
2142void ScChangeTrack::ClearMsgQueue()
2143{
2144 xBlockModifyMsg.reset();
2145 aMsgStackTmp.clear();
2146 aMsgStackFinal.clear();
2147 aMsgQueue.clear();
2148}
2149
2150void ScChangeTrack::Clear()
2151{
2152 DtorClear();
2153 aMap.clear();
2154 aGeneratedMap.clear();
2155 aPasteCutMap.clear();
2156 maUserCollection.clear();
2157 maUser.clear();
2158 Init();
2159}
2160
2161bool ScChangeTrack::IsGenerated( sal_uLong nAction ) const
2162{
2163 return nAction >= nGeneratedMin;
2164}
2165
2166ScChangeAction* ScChangeTrack::GetAction( sal_uLong nAction ) const
2167{
2168 ScChangeActionMap::const_iterator it = aMap.find( nAction );
2169 if( it != aMap.end() )
2170 return it->second;
2171 else
2172 return nullptr;
2173}
2174
2175ScChangeAction* ScChangeTrack::GetGenerated( sal_uLong nGenerated ) const
2176{
2177 ScChangeActionMap::const_iterator it = aGeneratedMap.find( nGenerated );
2178 if( it != aGeneratedMap.end() )
2179 return it->second;
2180 else
2181 return nullptr;
2182}
2183
2184ScChangeAction* ScChangeTrack::GetActionOrGenerated( sal_uLong nAction ) const
2185{
2186 return IsGenerated( nAction ) ?
2187 GetGenerated( nAction ) :
2188 GetAction( nAction );
2189}
2190sal_uLong ScChangeTrack::GetLastSavedActionNumber() const
2191{
2192 return nMarkLastSaved;
2193}
2194
2195void ScChangeTrack::SetLastSavedActionNumber(sal_uLong nNew)
2196{
2197 nMarkLastSaved = nNew;
2198}
2199
2200ScChangeAction* ScChangeTrack::GetLastSaved() const
2201{
2202 ScChangeActionMap::const_iterator it = aMap.find( nMarkLastSaved );
2203 if( it != aMap.end() )
2204 return it->second;
2205 else
2206 return nullptr;
2207}
2208
2209void ScChangeTrack::ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints )
2210{
2211 if ( rDoc.IsInDtorClear() )
2212 return;
2213
2214 const SvtUserOptions& rUserOptions = SC_MOD()( static_cast<ScModule*>(SfxApplication::GetModule(SfxToolsModule
::Calc)) )
->GetUserOptions();
2215 size_t nOldCount = maUserCollection.size();
2216
2217 OUStringBuffer aBuf;
2218 aBuf.append(rUserOptions.GetFirstName());
2219 aBuf.append(' ');
2220 aBuf.append(rUserOptions.GetLastName());
2221 SetUser(aBuf.makeStringAndClear());
2222
2223 if ( maUserCollection.size() != nOldCount )
2224 {
2225 // New user in collection -> have to repaint because
2226 // colors may be different now (#106697#).
2227 // (Has to be done in the Notify handler, to be sure
2228 // the user collection has already been updated)
2229
2230 SfxObjectShell* pDocSh = rDoc.GetDocumentShell();
2231 if (pDocSh)
2232 pDocSh->Broadcast( ScPaintHint( ScRange(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB), PaintPartFlags::Grid ) );
2233 }
2234}
2235
2236void ScChangeTrack::SetUser( const OUString& rUser )
2237{
2238 maUser = rUser;
2239 maUserCollection.insert(maUser);
2240}
2241
2242void ScChangeTrack::StartBlockModify( ScChangeTrackMsgType eMsgType,
2243 sal_uLong nStartAction )
2244{
2245 if ( aModifiedLink.IsSet() )
2246 {
2247 if ( xBlockModifyMsg )
2248 aMsgStackTmp.push_back( *xBlockModifyMsg ); // Block in Block
2249 xBlockModifyMsg = ScChangeTrackMsgInfo();
2250 xBlockModifyMsg->eMsgType = eMsgType;
2251 xBlockModifyMsg->nStartAction = nStartAction;
2252 xBlockModifyMsg->nEndAction = 0;
2253 }
2254}
2255
2256void ScChangeTrack::EndBlockModify( sal_uLong nEndAction )
2257{
2258 if ( !aModifiedLink.IsSet() )
2259 return;
2260
2261 if ( xBlockModifyMsg )
2262 {
2263 if ( xBlockModifyMsg->nStartAction <= nEndAction )
2264 {
2265 xBlockModifyMsg->nEndAction = nEndAction;
2266 // Blocks dissolved in Blocks
2267 aMsgStackFinal.push_back( *xBlockModifyMsg );
2268 }
2269 else
2270 xBlockModifyMsg.reset();
2271 if (aMsgStackTmp.empty())
2272 xBlockModifyMsg.reset();
2273 else
2274 {
2275 xBlockModifyMsg = aMsgStackTmp.back(); // Maybe Block in Block
2276 aMsgStackTmp.pop_back();
2277 }
2278 }
2279 if ( !xBlockModifyMsg )
2280 {
2281 bool bNew = !aMsgStackFinal.empty();
2282 aMsgQueue.reserve(aMsgQueue.size() + aMsgStackFinal.size());
2283 aMsgQueue.insert(aMsgQueue.end(), aMsgStackFinal.rbegin(), aMsgStackFinal.rend());
2284 aMsgStackFinal.clear();
2285 if ( bNew )
2286 aModifiedLink.Call( *this );
2287 }
2288}
2289
2290ScChangeTrackMsgQueue& ScChangeTrack::GetMsgQueue()
2291{
2292 return aMsgQueue;
2293}
2294
2295void ScChangeTrack::NotifyModified( ScChangeTrackMsgType eMsgType,
2296 sal_uLong nStartAction, sal_uLong nEndAction )
2297{
2298 if ( aModifiedLink.IsSet() )
2299 {
2300 if ( !xBlockModifyMsg || xBlockModifyMsg->eMsgType != eMsgType ||
2301 (IsGenerated( nStartAction ) &&
2302 (eMsgType == ScChangeTrackMsgType::Append || eMsgType == ScChangeTrackMsgType::Remove)) )
2303 { // Append within Append e.g. not
2304 StartBlockModify( eMsgType, nStartAction );
2305 EndBlockModify( nEndAction );
2306 }
2307 }
2308}
2309
2310void ScChangeTrack::MasterLinks( ScChangeAction* pAppend )
2311{
2312 ScChangeActionType eType = pAppend->GetType();
2313
2314 if ( eType == SC_CAT_CONTENT )
2315 {
2316 if ( !IsGenerated( pAppend->GetActionNumber() ) )
2317 {
2318 SCSIZE nSlot = ComputeContentSlot(
2319 pAppend->GetBigRange().aStart.Row() );
2320 static_cast<ScChangeActionContent*>(pAppend)->InsertInSlot(
2321 &ppContentSlots[nSlot] );
2322 }
2323 return ;
2324 }
2325
2326 if ( pAppend->IsRejecting() )
2327 return ; // Rejects do not have dependencies
2328
2329 switch ( eType )
2330 {
2331 case SC_CAT_INSERT_COLS :
2332 {
2333 ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
2334 &pLinkInsertCol, pAppend );
2335 pAppend->AddLink( nullptr, pLink );
2336 }
2337 break;
2338 case SC_CAT_INSERT_ROWS :
2339 {
2340 ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
2341 &pLinkInsertRow, pAppend );
2342 pAppend->AddLink( nullptr, pLink );
2343 }
2344 break;
2345 case SC_CAT_INSERT_TABS :
2346 {
2347 ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
2348 &pLinkInsertTab, pAppend );
2349 pAppend->AddLink( nullptr, pLink );
2350 }
2351 break;
2352 case SC_CAT_MOVE :
2353 {
2354 ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
2355 &pLinkMove, pAppend );
2356 pAppend->AddLink( nullptr, pLink );
2357 }
2358 break;
2359 default:
2360 {
2361 // added to avoid warnings
2362 }
2363 }
2364}
2365
2366void ScChangeTrack::AppendLoaded( std::unique_ptr<ScChangeAction> pActionParam )
2367{
2368 ScChangeAction* pAppend = pActionParam.release();
2369 aMap.insert( ::std::make_pair( pAppend->GetActionNumber(), pAppend ) );
2370 if ( !pLast )
2371 pFirst = pLast = pAppend;
2372 else
2373 {
2374 pLast->pNext = pAppend;
2375 pAppend->pPrev = pLast;
2376 pLast = pAppend;
2377 }
2378 MasterLinks( pAppend );
2379}
2380
2381void ScChangeTrack::Append( ScChangeAction* pAppend, sal_uLong nAction )
2382{
2383 if ( nActionMax < nAction )
2384 nActionMax = nAction;
2385 pAppend->SetUser( maUser );
2386 if ( bUseFixDateTime )
2387 pAppend->SetDateTimeUTC( aFixDateTime );
2388 pAppend->SetActionNumber( nAction );
2389 aMap.insert( ::std::make_pair( nAction, pAppend ) );
2390 // UpdateReference Inserts before Dependencies.
2391 // Delete rejecting Insert which had UpdateReference with Delete Undo.
2392 // UpdateReference also with pLast==NULL, as pAppend can be a Delete,
2393 // which could have generated DelContents.
2394 if ( pAppend->IsInsertType() && !pAppend->IsRejecting() )
2395 UpdateReference( pAppend, false );
2396 if ( !pLast )
2397 pFirst = pLast = pAppend;
2398 else
2399 {
2400 pLast->pNext = pAppend;
2401 pAppend->pPrev = pLast;
2402 pLast = pAppend;
2403 Dependencies( pAppend );
2404 }
2405 // UpdateReference does not Insert() after Dependencies.
2406 // Move rejecting Move, which had UpdateReference with Move Undo.
2407 // Do not delete content in ToRange.
2408 if ( !pAppend->IsInsertType() &&
2409 !(pAppend->GetType() == SC_CAT_MOVE && pAppend->IsRejecting()) )
2410 UpdateReference( pAppend, false );
2411 MasterLinks( pAppend );
2412
2413 if ( !aModifiedLink.IsSet() )
2414 return;
2415
2416 NotifyModified( ScChangeTrackMsgType::Append, nAction, nAction );
2417 if ( pAppend->GetType() == SC_CAT_CONTENT )
2418 {
2419 ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(pAppend);
2420 if ( ( pContent = pContent->GetPrevContent() ) != nullptr )
2421 {
2422 sal_uLong nMod = pContent->GetActionNumber();
2423 NotifyModified( ScChangeTrackMsgType::Change, nMod, nMod );
2424 }
2425 }
2426 else
2427 NotifyModified( ScChangeTrackMsgType::Change, pFirst->GetActionNumber(),
2428 pLast->GetActionNumber() );
2429}
2430
2431void ScChangeTrack::Append( ScChangeAction* pAppend )
2432{
2433 Append( pAppend, ++nActionMax );
2434}
2435
2436void ScChangeTrack::AppendDeleteRange( const ScRange& rRange,
2437 ScDocument* pRefDoc, sal_uLong& nStartAction, sal_uLong& nEndAction, SCTAB nDz )
2438{
2439 nStartAction = GetActionMax() + 1;
2440 AppendDeleteRange( rRange, pRefDoc, nDz, 0 );
2441 nEndAction = GetActionMax();
2442}
2443
2444void ScChangeTrack::AppendDeleteRange( const ScRange& rRange,
2445 ScDocument* pRefDoc, SCTAB nDz, sal_uLong nRejectingInsert )
2446{
2447 SetInDeleteRange( rRange );
2448 StartBlockModify( ScChangeTrackMsgType::Append, GetActionMax() + 1 );
2449 SCCOL nCol1;
2450 SCROW nRow1;
2451 SCTAB nTab1;
2452 SCCOL nCol2;
2453 SCROW nRow2;
2454 SCTAB nTab2;
2455 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
2456 for ( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ )
2457 {
2458 if ( !pRefDoc || nTab < pRefDoc->GetTableCount() )
2459 {
2460 if ( nCol1 == 0 && nCol2 == rDoc.MaxCol() )
2461 { // Whole Row and/or Tables
2462 if ( nRow1 == 0 && nRow2 == rDoc.MaxRow() )
2463 { // Whole Table
2464 // TODO: Can't we do the whole Table as a whole?
2465 ScRange aRange( 0, 0, nTab, 0, rDoc.MaxRow(), nTab );
2466 for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
2467 { // Column by column is less than row by row
2468 aRange.aStart.SetCol( nCol );
2469 aRange.aEnd.SetCol( nCol );
2470 if ( nCol == nCol2 )
2471 SetInDeleteTop( true );
2472 AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0,
2473 nTab-nTab1 + nDz, nRejectingInsert );
2474 }
2475 // Still InDeleteTop!
2476 AppendOneDeleteRange( rRange, pRefDoc, 0, 0,
2477 nTab-nTab1 + nDz, nRejectingInsert );
2478 }
2479 else
2480 { // Whole rows
2481 ScRange aRange( 0, 0, nTab, rDoc.MaxCol(), 0, nTab );
2482 for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
2483 {
2484 aRange.aStart.SetRow( nRow );
2485 aRange.aEnd.SetRow( nRow );
2486 if ( nRow == nRow2 )
2487 SetInDeleteTop( true );
2488 AppendOneDeleteRange( aRange, pRefDoc, 0, nRow-nRow1,
2489 0, nRejectingInsert );
2490 }
2491 }
2492 }
2493 else if ( nRow1 == 0 && nRow2 == rDoc.MaxRow() )
2494 { // Whole columns
2495 ScRange aRange( 0, 0, nTab, 0, rDoc.MaxRow(), nTab );
2496 for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
2497 {
2498 aRange.aStart.SetCol( nCol );
2499 aRange.aEnd.SetCol( nCol );
2500 if ( nCol == nCol2 )
2501 SetInDeleteTop( true );
2502 AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0,
2503 0, nRejectingInsert );
2504 }
2505 }
2506 else
2507 {
2508 OSL_FAIL( "ScChangeTrack::AppendDeleteRange: Block not supported!" )do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "2508" ": "), "%s", "ScChangeTrack::AppendDeleteRange: Block not supported!"
); } } while (false)
;
2509 }
2510 SetInDeleteTop( false );
2511 }
2512 }
2513 EndBlockModify( GetActionMax() );
2514}
2515
2516void ScChangeTrack::AppendOneDeleteRange( const ScRange& rOrgRange,
2517 ScDocument* pRefDoc, SCCOL nDx, SCROW nDy, SCTAB nDz,
2518 sal_uLong nRejectingInsert )
2519{
2520 ScRange aTrackRange( rOrgRange );
2521 if ( nDx )
2522 {
2523 aTrackRange.aStart.IncCol( -nDx );
2524 aTrackRange.aEnd.IncCol( -nDx );
2525 }
2526 if ( nDy )
2527 {
2528 aTrackRange.aStart.IncRow( -nDy );
2529 aTrackRange.aEnd.IncRow( -nDy );
2530 }
2531 if ( nDz )
2532 {
2533 aTrackRange.aStart.IncTab( -nDz );
2534 aTrackRange.aEnd.IncTab( -nDz );
2535 }
2536 ScChangeActionDel* pAct = new ScChangeActionDel( &rDoc, aTrackRange, nDx, nDy,
2537 this );
2538 // TabDelete not Contents; they are in separate columns
2539 if ( !(rOrgRange.aStart.Col() == 0 && rOrgRange.aStart.Row() == 0 &&
2540 rOrgRange.aEnd.Col() == rDoc.MaxCol() && rOrgRange.aEnd.Row() == rDoc.MaxRow()) )
2541 LookUpContents( rOrgRange, pRefDoc, -nDx, -nDy, -nDz );
2542 if ( nRejectingInsert )
2543 {
2544 pAct->SetRejectAction( nRejectingInsert );
2545 pAct->SetState( SC_CAS_ACCEPTED );
2546 }
2547 Append( pAct );
2548}
2549
2550void ScChangeTrack::LookUpContents( const ScRange& rOrgRange,
2551 ScDocument* pRefDoc, SCCOL nDx, SCROW nDy, SCTAB nDz )
2552{
2553 if (!pRefDoc)
2554 return;
2555
2556 ScAddress aPos;
2557 ScBigAddress aBigPos;
2558 ScCellIterator aIter( *pRefDoc, rOrgRange );
2559 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
2560 {
2561 if (!ScChangeActionContent::GetContentCellType(aIter.getRefCellValue()))
2562 continue;
2563
2564 aBigPos.Set( aIter.GetPos().Col() + nDx, aIter.GetPos().Row() + nDy,
2565 aIter.GetPos().Tab() + nDz );
2566 ScChangeActionContent* pContent = SearchContentAt( aBigPos, nullptr );
2567 if (pContent)
2568 continue;
2569
2570 // Untracked Contents
2571 aPos.Set( aIter.GetPos().Col() + nDx, aIter.GetPos().Row() + nDy,
2572 aIter.GetPos().Tab() + nDz );
2573
2574 GenerateDelContent(aPos, aIter.getCellValue(), pRefDoc);
2575 // The Content is _not_ added with AddContent here, but in UpdateReference.
2576 // We do this in order to e.g. handle intersecting Deletes correctly
2577 }
2578}
2579
2580void ScChangeTrack::AppendMove( const ScRange& rFromRange,
2581 const ScRange& rToRange, ScDocument* pRefDoc )
2582{
2583 ScChangeActionMove* pAct = new ScChangeActionMove( rFromRange, rToRange, this );
2584 LookUpContents( rToRange, pRefDoc, 0, 0, 0 ); // Overwritten Contents
2585 Append( pAct );
2586}
2587
2588bool ScChangeTrack::IsMatrixFormulaRangeDifferent(
2589 const ScCellValue& rOldCell, const ScCellValue& rNewCell )
2590{
2591 SCCOL nC1, nC2;
2592 SCROW nR1, nR2;
2593 nC1 = nC2 = 0;
2594 nR1 = nR2 = 0;
2595
2596 if (rOldCell.meType == CELLTYPE_FORMULA && rOldCell.mpFormula->GetMatrixFlag() == ScMatrixMode::Formula)
2597 rOldCell.mpFormula->GetMatColsRows(nC1, nR1);
2598
2599 if (rNewCell.meType == CELLTYPE_FORMULA && rNewCell.mpFormula->GetMatrixFlag() == ScMatrixMode::Formula)
2600 rNewCell.mpFormula->GetMatColsRows(nC1, nR1);
2601
2602 return nC1 != nC2 || nR1 != nR2;
2603}
2604
2605void ScChangeTrack::AppendContent(
2606 const ScAddress& rPos, const ScCellValue& rOldCell, sal_uLong nOldFormat, ScDocument* pRefDoc )
2607{
2608 if ( !pRefDoc )
2609 pRefDoc = &rDoc;
2610
2611 OUString aOldValue;
2612 ScChangeActionContent::GetStringOfCell(aOldValue, rOldCell, pRefDoc, nOldFormat);
2613
2614 OUString aNewValue;
2615 ScCellValue aNewCell;
2616 aNewCell.assign(rDoc, rPos);
2617 ScChangeActionContent::GetStringOfCell(aNewValue, aNewCell, &rDoc, rPos);
2618
2619 if (aOldValue != aNewValue || IsMatrixFormulaRangeDifferent(rOldCell, aNewCell))
2620 { // Only track real changes
2621 ScRange aRange( rPos );
2622 ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
2623 pAct->SetOldValue(rOldCell, pRefDoc, &rDoc, nOldFormat);
2624 pAct->SetNewValue(aNewCell, &rDoc);
2625 Append( pAct );
2626 }
2627}
2628
2629void ScChangeTrack::AppendContent( const ScAddress& rPos,
2630 const ScDocument* pRefDoc )
2631{
2632 OUString aOldValue;
2633 ScCellValue aOldCell;
2634 aOldCell.assign(*pRefDoc, rPos);
2635 ScChangeActionContent::GetStringOfCell(aOldValue, aOldCell, pRefDoc, rPos);
2636
2637 OUString aNewValue;
2638 ScCellValue aNewCell;
2639 aNewCell.assign(rDoc, rPos);
2640 ScChangeActionContent::GetStringOfCell(aNewValue, aNewCell, &rDoc, rPos);
2641
2642 if (aOldValue != aNewValue || IsMatrixFormulaRangeDifferent(aOldCell, aNewCell))
2643 { // Only track real changes
2644 ScRange aRange( rPos );
2645 ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
2646 pAct->SetOldValue(aOldCell, pRefDoc, &rDoc);
2647 pAct->SetNewValue(aNewCell, &rDoc);
2648 Append( pAct );
2649 }
2650}
2651
2652void ScChangeTrack::AppendContent( const ScAddress& rPos, const ScCellValue& rOldCell )
2653{
2654 if (ScChangeActionContent::NeedsNumberFormat(rOldCell))
2655 AppendContent(rPos, rOldCell, rDoc.GetNumberFormat(rPos), &rDoc);
2656 else
2657 AppendContent(rPos, rOldCell, 0, &rDoc);
2658}
2659
2660void ScChangeTrack::SetLastCutMoveRange( const ScRange& rRange,
2661 ScDocument* pRefDoc )
2662{
2663 if ( !pLastCutMove )
2664 return;
2665
2666 // Do not link ToRange with Deletes and don't change its size
2667 // This is actually unnecessary, as a delete triggers a ResetLastCut
2668 // in ScViewFunc::PasteFromClip before that
2669 ScBigRange& r = pLastCutMove->GetBigRange();
2670 r.aEnd.SetCol( -1 );
2671 r.aEnd.SetRow( -1 );
2672 r.aEnd.SetTab( -1 );
2673 r.aStart.SetCol( -1 - (rRange.aEnd.Col() - rRange.aStart.Col()) );
2674 r.aStart.SetRow( -1 - (rRange.aEnd.Row() - rRange.aStart.Row()) );
2675 r.aStart.SetTab( -1 - (rRange.aEnd.Tab() - rRange.aStart.Tab()) );
2676 // Contents in FromRange we should overwrite
2677 LookUpContents( rRange, pRefDoc, 0, 0, 0 );
2678}
2679
2680void ScChangeTrack::AppendContentRange( const ScRange& rRange,
2681 ScDocument* pRefDoc, sal_uLong& nStartAction, sal_uLong& nEndAction,
2682 ScChangeActionClipMode eClipMode )
2683{
2684 if ( eClipMode == SC_CACM_CUT )
2685 {
2686 ResetLastCut();
2687 pLastCutMove.reset(new ScChangeActionMove( rRange, rRange, this ));
2688 SetLastCutMoveRange( rRange, pRefDoc );
2689 }
2690 SCCOL nCol1;
2691 SCROW nRow1;
2692 SCTAB nTab1;
2693 SCCOL nCol2;
2694 SCROW nRow2;
2695 SCTAB nTab2;
2696 rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
2697 bool bDoContents;
2698 if ( eClipMode == SC_CACM_PASTE && HasLastCut() )
2699 {
2700 bDoContents = false;
2701 SetInPasteCut( true );
2702 // Adjust Paste and Cut; Paste can be larger a Range
2703 ScRange aRange( rRange );
2704 ScBigRange& r = pLastCutMove->GetBigRange();
2705 SCCOL nTmpCol;
2706 if ( (nTmpCol = static_cast<SCCOL>(r.aEnd.Col() - r.aStart.Col())) != (nCol2 - nCol1) )
2707 {
2708 aRange.aEnd.SetCol( aRange.aStart.Col() + nTmpCol );
2709 nCol1 += nTmpCol + 1;
2710 bDoContents = true;
2711 }
2712 SCROW nTmpRow;
2713 if ( (nTmpRow = static_cast<SCROW>(r.aEnd.Row() - r.aStart.Row())) != (nRow2 - nRow1) )
2714 {
2715 aRange.aEnd.SetRow( aRange.aStart.Row() + nTmpRow );
2716 nRow1 += nTmpRow + 1;
2717 bDoContents = true;
2718 }
2719 SCTAB nTmpTab;
2720 if ( (nTmpTab = static_cast<SCTAB>(r.aEnd.Tab() - r.aStart.Tab())) != (nTab2 - nTab1) )
2721 {
2722 aRange.aEnd.SetTab( aRange.aStart.Tab() + nTmpTab );
2723 nTab1 += nTmpTab + 1;
2724 bDoContents = true;
2725 }
2726 r = aRange;
2727 Undo( nStartLastCut, nEndLastCut ); // Remember Cuts here
2728 // StartAction only after Undo!
2729 nStartAction = GetActionMax() + 1;
2730 StartBlockModify( ScChangeTrackMsgType::Append, nStartAction );
2731 // Contents to overwrite in ToRange
2732 LookUpContents( aRange, pRefDoc, 0, 0, 0 );
2733 pLastCutMove->SetStartLastCut( nStartLastCut );
2734 pLastCutMove->SetEndLastCut( nEndLastCut );
2735 Append( pLastCutMove.release() );
2736 ResetLastCut();
2737 SetInPasteCut( false );
2738 }
2739 else
2740 {
2741 bDoContents = true;
2742 nStartAction = GetActionMax() + 1;
2743 StartBlockModify( ScChangeTrackMsgType::Append, nStartAction );
2744 }
2745 if ( bDoContents )
2746 {
2747 ScAddress aPos;
2748 for ( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ )
2749 {
2750 aPos.SetTab( nTab );
2751 for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
2752 {
2753 aPos.SetCol( nCol );
2754 for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
2755 {
2756 aPos.SetRow( nRow );
2757 AppendContent( aPos, pRefDoc );
2758 }
2759 }
2760 }
2761 }
2762 nEndAction = GetActionMax();
2763 EndBlockModify( nEndAction );
2764 if ( eClipMode == SC_CACM_CUT )
2765 {
2766 nStartLastCut = nStartAction;
2767 nEndLastCut = nEndAction;
2768 }
2769}
2770
2771void ScChangeTrack::AppendContentsIfInRefDoc( ScDocument& rRefDoc,
2772 sal_uLong& nStartAction, sal_uLong& nEndAction )
2773{
2774 ScCellIterator aIter(rRefDoc, ScRange(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB));
2775 if (aIter.first())
2776 {
2777 nStartAction = GetActionMax() + 1;
2778 StartBlockModify( ScChangeTrackMsgType::Append, nStartAction );
2779 SvNumberFormatter* pFormatter = rRefDoc.GetFormatTable();
2780 do
2781 {
2782 const ScAddress& rPos = aIter.GetPos();
2783 const ScPatternAttr* pPat = rRefDoc.GetPattern(rPos);
2784 AppendContent(
2785 rPos, aIter.getCellValue(), pPat->GetNumberFormat(pFormatter), &rRefDoc);
2786 }
2787 while (aIter.next());
2788
2789 nEndAction = GetActionMax();
2790 EndBlockModify( nEndAction );
2791 }
2792 else
2793 nStartAction = nEndAction = 0;
2794}
2795
2796ScChangeActionContent* ScChangeTrack::AppendContentOnTheFly(
2797 const ScAddress& rPos, const ScCellValue& rOldCell, const ScCellValue& rNewCell,
2798 sal_uLong nOldFormat, sal_uLong nNewFormat )
2799{
2800 ScRange aRange( rPos );
2801 ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
2802 pAct->SetOldNewCells(rOldCell, nOldFormat, rNewCell, nNewFormat, &rDoc);
2803 Append( pAct );
2804 return pAct;
2805}
2806
2807void ScChangeTrack::AppendInsert( const ScRange& rRange, bool bEndOfList )
2808{
2809 ScChangeActionIns* pAct = new ScChangeActionIns(&rDoc, rRange, bEndOfList);
2810 Append( pAct );
2811}
2812
2813void ScChangeTrack::DeleteCellEntries( std::vector<ScChangeActionContent*>& rCellList,
2814 const ScChangeAction* pDeletor )
2815{
2816 for (ScChangeActionContent* pContent : rCellList)
2817 {
2818 pContent->RemoveDeletedIn( pDeletor );
2819 if ( IsGenerated( pContent->GetActionNumber() ) &&
2820 !pContent->IsDeletedIn() )
2821 DeleteGeneratedDelContent( pContent );
2822 }
2823 rCellList.clear();
2824}
2825
2826ScChangeActionContent* ScChangeTrack::GenerateDelContent(
2827 const ScAddress& rPos, const ScCellValue& rCell, const ScDocument* pFromDoc )
2828{
2829 ScChangeActionContent* pContent = new ScChangeActionContent(
2830 ScRange( rPos ) );
2831 pContent->SetActionNumber( --nGeneratedMin );
2832 // Only NewValue
2833 ScChangeActionContent::SetValue( pContent->maNewValue, pContent->maNewCell,
2834 rPos, rCell, pFromDoc, &rDoc );
2835 // pNextContent and pPrevContent are not set
2836 if ( pFirstGeneratedDelContent )
2837 { // Insert at front
2838 pFirstGeneratedDelContent->pPrev = pContent;
2839 pContent->pNext = pFirstGeneratedDelContent;
2840 }
2841 pFirstGeneratedDelContent = pContent;
2842 aGeneratedMap.insert( std::make_pair( nGeneratedMin, pContent ) );
2843 NotifyModified( ScChangeTrackMsgType::Append, nGeneratedMin, nGeneratedMin );
2844 return pContent;
2845}
2846
2847void ScChangeTrack::DeleteGeneratedDelContent( ScChangeActionContent* pContent )
2848{
2849 sal_uLong nAct = pContent->GetActionNumber();
2850 aGeneratedMap.erase( nAct );
2851 if ( pFirstGeneratedDelContent == pContent )
2852 pFirstGeneratedDelContent = static_cast<ScChangeActionContent*>(pContent->pNext);
2853 if ( pContent->pNext )
2854 pContent->pNext->pPrev = pContent->pPrev;
2855 if ( pContent->pPrev )
2856 pContent->pPrev->pNext = pContent->pNext;
2857 delete pContent;
2858 NotifyModified( ScChangeTrackMsgType::Remove, nAct, nAct );
2859 if ( nAct == nGeneratedMin )
2860 ++nGeneratedMin; // Only after NotifyModified due to IsGenerated!
2861}
2862
2863ScChangeActionContent* ScChangeTrack::SearchContentAt(
2864 const ScBigAddress& rPos, const ScChangeAction* pButNotThis ) const
2865{
2866 SCSIZE nSlot = ComputeContentSlot( rPos.Row() );
2867 for ( ScChangeActionContent* p = ppContentSlots[nSlot]; p;
2868 p = p->GetNextInSlot() )
2869 {
2870 if ( p != pButNotThis && !p->IsDeletedIn() &&
2871 p->GetBigRange().aStart == rPos )
2872 {
2873 ScChangeActionContent* pContent = p->GetTopContent();
2874 if ( !pContent->IsDeletedIn() )
2875 return pContent;
2876 }
2877 }
2878 return nullptr;
2879}
2880
2881void ScChangeTrack::AddDependentWithNotify( ScChangeAction* pParent,
2882 ScChangeAction* pDependent )
2883{
2884 ScChangeActionLinkEntry* pLink = pParent->AddDependent( pDependent );
2885 pDependent->AddLink( pParent, pLink );
2886 if ( aModifiedLink.IsSet() )
2887 {
2888 sal_uLong nMod = pParent->GetActionNumber();
2889 NotifyModified( ScChangeTrackMsgType::Parent, nMod, nMod );
2890 }
2891}
2892
2893void ScChangeTrack::Dependencies( ScChangeAction* pAct )
2894{
2895 // Find the last dependency for Col/Row/Tab each
2896 // Concatenate Content at the same position
2897 // Move dependencies
2898 ScChangeActionType eActType = pAct->GetType();
2899 if ( eActType == SC_CAT_REJECT ||
2900 (eActType == SC_CAT_MOVE && pAct->IsRejecting()) )
2901 return ; // These Rejects are not dependent
2902
2903 if ( eActType == SC_CAT_CONTENT )
2904 {
2905 if ( !(static_cast<ScChangeActionContent*>(pAct)->GetNextContent() ||
2906 static_cast<ScChangeActionContent*>(pAct)->GetPrevContent()) )
2907 { // Concatenate Contents at same position
2908 ScChangeActionContent* pContent = SearchContentAt(
2909 pAct->GetBigRange().aStart, pAct );
2910 if ( pContent )
2911 {
2912 pContent->SetNextContent( static_cast<ScChangeActionContent*>(pAct) );
2913 static_cast<ScChangeActionContent*>(pAct)->SetPrevContent( pContent );
2914 }
2915 }
2916 const ScCellValue& rCell = static_cast<ScChangeActionContent*>(pAct)->GetNewCell();
2917 if ( ScChangeActionContent::GetContentCellType(rCell) == SC_CACCT_MATREF )
2918 {
2919 ScAddress aOrg;
2920 bool bOrgFound = rCell.mpFormula->GetMatrixOrigin(rDoc, aOrg);
2921 ScChangeActionContent* pContent = (bOrgFound ? SearchContentAt( aOrg, pAct ) : nullptr);
2922 if ( pContent && pContent->IsMatrixOrigin() )
2923 {
2924 AddDependentWithNotify( pContent, pAct );
2925 }
2926 else
2927 {
2928 OSL_FAIL( "ScChangeTrack::Dependencies: MatOrg not found" )do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "2928" ": "), "%s", "ScChangeTrack::Dependencies: MatOrg not found"
); } } while (false)
;
2929 }
2930 }
2931 }
2932
2933 if ( !(pLinkInsertCol || pLinkInsertRow || pLinkInsertTab || pLinkMove) )
2934 return ; // No Dependencies
2935 if ( pAct->IsRejecting() )
2936 return ; // Except for Content no Dependencies
2937
2938 // Insert in a corresponding Insert depends on it or else we would need
2939 // to split the preceding one.
2940 // Intersecting Inserts and Deletes are not dependent, everything else
2941 // is dependent.
2942 // The Insert last linked in is at the beginning of a chain, just the way we need it
2943
2944 const ScBigRange& rRange = pAct->GetBigRange();
2945 bool bActNoInsert = !pAct->IsInsertType();
2946 bool bActColDel = ( eActType == SC_CAT_DELETE_COLS );
2947 bool bActRowDel = ( eActType == SC_CAT_DELETE_ROWS );
2948 bool bActTabDel = ( eActType == SC_CAT_DELETE_TABS );
2949
2950 if ( pLinkInsertCol && (eActType == SC_CAT_INSERT_COLS ||
2951 (bActNoInsert && !bActRowDel && !bActTabDel)) )
2952 {
2953 for ( ScChangeActionLinkEntry* pL = pLinkInsertCol; pL; pL = pL->GetNext() )
2954 {
2955 ScChangeActionIns* pTest = static_cast<ScChangeActionIns*>(pL->GetAction());
2956 if ( !pTest->IsRejected() &&
2957 pTest->GetBigRange().Intersects( rRange ) )
2958 {
2959 AddDependentWithNotify( pTest, pAct );
2960 break; // for
2961 }
2962 }
2963 }
2964 if ( pLinkInsertRow && (eActType == SC_CAT_INSERT_ROWS ||
2965 (bActNoInsert && !bActColDel && !bActTabDel)) )
2966 {
2967 for ( ScChangeActionLinkEntry* pL = pLinkInsertRow; pL; pL = pL->GetNext() )
2968 {
2969 ScChangeActionIns* pTest = static_cast<ScChangeActionIns*>(pL->GetAction());
2970 if ( !pTest->IsRejected() &&
2971 pTest->GetBigRange().Intersects( rRange ) )
2972 {
2973 AddDependentWithNotify( pTest, pAct );
2974 break; // for
2975 }
2976 }
2977 }
2978 if ( pLinkInsertTab && (eActType == SC_CAT_INSERT_TABS ||
2979 (bActNoInsert && !bActColDel && !bActRowDel)) )
2980 {
2981 for ( ScChangeActionLinkEntry* pL = pLinkInsertTab; pL; pL = pL->GetNext() )
2982 {
2983 ScChangeActionIns* pTest = static_cast<ScChangeActionIns*>(pL->GetAction());
2984 if ( !pTest->IsRejected() &&
2985 pTest->GetBigRange().Intersects( rRange ) )
2986 {
2987 AddDependentWithNotify( pTest, pAct );
2988 break; // for
2989 }
2990 }
2991 }
2992
2993 if ( !pLinkMove )
2994 return;
2995
2996 if ( eActType == SC_CAT_CONTENT )
2997 { // Content is depending on FromRange
2998 const ScBigAddress& rPos = rRange.aStart;
2999 for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
3000 {
3001 ScChangeActionMove* pTest = static_cast<ScChangeActionMove*>(pL->GetAction());
3002 if ( !pTest->IsRejected() &&
3003 pTest->GetFromRange().In( rPos ) )
3004 {
3005 AddDependentWithNotify( pTest, pAct );
3006 }
3007 }
3008 }
3009 else if ( eActType == SC_CAT_MOVE )
3010 { // Move FromRange is depending on ToRange
3011 const ScBigRange& rFromRange = static_cast<ScChangeActionMove*>(pAct)->GetFromRange();
3012 for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
3013 {
3014 ScChangeActionMove* pTest = static_cast<ScChangeActionMove*>(pL->GetAction());
3015 if ( !pTest->IsRejected() &&
3016 pTest->GetBigRange().Intersects( rFromRange ) )
3017 {
3018 AddDependentWithNotify( pTest, pAct );
3019 }
3020 }
3021 }
3022 else
3023 { // Inserts and Deletes are depending as soon as they cross FromRange or
3024 // ToRange
3025 for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
3026 {
3027 ScChangeActionMove* pTest = static_cast<ScChangeActionMove*>(pL->GetAction());
3028 if ( !pTest->IsRejected() &&
3029 (pTest->GetFromRange().Intersects( rRange ) ||
3030 pTest->GetBigRange().Intersects( rRange )) )
3031 {
3032 AddDependentWithNotify( pTest, pAct );
3033 }
3034 }
3035 }
3036}
3037
3038void ScChangeTrack::Remove( ScChangeAction* pRemove )
3039{
3040 // Remove from Track
3041 sal_uLong nAct = pRemove->GetActionNumber();
3042 aMap.erase( nAct );
3043 if ( nAct == nActionMax )
3044 --nActionMax;
3045 if ( pRemove == pLast )
3046 pLast = pRemove->pPrev;
3047 if ( pRemove == pFirst )
3048 pFirst = pRemove->pNext;
3049 if ( nAct == nMarkLastSaved )
3050 nMarkLastSaved =
3051 ( pRemove->pPrev ? pRemove->pPrev->GetActionNumber() : 0 );
3052
3053 // Remove from global chain
3054 if ( pRemove->pNext )
3055 pRemove->pNext->pPrev = pRemove->pPrev;
3056 if ( pRemove->pPrev )
3057 pRemove->pPrev->pNext = pRemove->pNext;
3058
3059 // Don't delete Dependencies
3060 // That happens automatically on delete by LinkEntry without traversing lists
3061 if ( aModifiedLink.IsSet() )
3062 {
3063 NotifyModified( ScChangeTrackMsgType::Remove, nAct, nAct );
3064 if ( pRemove->GetType() == SC_CAT_CONTENT )
3065 {
3066 ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(pRemove);
3067 if ( ( pContent = pContent->GetPrevContent() ) != nullptr )
3068 {
3069 sal_uLong nMod = pContent->GetActionNumber();
3070 NotifyModified( ScChangeTrackMsgType::Change, nMod, nMod );
3071 }
3072 }
3073 else if ( pLast )
3074 NotifyModified( ScChangeTrackMsgType::Change, pFirst->GetActionNumber(),
3075 pLast->GetActionNumber() );
3076 }
3077
3078 if ( IsInPasteCut() && pRemove->GetType() == SC_CAT_CONTENT )
3079 { // Content is reused!
3080 ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(pRemove);
3081 pContent->RemoveAllLinks();
3082 pContent->ClearTrack();
3083 pContent->pNext = pContent->pPrev = nullptr;
3084 pContent->pNextContent = pContent->pPrevContent = nullptr;
3085 }
3086}
3087
3088void ScChangeTrack::Undo( sal_uLong nStartAction, sal_uLong nEndAction, bool bMerge )
3089{
3090 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3091 if ( bMerge )
3092 {
3093 SetMergeState( SC_CTMS_UNDO );
3094 }
3095
3096 if ( nStartAction == 0 )
3097 ++nStartAction;
3098 if ( nEndAction > nActionMax )
3099 nEndAction = nActionMax;
3100 if ( nEndAction && nStartAction <= nEndAction )
3101 {
3102 if ( nStartAction == nStartLastCut && nEndAction == nEndLastCut &&
3103 !IsInPasteCut() )
3104 ResetLastCut();
3105 StartBlockModify( ScChangeTrackMsgType::Remove, nStartAction );
3106 for ( sal_uLong j = nEndAction; j >= nStartAction; --j )
3107 { // Traverse backwards to recycle nActionMax and for faster access via pLast
3108 // Deletes are in right order
3109 ScChangeAction* pAct = IsLastAction(j) ? pLast : GetAction(j);
3110
3111 if (!pAct)
3112 continue;
3113
3114 if ( pAct->IsDeleteType() )
3115 {
3116 if (j == nEndAction || (pAct != pLast && static_cast<ScChangeActionDel*>(pAct)->IsTopDelete()))
3117 {
3118 SetInDeleteTop( true );
3119 SetInDeleteRange( static_cast<ScChangeActionDel*>(pAct)->GetOverAllRange().MakeRange() );
3120 }
3121 }
3122 UpdateReference( pAct, true );
3123 SetInDeleteTop( false );
3124 Remove( pAct );
3125 if ( IsInPasteCut() )
3126 {
3127 aPasteCutMap.insert( ::std::make_pair( pAct->GetActionNumber(), pAct ) );
3128 continue;
3129 }
3130
3131 if ( j == nStartAction && pAct->GetType() == SC_CAT_MOVE )
3132 {
3133 ScChangeActionMove* pMove = static_cast<ScChangeActionMove*>(pAct);
3134 sal_uLong nStart = pMove->GetStartLastCut();
3135 sal_uLong nEnd = pMove->GetEndLastCut();
3136 if ( nStart && nStart <= nEnd )
3137 { // Recover LastCut
3138 // Break Links before Cut Append!
3139 pMove->RemoveAllLinks();
3140 StartBlockModify( ScChangeTrackMsgType::Append, nStart );
3141 for ( sal_uLong nCut = nStart; nCut <= nEnd; nCut++ )
3142 {
3143 ScChangeActionMap::iterator itCut = aPasteCutMap.find( nCut );
3144
3145 if ( itCut != aPasteCutMap.end() )
3146 {
3147 OSL_ENSURE( aMap.find( nCut ) == aMap.end(), "ScChangeTrack::Undo: nCut dup" )do { if (true && (!(aMap.find( nCut ) == aMap.end()))
) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"
), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "3147" ": "), "%s", "ScChangeTrack::Undo: nCut dup"); } }
while (false)
;
3148 Append( itCut->second, nCut );
3149 aPasteCutMap.erase( itCut );
3150 }
3151 else
3152 {
3153 OSL_FAIL( "ScChangeTrack::Undo: nCut not found" )do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "3153" ": "), "%s", "ScChangeTrack::Undo: nCut not found"
); } } while (false)
;
3154 }
3155 }
3156 EndBlockModify( nEnd );
3157 ResetLastCut();
3158 nStartLastCut = nStart;
3159 nEndLastCut = nEnd;
3160 pLastCutMove.reset(pMove);
3161 SetLastCutMoveRange(
3162 pMove->GetFromRange().MakeRange(), &rDoc );
3163 }
3164 else
3165 delete pMove;
3166 }
3167 else
3168 delete pAct;
3169 }
3170 EndBlockModify( nEndAction );
3171 }
3172
3173 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3174 if ( bMerge )
3175 {
3176 SetMergeState( SC_CTMS_OTHER );
3177 }
3178}
3179
3180bool ScChangeTrack::MergeIgnore( const ScChangeAction& rAction, sal_uLong nFirstMerge )
3181{
3182 if ( rAction.IsRejected() )
3183 return true; // There's still a suitable Reject Action coming
3184
3185 if ( rAction.IsRejecting() && rAction.GetRejectAction() >= nFirstMerge )
3186 return true; // There it is
3187
3188 return false; // Everything else
3189}
3190
3191void ScChangeTrack::MergePrepare( const ScChangeAction* pFirstMerge, bool bShared )
3192{
3193 SetMergeState( SC_CTMS_PREPARE );
3194 sal_uLong nFirstMerge = pFirstMerge->GetActionNumber();
3195 ScChangeAction* pAct = GetLast();
3196 if ( pAct )
3197 {
3198 SetLastMerge( pAct->GetActionNumber() );
3199 while ( pAct )
3200 { // Traverse backwards; Deletes in right order
3201 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3202 if ( bShared || !ScChangeTrack::MergeIgnore( *pAct, nFirstMerge ) )
3203 {
3204 if ( pAct->IsDeleteType() )
3205 {
3206 if ( static_cast<ScChangeActionDel*>(pAct)->IsTopDelete() )
3207 {
3208 SetInDeleteTop( true );
3209 SetInDeleteRange( static_cast<ScChangeActionDel*>(pAct)->
3210 GetOverAllRange().MakeRange() );
3211 }
3212 }
3213 UpdateReference( pAct, true );
3214 SetInDeleteTop( false );
3215 pAct->DeleteCellEntries(); // Else segfault in Track Clear()
3216 }
3217 pAct = ( pAct == pFirstMerge ? nullptr : pAct->GetPrev() );
3218 }
3219 }
3220 SetMergeState( SC_CTMS_OTHER ); // Preceding by default MergeOther!
3221}
3222
3223void ScChangeTrack::MergeOwn( ScChangeAction* pAct, sal_uLong nFirstMerge, bool bShared )
3224{
3225 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3226 if ( !bShared && ScChangeTrack::MergeIgnore( *pAct, nFirstMerge ) )
3227 return;
3228
3229 SetMergeState( SC_CTMS_OWN );
3230 if ( pAct->IsDeleteType() )
3231 {
3232 if ( static_cast<ScChangeActionDel*>(pAct)->IsTopDelete() )
3233 {
3234 SetInDeleteTop( true );
3235 SetInDeleteRange( static_cast<ScChangeActionDel*>(pAct)->
3236 GetOverAllRange().MakeRange() );
3237 }
3238 }
3239 UpdateReference( pAct, false );
3240 SetInDeleteTop( false );
3241 SetMergeState( SC_CTMS_OTHER ); // Preceding by default MergeOther!
3242}
3243
3244void ScChangeTrack::UpdateReference( ScChangeAction* pAct, bool bUndo )
3245{
3246 ScChangeActionType eActType = pAct->GetType();
3247 if ( eActType == SC_CAT_CONTENT || eActType == SC_CAT_REJECT )
3248 return ;
3249
3250 // Formula cells are not in the Document!
3251 bool bOldAutoCalc = rDoc.GetAutoCalc();
3252 rDoc.SetAutoCalc( false );
3253 bool bOldNoListening = rDoc.GetNoListening();
3254 rDoc.SetNoListening( true );
3255
3256 // Formula cells ExpandRefs synchronized to the ones in the Document!
3257 bool bOldExpandRefs = rDoc.IsExpandRefs();
3258 if ( (!bUndo && pAct->IsInsertType()) || (bUndo && pAct->IsDeleteType()) )
3259 rDoc.SetExpandRefs( SC_MOD()( static_cast<ScModule*>(SfxApplication::GetModule(SfxToolsModule
::Calc)) )
->GetInputOptions().GetExpandRefs() );
3260
3261 if ( pAct->IsDeleteType() )
3262 {
3263 SetInDeleteUndo( bUndo );
3264 SetInDelete( true );
3265 }
3266 else if ( GetMergeState() == SC_CTMS_OWN )
3267 {
3268 // Recover references of formula cells
3269 // Previous MergePrepare behaved like a Delete when Inserting
3270 if ( pAct->IsInsertType() )
3271 SetInDeleteUndo( true );
3272 }
3273
3274 // First the generated ones, as if they were tracked previously!
3275 if ( pFirstGeneratedDelContent )
3276 UpdateReference( reinterpret_cast<ScChangeAction**>(&pFirstGeneratedDelContent), pAct,
3277 bUndo );
3278 UpdateReference( &pFirst, pAct, bUndo );
3279
3280 SetInDelete( false );
3281 SetInDeleteUndo( false );
3282
3283 rDoc.SetExpandRefs( bOldExpandRefs );
3284 rDoc.SetNoListening( bOldNoListening );
3285 rDoc.SetAutoCalc( bOldAutoCalc );
3286}
3287
3288void ScChangeTrack::UpdateReference( ScChangeAction** ppFirstAction,
3289 ScChangeAction* pAct, bool bUndo )
3290{
3291 ScChangeActionType eActType = pAct->GetType();
3292 bool bGeneratedDelContents =
3293 ( ppFirstAction == reinterpret_cast<ScChangeAction**>(&pFirstGeneratedDelContent) );
3294 const ScBigRange& rOrgRange = pAct->GetBigRange();
3295 ScBigRange aRange( rOrgRange );
3296 ScBigRange aDelRange( rOrgRange );
3297 sal_Int32 nDx, nDy, nDz;
3298 nDx = nDy = nDz = 0;
3299 UpdateRefMode eMode = URM_INSDEL;
3300 bool bDel = false;
3301 switch ( eActType )
3302 {
3303 case SC_CAT_INSERT_COLS :
3304 aRange.aEnd.SetCol( nInt32Max );
3305 nDx = rOrgRange.aEnd.Col() - rOrgRange.aStart.Col() + 1;
3306 break;
3307 case SC_CAT_INSERT_ROWS :
3308 aRange.aEnd.SetRow( nInt32Max );
3309 nDy = rOrgRange.aEnd.Row() - rOrgRange.aStart.Row() + 1;
3310 break;
3311 case SC_CAT_INSERT_TABS :
3312 aRange.aEnd.SetTab( nInt32Max );
3313 nDz = rOrgRange.aEnd.Tab() - rOrgRange.aStart.Tab() + 1;
3314 break;
3315 case SC_CAT_DELETE_COLS :
3316 aRange.aEnd.SetCol( nInt32Max );
3317 nDx = -(rOrgRange.aEnd.Col() - rOrgRange.aStart.Col() + 1);
3318 aDelRange.aEnd.SetCol( aDelRange.aStart.Col() - nDx - 1 );
3319 bDel = true;
3320 break;
3321 case SC_CAT_DELETE_ROWS :
3322 aRange.aEnd.SetRow( nInt32Max );
3323 nDy = -(rOrgRange.aEnd.Row() - rOrgRange.aStart.Row() + 1);
3324 aDelRange.aEnd.SetRow( aDelRange.aStart.Row() - nDy - 1 );
3325 bDel = true;
3326 break;
3327 case SC_CAT_DELETE_TABS :
3328 aRange.aEnd.SetTab( nInt32Max );
3329 nDz = -(rOrgRange.aEnd.Tab() - rOrgRange.aStart.Tab() + 1);
3330 aDelRange.aEnd.SetTab( aDelRange.aStart.Tab() - nDz - 1 );
3331 bDel = true;
3332 break;
3333 case SC_CAT_MOVE :
3334 eMode = URM_MOVE;
3335 static_cast<ScChangeActionMove*>(pAct)->GetDelta( nDx, nDy, nDz );
3336 break;
3337 default:
3338 OSL_FAIL( "ScChangeTrack::UpdateReference: unknown Type" )do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "3338" ": "), "%s", "ScChangeTrack::UpdateReference: unknown Type"
); } } while (false)
;
3339 }
3340 if ( bUndo )
3341 {
3342 nDx = -nDx;
3343 nDy = -nDy;
3344 nDz = -nDz;
3345 }
3346 if ( bDel )
3347 { // For this mechanism we assume:
3348 // There's only a whole, simple deleted row/column
3349 ScChangeActionDel* pActDel = static_cast<ScChangeActionDel*>(pAct);
3350 if ( !bUndo )
3351 { // Delete
3352 ScChangeActionType eInsType = SC_CAT_NONE; // for Insert Undo "Deletes"
3353 switch ( eActType )
3354 {
3355 case SC_CAT_DELETE_COLS :
3356 eInsType = SC_CAT_INSERT_COLS;
3357 break;
3358 case SC_CAT_DELETE_ROWS :
3359 eInsType = SC_CAT_INSERT_ROWS;
3360 break;
3361 case SC_CAT_DELETE_TABS :
3362 eInsType = SC_CAT_INSERT_TABS;
3363 break;
3364 default:
3365 {
3366 // added to avoid warnings
3367 }
3368 }
3369 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3370 {
3371 if ( p == pAct )
3372 continue; // for
3373 bool bUpdate = true;
3374 if ( GetMergeState() == SC_CTMS_OTHER &&
3375 p->GetActionNumber() <= GetLastMerge() )
3376 { // Delete in merged Document, Action in the one to be merged
3377 if ( p->IsInsertType() )
3378 {
3379 // On Insert only adjust references if the Delete does
3380 // not intersect the Insert
3381 if ( !aDelRange.Intersects( p->GetBigRange() ) )
3382 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3383 bUpdate = false;
3384 }
3385 else if ( p->GetType() == SC_CAT_CONTENT &&
3386 p->IsDeletedInDelType( eInsType ) )
3387 { // Content in Insert Undo "Delete"
3388 // Do not adjust if this Delete would be in the Insert "Delete" (was just moved)
3389 if ( aDelRange.In( p->GetBigRange().aStart ) )
3390 bUpdate = false;
3391 else
3392 {
3393 const ScChangeActionLinkEntry* pLink = p->GetDeletedIn();
3394 while ( pLink && bUpdate )
3395 {
3396 const ScChangeAction* pDel = pLink->GetAction();
3397 if ( pDel && pDel->GetType() == eInsType &&
3398 pDel->GetBigRange().In( aDelRange ) )
3399 bUpdate = false;
3400 pLink = pLink->GetNext();
3401 }
3402 }
3403 }
3404 if ( !bUpdate )
3405 continue; // for
3406 }
3407 if ( aDelRange.In( p->GetBigRange() ) )
3408 {
3409 // Do not adjust within a just deleted range,
3410 // instead assign the range.
3411 // Stack up ranges that have been deleted multiple times.
3412 // Intersecting Deletes cause "multiple delete" to be set.
3413 if ( !p->IsDeletedInDelType( eActType ) )
3414 {
3415 p->SetDeletedIn( pActDel );
3416 // Add GeneratedDelContent to the to-be-deleted list
3417 if ( bGeneratedDelContents )
3418 pActDel->AddContent( static_cast<ScChangeActionContent*>(p) );
3419 }
3420 bUpdate = false;
3421 }
3422 else
3423 {
3424 // Cut off inserted ranges, if Start/End is within the Delete,
3425 // but the Insert is not completely within the Delete or
3426 // the Delete is not completely within the Insert.
3427 // The Delete remembers which Insert it has cut off from;
3428 // it can also just be a single Insert (because Delete has
3429 // a single column/is a single row).
3430 // There can be a lot of cut-off Moves.
3431 //
3432 // ! A Delete is always a single column/a single row, therefore
3433 // ! 1 without calculating the intersection.
3434 switch ( p->GetType() )
3435 {
3436 case SC_CAT_INSERT_COLS :
3437 if ( eActType == SC_CAT_DELETE_COLS )
3438 {
3439 if ( aDelRange.In( p->GetBigRange().aStart ) )
3440 {
3441 pActDel->SetCutOffInsert(
3442 static_cast<ScChangeActionIns*>(p), 1 );
3443 p->GetBigRange().aStart.IncCol();
3444 }
3445 else if ( aDelRange.In( p->GetBigRange().aEnd ) )
3446 {
3447 pActDel->SetCutOffInsert(
3448 static_cast<ScChangeActionIns*>(p), -1 );
3449 p->GetBigRange().aEnd.IncCol( -1 );
3450 }
3451 }
3452 break;
3453 case SC_CAT_INSERT_ROWS :
3454 if ( eActType == SC_CAT_DELETE_ROWS )
3455 {
3456 if ( aDelRange.In( p->GetBigRange().aStart ) )
3457 {
3458 pActDel->SetCutOffInsert(
3459 static_cast<ScChangeActionIns*>(p), 1 );
3460 p->GetBigRange().aStart.IncRow();
3461 }
3462 else if ( aDelRange.In( p->GetBigRange().aEnd ) )
3463 {
3464 pActDel->SetCutOffInsert(
3465 static_cast<ScChangeActionIns*>(p), -1 );
3466 p->GetBigRange().aEnd.IncRow( -1 );
3467 }
3468 }
3469 break;
3470 case SC_CAT_INSERT_TABS :
3471 if ( eActType == SC_CAT_DELETE_TABS )
3472 {
3473 if ( aDelRange.In( p->GetBigRange().aStart ) )
3474 {
3475 pActDel->SetCutOffInsert(
3476 static_cast<ScChangeActionIns*>(p), 1 );
3477 p->GetBigRange().aStart.IncTab();
3478 }
3479 else if ( aDelRange.In( p->GetBigRange().aEnd ) )
3480 {
3481 pActDel->SetCutOffInsert(
3482 static_cast<ScChangeActionIns*>(p), -1 );
3483 p->GetBigRange().aEnd.IncTab( -1 );
3484 }
3485 }
3486 break;
3487 case SC_CAT_MOVE :
3488 {
3489 ScChangeActionMove* pMove = static_cast<ScChangeActionMove*>(p);
3490 short nFrom = 0;
3491 short nTo = 0;
3492 if ( aDelRange.In( pMove->GetBigRange().aStart ) )
3493 nTo = 1;
3494 else if ( aDelRange.In( pMove->GetBigRange().aEnd ) )
3495 nTo = -1;
3496 if ( aDelRange.In( pMove->GetFromRange().aStart ) )
3497 nFrom = 1;
3498 else if ( aDelRange.In( pMove->GetFromRange().aEnd ) )
3499 nFrom = -1;
3500 if ( nFrom )
3501 {
3502 switch ( eActType )
3503 {
3504 case SC_CAT_DELETE_COLS :
3505 if ( nFrom > 0 )
3506 pMove->GetFromRange().aStart.IncCol( nFrom );
3507 else
3508 pMove->GetFromRange().aEnd.IncCol( nFrom );
3509 break;
3510 case SC_CAT_DELETE_ROWS :
3511 if ( nFrom > 0 )
3512 pMove->GetFromRange().aStart.IncRow( nFrom );
3513 else
3514 pMove->GetFromRange().aEnd.IncRow( nFrom );
3515 break;
3516 case SC_CAT_DELETE_TABS :
3517 if ( nFrom > 0 )
3518 pMove->GetFromRange().aStart.IncTab( nFrom );
3519 else
3520 pMove->GetFromRange().aEnd.IncTab( nFrom );
3521 break;
3522 default:
3523 {
3524 // added to avoid warnings
3525 }
3526 }
3527 }
3528 if ( nTo )
3529 {
3530 switch ( eActType )
3531 {
3532 case SC_CAT_DELETE_COLS :
3533 if ( nTo > 0 )
3534 pMove->GetBigRange().aStart.IncCol( nTo );
3535 else
3536 pMove->GetBigRange().aEnd.IncCol( nTo );
3537 break;
3538 case SC_CAT_DELETE_ROWS :
3539 if ( nTo > 0 )
3540 pMove->GetBigRange().aStart.IncRow( nTo );
3541 else
3542 pMove->GetBigRange().aEnd.IncRow( nTo );
3543 break;
3544 case SC_CAT_DELETE_TABS :
3545 if ( nTo > 0 )
3546 pMove->GetBigRange().aStart.IncTab( nTo );
3547 else
3548 pMove->GetBigRange().aEnd.IncTab( nTo );
3549 break;
3550 default:
3551 {
3552 // added to avoid warnings
3553 }
3554 }
3555 }
3556 if ( nFrom || nTo )
3557 {
3558 ScChangeActionDelMoveEntry* pLink =
3559 pActDel->AddCutOffMove( pMove, nFrom, nTo );
3560 pMove->AddLink( pActDel, pLink );
3561 }
3562 }
3563 break;
3564 default:
3565 {
3566 // added to avoid warnings
3567 }
3568 }
3569 }
3570 if ( bUpdate )
3571 {
3572 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3573 if ( p->GetType() == eActType && !p->IsRejected() &&
3574 !pActDel->IsDeletedIn() &&
3575 p->GetBigRange().In( aDelRange ) )
3576 pActDel->SetDeletedIn( p ); // Slipped underneath it
3577 }
3578 }
3579 }
3580 else
3581 { // Undo Delete
3582 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3583 {
3584 if ( p == pAct )
3585 continue; // for
3586 bool bUpdate = true;
3587 if ( aDelRange.In( p->GetBigRange() ) )
3588 {
3589 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3590 if ( GetMergeState() == SC_CTMS_UNDO && !p->IsDeletedIn( pAct ) && pAct->IsDeleteType() &&
3591 ( p->GetType() == SC_CAT_CONTENT ||
3592 p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
3593 p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) )
3594 {
3595 p->SetDeletedIn( pAct );
3596 }
3597
3598 if ( p->IsDeletedInDelType( eActType ) )
3599 {
3600 if ( p->IsDeletedIn( pActDel ) )
3601 {
3602 if ( p->GetType() != SC_CAT_CONTENT ||
3603 static_cast<ScChangeActionContent*>(p)->IsTopContent() )
3604 { // First really remove the TopContent
3605 p->RemoveDeletedIn( pActDel );
3606 // Do NOT delete GeneratedDelContent from the list, we might need
3607 // it later on for Reject; we delete in DeleteCellEntries
3608 }
3609 }
3610 bUpdate = false;
3611 }
3612 else if ( eActType != SC_CAT_DELETE_TABS &&
3613 p->IsDeletedInDelType( SC_CAT_DELETE_TABS ) )
3614 { // Do not update in deleted Tables except for when moving Tables
3615 bUpdate = false;
3616 }
3617 if ( p->GetType() == eActType && pActDel->IsDeletedIn( p ) )
3618 {
3619 pActDel->RemoveDeletedIn( p );// Slipped underneath
3620 bUpdate = true;
3621 }
3622 }
3623 if ( bUpdate )
3624 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3625 }
3626 if ( !bGeneratedDelContents )
3627 { // These are else also needed for the real Undo
3628 pActDel->UndoCutOffInsert();
3629 pActDel->UndoCutOffMoves();
3630 }
3631 }
3632 }
3633 else if ( eActType == SC_CAT_MOVE )
3634 {
3635 ScChangeActionMove* pActMove = static_cast<ScChangeActionMove*>(pAct);
3636 bool bLastCutMove = ( pActMove == pLastCutMove.get() );
3637 const ScBigRange& rTo = pActMove->GetBigRange();
3638 const ScBigRange& rFrom = pActMove->GetFromRange();
3639 if ( !bUndo )
3640 { // Move
3641 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3642 {
3643 if ( p == pAct )
3644 continue; // for
3645 if ( p->GetType() == SC_CAT_CONTENT )
3646 {
3647 // Delete content in Target (Move Content to Source)
3648 if ( rTo.In( p->GetBigRange() ) )
3649 {
3650 if ( !p->IsDeletedIn( pActMove ) )
3651 {
3652 p->SetDeletedIn( pActMove );
3653 // Add GeneratedDelContent to the to-be-deleted list
3654 if ( bGeneratedDelContents )
3655 pActMove->AddContent( static_cast<ScChangeActionContent*>(p) );
3656 }
3657 }
3658 else if ( bLastCutMove &&
3659 p->GetActionNumber() > nEndLastCut &&
3660 rFrom.In( p->GetBigRange() ) )
3661 { // Paste Cut: insert new Content inserted after stays
3662 // Split up the ContentChain
3663 ScChangeActionContent *pHere, *pTmp;
3664 pHere = static_cast<ScChangeActionContent*>(p);
3665 for (;;)
3666 {
3667 pTmp = pHere->GetPrevContent();
3668 if (!pTmp || pTmp->GetActionNumber() <= nEndLastCut)
3669 break;
3670 pHere = pTmp;
3671 }
3672 if ( pTmp )
3673 { // Becomes TopContent of the Move
3674 pTmp->SetNextContent( nullptr );
3675 pHere->SetPrevContent( nullptr );
3676 }
3677 do
3678 { // Recover dependency from FromRange
3679 AddDependentWithNotify( pActMove, pHere );
3680 } while ( ( pHere = pHere->GetNextContent() ) != nullptr );
3681 }
3682 // #i87003# [Collaboration] Move range and insert content in FromRange is not merged correctly
3683 else if ( ( GetMergeState() != SC_CTMS_PREPARE && GetMergeState() != SC_CTMS_OWN ) || p->GetActionNumber() <= pAct->GetActionNumber() )
3684 p->UpdateReference( this, eMode, rFrom, nDx, nDy, nDz );
3685 }
3686 }
3687 }
3688 else
3689 { // Undo Move
3690 bool bActRejected = pActMove->IsRejected();
3691 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3692 {
3693 if ( p == pAct )
3694 continue; // for
3695 if ( p->GetType() == SC_CAT_CONTENT )
3696 {
3697 // Move Content into Target if not deleted else to delete (FIXME: What?)
3698 if ( p->IsDeletedIn( pActMove ) )
3699 {
3700 if ( static_cast<ScChangeActionContent*>(p)->IsTopContent() )
3701 { // First really remove the TopContent
3702 p->RemoveDeletedIn( pActMove );
3703 // Do NOT delete GeneratedDelContent from the list, we might need
3704 // it later on for Reject; we delete in DeleteCellEntries
3705 }
3706 }
3707 // #i87003# [Collaboration] Move range and insert content in FromRange is not merged correctly
3708 else if ( ( GetMergeState() != SC_CTMS_PREPARE && GetMergeState() != SC_CTMS_OWN ) || p->GetActionNumber() <= pAct->GetActionNumber() )
3709 p->UpdateReference( this, eMode, rTo, nDx, nDy, nDz );
3710 if ( bActRejected &&
3711 static_cast<ScChangeActionContent*>(p)->IsTopContent() &&
3712 rFrom.In( p->GetBigRange() ) )
3713 { // Recover dependency to write Content
3714 ScChangeActionLinkEntry* pLink =
3715 pActMove->AddDependent( p );
3716 p->AddLink( pActMove, pLink );
3717 }
3718 }
3719 }
3720 }
3721 }
3722 else
3723 { // Insert/Undo Insert
3724 switch ( GetMergeState() )
3725 {
3726 case SC_CTMS_NONE :
3727 case SC_CTMS_OTHER :
3728 {
3729 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3730 {
3731 if ( p == pAct )
3732 continue; // for
3733 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3734 }
3735 }
3736 break;
3737 case SC_CTMS_PREPARE :
3738 {
3739 // "Delete" in Insert-Undo
3740 const ScChangeActionLinkEntry* pLink = pAct->GetFirstDependentEntry();
3741 while ( pLink )
3742 {
3743 ScChangeAction* p = const_cast<ScChangeAction*>(pLink->GetAction());
3744 if ( p )
3745 p->SetDeletedIn( pAct );
3746 pLink = pLink->GetNext();
3747 }
3748
3749 // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly
3750 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3751 {
3752 if ( !p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
3753 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3754 ( p->GetType() == SC_CAT_CONTENT ||
3755 p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
3756 p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
3757 pAct->GetBigRange().Intersects( p->GetBigRange() ) )
3758 {
3759 p->SetDeletedIn( pAct );
3760 }
3761 }
3762
3763 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3764 {
3765 if ( p == pAct )
3766 continue; // for
3767 if ( !p->IsDeletedIn( pAct )
3768 // #i95212# [Collaboration] Bad handling of row insertion in shared spreadsheet
3769 && p->GetActionNumber() <= pAct->GetActionNumber() )
3770 {
3771 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3772 }
3773 }
3774 }
3775 break;
3776 case SC_CTMS_OWN :
3777 {
3778 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3779 {
3780 if ( p == pAct )
3781 continue; // for
3782 if ( !p->IsDeletedIn( pAct )
3783 // #i95212# [Collaboration] Bad handling of row insertion in shared spreadsheet
3784 && p->GetActionNumber() <= pAct->GetActionNumber() )
3785 {
3786 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3787 }
3788 }
3789 // Undo "Delete" in Insert-Undo
3790 const ScChangeActionLinkEntry* pLink = pAct->GetFirstDependentEntry();
3791 while ( pLink )
3792 {
3793 ScChangeAction* p = const_cast<ScChangeAction*>(pLink->GetAction());
3794 if ( p )
3795 p->RemoveDeletedIn( pAct );
3796 pLink = pLink->GetNext();
3797 }
3798
3799 // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly
3800 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3801 {
3802 if ( p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
3803 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3804 ( p->GetType() == SC_CAT_CONTENT ||
3805 p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
3806 p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
3807 pAct->GetBigRange().Intersects( p->GetBigRange() ) )
3808 {
3809 p->RemoveDeletedIn( pAct );
3810 }
3811 }
3812 }
3813 break;
3814 // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
3815 case SC_CTMS_UNDO :
3816 {
3817 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3818 {
3819 if ( !p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
3820 ( p->GetType() == SC_CAT_CONTENT ||
3821 p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
3822 p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
3823 pAct->GetBigRange().Intersects( p->GetBigRange() ) )
3824 {
3825 p->SetDeletedIn( pAct );
3826 }
3827 }
3828
3829 for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
3830 {
3831 if ( p == pAct )
3832 {
3833 continue;
3834 }
3835 if ( !p->IsDeletedIn( pAct ) && p->GetActionNumber() <= pAct->GetActionNumber() )
3836 {
3837 p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
3838 }
3839 }
3840 }
3841 break;
3842 }
3843 }
3844}
3845
3846void ScChangeTrack::GetDependents( ScChangeAction* pAct,
3847 ScChangeActionMap& rMap, bool bListMasterDelete, bool bAllFlat ) const
3848{
3849 //TODO: bAllFlat==TRUE: called internally from Accept or Reject
3850 //TODO: => Generated will not be added
3851 bool bIsDelete = pAct->IsDeleteType();
3852 bool bIsMasterDelete = ( bListMasterDelete && pAct->IsMasterDelete() );
3853
3854 const ScChangeAction* pCur = nullptr;
3855 ::std::stack<ScChangeAction*> cStack;
3856 cStack.push(pAct);
3857
3858 while ( !cStack.empty() )
3859 {
3860 pCur = cStack.top();
3861 cStack.pop();
3862
3863 if ( pCur->IsInsertType() )
3864 {
3865 const ScChangeActionLinkEntry* pL = pCur->GetFirstDependentEntry();
3866 while ( pL )
3867 {
3868 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
3869 if ( p != pAct )
3870 {
3871 if ( bAllFlat )
3872 {
3873 sal_uLong n = p->GetActionNumber();
3874 if ( !IsGenerated( n ) && rMap.insert( ::std::make_pair( n, p ) ).second )
3875 if ( p->HasDependent() )
3876 cStack.push( p );
3877 }
3878 else
3879 {
3880 if ( p->GetType() == SC_CAT_CONTENT )
3881 {
3882 if ( static_cast<ScChangeActionContent*>(p)->IsTopContent() )
3883 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3884 }
3885 else
3886 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3887 }
3888 }
3889 pL = pL->GetNext();
3890 }
3891 }
3892 else if ( pCur->IsDeleteType() )
3893 {
3894 if ( bIsDelete )
3895 { // Contents of deleted Ranges are only of interest on Delete
3896 ScChangeActionDel* pDel = const_cast<ScChangeActionDel*>(static_cast<const ScChangeActionDel*>(pCur));
3897 if ( !bAllFlat && bIsMasterDelete && pCur == pAct )
3898 {
3899 // Corresponding Deletes to this Delete to the same level,
3900 // if this Delete is at the top of a Row
3901 ScChangeActionType eType = pDel->GetType();
3902 ScChangeAction* p = pDel;
3903 for (;;)
3904 {
3905 p = p->GetPrev();
3906 if (!p || p->GetType() != eType ||
3907 static_cast<ScChangeActionDel*>(p)->IsTopDelete() )
3908 break;
3909 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3910 }
3911 // delete this in the map too
3912 rMap.insert( ::std::make_pair( pAct->GetActionNumber(), pAct ) );
3913 }
3914 else
3915 {
3916 const ScChangeActionLinkEntry* pL = pCur->GetFirstDeletedEntry();
3917 while ( pL )
3918 {
3919 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
3920 if ( p != pAct )
3921 {
3922 if ( bAllFlat )
3923 {
3924 // Only a TopContent of a chain is in LinkDeleted
3925 sal_uLong n = p->GetActionNumber();
3926 if ( !IsGenerated( n ) && rMap.insert( ::std::make_pair( n, p ) ).second )
3927 if ( p->HasDeleted() ||
3928 p->GetType() == SC_CAT_CONTENT )
3929 cStack.push( p );
3930 }
3931 else
3932 {
3933 if ( p->IsDeleteType() )
3934 { // Further TopDeletes to same level: it's not rejectable
3935 if ( static_cast<ScChangeActionDel*>(p)->IsTopDelete() )
3936 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3937 }
3938 else
3939 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3940 }
3941 }
3942 pL = pL->GetNext();
3943 }
3944 }
3945 }
3946 }
3947 else if ( pCur->GetType() == SC_CAT_MOVE )
3948 {
3949 // Deleted Contents in ToRange
3950 const ScChangeActionLinkEntry* pL = pCur->GetFirstDeletedEntry();
3951 while ( pL )
3952 {
3953 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
3954 if ( p != pAct && rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) ).second )
3955 {
3956 // Only one TopContent of a chain is in LinkDeleted
3957 if ( bAllFlat && (p->HasDeleted() ||
3958 p->GetType() == SC_CAT_CONTENT) )
3959 cStack.push( p );
3960 }
3961 pL = pL->GetNext();
3962 }
3963 // New Contents in FromRange or new FromRange in ToRange
3964 // or Inserts/Deletes in FromRange/ToRange
3965 pL = pCur->GetFirstDependentEntry();
3966 while ( pL )
3967 {
3968 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
3969 if ( p != pAct )
3970 {
3971 if ( bAllFlat )
3972 {
3973 sal_uLong n = p->GetActionNumber();
3974 if ( !IsGenerated( n ) && rMap.insert( ::std::make_pair( n, p ) ).second )
3975 if ( p->HasDependent() || p->HasDeleted() )
3976 cStack.push( p );
3977 }
3978 else
3979 {
3980 if ( p->GetType() == SC_CAT_CONTENT )
3981 {
3982 if ( static_cast<ScChangeActionContent*>(p)->IsTopContent() )
3983 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3984 }
3985 else
3986 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
3987 }
3988 }
3989 pL = pL->GetNext();
3990 }
3991 }
3992 else if ( pCur->GetType() == SC_CAT_CONTENT )
3993 { // All changes at same position
3994 ScChangeActionContent* pContent = const_cast<ScChangeActionContent*>(static_cast<const ScChangeActionContent*>(pCur));
3995 // All preceding ones
3996 while ( ( pContent = pContent->GetPrevContent() ) != nullptr )
3997 {
3998 if ( !pContent->IsRejected() )
3999 rMap.insert( ::std::make_pair( pContent->GetActionNumber(), pContent ) );
4000 }
4001 pContent = const_cast<ScChangeActionContent*>(static_cast<const ScChangeActionContent*>(pCur));
4002 // All succeeding ones
4003 while ( ( pContent = pContent->GetNextContent() ) != nullptr )
4004 {
4005 if ( !pContent->IsRejected() )
4006 rMap.insert( ::std::make_pair( pContent->GetActionNumber(), pContent ) );
4007 }
4008 // all MatrixReferences of a MatrixOrigin
4009 const ScChangeActionLinkEntry* pL = pCur->GetFirstDependentEntry();
4010 while ( pL )
4011 {
4012 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
4013 if ( p != pAct )
4014 {
4015 if ( bAllFlat )
4016 {
4017 sal_uLong n = p->GetActionNumber();
4018 if ( !IsGenerated( n ) && rMap.insert( ::std::make_pair( n, p ) ).second )
4019 if ( p->HasDependent() )
4020 cStack.push( p );
4021 }
4022 else
4023 rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
4024 }
4025 pL = pL->GetNext();
4026 }
4027 }
4028 else if ( pCur->GetType() == SC_CAT_REJECT )
4029 {
4030 if ( bAllFlat )
4031 {
4032 ScChangeAction* p = GetAction(
4033 static_cast<const ScChangeActionReject*>(pCur)->GetRejectAction() );
4034 if (p != pAct && rMap.find( p->GetActionNumber() ) == rMap.end())
4035 cStack.push( p );
4036 }
4037 }
4038 }
4039}
4040
4041bool ScChangeTrack::SelectContent( ScChangeAction* pAct, bool bOldest )
4042{
4043 if ( pAct->GetType() != SC_CAT_CONTENT )
4044 return false;
4045
4046 ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(pAct);
4047 if ( bOldest )
4048 {
4049 pContent = pContent->GetTopContent();
4050 for (;;)
4051 {
4052 ScChangeActionContent* pPrevContent = pContent->GetPrevContent();
4053 if ( !pPrevContent || !pPrevContent->IsVirgin() )
4054 break;
4055 pContent = pPrevContent;
4056 }
4057 }
4058
4059 if ( !pContent->IsClickable() )
4060 return false;
4061
4062 ScBigRange aBigRange( pContent->GetBigRange() );
4063 const ScCellValue& rCell = (bOldest ? pContent->GetOldCell() : pContent->GetNewCell());
4064 if ( ScChangeActionContent::GetContentCellType(rCell) == SC_CACCT_MATORG )
4065 {
4066 SCCOL nC;
4067 SCROW nR;
4068 rCell.mpFormula->GetMatColsRows(nC, nR);
4069 aBigRange.aEnd.IncCol( nC-1 );
4070 aBigRange.aEnd.IncRow( nR-1 );
4071 }
4072
4073 if ( !aBigRange.IsValid( rDoc ) )
4074 return false;
4075
4076 ScRange aRange( aBigRange.MakeRange() );
4077 if ( !rDoc.IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(),
4078 aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) )
4079 return false;
4080
4081 if ( pContent->HasDependent() )
4082 {
4083 bool bOk = true;
4084 ::std::stack<ScChangeActionContent*> aRejectActions;
4085 const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry();
4086 while ( pL )
4087 {
4088 ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
4089 if ( p != pContent )
4090 {
4091 if ( p->GetType() == SC_CAT_CONTENT )
4092 {
4093 // we don't need no recursion here, do we?
4094 bOk &= static_cast<ScChangeActionContent*>(p)->Select( rDoc, this,
4095 bOldest, &aRejectActions );
4096 }
4097 else
4098 {
4099 OSL_FAIL( "ScChangeTrack::SelectContent: content dependent no content" )do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "4099" ": "), "%s", "ScChangeTrack::SelectContent: content dependent no content"
); } } while (false)
;
4100 }
4101 }
4102 pL = pL->GetNext();
4103 }
4104
4105 bOk &= pContent->Select( rDoc, this, bOldest, nullptr );
4106 // now the matrix is inserted and new content values are ready
4107
4108 while ( !aRejectActions.empty() )
4109 {
4110 ScChangeActionContent* pNew = aRejectActions.top();
4111 aRejectActions.pop();
4112 ScAddress aPos( pNew->GetBigRange().aStart.MakeAddress() );
4113 ScCellValue aCell;
4114 aCell.assign(rDoc, aPos);
4115 pNew->SetNewValue(aCell, &rDoc);
4116 Append( pNew );
4117 }
4118 return bOk;
4119 }
4120 else
4121 return pContent->Select( rDoc, this, bOldest, nullptr );
4122}
4123
4124void ScChangeTrack::AcceptAll()
4125{
4126 for ( ScChangeAction* p = GetFirst(); p; p = p->GetNext() )
4127 {
4128 p->Accept();
4129 }
4130}
4131
4132bool ScChangeTrack::Accept( ScChangeAction* pAct )
4133{
4134 if ( !pAct->IsClickable() )
4135 return false;
4136
4137 if ( pAct->IsDeleteType() || pAct->GetType() == SC_CAT_CONTENT )
4138 {
4139 ScChangeActionMap aActionMap;
4140
4141 GetDependents( pAct, aActionMap, false, true );
4142
4143 for( auto& rEntry : aActionMap )
4144 {
4145 rEntry.second->Accept();
4146 }
4147 }
4148 pAct->Accept();
4149 return true;
4150}
4151
4152bool ScChangeTrack::RejectAll()
4153{
4154 bool bOk = true;
4155 for ( ScChangeAction* p = GetLast(); p && bOk; p = p->GetPrev() )
4156 { //TODO: Traverse backwards as dependencies attached to RejectActions
4157 if ( p->IsInternalRejectable() )
4158 bOk = Reject( p );
4159 }
4160 return bOk;
4161}
4162
4163bool ScChangeTrack::Reject( ScChangeAction* pAct, bool bShared )
4164{
4165 // #i100895# When collaboration changes are reversed, it must be possible
4166 // to reject a deleted row above another deleted row.
4167 if ( bShared && pAct->IsDeletedIn() )
4168 pAct->RemoveAllDeletedIn();
4169
4170 if ( !pAct->IsRejectable() )
4171 return false;
4172
4173 std::unique_ptr<ScChangeActionMap> pMap;
4174 if ( pAct->HasDependent() )
4175 {
4176 pMap.reset(new ScChangeActionMap);
4177 GetDependents( pAct, *pMap, false, true );
4178 }
4179 bool bRejected = Reject( pAct, pMap.get(), false );
4180 return bRejected;
4181}
4182
4183bool ScChangeTrack::Reject(
4184 ScChangeAction* pAct, ScChangeActionMap* pMap, bool bRecursion )
4185{
4186 if ( !pAct->IsInternalRejectable() )
4187 return false;
4188
4189 bool bOk = true;
4190 bool bRejected = false;
4191 if ( pAct->IsInsertType() )
4192 {
4193 if ( pAct->HasDependent() && !bRecursion )
4194 {
4195 OSL_ENSURE( pMap, "ScChangeTrack::Reject: Insert without map" )do { if (true && (!(pMap))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN
), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "4195" ": "), "%s", "ScChangeTrack::Reject: Insert without map"
); } } while (false)
;
4196 ScChangeActionMap::reverse_iterator itChangeAction;
4197 for (itChangeAction = pMap->rbegin();
4198 itChangeAction != pMap->rend() && bOk; ++itChangeAction)
4199 {
4200 // Do not restore Contents which would end up being deleted anyways
4201 if ( itChangeAction->second->GetType() == SC_CAT_CONTENT )
4202 itChangeAction->second->SetRejected();
4203 else if ( itChangeAction->second->IsDeleteType() )
4204 itChangeAction->second->Accept(); // Deleted to Nirvana
4205 else
4206 bOk = Reject( itChangeAction->second, nullptr, true ); // Recursion!
4207 }
4208 }
4209 if ( bOk )
4210 {
4211 bRejected = pAct->Reject( rDoc );
4212 if ( bRejected )
4213 {
4214 // pRefDoc NULL := Do not save deleted Cells
4215 AppendDeleteRange( pAct->GetBigRange().MakeRange(), nullptr, short(0),
4216 pAct->GetActionNumber() );
4217 }
4218 }
4219 }
4220 else if ( pAct->IsDeleteType() )
4221 {
4222 OSL_ENSURE( !pMap, "ScChangeTrack::Reject: Delete with map" )do { if (true && (!(!pMap))) { sal_detail_logFormat((
SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "4222" ": "), "%s", "ScChangeTrack::Reject: Delete with map"
); } } while (false)
;
4223 ScBigRange aDelRange;
4224 sal_uLong nRejectAction = pAct->GetActionNumber();
4225 bool bTabDel, bTabDelOk;
4226 if ( pAct->GetType() == SC_CAT_DELETE_TABS )
4227 {
4228 bTabDel = true;
4229 aDelRange = pAct->GetBigRange();
4230 bTabDelOk = pAct->Reject( rDoc );
4231 bOk = bTabDelOk;
4232 if ( bOk )
4233 {
4234 pAct = pAct->GetPrev();
4235 bOk = ( pAct && pAct->GetType() == SC_CAT_DELETE_COLS );
4236 }
4237 }
4238 else
4239 bTabDel = bTabDelOk = false;
4240 ScChangeActionDel* pDel = static_cast<ScChangeActionDel*>(pAct);
4241 if ( bOk )
4242 {
4243 aDelRange = pDel->GetOverAllRange();
4244 bOk = aDelRange.IsValid( rDoc );
4245 }
4246 bool bOneOk = false;
4247 if ( bOk )
4248 {
4249 ScChangeActionType eActType = pAct->GetType();
4250 switch ( eActType )
4251 {
4252 case SC_CAT_DELETE_COLS :
4253 aDelRange.aStart.SetCol( aDelRange.aEnd.Col() );
4254 break;
4255 case SC_CAT_DELETE_ROWS :
4256 aDelRange.aStart.SetRow( aDelRange.aEnd.Row() );
4257 break;
4258 case SC_CAT_DELETE_TABS :
4259 aDelRange.aStart.SetTab( aDelRange.aEnd.Tab() );
4260 break;
4261 default:
4262 {
4263 // added to avoid warnings
4264 }
4265 }
4266 ScChangeAction* p = pAct;
4267 bool bLoop = true;
4268 do
4269 {
4270 pDel = static_cast<ScChangeActionDel*>(p);
4271 bOk = pDel->Reject( rDoc );
4272 if ( bOk )
4273 {
4274 if ( bOneOk )
4275 {
4276 switch ( pDel->GetType() )
4277 {
4278 case SC_CAT_DELETE_COLS :
4279 aDelRange.aStart.IncCol( -1 );
4280 break;
4281 case SC_CAT_DELETE_ROWS :
4282 aDelRange.aStart.IncRow( -1 );
4283 break;
4284 case SC_CAT_DELETE_TABS :
4285 aDelRange.aStart.IncTab( -1 );
4286 break;
4287 default:
4288 {
4289 // added to avoid warnings
4290 }
4291 }
4292 }
4293 else
4294 bOneOk = true;
4295 }
4296 if ( pDel->IsBaseDelete() )
4297 bLoop = false;
4298 else
4299 p = p->GetPrev();
4300 } while ( bOk && bLoop && p && p->GetType() == eActType &&
4301 !static_cast<ScChangeActionDel*>(p)->IsTopDelete() );
4302 }
4303 bRejected = bOk;
4304 if ( bOneOk || (bTabDel && bTabDelOk) )
4305 {
4306 // Delete Reject made UpdateReference Undo
4307 ScChangeActionIns* pReject = new ScChangeActionIns( &rDoc,
4308 aDelRange.MakeRange() );
4309 pReject->SetRejectAction( nRejectAction );
4310 pReject->SetState( SC_CAS_ACCEPTED );
4311 Append( pReject );
4312 }
4313 }
4314 else if ( pAct->GetType() == SC_CAT_MOVE )
4315 {
4316 if ( pAct->HasDependent() && !bRecursion )
4317 {
4318 OSL_ENSURE( pMap, "ScChangeTrack::Reject: Move without Map" )do { if (true && (!(pMap))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN
), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "4318" ": "), "%s", "ScChangeTrack::Reject: Move without Map"
); } } while (false)
;
4319 ScChangeActionMap::reverse_iterator itChangeAction;
4320
4321 for( itChangeAction = pMap->rbegin(); itChangeAction != pMap->rend() && bOk; ++itChangeAction )
4322 {
4323 bOk = Reject( itChangeAction->second, nullptr, true ); // Recursion!
4324 }
4325 }
4326 if ( bOk )
4327 {
4328 bRejected = pAct->Reject( rDoc );
4329 if ( bRejected )
4330 {
4331 ScChangeActionMove* pReject = new ScChangeActionMove(
4332 pAct->GetBigRange().MakeRange(),
4333 static_cast<ScChangeActionMove*>(pAct)->GetFromRange().MakeRange(), this );
4334 pReject->SetRejectAction( pAct->GetActionNumber() );
4335 pReject->SetState( SC_CAS_ACCEPTED );
4336 Append( pReject );
4337 }
4338 }
4339 }
4340 else if ( pAct->GetType() == SC_CAT_CONTENT )
4341 {
4342 ScRange aRange;
4343 ScChangeActionContent* pReject;
4344 if ( bRecursion )
4345 pReject = nullptr;
4346 else
4347 {
4348 aRange = pAct->GetBigRange().aStart.MakeAddress();
4349 pReject = new ScChangeActionContent( aRange );
4350 ScCellValue aCell;
4351 aCell.assign(rDoc, aRange.aStart);
4352 pReject->SetOldValue(aCell, &rDoc, &rDoc);
4353 }
4354 bRejected = pAct->Reject( rDoc );
4355 if ( bRejected && !bRecursion )
4356 {
4357 ScCellValue aCell;
4358 aCell.assign(rDoc, aRange.aStart);
4359 pReject->SetNewValue(aCell, &rDoc);
4360 pReject->SetRejectAction( pAct->GetActionNumber() );
4361 pReject->SetState( SC_CAS_ACCEPTED );
4362 Append( pReject );
4363 }
4364 else
4365 delete pReject;
4366 }
4367 else
4368 {
4369 OSL_FAIL( "ScChangeTrack::Reject: say what?" )do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
":" "4369" ": "), "%s", "ScChangeTrack::Reject: say what?");
} } while (false)
;
4370 }
4371
4372 return bRejected;
4373}
4374
4375bool ScChangeTrack::IsLastAction( sal_uLong nNum ) const
4376{
4377 return nNum == nActionMax && pLast && pLast->GetActionNumber() == nNum;
4378}
4379
4380sal_uLong ScChangeTrack::AddLoadedGenerated(
4381 const ScCellValue& rNewCell, const ScBigRange& aBigRange, const OUString& sNewValue )
4382{
4383 ScChangeActionContent* pAct = new ScChangeActionContent( --nGeneratedMin, rNewCell, aBigRange, &rDoc, sNewValue );
4384 if ( pFirstGeneratedDelContent )
4385 pFirstGeneratedDelContent->pPrev = pAct;
4386 pAct->pNext = pFirstGeneratedDelContent;
4387 pFirstGeneratedDelContent = pAct;
4388 aGeneratedMap.insert( ::std::make_pair( pAct->GetActionNumber(), pAct ) );
4389 return pAct->GetActionNumber();
4390}
4391
4392void ScChangeTrack::AppendCloned( ScChangeAction* pAppend )
4393{
4394 aMap.insert( ::std::make_pair( pAppend->GetActionNumber(), pAppend ) );
4395 if ( !pLast )
4396 pFirst = pLast = pAppend;
4397 else
4398 {
4399 pLast->pNext = pAppend;
4400 pAppend->pPrev = pLast;
4401 pLast = pAppend;
4402 }
4403}
4404
4405ScChangeTrack* ScChangeTrack::Clone( ScDocument* pDocument ) const
4406{
4407 if ( !pDocument )
4408 {
4409 return nullptr;
4410 }
4411
4412 std::unique_ptr<ScChangeTrack> pClonedTrack(new ScChangeTrack( *pDocument ));
4413 pClonedTrack->SetTimeNanoSeconds( IsTimeNanoSeconds() );
4414
4415 // clone generated actions
4416 ::std::stack< const ScChangeAction* > aGeneratedStack;
4417 const ScChangeAction* pGenerated = GetFirstGenerated();
4418 while ( pGenerated )
4419 {
4420 aGeneratedStack.push( pGenerated );
4421 pGenerated = pGenerated->GetNext();
4422 }
4423 while ( !aGeneratedStack.empty() )
4424 {
4425 pGenerated = aGeneratedStack.top();
4426 aGeneratedStack.pop();
4427 const ScChangeActionContent& rContent = dynamic_cast<const ScChangeActionContent&>(*pGenerated);
4428 const ScCellValue& rNewCell = rContent.GetNewCell();
4429 if (!rNewCell.isEmpty())
4430 {
4431 ScCellValue aClonedNewCell;
4432 aClonedNewCell.assign(rNewCell, *pDocument);
4433 OUString aNewValue;
4434 rContent.GetNewString( aNewValue, pDocument );
4435 pClonedTrack->nGeneratedMin = pGenerated->GetActionNumber() + 1;
4436 pClonedTrack->AddLoadedGenerated(aClonedNewCell, pGenerated->GetBigRange(), aNewValue);
4437 }
4438 }
4439
4440 // clone actions
4441 const ScChangeAction* pAction = GetFirst();
4442 while ( pAction )
4443 {
4444 ScChangeAction* pClonedAction = nullptr;
4445
4446 switch ( pAction->GetType() )
4447 {
4448 case SC_CAT_INSERT_COLS:
4449 case SC_CAT_INSERT_ROWS:
4450 case SC_CAT_INSERT_TABS:
4451 {
4452 bool bEndOfList = static_cast<const ScChangeActionIns*>(pAction)->IsEndOfList();
4453 pClonedAction = new ScChangeActionIns(
4454 pAction->GetActionNumber(),
4455 pAction->GetState(),
4456 pAction->GetRejectAction(),
4457 pAction->GetBigRange(),
4458 pAction->GetUser(),
4459 pAction->GetDateTimeUTC(),
4460 pAction->GetComment(),
4461 pAction->GetType(),
4462 bEndOfList );
4463 }
4464 break;
4465 case SC_CAT_DELETE_COLS:
4466 case SC_CAT_DELETE_ROWS:
4467 case SC_CAT_DELETE_TABS:
4468 {
4469 const ScChangeActionDel& rDelete = dynamic_cast<const ScChangeActionDel&>(*pAction);
4470
4471 SCCOLROW nD = 0;
4472 ScChangeActionType eType = pAction->GetType();
4473 if ( eType == SC_CAT_DELETE_COLS )
4474 {
4475 nD = static_cast< SCCOLROW >( rDelete.GetDx() );
4476 }
4477 else if ( eType == SC_CAT_DELETE_ROWS )
4478 {
4479 nD = static_cast< SCCOLROW >( rDelete.GetDy() );
4480 }
4481
4482 pClonedAction = new ScChangeActionDel(
4483 pAction->GetActionNumber(),
4484 pAction->GetState(),
4485 pAction->GetRejectAction(),
4486 pAction->GetBigRange(),
4487 pAction->GetUser(),
4488 pAction->GetDateTimeUTC(),
4489 pAction->GetComment(),
4490 eType,
4491 nD,
4492 pClonedTrack.get() );
4493 }
4494 break;
4495 case SC_CAT_MOVE:
4496 {
4497 auto pMove = dynamic_cast<const ScChangeActionMove*>(pAction);
4498 assert(pMove && "ScChangeTrack::Clone: pMove is null!")(static_cast <bool> (pMove && "ScChangeTrack::Clone: pMove is null!"
) ? void (0) : __assert_fail ("pMove && \"ScChangeTrack::Clone: pMove is null!\""
, "/home/maarten/src/libreoffice/core/sc/source/core/tool/chgtrack.cxx"
, 4498, __extension__ __PRETTY_FUNCTION__))
;
4499
4500 pClonedAction = new ScChangeActionMove(
4501 pAction->GetActionNumber(),
4502 pAction->GetState(),
4503 pAction->GetRejectAction(),
4504 pAction->GetBigRange(),
4505 pAction->GetUser(),
4506 pAction->GetDateTimeUTC(),
4507 pAction->GetComment(),
4508 pMove->GetFromRange(),
4509 pClonedTrack.get() );
4510 }
4511 break;
4512 case SC_CAT_CONTENT:
4513 {
4514 const ScChangeActionContent& rContent = dynamic_cast<const ScChangeActionContent&>(*pAction);
4515 const ScCellValue& rOldCell = rContent.GetOldCell();
4516 ScCellValue aClonedOldCell;
4517 aClonedOldCell.assign(rOldCell, *pDocument);
4518 OUString aOldValue;
4519 rContent.GetOldString( aOldValue, pDocument );
4520
4521 ScChangeActionContent* pClonedContent = new ScChangeActionContent(
4522 pAction->GetActionNumber(),
4523 pAction->GetState(),
4524 pAction->GetRejectAction(),
4525 pAction->GetBigRange(),
4526 pAction->GetUser(),
4527 pAction->GetDateTimeUTC(),
4528 pAction->GetComment(),
4529 aClonedOldCell,
4530 pDocument,
4531 aOldValue );
4532
4533 const ScCellValue& rNewCell = rContent.GetNewCell();
4534 if (!rNewCell.isEmpty())
4535 {
4536 ScCellValue aClonedNewCell;
4537 aClonedNewCell.assign(rNewCell, *pDocument);
4538 pClonedContent->SetNewValue(aClonedNewCell, pDocument);
4539 }
4540
4541 pClonedAction = pClonedContent;
4542 }
4543 break;
4544 case SC_CAT_REJECT:
4545 {
4546 pClonedAction = new ScChangeActionReject(
4547 pAction->GetActionNumber(),
4548 pAction->GetState(),
4549 pAction->GetRejectAction(),
4550 pAction->GetBigRange(),
4551 pAction->GetUser(),
4552 pAction->GetDateTimeUTC(),
4553 pAction->GetComment() );
4554 }
4555 break;
4556 default:
4557 {
4558 }
4559 break;
4560 }
4561
4562 if ( pClonedAction )
4563 {
4564 pClonedTrack->AppendCloned( pClonedAction );
4565 }
4566
4567 pAction = pAction->GetNext();
4568 }
4569
4570 if ( pClonedTrack->GetLast() )
4571 {
4572 pClonedTrack->SetActionMax( pClonedTrack->GetLast()->GetActionNumber() );
4573 }
4574
4575 // set dependencies for Deleted/DeletedIn
4576 pAction = GetFirst();
4577 while ( pAction )
4578 {
4579 if ( pAction->HasDeleted() )
4580 {
4581 ::std::stack< sal_uLong > aStack;
4582 const ScChangeActionLinkEntry* pL = pAction->GetFirstDeletedEntry();
4583 while ( pL )
4584 {
4585 const ScChangeAction* pDeleted = pL->GetAction();
4586 if ( pDeleted )
4587 {
4588 aStack.push( pDeleted->GetActionNumber() );
4589 }
4590 pL = pL->GetNext();
4591 }
4592 ScChangeAction* pClonedAction = pClonedTrack->GetAction( pAction->GetActionNumber() );
4593 if ( pClonedAction )
4594 {
4595 while ( !aStack.empty() )
4596 {
4597 ScChangeAction* pClonedDeleted = pClonedTrack->GetActionOrGenerated( aStack.top() );
4598 aStack.pop();
4599 if ( pClonedDeleted )
4600 {
4601 pClonedDeleted->SetDeletedIn( pClonedAction );
4602 }
4603 }
4604 }
4605 }
4606 pAction = pAction->GetNext();
4607 }
4608
4609 // set dependencies for Dependent/Any
4610 pAction = GetLast();
4611 while ( pAction )
4612 {
4613 if ( pAction->HasDependent() )
4614 {
4615 ::std::stack< sal_uLong > aStack;
4616 const ScChangeActionLinkEntry* pL = pAction->GetFirstDependentEntry();
4617 while ( pL )
4618 {
4619 const ScChangeAction* pDependent = pL->GetAction();
4620 if ( pDependent )
4621 {
4622 aStack.push( pDependent->GetActionNumber() );
4623 }
4624 pL = pL->GetNext();
4625 }
4626 ScChangeAction* pClonedAction = pClonedTrack->GetAction( pAction->GetActionNumber() );
4627 if ( pClonedAction )
4628 {
4629 while ( !aStack.empty() )
4630 {
4631 ScChangeAction* pClonedDependent = pClonedTrack->GetActionOrGenerated( aStack.top() );
4632 aStack.pop();
4633 if ( pClonedDependent )
4634 {
4635 ScChangeActionLinkEntry* pLink = pClonedAction->AddDependent( pClonedDependent );
4636 pClonedDependent->AddLink( pClonedAction, pLink );
4637 }
4638 }
4639 }
4640 }
4641 pAction = pAction->GetPrev();
4642 }
4643
4644 // masterlinks
4645 ScChangeAction* pClonedAction = pClonedTrack->GetFirst();
4646 while ( pClonedAction )
4647 {
4648 pClonedTrack->MasterLinks( pClonedAction );
4649 pClonedAction = pClonedAction->GetNext();
4650 }
4651
4652 if ( IsProtected() )
4653 {
4654 pClonedTrack->SetProtection( GetProtection() );
4655 }
4656
4657 if ( pClonedTrack->GetLast() )
4658 {
4659 pClonedTrack->SetLastSavedActionNumber( pClonedTrack->GetLast()->GetActionNumber() );
4660 }
4661
4662 auto tmp = pClonedTrack.get();
4663 pDocument->SetChangeTrack( std::move(pClonedTrack) );
4664
4665 return tmp;
4666}
4667
4668void ScChangeTrack::MergeActionState( ScChangeAction* pAct, const ScChangeAction* pOtherAct )
4669{
4670 if ( !pAct->IsVirgin() )
4671 return;
4672
4673 if ( pOtherAct->IsAccepted() )
4674 {
4675 pAct->Accept();
4676 if ( pOtherAct->IsRejecting() )
4677 {
4678 pAct->SetRejectAction( pOtherAct->GetRejectAction() );
4679 }
4680 }
4681 else if ( pOtherAct->IsRejected() )
4682 {
4683 pAct->SetRejected();
4684 }
4685}
4686
4687/// Get info about a single ScChangeAction element.
4688static void lcl_getTrackedChange(ScDocument& rDoc, int nIndex, const ScChangeAction* pAction, tools::JsonWriter& rRedlines)
4689{
4690 if (pAction->GetType() != SC_CAT_CONTENT)
4691 return;
4692
4693 auto redlinesNode = rRedlines.startNode("");
4694 rRedlines.put("index", static_cast<sal_Int64>(nIndex));
4695
4696 rRedlines.put("author", pAction->GetUser());
4697
4698 rRedlines.put("type", "Modify");
4699
4700 rRedlines.put("comment", pAction->GetComment());
4701
4702 OUString aDescription;
4703 pAction->GetDescription(aDescription, rDoc, true);
4704 rRedlines.put("description", aDescription);
4705
4706 OUString sDateTime = utl::toISO8601(pAction->GetDateTimeUTC().GetUNODateTime());
4707 rRedlines.put("dateTime", sDateTime);
4708}
4709
4710void ScChangeTrack::GetChangeTrackInfo(tools::JsonWriter& aRedlines)
4711{
4712 auto redlinesNode = aRedlines.startNode("redlines");
4713
4714 ScChangeAction* pAction = GetFirst();
4715 if (pAction)
4716 {
4717 int i = 0;
4718 lcl_getTrackedChange(rDoc, i++, pAction, aRedlines);
4719 ScChangeAction* pLastAction = GetLast();
4720 while (pAction != pLastAction)
4721 {
4722 pAction = pAction->GetNext();
4723 lcl_getTrackedChange(rDoc, i++, pAction, aRedlines);
4724 }
4725 }
4726}
4727
4728/* vim:set shiftwidth=4 softtabstop=4 expandtab: */