1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svl/intitem.hxx>
21 #include <svl/zforlist.hxx>
22 #include <formula/token.hxx>
24 #include "document.hxx"
26 #include "globstr.hrc"
27 #include "subtotal.hxx"
28 #include "docoptio.hxx"
29 #include "interpre.hxx"
30 #include "markdata.hxx"
31 #include "validat.hxx"
32 #include "scitems.hxx"
33 #include "stlpool.hxx"
34 #include "poolhelp.hxx"
35 #include "detdata.hxx"
36 #include "patattr.hxx"
37 #include "chgtrack.hxx"
38 #include "progress.hxx"
39 #include "paramisc.hxx"
40 #include "compiler.hxx"
41 #include "externalrefmgr.hxx"
42 #include "colorscale.hxx"
44 #include "formulacell.hxx"
45 #include "tokenarray.hxx"
46 #include "scmatrix.hxx"
47 #include <tokenstringcontext.hxx>
48 #include <boost/scoped_array.hpp>
50 using namespace formula
;
52 /** (Goal Seek) Find a value of x that is a root of f(x)
54 This function is used internally for the goal seek operation. It uses the
55 Regula Falsi (aka false position) algorithm to find a root of f(x). The
56 start value and the target value are to be given by the user in the
57 goal seek dialog. The f(x) in this case is defined as the formula in the
58 formula cell minus target value. This function may also perform additional
59 search in the horizontal directions when the f(x) is discrete in order to
60 ensure a non-zero slope necessary for deriving a subsequent x that is
61 reasonably close to the root of interest.
63 @change 24.10.2004 by Kohei Yoshida (kohei@openoffice.org)
67 @change 6 Aug 2013, fdo37341
69 bool ScDocument::Solver(SCCOL nFCol
, SCROW nFRow
, SCTAB nFTab
,
70 SCCOL nVCol
, SCROW nVRow
, SCTAB nVTab
,
71 const OUString
& sValStr
, double& nX
)
75 if ( ValidColRow( nFCol
, nFRow
) && ValidTab( nFTab
) &&
76 ValidColRow( nVCol
, nVRow
) && ValidTab( nVTab
) &&
77 nFTab
< static_cast<SCTAB
>( maTabs
.size() ) && maTabs
[nFTab
] &&
78 nVTab
< static_cast<SCTAB
>( maTabs
.size() ) && maTabs
[nVTab
] )
80 CellType eFType
, eVType
;
81 GetCellType(nFCol
, nFRow
, nFTab
, eFType
);
82 GetCellType(nVCol
, nVRow
, nVTab
, eVType
);
83 // #i108005# convert target value to number using default format,
84 // as previously done in ScInterpreter::GetDouble
85 ScFormulaCell
* pFormula
= NULL
;
86 double fTargetVal
= 0.0;
87 sal_uInt32 nFIndex
= 0;
88 if ( eFType
== CELLTYPE_FORMULA
&& eVType
== CELLTYPE_VALUE
&&
89 GetFormatTable()->IsNumberFormat( sValStr
, nFIndex
, fTargetVal
) )
91 ScAddress
aFormulaAdr( nFCol
, nFRow
, nFTab
);
92 pFormula
= GetFormulaCell( aFormulaAdr
);
96 bool bDoneIteration
= false;
97 ScAddress
aValueAdr( nVCol
, nVRow
, nVTab
);
98 double* pVCell
= GetValueCell( aValueAdr
);
100 ScRange
aVRange( aValueAdr
, aValueAdr
); // for SetDirty
101 // Original value to be restored later if necessary
102 double fSaveVal
= *pVCell
;
104 const sal_uInt16 nMaxIter
= 100;
105 const double fEps
= 1E-10;
106 const double fDelta
= 1E-6;
108 double fBestX
, fXPrev
;
109 double fBestF
, fFPrev
;
110 fBestX
= fXPrev
= fSaveVal
;
112 pFormula
->Interpret();
113 bool bError
= ( pFormula
->GetErrCode() != 0 );
114 // bError always corresponds with fF
116 fFPrev
= pFormula
->GetValue() - fTargetVal
;
118 fBestF
= fabs( fFPrev
);
119 if ( fBestF
< fDelta
)
120 bDoneIteration
= true;
122 double fX
= fXPrev
+ fEps
;
126 sal_uInt16 nIter
= 0;
128 bool bHorMoveError
= false;
129 // Conform Regula Falsi Method
130 while ( !bDoneIteration
&& ( nIter
++ < nMaxIter
) )
133 SetDirty( aVRange
, false );
134 pFormula
->Interpret();
135 bError
= ( pFormula
->GetErrCode() != 0 );
136 fF
= pFormula
->GetValue() - fTargetVal
;
138 if ( fF
== fFPrev
&& !bError
)
140 // HORIZONTAL SEARCH: Keep moving x in both directions until the f(x)
141 // becomes different from the previous f(x). This routine is needed
142 // when a given function is discrete, in which case the resulting slope
143 // may become zero which ultimately causes the goal seek operation
146 sal_uInt16 nHorIter
= 0;
147 const double fHorStepAngle
= 5.0;
148 const double fHorMaxAngle
= 80.0;
149 int nHorMaxIter
= static_cast<int>( fHorMaxAngle
/ fHorStepAngle
);
150 bool bDoneHorMove
= false;
152 while ( !bDoneHorMove
&& !bHorMoveError
&& nHorIter
++ < nHorMaxIter
)
154 double fHorAngle
= fHorStepAngle
* static_cast<double>( nHorIter
);
155 double fHorTangent
= ::rtl::math::tan( fHorAngle
* F_PI
/ 180 );
158 while( nIdx
++ < 2 && !bDoneHorMove
)
162 fHorX
= fX
+ fabs( fF
) * fHorTangent
;
164 fHorX
= fX
- fabs( fF
) * fHorTangent
;
167 SetDirty( aVRange
, false );
168 pFormula
->Interpret();
169 bHorMoveError
= ( pFormula
->GetErrCode() != 0 );
173 fF
= pFormula
->GetValue() - fTargetVal
;
182 bHorMoveError
= true;
187 // move closer to last valid value (fXPrev), keep fXPrev & fFPrev
188 double fDiff
= ( fXPrev
- fX
) / 2;
189 if ( fabs( fDiff
) < fEps
)
190 fDiff
= ( fDiff
< 0.0 ? - fEps
: fEps
);
193 else if ( bHorMoveError
)
195 else if ( fabs(fF
) < fDelta
)
199 bDoneIteration
= true;
203 if ( fabs(fF
) + fDelta
< fBestF
)
209 if ( ( fXPrev
- fX
) != 0 )
211 fSlope
= ( fFPrev
- fF
) / ( fXPrev
- fX
);
212 if ( fabs( fSlope
) < fEps
)
213 fSlope
= fSlope
< 0.0 ? -fEps
: fEps
;
220 fX
= fX
- ( fF
/ fSlope
);
224 // Try a nice rounded input value if possible.
225 const double fNiceDelta
= ( bDoneIteration
&& fabs( fBestX
) >= 1e-3 ? 1e-3 : fDelta
);
226 nX
= ::rtl::math::approxFloor( ( fBestX
/ fNiceDelta
) + 0.5 ) * fNiceDelta
;
228 if ( bDoneIteration
)
231 SetDirty( aVRange
, false );
232 pFormula
->Interpret();
233 if ( fabs( pFormula
->GetValue() - fTargetVal
) > fabs( fF
) )
237 else if ( bError
|| bHorMoveError
)
242 SetDirty( aVRange
, false );
243 pFormula
->Interpret();
244 if ( !bDoneIteration
)
246 SetError( nVCol
, nVRow
, nVTab
, NOTAVAILABLE
);
251 SetError( nVCol
, nVRow
, nVTab
, NOTAVAILABLE
);
257 void ScDocument::InsertMatrixFormula(SCCOL nCol1
, SCROW nRow1
,
258 SCCOL nCol2
, SCROW nRow2
,
259 const ScMarkData
& rMark
,
260 const OUString
& rFormula
,
261 const ScTokenArray
* pArr
,
262 const formula::FormulaGrammar::Grammar eGram
,
265 PutInOrder(nCol1
, nCol2
);
266 PutInOrder(nRow1
, nRow2
);
267 nCol2
= std::min
<SCCOL
>(nCol2
, MAXCOL
);
268 nRow2
= std::min
<SCROW
>(nRow2
, MAXROW
);
269 if (!rMark
.GetSelectCount())
271 SAL_WARN("sc", "ScDocument::InsertMatrixFormula: No table marked");
275 SCTAB nTab1
= *rMark
.begin();
277 ScFormulaCell
* pCell
;
278 ScAddress
aPos( nCol1
, nRow1
, nTab1
);
280 pCell
= new ScFormulaCell(this, aPos
, *pArr
, eGram
, MM_FORMULA
);
282 pCell
= new ScFormulaCell( this, aPos
, rFormula
, eGram
, MM_FORMULA
);
283 pCell
->SetMatColsRows( nCol2
- nCol1
+ 1, nRow2
- nRow1
+ 1, bDirtyFlag
);
284 ScMarkData::const_iterator itr
= rMark
.begin(), itrEnd
= rMark
.end();
285 SCTAB nMax
= static_cast<SCTAB
>(maTabs
.size());
286 for (; itr
!= itrEnd
&& *itr
< nMax
; ++itr
)
293 pCell
= maTabs
[*itr
]->SetFormulaCell(nCol1
, nRow1
, pCell
);
294 if (!pCell
) //NULL if nCol1/nRow1 is invalid, which it can't be here
298 maTabs
[*itr
]->SetFormulaCell(
301 *pCell
, *this, ScAddress(nCol1
, nRow1
, *itr
), SC_CLONECELL_STARTLISTENING
));
304 ScAddress
aBasePos(nCol1
, nRow1
, nTab1
);
305 ScSingleRefData aRefData
;
306 aRefData
.InitFlags();
307 aRefData
.SetColRel( true );
308 aRefData
.SetRowRel( true );
309 aRefData
.SetTabRel( true );
310 aRefData
.SetAddress(aBasePos
, aBasePos
);
312 ScTokenArray aArr
; // consists only of one single reference token.
313 formula::FormulaToken
* t
= aArr
.AddMatrixSingleReference( aRefData
);
316 for (; itr
!= itrEnd
&& *itr
< nMax
; ++itr
)
319 ScTable
* pTab
= FetchTable(nTab
);
325 aRefData
.SetRelTab(nTab
- aBasePos
.Tab());
326 *t
->GetSingleRef() = aRefData
;
329 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
331 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
333 if (nCol
== nCol1
&& nRow
== nRow1
)
334 // Skip the base position.
337 // Token array must be cloned so that each formula cell receives its own copy.
338 aPos
= ScAddress(nCol
, nRow
, nTab
);
339 // Reference in each cell must point to the origin cell relative to the current cell.
340 aRefData
.SetAddress(aBasePos
, aPos
);
341 *t
->GetSingleRef() = aRefData
;
342 boost::scoped_ptr
<ScTokenArray
> pTokArr(aArr
.Clone());
343 pCell
= new ScFormulaCell(this, aPos
, *pTokArr
, eGram
, MM_REFERENCE
);
344 pTab
->SetFormulaCell(nCol
, nRow
, pCell
);
350 void ScDocument::InsertTableOp(const ScTabOpParam
& rParam
, // multiple (repeated?) operation
351 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
352 const ScMarkData
& rMark
)
354 PutInOrder(nCol1
, nCol2
);
355 PutInOrder(nRow1
, nRow2
);
361 SCTAB nMax
= static_cast<SCTAB
>(maTabs
.size());
362 ScMarkData::const_iterator itr
= rMark
.begin(), itrEnd
= rMark
.end();
363 for (; itr
!= itrEnd
&& *itr
< nMax
; ++itr
)
375 OSL_FAIL("ScDocument::InsertTableOp: No table marked");
380 OUStringBuffer aForString
;
381 aForString
.append('=');
382 aForString
.append(ScCompiler::GetNativeSymbol(ocTableOp
));
383 aForString
.append(ScCompiler::GetNativeSymbol( ocOpen
));
385 const OUString
& sSep
= ScCompiler::GetNativeSymbol( ocSep
);
386 if (rParam
.meMode
== ScTabOpParam::Column
) // column only
388 aRef
.Set( rParam
.aRefFormulaCell
.GetAddress(), true, false, false );
389 aForString
.append(aRef
.GetRefString(this, nTab1
));
390 aForString
.append(sSep
);
391 aForString
.append(rParam
.aRefColCell
.GetRefString(this, nTab1
));
392 aForString
.append(sSep
);
393 aRef
.Set( nCol1
, nRow1
, nTab1
, false, true, true );
394 aForString
.append(aRef
.GetRefString(this, nTab1
));
396 nCol2
= std::min( nCol2
, (SCCOL
)(rParam
.aRefFormulaEnd
.Col() -
397 rParam
.aRefFormulaCell
.Col() + nCol1
+ 1));
399 else if (rParam
.meMode
== ScTabOpParam::Row
) // row only
401 aRef
.Set( rParam
.aRefFormulaCell
.GetAddress(), false, true, false );
402 aForString
.append(aRef
.GetRefString(this, nTab1
));
403 aForString
.append(sSep
);
404 aForString
.append(rParam
.aRefRowCell
.GetRefString(this, nTab1
));
405 aForString
.append(sSep
);
406 aRef
.Set( nCol1
, nRow1
, nTab1
, true, false, true );
407 aForString
.append(aRef
.GetRefString(this, nTab1
));
409 nRow2
= std::min( nRow2
, (SCROW
)(rParam
.aRefFormulaEnd
.Row() -
410 rParam
.aRefFormulaCell
.Row() + nRow1
+ 1));
414 aForString
.append(rParam
.aRefFormulaCell
.GetRefString(this, nTab1
));
415 aForString
.append(sSep
);
416 aForString
.append(rParam
.aRefColCell
.GetRefString(this, nTab1
));
417 aForString
.append(sSep
);
418 aRef
.Set( nCol1
, nRow1
+ 1, nTab1
, false, true, true );
419 aForString
.append(aRef
.GetRefString(this, nTab1
));
420 aForString
.append(sSep
);
421 aForString
.append(rParam
.aRefRowCell
.GetRefString(this, nTab1
));
422 aForString
.append(sSep
);
423 aRef
.Set( nCol1
+ 1, nRow1
, nTab1
, true, false, true );
424 aForString
.append(aRef
.GetRefString(this, nTab1
));
427 aForString
.append(ScCompiler::GetNativeSymbol( ocClose
));
429 ScFormulaCell
aRefCell( this, ScAddress( nCol1
, nRow1
, nTab1
), aForString
.makeStringAndClear(),
430 formula::FormulaGrammar::GRAM_NATIVE
, MM_NONE
);
431 for( j
= nCol1
; j
<= nCol2
; j
++ )
432 for( k
= nRow1
; k
<= nRow2
; k
++ )
433 for (i
= 0; i
< static_cast<SCTAB
>(maTabs
.size()); i
++)
436 for (; itr
!= itrEnd
&& *itr
< nMax
; ++itr
)
438 maTabs
[*itr
]->SetFormulaCell(
439 j
, k
, new ScFormulaCell(aRefCell
, *this, ScAddress(j
, k
, *itr
), SC_CLONECELL_STARTLISTENING
));
445 bool setCacheTableReferenced(formula::FormulaToken
& rToken
, ScExternalRefManager
& rRefMgr
, const ScAddress
& rPos
)
447 switch (rToken
.GetType())
449 case svExternalSingleRef
:
450 return rRefMgr
.setCacheTableReferenced(
451 rToken
.GetIndex(), rToken
.GetString().getString(), 1);
452 case svExternalDoubleRef
:
454 const ScComplexRefData
& rRef
= *rToken
.GetDoubleRef();
455 ScRange aAbs
= rRef
.toAbs(rPos
);
456 size_t nSheets
= aAbs
.aEnd
.Tab() - aAbs
.aStart
.Tab() + 1;
457 return rRefMgr
.setCacheTableReferenced(
458 rToken
.GetIndex(), rToken
.GetString().getString(), nSheets
);
461 /* TODO: external names aren't supported yet, but would
462 * have to be marked as well, if so. Mechanism would be
464 OSL_FAIL("ScDocument::MarkUsedExternalReferences: implement the svExternalName case!");
473 bool ScDocument::MarkUsedExternalReferences( ScTokenArray
& rArr
, const ScAddress
& rPos
)
478 ScExternalRefManager
* pRefMgr
= NULL
;
480 formula::FormulaToken
* t
= NULL
;
481 bool bAllMarked
= false;
482 while (!bAllMarked
&& (t
= rArr
.GetNextReferenceOrName()) != NULL
)
484 if (t
->IsExternalRef())
487 pRefMgr
= GetExternalRefManager();
489 bAllMarked
= setCacheTableReferenced(*t
, *pRefMgr
, rPos
);
491 else if (t
->GetType() == svIndex
)
493 // this is a named range. Check if the range contains an external
495 ScRangeData
* pRangeData
= GetRangeName()->findByIndex(t
->GetIndex());
499 ScTokenArray
* pArray
= pRangeData
->GetCode();
500 for (t
= pArray
->First(); t
; t
= pArray
->Next())
502 if (!t
->IsExternalRef())
506 pRefMgr
= GetExternalRefManager();
508 bAllMarked
= setCacheTableReferenced(*t
, *pRefMgr
, rPos
);
515 bool ScDocument::GetNextSpellingCell(SCCOL
& nCol
, SCROW
& nRow
, SCTAB nTab
,
516 bool bInSel
, const ScMarkData
& rMark
) const
518 if (ValidTab(nTab
) && nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
])
519 return maTabs
[nTab
]->GetNextSpellingCell( nCol
, nRow
, bInSel
, rMark
);
524 bool ScDocument::GetNextMarkedCell( SCCOL
& rCol
, SCROW
& rRow
, SCTAB nTab
,
525 const ScMarkData
& rMark
)
527 if (ValidTab(nTab
) && nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
])
528 return maTabs
[nTab
]->GetNextMarkedCell( rCol
, rRow
, rMark
);
533 bool ScDocument::ReplaceStyle(const SvxSearchItem
& rSearchItem
,
534 SCCOL nCol
, SCROW nRow
, SCTAB nTab
,
538 if (nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
])
539 return maTabs
[nTab
]->ReplaceStyle(rSearchItem
, nCol
, nRow
, rMark
, bIsUndoP
);
544 void ScDocument::CompileDBFormula()
546 sc::CompileFormulaContext
aCxt(this);
547 TableContainer::iterator it
= maTabs
.begin();
548 for (;it
!= maTabs
.end(); ++it
)
551 (*it
)->CompileDBFormula(aCxt
);
555 void ScDocument::CompileColRowNameFormula()
557 sc::CompileFormulaContext
aCxt(this);
558 TableContainer::iterator it
= maTabs
.begin();
559 for (;it
!= maTabs
.end(); ++it
)
562 (*it
)->CompileColRowNameFormula(aCxt
);
566 void ScDocument::InvalidateTableArea()
568 TableContainer::iterator it
= maTabs
.begin();
569 for (;it
!= maTabs
.end() && *it
; ++it
)
571 (*it
)->InvalidateTableArea();
572 if ( (*it
)->IsScenario() )
573 (*it
)->InvalidateScenarioRanges();
577 sal_Int32
ScDocument::GetMaxStringLen( SCTAB nTab
, SCCOL nCol
,
578 SCROW nRowStart
, SCROW nRowEnd
, rtl_TextEncoding eCharSet
) const
580 if (ValidTab(nTab
) && nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
])
581 return maTabs
[nTab
]->GetMaxStringLen( nCol
, nRowStart
, nRowEnd
, eCharSet
);
586 sal_Int32
ScDocument::GetMaxNumberStringLen( sal_uInt16
& nPrecision
, SCTAB nTab
,
588 SCROW nRowStart
, SCROW nRowEnd
) const
590 if (ValidTab(nTab
) && nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
])
591 return maTabs
[nTab
]->GetMaxNumberStringLen( nPrecision
, nCol
,
592 nRowStart
, nRowEnd
);
597 bool ScDocument::GetSelectionFunction( ScSubTotalFunc eFunc
,
598 const ScAddress
& rCursor
, const ScMarkData
& rMark
,
601 ScFunctionData
aData(eFunc
);
603 ScMarkData
aMark(rMark
);
605 if (!aMark
.IsMultiMarked() && !aMark
.IsCellMarked(rCursor
.Col(), rCursor
.Row(), false))
606 aMark
.SetMarkArea(rCursor
);
608 SCTAB nMax
= static_cast<SCTAB
>(maTabs
.size());
609 ScMarkData::const_iterator itr
= aMark
.begin(), itrEnd
= aMark
.end();
611 for (; itr
!= itrEnd
&& *itr
< nMax
&& !aData
.bError
; ++itr
)
613 maTabs
[*itr
]->UpdateSelectionFunction(aData
, aMark
);
615 //TODO: pass rMark to UpdateSelection Function !!!!!
620 case SUBTOTAL_FUNC_SUM
:
621 rResult
= aData
.nVal
;
623 case SUBTOTAL_FUNC_SELECTION_COUNT
:
624 rResult
= aData
.nCount
;
626 case SUBTOTAL_FUNC_CNT
:
627 case SUBTOTAL_FUNC_CNT2
:
628 rResult
= aData
.nCount
;
630 case SUBTOTAL_FUNC_AVE
:
632 rResult
= aData
.nVal
/ (double) aData
.nCount
;
636 case SUBTOTAL_FUNC_MAX
:
637 case SUBTOTAL_FUNC_MIN
:
639 rResult
= aData
.nVal
;
645 // added to avoid warnings
652 return !aData
.bError
;
655 double ScDocument::RoundValueAsShown( double fVal
, sal_uInt32 nFormat
) const
658 if ( (nType
= GetFormatTable()->GetType( nFormat
)) != css::util::NumberFormat::DATE
659 && nType
!= css::util::NumberFormat::TIME
&& nType
!= css::util::NumberFormat::DATETIME
)
662 if ((nFormat
% SV_COUNTRY_LANGUAGE_OFFSET
) != 0)
664 nPrecision
= (short)GetFormatTable()->GetFormatPrecision( nFormat
);
667 case css::util::NumberFormat::PERCENT
: // 0.41% == 0.0041
670 case css::util::NumberFormat::SCIENTIFIC
: // 1.23e-3 == 0.00123
673 nPrecision
= sal::static_int_cast
<short>( nPrecision
- (short)floor( log10( fVal
) ) );
674 else if ( fVal
< 0.0 )
675 nPrecision
= sal::static_int_cast
<short>( nPrecision
- (short)floor( log10( -fVal
) ) );
682 nPrecision
= (short)GetDocOptions().GetStdPrecision();
683 // #i115512# no rounding for automatic decimals
684 if (nPrecision
== static_cast<short>(SvNumberFormatter::UNLIMITED_PRECISION
))
687 double fRound
= ::rtl::math::round( fVal
, nPrecision
);
688 if ( ::rtl::math::approxEqual( fVal
, fRound
) )
689 return fVal
; // rounding might introduce some error
697 // conditional formats and validation ranges
699 sal_uLong
ScDocument::AddCondFormat( ScConditionalFormat
* pNew
, SCTAB nTab
)
704 if(ValidTab(nTab
) && nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
])
705 return maTabs
[nTab
]->AddCondFormat( pNew
);
710 sal_uLong
ScDocument::AddValidationEntry( const ScValidationData
& rNew
)
713 return 0; // empty is always 0
715 if (!pValidationList
)
716 pValidationList
= new ScValidationDataList
;
719 for( ScValidationDataList::iterator it
= pValidationList
->begin(); it
!= pValidationList
->end(); ++it
)
721 const ScValidationData
* pData
= *it
;
722 sal_uLong nKey
= pData
->GetKey();
723 if ( pData
->EqualEntries( rNew
) )
729 // might be called from ScPatternAttr::PutInPool; thus clone (real copy)
731 sal_uLong nNewKey
= nMax
+ 1;
732 ScValidationData
* pInsert
= rNew
.Clone(this);
733 pInsert
->SetKey( nNewKey
);
734 pValidationList
->InsertNew( pInsert
);
738 const SfxPoolItem
* ScDocument::GetEffItem(
739 SCCOL nCol
, SCROW nRow
, SCTAB nTab
, sal_uInt16 nWhich
) const
741 const ScPatternAttr
* pPattern
= GetPattern( nCol
, nRow
, nTab
);
744 const SfxItemSet
& rSet
= pPattern
->GetItemSet();
745 const SfxPoolItem
* pItem
;
746 if ( rSet
.GetItemState( ATTR_CONDITIONAL
, true, &pItem
) == SfxItemState::SET
)
748 const std::vector
<sal_uInt32
>& rIndex
= static_cast<const ScCondFormatItem
&>(pPattern
->GetItem(ATTR_CONDITIONAL
)).GetCondFormatData();
749 ScConditionalFormatList
* pCondFormList
= GetCondFormList( nTab
);
750 if (!rIndex
.empty() && pCondFormList
)
752 for(std::vector
<sal_uInt32
>::const_iterator itr
= rIndex
.begin(), itrEnd
= rIndex
.end();
753 itr
!= itrEnd
; ++itr
)
755 const ScConditionalFormat
* pForm
= pCondFormList
->GetFormat( *itr
);
758 ScAddress
aPos(nCol
, nRow
, nTab
);
759 ScRefCellValue aCell
;
760 aCell
.assign(const_cast<ScDocument
&>(*this), aPos
);
761 OUString aStyle
= pForm
->GetCellStyle(aCell
, aPos
);
762 if (!aStyle
.isEmpty())
764 SfxStyleSheetBase
* pStyleSheet
= xPoolHelper
->GetStylePool()->Find(
765 aStyle
, SFX_STYLE_FAMILY_PARA
);
766 if ( pStyleSheet
&& pStyleSheet
->GetItemSet().GetItemState(
767 nWhich
, true, &pItem
) == SfxItemState::SET
)
774 return &rSet
.Get( nWhich
);
776 OSL_FAIL("no pattern");
780 const SfxItemSet
* ScDocument::GetCondResult( SCCOL nCol
, SCROW nRow
, SCTAB nTab
) const
782 ScConditionalFormatList
* pFormatList
= GetCondFormList(nTab
);
786 ScAddress
aPos(nCol
, nRow
, nTab
);
787 ScRefCellValue aCell
;
788 aCell
.assign(const_cast<ScDocument
&>(*this), aPos
);
789 const ScPatternAttr
* pPattern
= GetPattern( nCol
, nRow
, nTab
);
790 const std::vector
<sal_uInt32
>& rIndex
=
791 static_cast<const ScCondFormatItem
&>(pPattern
->GetItem(ATTR_CONDITIONAL
)).GetCondFormatData();
793 return GetCondResult(aCell
, aPos
, *pFormatList
, rIndex
);
796 const SfxItemSet
* ScDocument::GetCondResult(
797 ScRefCellValue
& rCell
, const ScAddress
& rPos
, const ScConditionalFormatList
& rList
,
798 const std::vector
<sal_uInt32
>& rIndex
) const
800 std::vector
<sal_uInt32
>::const_iterator itr
= rIndex
.begin(), itrEnd
= rIndex
.end();
801 for (; itr
!= itrEnd
; ++itr
)
803 const ScConditionalFormat
* pForm
= rList
.GetFormat(*itr
);
807 const OUString
& aStyle
= pForm
->GetCellStyle(rCell
, rPos
);
808 if (!aStyle
.isEmpty())
810 SfxStyleSheetBase
* pStyleSheet
=
811 xPoolHelper
->GetStylePool()->Find(aStyle
, SFX_STYLE_FAMILY_PARA
);
814 return &pStyleSheet
->GetItemSet();
816 // if style is not there, treat like no condition
823 ScConditionalFormat
* ScDocument::GetCondFormat(
824 SCCOL nCol
, SCROW nRow
, SCTAB nTab
) const
826 sal_uInt32 nIndex
= 0;
827 const std::vector
<sal_uInt32
>& rCondFormats
= static_cast<const ScCondFormatItem
*>(GetAttr(nCol
, nRow
, nTab
, ATTR_CONDITIONAL
))->GetCondFormatData();
829 if(!rCondFormats
.empty())
830 nIndex
= rCondFormats
[0];
834 ScConditionalFormatList
* pCondFormList
= GetCondFormList(nTab
);
836 return pCondFormList
->GetFormat( nIndex
);
839 OSL_FAIL("pCondFormList is 0");
846 ScConditionalFormatList
* ScDocument::GetCondFormList(SCTAB nTab
) const
848 if(ValidTab(nTab
) && nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
])
849 return maTabs
[nTab
]->GetCondFormList();
854 void ScDocument::SetCondFormList( ScConditionalFormatList
* pList
, SCTAB nTab
)
856 if(ValidTab(nTab
) && nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
])
857 maTabs
[nTab
]->SetCondFormList(pList
);
860 const ScValidationData
* ScDocument::GetValidationEntry( sal_uLong nIndex
) const
862 if ( pValidationList
)
863 return pValidationList
->GetData( nIndex
);
868 void ScDocument::DeleteConditionalFormat(sal_uLong nOldIndex
, SCTAB nTab
)
870 if(ValidTab(nTab
) && nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
])
871 maTabs
[nTab
]->DeleteConditionalFormat(nOldIndex
);
874 bool ScDocument::HasDetectiveOperations() const
876 return pDetOpList
&& pDetOpList
->Count();
879 void ScDocument::AddDetectiveOperation( const ScDetOpData
& rData
)
882 pDetOpList
= new ScDetOpList
;
884 pDetOpList
->Append( new ScDetOpData( rData
) );
887 void ScDocument::ClearDetectiveOperations()
889 delete pDetOpList
; // deletes also the entries
893 void ScDocument::SetDetOpList(ScDetOpList
* pNew
)
895 delete pDetOpList
; // deletes also the entries
899 // Comparison of Documents
902 #define SC_DOCCOMP_MAXDIFF 256
903 #define SC_DOCCOMP_MINGOOD 128
904 #define SC_DOCCOMP_COLUMNS 10
905 #define SC_DOCCOMP_ROWS 100
907 sal_uInt16
ScDocument::RowDifferences( SCROW nThisRow
, SCTAB nThisTab
,
908 ScDocument
& rOtherDoc
, SCROW nOtherRow
, SCTAB nOtherTab
,
909 SCCOL nMaxCol
, SCCOLROW
* pOtherCols
)
913 for (SCCOL nThisCol
=0; nThisCol
<=nMaxCol
; nThisCol
++)
917 nOtherCol
= static_cast<SCCOL
>(pOtherCols
[nThisCol
]);
919 nOtherCol
= nThisCol
;
921 if (ValidCol(nOtherCol
)) // only compare columns that are common to both docs
923 ScRefCellValue aThisCell
, aOtherCell
;
924 aThisCell
.assign(*this, ScAddress(nThisCol
, nThisRow
, nThisTab
));
925 aOtherCell
.assign(rOtherDoc
, ScAddress(nOtherCol
, nOtherRow
, nOtherTab
));
926 if (!aThisCell
.equalsWithoutFormat(aOtherCell
))
928 if (!aThisCell
.isEmpty() && !aOtherCell
.isEmpty())
931 nDif
+= 4; // content <-> empty counts more
934 if (!aThisCell
.isEmpty() || !aOtherCell
.isEmpty())
940 return static_cast<sal_uInt16
>((nDif
*64)/nUsed
); // max.256 (SC_DOCCOMP_MAXDIFF)
942 OSL_ENSURE(!nDif
,"Diff withoud Used");
946 sal_uInt16
ScDocument::ColDifferences( SCCOL nThisCol
, SCTAB nThisTab
,
947 ScDocument
& rOtherDoc
, SCCOL nOtherCol
, SCTAB nOtherTab
,
948 SCROW nMaxRow
, SCCOLROW
* pOtherRows
)
951 //TODO: optimize e.g. with iterator?
955 for (SCROW nThisRow
=0; nThisRow
<=nMaxRow
; nThisRow
++)
959 nOtherRow
= pOtherRows
[nThisRow
];
961 nOtherRow
= nThisRow
;
963 if (ValidRow(nOtherRow
)) // only compare rows that are common to both docs
965 ScRefCellValue aThisCell
, aOtherCell
;
966 aThisCell
.assign(*this, ScAddress(nThisCol
, nThisRow
, nThisTab
));
967 aOtherCell
.assign(rOtherDoc
, ScAddress(nOtherCol
, nOtherRow
, nOtherTab
));
968 if (!aThisCell
.equalsWithoutFormat(aOtherCell
))
970 if (!aThisCell
.isEmpty() && !aOtherCell
.isEmpty())
973 nDif
+= 4; // content <-> empty counts more
976 if (!aThisCell
.isEmpty() || !aOtherCell
.isEmpty())
982 return static_cast<sal_uInt16
>((nDif
*64)/nUsed
); // max.256
984 OSL_ENSURE(!nDif
,"Diff without Used");
988 void ScDocument::FindOrder( SCCOLROW
* pOtherRows
, SCCOLROW nThisEndRow
, SCCOLROW nOtherEndRow
,
989 bool bColumns
, ScDocument
& rOtherDoc
, SCTAB nThisTab
, SCTAB nOtherTab
,
990 SCCOLROW nEndCol
, SCCOLROW
* pTranslate
, ScProgress
* pProgress
, sal_uLong nProAdd
)
992 // bColumns=true: rows are columns and vice versa
994 SCCOLROW nMaxCont
; // continue by how much
995 SCCOLROW nMinGood
; // what is a hit (incl.)
998 nMaxCont
= SC_DOCCOMP_COLUMNS
; // 10 columns
999 nMinGood
= SC_DOCCOMP_MINGOOD
;
1001 //TODO: additional pass with nMinGood = 0 ????
1006 nMaxCont
= SC_DOCCOMP_ROWS
; // 100 rows
1007 nMinGood
= SC_DOCCOMP_MINGOOD
;
1009 bool bUseTotal
= bColumns
&& !pTranslate
; // only for the 1st pass
1011 SCCOLROW nOtherRow
= 0;
1014 bool bTotal
= false; // hold for several nThisRow
1015 SCCOLROW nUnknown
= 0;
1016 for (nThisRow
= 0; nThisRow
<= nThisEndRow
; nThisRow
++)
1018 SCCOLROW nTempOther
= nOtherRow
;
1019 bool bFound
= false;
1020 sal_uInt16 nBest
= SC_DOCCOMP_MAXDIFF
;
1021 SCCOLROW nMax
= std::min( nOtherEndRow
, static_cast<SCCOLROW
>(( nTempOther
+ nMaxCont
+ nUnknown
)) );
1022 for (SCCOLROW i
=nTempOther
; i
<=nMax
&& nBest
>0; i
++) // stop at 0
1025 nComp
= ColDifferences( static_cast<SCCOL
>(nThisRow
), nThisTab
, rOtherDoc
, static_cast<SCCOL
>(i
), nOtherTab
, nEndCol
, pTranslate
);
1027 nComp
= RowDifferences( nThisRow
, nThisTab
, rOtherDoc
, i
, nOtherTab
, static_cast<SCCOL
>(nEndCol
), pTranslate
);
1028 if ( nComp
< nBest
&& ( nComp
<= nMinGood
|| bTotal
) )
1034 if ( nComp
< SC_DOCCOMP_MAXDIFF
|| bFound
)
1036 else if ( i
== nTempOther
&& bUseTotal
)
1037 bTotal
= true; // only at the very top
1041 pOtherRows
[nThisRow
] = nTempOther
;
1042 nOtherRow
= nTempOther
+ 1;
1047 pOtherRows
[nThisRow
] = SCROW_MAX
;
1052 pProgress
->SetStateOnPercent(nProAdd
+static_cast<sal_uLong
>(nThisRow
));
1055 // fill in blocks that don't match
1057 SCROW nFillStart
= 0;
1059 bool bInFill
= false;
1060 for (nThisRow
= 0; nThisRow
<= nThisEndRow
+1; nThisRow
++)
1062 SCROW nThisOther
= ( nThisRow
<= nThisEndRow
) ? pOtherRows
[nThisRow
] : (nOtherEndRow
+1);
1063 if ( ValidRow(nThisOther
) )
1067 if ( nThisOther
> nFillStart
) // is there something to distribute?
1069 SCROW nDiff1
= nThisOther
- nFillStart
;
1070 SCROW nDiff2
= nThisRow
- nFillPos
;
1071 SCROW nMinDiff
= std::min(nDiff1
, nDiff2
);
1072 for (SCROW i
=0; i
<nMinDiff
; i
++)
1073 pOtherRows
[nFillPos
+i
] = nFillStart
+i
;
1078 nFillStart
= nThisOther
+ 1;
1079 nFillPos
= nThisRow
+ 1;
1086 void ScDocument::CompareDocument( ScDocument
& rOtherDoc
)
1091 SCTAB nThisCount
= GetTableCount();
1092 SCTAB nOtherCount
= rOtherDoc
.GetTableCount();
1093 boost::scoped_array
<SCTAB
> pOtherTabs(new SCTAB
[nThisCount
]);
1096 // compare tables with identical names
1098 OUString aOtherName
;
1099 for (nThisTab
=0; nThisTab
<nThisCount
; nThisTab
++)
1101 SCTAB nOtherTab
= SCTAB_MAX
;
1102 if (!IsScenario(nThisTab
)) // skip scenarios
1104 GetName( nThisTab
, aThisName
);
1105 for (SCTAB nTemp
=0; nTemp
<nOtherCount
&& nOtherTab
>MAXTAB
; nTemp
++)
1106 if (!rOtherDoc
.IsScenario(nTemp
))
1108 rOtherDoc
.GetName( nTemp
, aOtherName
);
1109 if ( aThisName
.equals(aOtherName
) )
1113 pOtherTabs
[nThisTab
] = nOtherTab
;
1115 // fill in, so that un-named tables don't get lost
1116 SCTAB nFillStart
= 0;
1118 bool bInFill
= false;
1119 for (nThisTab
= 0; nThisTab
<= nThisCount
; nThisTab
++)
1121 SCTAB nThisOther
= ( nThisTab
< nThisCount
) ? pOtherTabs
[nThisTab
] : nOtherCount
;
1122 if ( ValidTab(nThisOther
) )
1126 if ( nThisOther
> nFillStart
) // is there something to distribute?
1128 SCTAB nDiff1
= nThisOther
- nFillStart
;
1129 SCTAB nDiff2
= nThisTab
- nFillPos
;
1130 SCTAB nMinDiff
= std::min(nDiff1
, nDiff2
);
1131 for (SCTAB i
=0; i
<nMinDiff
; i
++)
1132 if ( !IsScenario(nFillPos
+i
) && !rOtherDoc
.IsScenario(nFillStart
+i
) )
1133 pOtherTabs
[nFillPos
+i
] = nFillStart
+i
;
1138 nFillStart
= nThisOther
+ 1;
1139 nFillPos
= nThisTab
+ 1;
1145 // compare tables in the original order
1147 for (nThisTab
=0; nThisTab
<nThisCount
; nThisTab
++)
1149 SCTAB nOtherTab
= pOtherTabs
[nThisTab
];
1150 if ( ValidTab(nOtherTab
) )
1152 SCCOL nThisEndCol
= 0;
1153 SCROW nThisEndRow
= 0;
1154 SCCOL nOtherEndCol
= 0;
1155 SCROW nOtherEndRow
= 0;
1156 GetCellArea( nThisTab
, nThisEndCol
, nThisEndRow
);
1157 rOtherDoc
.GetCellArea( nOtherTab
, nOtherEndCol
, nOtherEndRow
);
1158 SCCOL nEndCol
= std::max(nThisEndCol
, nOtherEndCol
);
1159 SCROW nEndRow
= std::max(nThisEndRow
, nOtherEndRow
);
1162 sal_uLong n1
,n2
; // for AppendDeleteRange
1164 //TODO: one Progress over all tables ???
1167 GetName( nThisTab
, aTabName
);
1168 OUString aTemplate
= ScGlobal::GetRscString(STR_PROGRESS_COMPARING
);
1169 sal_Int32 nIndex
= 0;
1170 OUStringBuffer aProText
= aTemplate
.getToken( 0, '#', nIndex
);
1171 aProText
.append(aTabName
);
1173 aProText
.append(aTemplate
.getToken( 1, '#', nIndex
));
1174 ScProgress
aProgress( GetDocumentShell(),
1175 aProText
.makeStringAndClear(), 3*nThisEndRow
); // 2x FindOrder, 1x here
1176 long nProgressStart
= 2*nThisEndRow
; // start for here
1178 boost::scoped_array
<SCCOLROW
> pTempRows(new SCCOLROW
[nThisEndRow
+1]);
1179 boost::scoped_array
<SCCOLROW
> pOtherRows(new SCCOLROW
[nThisEndRow
+1]);
1180 boost::scoped_array
<SCCOLROW
> pOtherCols(new SCCOLROW
[nThisEndCol
+1]);
1182 // find inserted/deleted columns/rows:
1184 // 1) compare original rows (pTempRows)
1185 // 2) compare original columns (pOtherCols)
1186 // with this column order compare rows (pOtherRows)
1188 //TODO: compare columns twice with different nMinGood ???
1191 FindOrder( pTempRows
.get(), nThisEndRow
, nOtherEndRow
, false,
1192 rOtherDoc
, nThisTab
, nOtherTab
, nEndCol
, NULL
, &aProgress
, 0 );
1194 FindOrder( pOtherCols
.get(), nThisEndCol
, nOtherEndCol
, true,
1195 rOtherDoc
, nThisTab
, nOtherTab
, nEndRow
, NULL
, NULL
, 0 );
1196 FindOrder( pOtherRows
.get(), nThisEndRow
, nOtherEndRow
, false,
1197 rOtherDoc
, nThisTab
, nOtherTab
, nThisEndCol
,
1198 pOtherCols
.get(), &aProgress
, nThisEndRow
);
1200 sal_uLong nMatch1
= 0; // pTempRows, no columns
1201 for (nThisRow
= 0; nThisRow
<=nThisEndRow
; nThisRow
++)
1202 if (ValidRow(pTempRows
[nThisRow
]))
1203 nMatch1
+= SC_DOCCOMP_MAXDIFF
-
1204 RowDifferences( nThisRow
, nThisTab
, rOtherDoc
, pTempRows
[nThisRow
],
1205 nOtherTab
, nEndCol
, NULL
);
1207 sal_uLong nMatch2
= 0; // pOtherRows, pOtherCols
1208 for (nThisRow
= 0; nThisRow
<=nThisEndRow
; nThisRow
++)
1209 if (ValidRow(pOtherRows
[nThisRow
]))
1210 nMatch2
+= SC_DOCCOMP_MAXDIFF
-
1211 RowDifferences( nThisRow
, nThisTab
, rOtherDoc
, pOtherRows
[nThisRow
],
1212 nOtherTab
, nThisEndCol
, pOtherCols
.get() );
1214 if ( nMatch1
>= nMatch2
) // without columns ?
1217 for (nThisCol
= 0; nThisCol
<=nThisEndCol
; nThisCol
++)
1218 pOtherCols
[nThisCol
] = nThisCol
;
1220 // swap row-arrays (they get both deleted anyway)
1221 pTempRows
.swap(pOtherRows
);
1225 // remains for pOtherCols, pOtherRows
1228 // Generate Change-Actions
1229 // 1) columns from the right
1230 // 2) rows from below
1231 // 3) single cells in normal order
1233 // Actions for inserted/deleted columns
1235 SCCOL nLastOtherCol
= static_cast<SCCOL
>(nOtherEndCol
+ 1);
1236 // nThisEndCol ... 0
1237 for ( nThisCol
= nThisEndCol
+1; nThisCol
> 0; )
1240 SCCOL nOtherCol
= static_cast<SCCOL
>(pOtherCols
[nThisCol
]);
1241 if ( ValidCol(nOtherCol
) && nOtherCol
+1 < nLastOtherCol
)
1244 ScRange
aDelRange( nOtherCol
+1, 0, nOtherTab
,
1245 nLastOtherCol
-1, MAXROW
, nOtherTab
);
1246 pChangeTrack
->AppendDeleteRange( aDelRange
, &rOtherDoc
, n1
, n2
);
1248 if ( nOtherCol
> MAXCOL
) // inserted
1251 if ( nThisCol
== nThisEndCol
|| ValidCol(static_cast<SCCOL
>(pOtherCols
[nThisCol
+1])) )
1253 SCCOL nFirstNew
= static_cast<SCCOL
>(nThisCol
);
1254 while ( nFirstNew
> 0 && pOtherCols
[nFirstNew
-1] > MAXCOL
)
1256 SCCOL nDiff
= nThisCol
- nFirstNew
;
1257 ScRange
aRange( nLastOtherCol
, 0, nOtherTab
,
1258 nLastOtherCol
+nDiff
, MAXROW
, nOtherTab
);
1259 pChangeTrack
->AppendInsert( aRange
);
1263 nLastOtherCol
= nOtherCol
;
1265 if ( nLastOtherCol
> 0 ) // deleted at the very top
1267 ScRange
aDelRange( 0, 0, nOtherTab
,
1268 nLastOtherCol
-1, MAXROW
, nOtherTab
);
1269 pChangeTrack
->AppendDeleteRange( aDelRange
, &rOtherDoc
, n1
, n2
);
1272 // Actions for inserted/deleted rows
1274 SCROW nLastOtherRow
= nOtherEndRow
+ 1;
1275 // nThisEndRow ... 0
1276 for ( nThisRow
= nThisEndRow
+1; nThisRow
> 0; )
1279 SCROW nOtherRow
= pOtherRows
[nThisRow
];
1280 if ( ValidRow(nOtherRow
) && nOtherRow
+1 < nLastOtherRow
)
1283 ScRange
aDelRange( 0, nOtherRow
+1, nOtherTab
,
1284 MAXCOL
, nLastOtherRow
-1, nOtherTab
);
1285 pChangeTrack
->AppendDeleteRange( aDelRange
, &rOtherDoc
, n1
, n2
);
1287 if ( nOtherRow
> MAXROW
) // inserted
1290 if ( nThisRow
== nThisEndRow
|| ValidRow(pOtherRows
[nThisRow
+1]) )
1292 SCROW nFirstNew
= nThisRow
;
1293 while ( nFirstNew
> 0 && pOtherRows
[nFirstNew
-1] > MAXROW
)
1295 SCROW nDiff
= nThisRow
- nFirstNew
;
1296 ScRange
aRange( 0, nLastOtherRow
, nOtherTab
,
1297 MAXCOL
, nLastOtherRow
+nDiff
, nOtherTab
);
1298 pChangeTrack
->AppendInsert( aRange
);
1302 nLastOtherRow
= nOtherRow
;
1304 if ( nLastOtherRow
> 0 ) // deleted at the very top
1306 ScRange
aDelRange( 0, 0, nOtherTab
,
1307 MAXCOL
, nLastOtherRow
-1, nOtherTab
);
1308 pChangeTrack
->AppendDeleteRange( aDelRange
, &rOtherDoc
, n1
, n2
);
1311 // walk rows to find single cells
1313 for (nThisRow
= 0; nThisRow
<= nThisEndRow
; nThisRow
++)
1315 SCROW nOtherRow
= pOtherRows
[nThisRow
];
1316 for (nThisCol
= 0; nThisCol
<= nThisEndCol
; nThisCol
++)
1318 SCCOL nOtherCol
= static_cast<SCCOL
>(pOtherCols
[nThisCol
]);
1319 ScAddress
aThisPos( nThisCol
, nThisRow
, nThisTab
);
1320 ScCellValue aThisCell
;
1321 aThisCell
.assign(*this, aThisPos
);
1322 ScCellValue aOtherCell
; // start empty
1323 if ( ValidCol(nOtherCol
) && ValidRow(nOtherRow
) )
1325 ScAddress
aOtherPos( nOtherCol
, nOtherRow
, nOtherTab
);
1326 aOtherCell
.assign(rOtherDoc
, aOtherPos
);
1329 if (!aThisCell
.equalsWithoutFormat(aOtherCell
))
1331 ScRange
aRange( aThisPos
);
1332 ScChangeActionContent
* pAction
= new ScChangeActionContent( aRange
);
1333 pAction
->SetOldValue(aOtherCell
, &rOtherDoc
, this);
1334 pAction
->SetNewValue(aThisCell
, this);
1335 pChangeTrack
->Append( pAction
);
1338 aProgress
.SetStateOnPercent(nProgressStart
+nThisRow
);
1344 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */