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 <svl/zforlist.hxx>
21 #include <svl/zformat.hxx>
22 #include <formula/token.hxx>
23 #include <sal/log.hxx>
24 #include <unotools/configmgr.hxx>
25 #include <osl/diagnose.h>
26
27 #include <document.hxx>
28 #include <table.hxx>
29 #include <globstr.hrc>
30 #include <scresid.hxx>
31 #include <subtotal.hxx>
32 #include <docoptio.hxx>
33 #include <markdata.hxx>
34 #include <validat.hxx>
35 #include <scitems.hxx>
36 #include <stlpool.hxx>
37 #include <poolhelp.hxx>
38 #include <detdata.hxx>
39 #include <patattr.hxx>
40 #include <chgtrack.hxx>
41 #include <progress.hxx>
42 #include <paramisc.hxx>
43 #include <compiler.hxx>
44 #include <externalrefmgr.hxx>
45 #include <attrib.hxx>
46 #include <formulacell.hxx>
47 #include <tokenarray.hxx>
48 #include <tokenstringcontext.hxx>
49 #include <memory>
50
51 using namespace formula;
52
53 /** (Goal Seek) Find a value of x that is a root of f(x)
54
55 This function is used internally for the goal seek operation. It uses the
56 Regula Falsi (aka false position) algorithm to find a root of f(x). The
57 start value and the target value are to be given by the user in the
58 goal seek dialog. The f(x) in this case is defined as the formula in the
59 formula cell minus target value. This function may also perform additional
60 search in the horizontal directions when the f(x) is discrete in order to
61 ensure a non-zero slope necessary for deriving a subsequent x that is
62 reasonably close to the root of interest.
63
64 @change 24.10.2004 by Kohei Yoshida (kohei@openoffice.org)
65
66 @see #i28955#
67
68 @change 6 Aug 2013, fdo37341
69 */
Solver(SCCOL nFCol,SCROW nFRow,SCTAB nFTab,SCCOL nVCol,SCROW nVRow,SCTAB nVTab,const OUString & sValStr,double & nX)70 bool ScDocument::Solver(SCCOL nFCol, SCROW nFRow, SCTAB nFTab,
71 SCCOL nVCol, SCROW nVRow, SCTAB nVTab,
72 const OUString& sValStr, double& nX)
73 {
74 bool bRet = false;
75 nX = 0.0;
76 if ( ValidColRow( nFCol, nFRow ) && ValidTab( nFTab ) &&
77 ValidColRow( nVCol, nVRow ) && ValidTab( nVTab ) &&
78 nFTab < static_cast<SCTAB>( maTabs.size() ) && maTabs[nFTab] &&
79 nVTab < static_cast<SCTAB>( maTabs.size() ) && maTabs[nVTab] )
80 {
81 CellType eFType, eVType;
82 GetCellType(nFCol, nFRow, nFTab, eFType);
83 GetCellType(nVCol, nVRow, nVTab, eVType);
84 // #i108005# convert target value to number using default format,
85 // as previously done in ScInterpreter::GetDouble
86 ScFormulaCell* pFormula = nullptr;
87 double fTargetVal = 0.0;
88 sal_uInt32 nFIndex = 0;
89 if ( eFType == CELLTYPE_FORMULA && eVType == CELLTYPE_VALUE &&
90 GetFormatTable()->IsNumberFormat( sValStr, nFIndex, fTargetVal ) )
91 {
92 ScAddress aFormulaAdr( nFCol, nFRow, nFTab );
93 pFormula = GetFormulaCell( aFormulaAdr );
94 }
95 if (pFormula)
96 {
97 bool bDoneIteration = false;
98 ScAddress aValueAdr( nVCol, nVRow, nVTab );
99 double* pVCell = GetValueCell( aValueAdr );
100
101 ScRange aVRange( aValueAdr, aValueAdr ); // for SetDirty
102 // Original value to be restored later if necessary
103 double fSaveVal = *pVCell;
104
105 const sal_uInt16 nMaxIter = 100;
106 const double fEps = 1E-10;
107 const double fDelta = 1E-6;
108
109 double fBestX, fXPrev;
110 double fBestF, fFPrev;
111 fBestX = fXPrev = fSaveVal;
112
113 pFormula->Interpret();
114 bool bError = ( pFormula->GetErrCode() != FormulaError::NONE );
115 // bError always corresponds with fF
116
117 fFPrev = pFormula->GetValue() - fTargetVal;
118
119 fBestF = fabs( fFPrev );
120 if ( fBestF < fDelta )
121 bDoneIteration = true;
122
123 double fX = fXPrev + fEps;
124 double fF = fFPrev;
125 double fSlope;
126
127 sal_uInt16 nIter = 0;
128
129 bool bHorMoveError = false;
130 // Conform Regula Falsi Method
131 while ( !bDoneIteration && ( nIter++ < nMaxIter ) )
132 {
133 *pVCell = fX;
134 SetDirty( aVRange, false );
135 pFormula->Interpret();
136 bError = ( pFormula->GetErrCode() != FormulaError::NONE );
137 fF = pFormula->GetValue() - fTargetVal;
138
139 if ( fF == fFPrev && !bError )
140 {
141 // HORIZONTAL SEARCH: Keep moving x in both directions until the f(x)
142 // becomes different from the previous f(x). This routine is needed
143 // when a given function is discrete, in which case the resulting slope
144 // may become zero which ultimately causes the goal seek operation
145 // to fail. #i28955#
146
147 sal_uInt16 nHorIter = 0;
148 const double fHorStepAngle = 5.0;
149 const double fHorMaxAngle = 80.0;
150 int const nHorMaxIter = static_cast<int>( fHorMaxAngle / fHorStepAngle );
151 bool bDoneHorMove = false;
152
153 while ( !bDoneHorMove && !bHorMoveError && nHorIter++ < nHorMaxIter )
154 {
155 double fHorAngle = fHorStepAngle * static_cast<double>( nHorIter );
156 double fHorTangent = ::rtl::math::tan(basegfx::deg2rad(fHorAngle));
157
158 sal_uInt16 nIdx = 0;
159 while( nIdx++ < 2 && !bDoneHorMove )
160 {
161 double fHorX;
162 if ( nIdx == 1 )
163 fHorX = fX + fabs( fF ) * fHorTangent;
164 else
165 fHorX = fX - fabs( fF ) * fHorTangent;
166
167 *pVCell = fHorX;
168 SetDirty( aVRange, false );
169 pFormula->Interpret();
170 bHorMoveError = ( pFormula->GetErrCode() != FormulaError::NONE );
171 if ( bHorMoveError )
172 break;
173
174 fF = pFormula->GetValue() - fTargetVal;
175 if ( fF != fFPrev )
176 {
177 fX = fHorX;
178 bDoneHorMove = true;
179 }
180 }
181 }
182 if ( !bDoneHorMove )
183 bHorMoveError = true;
184 }
185
186 if ( bError )
187 {
188 // move closer to last valid value (fXPrev), keep fXPrev & fFPrev
189 double fDiff = ( fXPrev - fX ) / 2;
190 if ( fabs( fDiff ) < fEps )
191 fDiff = ( fDiff < 0.0 ? - fEps : fEps );
192 fX += fDiff;
193 }
194 else if ( bHorMoveError )
195 break;
196 else if ( fabs(fF) < fDelta )
197 {
198 // converged to root
199 fBestX = fX;
200 bDoneIteration = true;
201 }
202 else
203 {
204 if ( fabs(fF) + fDelta < fBestF )
205 {
206 fBestX = fX;
207 fBestF = fabs( fF );
208 }
209
210 if ( ( fXPrev - fX ) != 0 )
211 {
212 fSlope = ( fFPrev - fF ) / ( fXPrev - fX );
213 if ( fabs( fSlope ) < fEps )
214 fSlope = fSlope < 0.0 ? -fEps : fEps;
215 }
216 else
217 fSlope = fEps;
218
219 fXPrev = fX;
220 fFPrev = fF;
221 fX = fX - ( fF / fSlope );
222 }
223 }
224
225 // Try a nice rounded input value if possible.
226 const double fNiceDelta = ( bDoneIteration && fabs( fBestX ) >= 1e-3 ? 1e-3 : fDelta );
227 nX = ::rtl::math::approxFloor( ( fBestX / fNiceDelta ) + 0.5 ) * fNiceDelta;
228
229 if ( bDoneIteration )
230 {
231 *pVCell = nX;
232 SetDirty( aVRange, false );
233 pFormula->Interpret();
234 if ( fabs( pFormula->GetValue() - fTargetVal ) > fabs( fF ) )
235 nX = fBestX;
236 bRet = true;
237 }
238 else if ( bError || bHorMoveError )
239 {
240 nX = fBestX;
241 }
242 *pVCell = fSaveVal;
243 SetDirty( aVRange, false );
244 pFormula->Interpret();
245 if ( !bDoneIteration )
246 {
247 SetError( nVCol, nVRow, nVTab, FormulaError::NotAvailable );
248 }
249 }
250 else
251 {
252 SetError( nVCol, nVRow, nVTab, FormulaError::NotAvailable );
253 }
254 }
255 return bRet;
256 }
257
InsertMatrixFormula(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,const ScMarkData & rMark,const OUString & rFormula,const ScTokenArray * pArr,const formula::FormulaGrammar::Grammar eGram)258 void ScDocument::InsertMatrixFormula(SCCOL nCol1, SCROW nRow1,
259 SCCOL nCol2, SCROW nRow2,
260 const ScMarkData& rMark,
261 const OUString& rFormula,
262 const ScTokenArray* pArr,
263 const formula::FormulaGrammar::Grammar eGram )
264 {
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())
270 {
271 SAL_WARN("sc", "ScDocument::InsertMatrixFormula: No table marked");
272 return;
273 }
274 if (utl::ConfigManager::IsFuzzing()) //just too slow
275 return;
276 assert( ValidColRow( nCol1, nRow1) && ValidColRow( nCol2, nRow2));
277
278 SCTAB nTab1 = *rMark.begin();
279
280 ScFormulaCell* pCell;
281 ScAddress aPos( nCol1, nRow1, nTab1 );
282 if (pArr)
283 pCell = new ScFormulaCell(*this, aPos, *pArr, eGram, ScMatrixMode::Formula);
284 else
285 pCell = new ScFormulaCell(*this, aPos, rFormula, eGram, ScMatrixMode::Formula);
286 pCell->SetMatColsRows( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1 );
287 SCTAB nMax = static_cast<SCTAB>(maTabs.size());
288 for (const auto& rTab : rMark)
289 {
290 if (rTab >= nMax)
291 break;
292
293 if (!maTabs[rTab])
294 continue;
295
296 if (rTab == nTab1)
297 {
298 pCell = maTabs[rTab]->SetFormulaCell(nCol1, nRow1, pCell);
299 if (!pCell) //NULL if nCol1/nRow1 is invalid, which it can't be here
300 break;
301 }
302 else
303 maTabs[rTab]->SetFormulaCell(
304 nCol1, nRow1,
305 new ScFormulaCell(
306 *pCell, *this, ScAddress(nCol1, nRow1, rTab), ScCloneFlags::StartListening));
307 }
308
309 ScAddress aBasePos(nCol1, nRow1, nTab1);
310 ScSingleRefData aRefData;
311 aRefData.InitFlags();
312 aRefData.SetColRel( true );
313 aRefData.SetRowRel( true );
314 aRefData.SetTabRel( true );
315 aRefData.SetAddress(GetSheetLimits(), aBasePos, aBasePos);
316
317 ScTokenArray aArr(*this); // consists only of one single reference token.
318 formula::FormulaToken* t = aArr.AddMatrixSingleReference(aRefData);
319
320 for (const SCTAB& nTab : rMark)
321 {
322 if (nTab >= nMax)
323 break;
324
325 ScTable* pTab = FetchTable(nTab);
326 if (!pTab)
327 continue;
328
329 if (nTab != nTab1)
330 {
331 aRefData.SetRelTab(nTab - aBasePos.Tab());
332 *t->GetSingleRef() = aRefData;
333 }
334
335 for (SCCOL nCol : GetColumnsRange(nTab1, nCol1, nCol2))
336 {
337 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
338 {
339 if (nCol == nCol1 && nRow == nRow1)
340 // Skip the base position.
341 continue;
342
343 // Token array must be cloned so that each formula cell receives its own copy.
344 aPos = ScAddress(nCol, nRow, nTab);
345 // Reference in each cell must point to the origin cell relative to the current cell.
346 aRefData.SetAddress(GetSheetLimits(), aBasePos, aPos);
347 *t->GetSingleRef() = aRefData;
348 std::unique_ptr<ScTokenArray> pTokArr(aArr.Clone());
349 pCell = new ScFormulaCell(*this, aPos, *pTokArr, eGram, ScMatrixMode::Reference);
350 pTab->SetFormulaCell(nCol, nRow, pCell);
351 }
352 }
353 }
354 }
355
InsertTableOp(const ScTabOpParam & rParam,SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,const ScMarkData & rMark)356 void ScDocument::InsertTableOp(const ScTabOpParam& rParam, // multiple (repeated?) operation
357 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
358 const ScMarkData& rMark)
359 {
360 PutInOrder(nCol1, nCol2);
361 PutInOrder(nRow1, nRow2);
362 assert( ValidColRow( nCol1, nRow1) && ValidColRow( nCol2, nRow2));
363 SCTAB i, nTab1;
364 SCCOL j;
365 SCROW k;
366 i = 0;
367 bool bStop = false;
368 SCTAB nMax = static_cast<SCTAB>(maTabs.size());
369 for (const auto& rTab : rMark)
370 {
371 if (rTab >= nMax)
372 break;
373
374 if (maTabs[rTab])
375 {
376 i = rTab;
377 bStop = true;
378 break;
379 }
380 }
381 nTab1 = i;
382 if (!bStop)
383 {
384 OSL_FAIL("ScDocument::InsertTableOp: No table marked");
385 return;
386 }
387
388 ScRefAddress aRef;
389 OUStringBuffer aForString;
390 aForString.append('=');
391 aForString.append(ScCompiler::GetNativeSymbol(ocTableOp));
392 aForString.append(ScCompiler::GetNativeSymbol( ocOpen));
393
394 const OUString& sSep = ScCompiler::GetNativeSymbol( ocSep);
395 if (rParam.meMode == ScTabOpParam::Column) // column only
396 {
397 aRef.Set( rParam.aRefFormulaCell.GetAddress(), true, false, false );
398 aForString.append(aRef.GetRefString(*this, nTab1));
399 aForString.append(sSep);
400 aForString.append(rParam.aRefColCell.GetRefString(*this, nTab1));
401 aForString.append(sSep);
402 aRef.Set( nCol1, nRow1, nTab1, false, true, true );
403 aForString.append(aRef.GetRefString(*this, nTab1));
404 nCol1++;
405 nCol2 = std::min( nCol2, static_cast<SCCOL>(rParam.aRefFormulaEnd.Col() -
406 rParam.aRefFormulaCell.Col() + nCol1 + 1));
407 }
408 else if (rParam.meMode == ScTabOpParam::Row) // row only
409 {
410 aRef.Set( rParam.aRefFormulaCell.GetAddress(), false, true, false );
411 aForString.append(aRef.GetRefString(*this, nTab1));
412 aForString.append(sSep);
413 aForString.append(rParam.aRefRowCell.GetRefString(*this, nTab1));
414 aForString.append(sSep);
415 aRef.Set( nCol1, nRow1, nTab1, true, false, true );
416 aForString.append(aRef.GetRefString(*this, nTab1));
417 nRow1++;
418 nRow2 = std::min( nRow2, static_cast<SCROW>(rParam.aRefFormulaEnd.Row() -
419 rParam.aRefFormulaCell.Row() + nRow1 + 1));
420 }
421 else // both
422 {
423 aForString.append(rParam.aRefFormulaCell.GetRefString(*this, nTab1));
424 aForString.append(sSep);
425 aForString.append(rParam.aRefColCell.GetRefString(*this, nTab1));
426 aForString.append(sSep);
427 aRef.Set( nCol1, nRow1 + 1, nTab1, false, true, true );
428 aForString.append(aRef.GetRefString(*this, nTab1));
429 aForString.append(sSep);
430 aForString.append(rParam.aRefRowCell.GetRefString(*this, nTab1));
431 aForString.append(sSep);
432 aRef.Set( nCol1 + 1, nRow1, nTab1, true, false, true );
433 aForString.append(aRef.GetRefString(*this, nTab1));
434 nCol1++; nRow1++;
435 }
436 aForString.append(ScCompiler::GetNativeSymbol( ocClose ));
437
438 ScFormulaCell aRefCell( *this, ScAddress( nCol1, nRow1, nTab1 ), aForString.makeStringAndClear(),
439 formula::FormulaGrammar::GRAM_NATIVE, ScMatrixMode::NONE );
440 for( j = nCol1; j <= nCol2; j++ )
441 for( k = nRow1; k <= nRow2; k++ )
442 for (i = 0; i < static_cast<SCTAB>(maTabs.size()); i++)
443 {
444 for (const auto& rTab : rMark)
445 {
446 if (rTab >= nMax)
447 break;
448 if( maTabs[rTab] )
449 maTabs[rTab]->SetFormulaCell(
450 j, k, new ScFormulaCell(aRefCell, *this, ScAddress(j, k, rTab), ScCloneFlags::StartListening));
451 }
452 }
453 }
454
455 namespace {
456
setCacheTableReferenced(const ScDocument & rDoc,formula::FormulaToken & rToken,ScExternalRefManager & rRefMgr,const ScAddress & rPos)457 bool setCacheTableReferenced(const ScDocument& rDoc, formula::FormulaToken& rToken, ScExternalRefManager& rRefMgr, const ScAddress& rPos)
458 {
459 switch (rToken.GetType())
460 {
461 case svExternalSingleRef:
462 return rRefMgr.setCacheTableReferenced(
463 rToken.GetIndex(), rToken.GetString().getString(), 1);
464 case svExternalDoubleRef:
465 {
466 const ScComplexRefData& rRef = *rToken.GetDoubleRef();
467 ScRange aAbs = rRef.toAbs(rDoc, rPos);
468 size_t nSheets = aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1;
469 return rRefMgr.setCacheTableReferenced(
470 rToken.GetIndex(), rToken.GetString().getString(), nSheets);
471 }
472 case svExternalName:
473 /* TODO: external names aren't supported yet, but would
474 * have to be marked as well, if so. Mechanism would be
475 * different. */
476 OSL_FAIL("ScDocument::MarkUsedExternalReferences: implement the svExternalName case!");
477 break;
478 default:
479 break;
480 }
481 return false;
482 }
483
484 }
485
MarkUsedExternalReferences(const ScTokenArray & rArr,const ScAddress & rPos)486 bool ScDocument::MarkUsedExternalReferences( const ScTokenArray& rArr, const ScAddress& rPos )
487 {
488 if (!rArr.GetLen())
489 return false;
490
491 ScExternalRefManager* pRefMgr = nullptr;
492 formula::FormulaTokenArrayPlainIterator aIter( rArr );
493 bool bAllMarked = false;
494 while (!bAllMarked)
495 {
496 formula::FormulaToken* t = aIter.GetNextReferenceOrName();
497 if (!t)
498 break;
499 if (t->IsExternalRef())
500 {
501 if (!pRefMgr)
502 pRefMgr = GetExternalRefManager();
503
504 bAllMarked = setCacheTableReferenced(*this, *t, *pRefMgr, rPos);
505 }
506 else if (t->GetType() == svIndex)
507 {
508 // this is a named range. Check if the range contains an external
509 // reference.
510 ScRangeData* pRangeData = GetRangeName()->findByIndex(t->GetIndex());
511 if (!pRangeData)
512 continue;
513
514 ScTokenArray* pArray = pRangeData->GetCode();
515 formula::FormulaTokenArrayPlainIterator aArrayIter(*pArray);
516 for (t = aArrayIter.First(); t; t = aArrayIter.Next())
517 {
518 if (!t->IsExternalRef())
519 continue;
520
521 if (!pRefMgr)
522 pRefMgr = GetExternalRefManager();
523
524 bAllMarked = setCacheTableReferenced(*this, *t, *pRefMgr, rPos);
525 }
526 }
527 }
528 return bAllMarked;
529 }
530
GetNextSpellingCell(SCCOL & nCol,SCROW & nRow,SCTAB nTab,bool bInSel,const ScMarkData & rMark) const531 bool ScDocument::GetNextSpellingCell(SCCOL& nCol, SCROW& nRow, SCTAB nTab,
532 bool bInSel, const ScMarkData& rMark) const
533 {
534 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
535 return maTabs[nTab]->GetNextSpellingCell( nCol, nRow, bInSel, rMark );
536 else
537 return false;
538 }
539
GetNextMarkedCell(SCCOL & rCol,SCROW & rRow,SCTAB nTab,const ScMarkData & rMark)540 bool ScDocument::GetNextMarkedCell( SCCOL& rCol, SCROW& rRow, SCTAB nTab,
541 const ScMarkData& rMark )
542 {
543 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
544 return maTabs[nTab]->GetNextMarkedCell( rCol, rRow, rMark );
545 else
546 return false;
547 }
548
ReplaceStyle(const SvxSearchItem & rSearchItem,SCCOL nCol,SCROW nRow,SCTAB nTab,const ScMarkData & rMark)549 void ScDocument::ReplaceStyle(const SvxSearchItem& rSearchItem,
550 SCCOL nCol, SCROW nRow, SCTAB nTab,
551 const ScMarkData& rMark)
552 {
553 if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
554 maTabs[nTab]->ReplaceStyle(rSearchItem, nCol, nRow, rMark, true/*bIsUndoP*/);
555 }
556
CompileDBFormula()557 void ScDocument::CompileDBFormula()
558 {
559 sc::CompileFormulaContext aCxt(*this);
560 for (auto& rxTab : maTabs)
561 {
562 if (rxTab)
563 rxTab->CompileDBFormula(aCxt);
564 }
565 }
566
CompileColRowNameFormula()567 void ScDocument::CompileColRowNameFormula()
568 {
569 sc::CompileFormulaContext aCxt(*this);
570 for (auto& rxTab : maTabs)
571 {
572 if (rxTab)
573 rxTab->CompileColRowNameFormula(aCxt);
574 }
575 }
576
InvalidateTableArea()577 void ScDocument::InvalidateTableArea()
578 {
579 for (auto& rxTab : maTabs)
580 {
581 if (!rxTab)
582 break;
583 rxTab->InvalidateTableArea();
584 if ( rxTab->IsScenario() )
585 rxTab->InvalidateScenarioRanges();
586 }
587 }
588
GetMaxStringLen(SCTAB nTab,SCCOL nCol,SCROW nRowStart,SCROW nRowEnd,rtl_TextEncoding eCharSet) const589 sal_Int32 ScDocument::GetMaxStringLen( SCTAB nTab, SCCOL nCol,
590 SCROW nRowStart, SCROW nRowEnd, rtl_TextEncoding eCharSet ) const
591 {
592 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
593 return maTabs[nTab]->GetMaxStringLen( nCol, nRowStart, nRowEnd, eCharSet );
594 else
595 return 0;
596 }
597
GetMaxNumberStringLen(sal_uInt16 & nPrecision,SCTAB nTab,SCCOL nCol,SCROW nRowStart,SCROW nRowEnd) const598 sal_Int32 ScDocument::GetMaxNumberStringLen( sal_uInt16& nPrecision, SCTAB nTab,
599 SCCOL nCol,
600 SCROW nRowStart, SCROW nRowEnd ) const
601 {
602 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
603 return maTabs[nTab]->GetMaxNumberStringLen( nPrecision, nCol,
604 nRowStart, nRowEnd );
605 else
606 return 0;
607 }
608
GetSelectionFunction(ScSubTotalFunc eFunc,const ScAddress & rCursor,const ScMarkData & rMark,double & rResult)609 bool ScDocument::GetSelectionFunction( ScSubTotalFunc eFunc,
610 const ScAddress& rCursor, const ScMarkData& rMark,
611 double& rResult )
612 {
613 ScFunctionData aData(eFunc);
614
615 ScMarkData aMark(rMark);
616 aMark.MarkToMulti();
617 if (!aMark.IsMultiMarked() && !aMark.IsCellMarked(rCursor.Col(), rCursor.Row()))
618 aMark.SetMarkArea(rCursor);
619
620 SCTAB nMax = static_cast<SCTAB>(maTabs.size());
621 ScMarkData::const_iterator itr = aMark.begin(), itrEnd = aMark.end();
622
623 for (; itr != itrEnd && *itr < nMax && !aData.getError(); ++itr)
624 if (maTabs[*itr])
625 maTabs[*itr]->UpdateSelectionFunction(aData, aMark);
626
627 rResult = aData.getResult();
628 if (aData.getError())
629 rResult = 0.0;
630
631 return !aData.getError();
632 }
633
RoundValueAsShown(double fVal,sal_uInt32 nFormat,const ScInterpreterContext * pContext) const634 double ScDocument::RoundValueAsShown( double fVal, sal_uInt32 nFormat, const ScInterpreterContext* pContext ) const
635 {
636 const SvNumberFormatter* pFormatter = pContext ? pContext->GetFormatTable() : GetFormatTable();
637 const SvNumberformat* pFormat = pFormatter->GetEntry( nFormat );
638 if (!pFormat)
639 return fVal;
640 SvNumFormatType nType = pFormat->GetMaskedType();
641 if (nType != SvNumFormatType::DATE && nType != SvNumFormatType::TIME && nType != SvNumFormatType::DATETIME )
642 {
643 // MSVC doesn't recognize all paths init nPrecision and wails about
644 // "potentially uninitialized local variable 'nPrecision' used"
645 // so init to some random sensible value preserving all decimals.
646 short nPrecision = 20;
647 bool bStdPrecision = ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0);
648 if (!bStdPrecision)
649 {
650 sal_uInt16 nIdx = pFormat->GetSubformatIndex( fVal );
651 nPrecision = static_cast<short>(pFormat->GetFormatPrecision( nIdx ));
652 switch ( nType )
653 {
654 case SvNumFormatType::PERCENT: // 0.41% == 0.0041
655 nPrecision += 2;
656 break;
657 case SvNumFormatType::SCIENTIFIC: // 1.23e-3 == 0.00123
658 {
659 short nExp = 0;
660 if ( fVal > 0.0 )
661 nExp = static_cast<short>(floor( log10( fVal ) ));
662 else if ( fVal < 0.0 )
663 nExp = static_cast<short>(floor( log10( -fVal ) ));
664 nPrecision -= nExp;
665 short nInteger = static_cast<short>(pFormat->GetFormatIntegerDigits( nIdx ));
666 if ( nInteger > 1 ) // Engineering notation
667 {
668 short nIncrement = nExp % nInteger;
669 if ( nIncrement != 0 )
670 {
671 nPrecision += nIncrement;
672 if (nExp < 0 )
673 nPrecision += nInteger;
674 }
675 }
676 break;
677 }
678 case SvNumFormatType::FRACTION: // get value of fraction representation
679 {
680 return pFormat->GetRoundFractionValue( fVal );
681 }
682 case SvNumFormatType::NUMBER:
683 case SvNumFormatType::CURRENCY:
684 { // tdf#106253 Thousands divisors for format "0,"
685 const sal_uInt16 nTD = pFormat->GetThousandDivisorPrecision( nIdx );
686 if (nTD == SvNumberFormatter::UNLIMITED_PRECISION)
687 // Format contains General keyword, handled below.
688 bStdPrecision = true;
689 else
690 nPrecision -= nTD;
691 break;
692 }
693 default: break;
694 }
695 }
696 if (bStdPrecision)
697 {
698 nPrecision = static_cast<short>(GetDocOptions().GetStdPrecision());
699 // #i115512# no rounding for automatic decimals
700 if (nPrecision == static_cast<short>(SvNumberFormatter::UNLIMITED_PRECISION))
701 return fVal;
702 }
703 double fRound = ::rtl::math::round( fVal, nPrecision );
704 if ( ::rtl::math::approxEqual( fVal, fRound ) )
705 return fVal; // rounding might introduce some error
706 else
707 return fRound;
708 }
709 else
710 return fVal;
711 }
712
713 // conditional formats and validation ranges
714
AddCondFormat(std::unique_ptr<ScConditionalFormat> pNew,SCTAB nTab)715 sal_uLong ScDocument::AddCondFormat( std::unique_ptr<ScConditionalFormat> pNew, SCTAB nTab )
716 {
717 if(!pNew)
718 return 0;
719
720 if(ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
721 return maTabs[nTab]->AddCondFormat( std::move(pNew) );
722
723 return 0;
724 }
725
AddValidationEntry(const ScValidationData & rNew)726 sal_uLong ScDocument::AddValidationEntry( const ScValidationData& rNew )
727 {
728 if (rNew.IsEmpty())
729 return 0; // empty is always 0
730
731 if (!pValidationList)
732 {
733 ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
734 pValidationList.reset(new ScValidationDataList);
735 }
736
737 sal_uLong nMax = 0;
738 for( const auto& rxData : *pValidationList )
739 {
740 const ScValidationData* pData = rxData.get();
741 sal_uLong nKey = pData->GetKey();
742 if ( pData->EqualEntries( rNew ) )
743 return nKey;
744 if ( nKey > nMax )
745 nMax = nKey;
746 }
747
748 // might be called from ScPatternAttr::PutInPool; thus clone (real copy)
749
750 sal_uLong nNewKey = nMax + 1;
751 std::unique_ptr<ScValidationData> pInsert(rNew.Clone(this));
752 pInsert->SetKey( nNewKey );
753 ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
754 pValidationList->InsertNew( std::move(pInsert) );
755 return nNewKey;
756 }
757
GetEffItem(SCCOL nCol,SCROW nRow,SCTAB nTab,sal_uInt16 nWhich) const758 const SfxPoolItem* ScDocument::GetEffItem(
759 SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich ) const
760 {
761 const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
762 if ( pPattern )
763 {
764 const SfxItemSet& rSet = pPattern->GetItemSet();
765 const SfxPoolItem* pItem;
766 if ( rSet.GetItemState( ATTR_CONDITIONAL, true, &pItem ) == SfxItemState::SET )
767 {
768 const ScCondFormatIndexes& rIndex = pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData();
769 ScConditionalFormatList* pCondFormList = GetCondFormList( nTab );
770 if (!rIndex.empty() && pCondFormList)
771 {
772 for(const auto& rItem : rIndex)
773 {
774 const ScConditionalFormat* pForm = pCondFormList->GetFormat( rItem );
775 if ( pForm )
776 {
777 ScAddress aPos(nCol, nRow, nTab);
778 ScRefCellValue aCell(const_cast<ScDocument&>(*this), aPos);
779 const OUString& aStyle = pForm->GetCellStyle(aCell, aPos);
780 if (!aStyle.isEmpty())
781 {
782 SfxStyleSheetBase* pStyleSheet = mxPoolHelper->GetStylePool()->Find(
783 aStyle, SfxStyleFamily::Para );
784 if ( pStyleSheet && pStyleSheet->GetItemSet().GetItemState(
785 nWhich, true, &pItem ) == SfxItemState::SET )
786 return pItem;
787 }
788 }
789 }
790 }
791 }
792 return &rSet.Get( nWhich );
793 }
794 OSL_FAIL("no pattern");
795 return nullptr;
796 }
797
GetCondResult(SCCOL nCol,SCROW nRow,SCTAB nTab,ScRefCellValue * pCell) const798 const SfxItemSet* ScDocument::GetCondResult( SCCOL nCol, SCROW nRow, SCTAB nTab, ScRefCellValue* pCell ) const
799 {
800 ScConditionalFormatList* pFormatList = GetCondFormList(nTab);
801 if (!pFormatList)
802 return nullptr;
803
804 ScAddress aPos(nCol, nRow, nTab);
805 ScRefCellValue aCell;
806 if( pCell == nullptr )
807 {
808 aCell.assign(const_cast<ScDocument&>(*this), aPos);
809 pCell = &aCell;
810 }
811 const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
812 const ScCondFormatIndexes& rIndex =
813 pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData();
814
815 return GetCondResult(*pCell, aPos, *pFormatList, rIndex);
816 }
817
GetCondResult(ScRefCellValue & rCell,const ScAddress & rPos,const ScConditionalFormatList & rList,const ScCondFormatIndexes & rIndex) const818 const SfxItemSet* ScDocument::GetCondResult(
819 ScRefCellValue& rCell, const ScAddress& rPos, const ScConditionalFormatList& rList,
820 const ScCondFormatIndexes& rIndex ) const
821 {
822 for (const auto& rItem : rIndex)
823 {
824 const ScConditionalFormat* pForm = rList.GetFormat(rItem);
825 if (!pForm)
826 continue;
827
828 const OUString& aStyle = pForm->GetCellStyle(rCell, rPos);
829 if (!aStyle.isEmpty())
830 {
831 SfxStyleSheetBase* pStyleSheet =
832 mxPoolHelper->GetStylePool()->Find(aStyle, SfxStyleFamily::Para);
833
834 if (pStyleSheet)
835 return &pStyleSheet->GetItemSet();
836
837 // if style is not there, treat like no condition
838 }
839 }
840
841 return nullptr;
842 }
843
GetCondFormat(SCCOL nCol,SCROW nRow,SCTAB nTab) const844 ScConditionalFormat* ScDocument::GetCondFormat(
845 SCCOL nCol, SCROW nRow, SCTAB nTab ) const
846 {
847 sal_uInt32 nIndex = 0;
848 const ScCondFormatIndexes& rCondFormats = GetAttr(nCol, nRow, nTab, ATTR_CONDITIONAL)->GetCondFormatData();
849
850 if(!rCondFormats.empty())
851 nIndex = rCondFormats[0];
852
853 if (nIndex)
854 {
855 ScConditionalFormatList* pCondFormList = GetCondFormList(nTab);
856 if (pCondFormList)
857 return pCondFormList->GetFormat( nIndex );
858 else
859 {
860 OSL_FAIL("pCondFormList is 0");
861 }
862 }
863
864 return nullptr;
865 }
866
GetCondFormList(SCTAB nTab) const867 ScConditionalFormatList* ScDocument::GetCondFormList(SCTAB nTab) const
868 {
869 if(ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
870 return maTabs[nTab]->GetCondFormList();
871
872 return nullptr;
873 }
874
SetCondFormList(ScConditionalFormatList * pList,SCTAB nTab)875 void ScDocument::SetCondFormList( ScConditionalFormatList* pList, SCTAB nTab )
876 {
877 if(ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
878 maTabs[nTab]->SetCondFormList(pList);
879 }
880
GetValidationEntry(sal_uLong nIndex) const881 const ScValidationData* ScDocument::GetValidationEntry( sal_uLong nIndex ) const
882 {
883 if ( pValidationList )
884 return pValidationList->GetData( nIndex );
885 else
886 return nullptr;
887 }
888
DeleteConditionalFormat(sal_uLong nOldIndex,SCTAB nTab)889 void ScDocument::DeleteConditionalFormat(sal_uLong nOldIndex, SCTAB nTab)
890 {
891 if(ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
892 maTabs[nTab]->DeleteConditionalFormat(nOldIndex);
893 }
894
HasDetectiveOperations() const895 bool ScDocument::HasDetectiveOperations() const
896 {
897 return pDetOpList && pDetOpList->Count();
898 }
899
AddDetectiveOperation(const ScDetOpData & rData)900 void ScDocument::AddDetectiveOperation( const ScDetOpData& rData )
901 {
902 if (!pDetOpList)
903 pDetOpList.reset(new ScDetOpList);
904
905 pDetOpList->Append( new ScDetOpData( rData ) );
906 }
907
ClearDetectiveOperations()908 void ScDocument::ClearDetectiveOperations()
909 {
910 pDetOpList.reset(); // deletes also the entries
911 }
912
SetDetOpList(std::unique_ptr<ScDetOpList> pNew)913 void ScDocument::SetDetOpList(std::unique_ptr<ScDetOpList> pNew)
914 {
915 pDetOpList = std::move(pNew);
916 }
917
918 // Comparison of Documents
919
920 // Pfriemel-Factors
921 #define SC_DOCCOMP_MAXDIFF 256
922 #define SC_DOCCOMP_MINGOOD 128
923 #define SC_DOCCOMP_COLUMNS 10
924 #define SC_DOCCOMP_ROWS 100
925
RowDifferences(SCROW nThisRow,SCTAB nThisTab,ScDocument & rOtherDoc,SCROW nOtherRow,SCTAB nOtherTab,SCCOL nMaxCol,const SCCOLROW * pOtherCols)926 sal_uInt16 ScDocument::RowDifferences( SCROW nThisRow, SCTAB nThisTab,
927 ScDocument& rOtherDoc, SCROW nOtherRow, SCTAB nOtherTab,
928 SCCOL nMaxCol, const SCCOLROW* pOtherCols )
929 {
930 sal_uLong nDif = 0;
931 sal_uLong nUsed = 0;
932 for (SCCOL nThisCol=0; nThisCol<=nMaxCol; nThisCol++)
933 {
934 SCCOL nOtherCol;
935 if ( pOtherCols )
936 nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
937 else
938 nOtherCol = nThisCol;
939
940 if (ValidCol(nOtherCol)) // only compare columns that are common to both docs
941 {
942 ScRefCellValue aThisCell(*this, ScAddress(nThisCol, nThisRow, nThisTab));
943 ScRefCellValue aOtherCell(rOtherDoc, ScAddress(nOtherCol, nOtherRow, nOtherTab));
944 if (!aThisCell.equalsWithoutFormat(aOtherCell))
945 {
946 if (!aThisCell.isEmpty() && !aOtherCell.isEmpty())
947 nDif += 3;
948 else
949 nDif += 4; // content <-> empty counts more
950 }
951
952 if (!aThisCell.isEmpty() || !aOtherCell.isEmpty())
953 ++nUsed;
954 }
955 }
956
957 if (nUsed > 0)
958 return static_cast<sal_uInt16>((nDif*64)/nUsed); // max.256 (SC_DOCCOMP_MAXDIFF)
959
960 OSL_ENSURE(!nDif,"Diff without Used");
961 return 0;
962 }
963
ColDifferences(SCCOL nThisCol,SCTAB nThisTab,ScDocument & rOtherDoc,SCCOL nOtherCol,SCTAB nOtherTab,SCROW nMaxRow,const SCCOLROW * pOtherRows)964 sal_uInt16 ScDocument::ColDifferences( SCCOL nThisCol, SCTAB nThisTab,
965 ScDocument& rOtherDoc, SCCOL nOtherCol, SCTAB nOtherTab,
966 SCROW nMaxRow, const SCCOLROW* pOtherRows )
967 {
968
969 //TODO: optimize e.g. with iterator?
970
971 sal_uLong nDif = 0;
972 sal_uLong nUsed = 0;
973 for (SCROW nThisRow=0; nThisRow<=nMaxRow; nThisRow++)
974 {
975 SCROW nOtherRow;
976 if ( pOtherRows )
977 nOtherRow = pOtherRows[nThisRow];
978 else
979 nOtherRow = nThisRow;
980
981 if (ValidRow(nOtherRow)) // only compare rows that are common to both docs
982 {
983 ScRefCellValue aThisCell(*this, ScAddress(nThisCol, nThisRow, nThisTab));
984 ScRefCellValue aOtherCell(rOtherDoc, ScAddress(nOtherCol, nOtherRow, nOtherTab));
985 if (!aThisCell.equalsWithoutFormat(aOtherCell))
986 {
987 if (!aThisCell.isEmpty() && !aOtherCell.isEmpty())
988 nDif += 3;
989 else
990 nDif += 4; // content <-> empty counts more
991 }
992
993 if (!aThisCell.isEmpty() || !aOtherCell.isEmpty())
994 ++nUsed;
995 }
996 }
997
998 if (nUsed > 0)
999 return static_cast<sal_uInt16>((nDif*64)/nUsed); // max.256
1000
1001 OSL_ENSURE(!nDif,"Diff without Used");
1002 return 0;
1003 }
1004
FindOrder(SCCOLROW * pOtherRows,SCCOLROW nThisEndRow,SCCOLROW nOtherEndRow,bool bColumns,ScDocument & rOtherDoc,SCTAB nThisTab,SCTAB nOtherTab,SCCOLROW nEndCol,const SCCOLROW * pTranslate,ScProgress * pProgress,sal_uLong nProAdd)1005 void ScDocument::FindOrder( SCCOLROW* pOtherRows, SCCOLROW nThisEndRow, SCCOLROW nOtherEndRow,
1006 bool bColumns, ScDocument& rOtherDoc, SCTAB nThisTab, SCTAB nOtherTab,
1007 SCCOLROW nEndCol, const SCCOLROW* pTranslate, ScProgress* pProgress, sal_uLong nProAdd )
1008 {
1009 // bColumns=true: rows are columns and vice versa
1010
1011 SCCOLROW nMaxCont; // continue by how much
1012 SCCOLROW nMinGood; // what is a hit (incl.)
1013 if ( bColumns )
1014 {
1015 nMaxCont = SC_DOCCOMP_COLUMNS; // 10 columns
1016 nMinGood = SC_DOCCOMP_MINGOOD;
1017
1018 //TODO: additional pass with nMinGood = 0 ????
1019
1020 }
1021 else
1022 {
1023 nMaxCont = SC_DOCCOMP_ROWS; // 100 rows
1024 nMinGood = SC_DOCCOMP_MINGOOD;
1025 }
1026 bool bUseTotal = bColumns && !pTranslate; // only for the 1st pass
1027
1028 SCCOLROW nOtherRow = 0;
1029 sal_uInt16 nComp;
1030 SCCOLROW nThisRow;
1031 bool bTotal = false; // hold for several nThisRow
1032 SCCOLROW nUnknown = 0;
1033 for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++)
1034 {
1035 SCCOLROW nTempOther = nOtherRow;
1036 bool bFound = false;
1037 sal_uInt16 nBest = SC_DOCCOMP_MAXDIFF;
1038 SCCOLROW nMax = std::min( nOtherEndRow, static_cast<SCCOLROW>(( nTempOther + nMaxCont + nUnknown )) );
1039 for (SCCOLROW i=nTempOther; i<=nMax && nBest>0; i++) // stop at 0
1040 {
1041 if (bColumns)
1042 nComp = ColDifferences( static_cast<SCCOL>(nThisRow), nThisTab, rOtherDoc, static_cast<SCCOL>(i), nOtherTab, nEndCol, pTranslate );
1043 else
1044 nComp = RowDifferences( nThisRow, nThisTab, rOtherDoc, i, nOtherTab, static_cast<SCCOL>(nEndCol), pTranslate );
1045 if ( nComp < nBest && ( nComp <= nMinGood || bTotal ) )
1046 {
1047 nTempOther = i;
1048 nBest = nComp;
1049 bFound = true;
1050 }
1051 if ( nComp < SC_DOCCOMP_MAXDIFF || bFound )
1052 bTotal = false;
1053 else if ( i == nTempOther && bUseTotal )
1054 bTotal = true; // only at the very top
1055 }
1056 if ( bFound )
1057 {
1058 pOtherRows[nThisRow] = nTempOther;
1059 nOtherRow = nTempOther + 1;
1060 nUnknown = 0;
1061 }
1062 else
1063 {
1064 pOtherRows[nThisRow] = SCROW_MAX;
1065 ++nUnknown;
1066 }
1067
1068 if (pProgress)
1069 pProgress->SetStateOnPercent(nProAdd+static_cast<sal_uLong>(nThisRow));
1070 }
1071
1072 // fill in blocks that don't match
1073
1074 SCROW nFillStart = 0;
1075 SCROW nFillPos = 0;
1076 bool bInFill = false;
1077 for (nThisRow = 0; nThisRow <= nThisEndRow+1; nThisRow++)
1078 {
1079 SCROW nThisOther = ( nThisRow <= nThisEndRow ) ? pOtherRows[nThisRow] : (nOtherEndRow+1);
1080 if ( ValidRow(nThisOther) )
1081 {
1082 if ( bInFill )
1083 {
1084 if ( nThisOther > nFillStart ) // is there something to distribute?
1085 {
1086 SCROW nDiff1 = nThisOther - nFillStart;
1087 SCROW nDiff2 = nThisRow - nFillPos;
1088 SCROW nMinDiff = std::min(nDiff1, nDiff2);
1089 for (SCROW i=0; i<nMinDiff; i++)
1090 pOtherRows[nFillPos+i] = nFillStart+i;
1091 }
1092
1093 bInFill = false;
1094 }
1095 nFillStart = nThisOther + 1;
1096 nFillPos = nThisRow + 1;
1097 }
1098 else
1099 bInFill = true;
1100 }
1101 }
1102
CompareDocument(ScDocument & rOtherDoc)1103 void ScDocument::CompareDocument( ScDocument& rOtherDoc )
1104 {
1105 if (!pChangeTrack)
1106 return;
1107
1108 SCTAB nThisCount = GetTableCount();
1109 SCTAB nOtherCount = rOtherDoc.GetTableCount();
1110 std::unique_ptr<SCTAB[]> pOtherTabs(new SCTAB[nThisCount]);
1111 SCTAB nThisTab;
1112
1113 // compare tables with identical names
1114 OUString aThisName;
1115 OUString aOtherName;
1116 for (nThisTab=0; nThisTab<nThisCount; nThisTab++)
1117 {
1118 SCTAB nOtherTab = SCTAB_MAX;
1119 if (!IsScenario(nThisTab)) // skip scenarios
1120 {
1121 GetName( nThisTab, aThisName );
1122 for (SCTAB nTemp=0; nTemp<nOtherCount && nOtherTab>MAXTAB; nTemp++)
1123 if (!rOtherDoc.IsScenario(nTemp))
1124 {
1125 rOtherDoc.GetName( nTemp, aOtherName );
1126 if ( aThisName == aOtherName )
1127 nOtherTab = nTemp;
1128 }
1129 }
1130 pOtherTabs[nThisTab] = nOtherTab;
1131 }
1132 // fill in, so that un-named tables don't get lost
1133 SCTAB nFillStart = 0;
1134 SCTAB nFillPos = 0;
1135 bool bInFill = false;
1136 for (nThisTab = 0; nThisTab <= nThisCount; nThisTab++)
1137 {
1138 SCTAB nThisOther = ( nThisTab < nThisCount ) ? pOtherTabs[nThisTab] : nOtherCount;
1139 if ( ValidTab(nThisOther) )
1140 {
1141 if ( bInFill )
1142 {
1143 if ( nThisOther > nFillStart ) // is there something to distribute?
1144 {
1145 SCTAB nDiff1 = nThisOther - nFillStart;
1146 SCTAB nDiff2 = nThisTab - nFillPos;
1147 SCTAB nMinDiff = std::min(nDiff1, nDiff2);
1148 for (SCTAB i=0; i<nMinDiff; i++)
1149 if ( !IsScenario(nFillPos+i) && !rOtherDoc.IsScenario(nFillStart+i) )
1150 pOtherTabs[nFillPos+i] = nFillStart+i;
1151 }
1152
1153 bInFill = false;
1154 }
1155 nFillStart = nThisOther + 1;
1156 nFillPos = nThisTab + 1;
1157 }
1158 else
1159 bInFill = true;
1160 }
1161
1162 // compare tables in the original order
1163
1164 for (nThisTab=0; nThisTab<nThisCount; nThisTab++)
1165 {
1166 SCTAB nOtherTab = pOtherTabs[nThisTab];
1167 if ( ValidTab(nOtherTab) )
1168 {
1169 SCCOL nThisEndCol = 0;
1170 SCROW nThisEndRow = 0;
1171 SCCOL nOtherEndCol = 0;
1172 SCROW nOtherEndRow = 0;
1173 GetCellArea( nThisTab, nThisEndCol, nThisEndRow );
1174 rOtherDoc.GetCellArea( nOtherTab, nOtherEndCol, nOtherEndRow );
1175 SCCOL nEndCol = std::max(nThisEndCol, nOtherEndCol);
1176 SCROW nEndRow = std::max(nThisEndRow, nOtherEndRow);
1177 SCCOL nThisCol;
1178 SCROW nThisRow;
1179 sal_uLong n1,n2; // for AppendDeleteRange
1180
1181 //TODO: one Progress over all tables ???
1182
1183 OUString aTabName;
1184 GetName( nThisTab, aTabName );
1185 OUString aTemplate = ScResId(STR_PROGRESS_COMPARING);
1186 sal_Int32 nIndex = 0;
1187 OUString aProText = aTemplate.getToken( 0, '#', nIndex ) +
1188 aTabName +
1189 aTemplate.getToken( 0, '#', nIndex );
1190 ScProgress aProgress( GetDocumentShell(), aProText, 3*nThisEndRow, true ); // 2x FindOrder, 1x here
1191 tools::Long nProgressStart = 2*nThisEndRow; // start for here
1192
1193 std::unique_ptr<SCCOLROW[]> pTempRows(new SCCOLROW[nThisEndRow+1]);
1194 std::unique_ptr<SCCOLROW[]> pOtherRows(new SCCOLROW[nThisEndRow+1]);
1195 std::unique_ptr<SCCOLROW[]> pOtherCols(new SCCOLROW[nThisEndCol+1]);
1196
1197 // find inserted/deleted columns/rows:
1198 // Two attempts:
1199 // 1) compare original rows (pTempRows)
1200 // 2) compare original columns (pOtherCols)
1201 // with this column order compare rows (pOtherRows)
1202
1203 //TODO: compare columns twice with different nMinGood ???
1204
1205 // 1
1206 FindOrder( pTempRows.get(), nThisEndRow, nOtherEndRow, false,
1207 rOtherDoc, nThisTab, nOtherTab, nEndCol, nullptr, &aProgress, 0 );
1208 // 2
1209 FindOrder( pOtherCols.get(), nThisEndCol, nOtherEndCol, true,
1210 rOtherDoc, nThisTab, nOtherTab, nEndRow, nullptr, nullptr, 0 );
1211 FindOrder( pOtherRows.get(), nThisEndRow, nOtherEndRow, false,
1212 rOtherDoc, nThisTab, nOtherTab, nThisEndCol,
1213 pOtherCols.get(), &aProgress, nThisEndRow );
1214
1215 sal_uLong nMatch1 = 0; // pTempRows, no columns
1216 for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++)
1217 if (ValidRow(pTempRows[nThisRow]))
1218 nMatch1 += SC_DOCCOMP_MAXDIFF -
1219 RowDifferences( nThisRow, nThisTab, rOtherDoc, pTempRows[nThisRow],
1220 nOtherTab, nEndCol, nullptr );
1221
1222 sal_uLong nMatch2 = 0; // pOtherRows, pOtherCols
1223 for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++)
1224 if (ValidRow(pOtherRows[nThisRow]))
1225 nMatch2 += SC_DOCCOMP_MAXDIFF -
1226 RowDifferences( nThisRow, nThisTab, rOtherDoc, pOtherRows[nThisRow],
1227 nOtherTab, nThisEndCol, pOtherCols.get() );
1228
1229 if ( nMatch1 >= nMatch2 ) // without columns ?
1230 {
1231 // reset columns
1232 for (nThisCol = 0; nThisCol<=nThisEndCol; nThisCol++)
1233 pOtherCols[nThisCol] = nThisCol;
1234
1235 // swap row-arrays (they get both deleted anyway)
1236 pTempRows.swap(pOtherRows);
1237 }
1238 else
1239 {
1240 // remains for pOtherCols, pOtherRows
1241 }
1242
1243 // Generate Change-Actions
1244 // 1) columns from the right
1245 // 2) rows from below
1246 // 3) single cells in normal order
1247
1248 // Actions for inserted/deleted columns
1249
1250 SCCOL nLastOtherCol = static_cast<SCCOL>(nOtherEndCol + 1);
1251 // nThisEndCol ... 0
1252 for ( nThisCol = nThisEndCol+1; nThisCol > 0; )
1253 {
1254 --nThisCol;
1255 SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
1256 if ( ValidCol(nOtherCol) && nOtherCol+1 < nLastOtherCol )
1257 {
1258 // gap -> deleted
1259 ScRange aDelRange( nOtherCol+1, 0, nOtherTab,
1260 nLastOtherCol-1, MaxRow(), nOtherTab );
1261 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1262 }
1263 if ( nOtherCol > MaxCol() ) // inserted
1264 {
1265 // combine
1266 if ( nThisCol == nThisEndCol || ValidCol(static_cast<SCCOL>(pOtherCols[nThisCol+1])) )
1267 {
1268 SCCOL nFirstNew = nThisCol;
1269 while ( nFirstNew > 0 && pOtherCols[nFirstNew-1] > MaxCol() )
1270 --nFirstNew;
1271 SCCOL nDiff = nThisCol - nFirstNew;
1272 ScRange aRange( nLastOtherCol, 0, nOtherTab,
1273 nLastOtherCol+nDiff, MaxRow(), nOtherTab );
1274 pChangeTrack->AppendInsert( aRange );
1275 }
1276 }
1277 else
1278 nLastOtherCol = nOtherCol;
1279 }
1280 if ( nLastOtherCol > 0 ) // deleted at the very top
1281 {
1282 ScRange aDelRange( 0, 0, nOtherTab,
1283 nLastOtherCol-1, MaxRow(), nOtherTab );
1284 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1285 }
1286
1287 // Actions for inserted/deleted rows
1288
1289 SCROW nLastOtherRow = nOtherEndRow + 1;
1290 // nThisEndRow ... 0
1291 for ( nThisRow = nThisEndRow+1; nThisRow > 0; )
1292 {
1293 --nThisRow;
1294 SCROW nOtherRow = pOtherRows[nThisRow];
1295 if ( ValidRow(nOtherRow) && nOtherRow+1 < nLastOtherRow )
1296 {
1297 // gap -> deleted
1298 ScRange aDelRange( 0, nOtherRow+1, nOtherTab,
1299 MaxCol(), nLastOtherRow-1, nOtherTab );
1300 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1301 }
1302 if ( nOtherRow > MaxRow() ) // inserted
1303 {
1304 // combine
1305 if ( nThisRow == nThisEndRow || ValidRow(pOtherRows[nThisRow+1]) )
1306 {
1307 SCROW nFirstNew = nThisRow;
1308 while ( nFirstNew > 0 && pOtherRows[nFirstNew-1] > MaxRow() )
1309 --nFirstNew;
1310 SCROW nDiff = nThisRow - nFirstNew;
1311 ScRange aRange( 0, nLastOtherRow, nOtherTab,
1312 MaxCol(), nLastOtherRow+nDiff, nOtherTab );
1313 pChangeTrack->AppendInsert( aRange );
1314 }
1315 }
1316 else
1317 nLastOtherRow = nOtherRow;
1318 }
1319 if ( nLastOtherRow > 0 ) // deleted at the very top
1320 {
1321 ScRange aDelRange( 0, 0, nOtherTab,
1322 MaxCol(), nLastOtherRow-1, nOtherTab );
1323 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1324 }
1325
1326 // walk rows to find single cells
1327
1328 for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++)
1329 {
1330 SCROW nOtherRow = pOtherRows[nThisRow];
1331 for (nThisCol = 0; nThisCol <= nThisEndCol; nThisCol++)
1332 {
1333 SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
1334 ScAddress aThisPos( nThisCol, nThisRow, nThisTab );
1335 ScCellValue aThisCell;
1336 aThisCell.assign(*this, aThisPos);
1337 ScCellValue aOtherCell; // start empty
1338 if ( ValidCol(nOtherCol) && ValidRow(nOtherRow) )
1339 {
1340 ScAddress aOtherPos( nOtherCol, nOtherRow, nOtherTab );
1341 aOtherCell.assign(rOtherDoc, aOtherPos);
1342 }
1343
1344 if (!aThisCell.equalsWithoutFormat(aOtherCell))
1345 {
1346 ScRange aRange( aThisPos );
1347 ScChangeActionContent* pAction = new ScChangeActionContent( aRange );
1348 pAction->SetOldValue(aOtherCell, &rOtherDoc, this);
1349 pAction->SetNewValue(aThisCell, this);
1350 pChangeTrack->Append( pAction );
1351 }
1352 }
1353 aProgress.SetStateOnPercent(nProgressStart+nThisRow);
1354 }
1355 }
1356 }
1357 }
1358
GetSheetSeparator() const1359 sal_Unicode ScDocument::GetSheetSeparator() const
1360 {
1361 const ScCompiler::Convention* pConv = ScCompiler::GetRefConvention(
1362 FormulaGrammar::extractRefConvention( GetGrammar()));
1363 assert(pConv);
1364 return pConv ? pConv->getSpecialSymbol( ScCompiler::Convention::SHEET_SEPARATOR) : '.';
1365 }
1366
1367 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1368