LibreOffice Module sc (master)  1
documen4.cxx
Go to the documentation of this file.
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 
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 = std::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 
259  SCCOL nCol2, SCROW nRow2,
260  const ScMarkData& rMark,
261  const OUString& rFormula,
262  const ScTokenArray* pArr,
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.
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 
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(),
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 
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 
487 {
488  if (!rArr.GetLen())
489  return false;
490 
491  ScExternalRefManager* pRefMgr = nullptr;
493  bool bAllMarked = false;
494  while (!bAllMarked)
495  {
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 
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 
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 
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 
558 {
559  sc::CompileFormulaContext aCxt(*this);
560  for (auto& rxTab : maTabs)
561  {
562  if (rxTab)
563  rxTab->CompileDBFormula(aCxt);
564  }
565 }
566 
568 {
569  sc::CompileFormulaContext aCxt(*this);
570  for (auto& rxTab : maTabs)
571  {
572  if (rxTab)
573  rxTab->CompileColRowNameFormula(aCxt);
574  }
575 }
576 
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 
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 
598 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 
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 
634 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  short nPrecision;
644  if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
645  {
646  sal_uInt16 nIdx = pFormat->GetSubformatIndex( fVal );
647  nPrecision = static_cast<short>(pFormat->GetFormatPrecision( nIdx ));
648  switch ( nType )
649  {
650  case SvNumFormatType::PERCENT: // 0.41% == 0.0041
651  nPrecision += 2;
652  break;
653  case SvNumFormatType::SCIENTIFIC: // 1.23e-3 == 0.00123
654  {
655  short nExp = 0;
656  if ( fVal > 0.0 )
657  nExp = static_cast<short>(floor( log10( fVal ) ));
658  else if ( fVal < 0.0 )
659  nExp = static_cast<short>(floor( log10( -fVal ) ));
660  nPrecision -= nExp;
661  short nInteger = static_cast<short>(pFormat->GetFormatIntegerDigits( nIdx ));
662  if ( nInteger > 1 ) // Engineering notation
663  {
664  short nIncrement = nExp % nInteger;
665  if ( nIncrement != 0 )
666  {
667  nPrecision += nIncrement;
668  if (nExp < 0 )
669  nPrecision += nInteger;
670  }
671  }
672  break;
673  }
674  case SvNumFormatType::FRACTION: // get value of fraction representation
675  {
676  return pFormat->GetRoundFractionValue( fVal );
677  }
678  case SvNumFormatType::NUMBER:
679  case SvNumFormatType::CURRENCY:
680  { // tdf#106253 Thousands divisors for format "0,"
681  nPrecision -= pFormat->GetThousandDivisorPrecision( nIdx );
682  break;
683  }
684  default: break;
685  }
686  }
687  else
688  {
689  nPrecision = static_cast<short>(GetDocOptions().GetStdPrecision());
690  // #i115512# no rounding for automatic decimals
691  if (nPrecision == static_cast<short>(SvNumberFormatter::UNLIMITED_PRECISION))
692  return fVal;
693  }
694  double fRound = ::rtl::math::round( fVal, nPrecision );
695  if ( ::rtl::math::approxEqual( fVal, fRound ) )
696  return fVal; // rounding might introduce some error
697  else
698  return fRound;
699  }
700  else
701  return fVal;
702 }
703 
704 // conditional formats and validation ranges
705 
706 sal_uLong ScDocument::AddCondFormat( std::unique_ptr<ScConditionalFormat> pNew, SCTAB nTab )
707 {
708  if(!pNew)
709  return 0;
710 
711  if(ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
712  return maTabs[nTab]->AddCondFormat( std::move(pNew) );
713 
714  return 0;
715 }
716 
718 {
719  if (rNew.IsEmpty())
720  return 0; // empty is always 0
721 
722  if (!pValidationList)
723  {
725  pValidationList.reset(new ScValidationDataList);
726  }
727 
728  sal_uLong nMax = 0;
729  for( const auto& rxData : *pValidationList )
730  {
731  const ScValidationData* pData = rxData.get();
732  sal_uLong nKey = pData->GetKey();
733  if ( pData->EqualEntries( rNew ) )
734  return nKey;
735  if ( nKey > nMax )
736  nMax = nKey;
737  }
738 
739  // might be called from ScPatternAttr::PutInPool; thus clone (real copy)
740 
741  sal_uLong nNewKey = nMax + 1;
742  std::unique_ptr<ScValidationData> pInsert(rNew.Clone(this));
743  pInsert->SetKey( nNewKey );
745  pValidationList->InsertNew( std::move(pInsert) );
746  return nNewKey;
747 }
748 
750  SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich ) const
751 {
752  const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
753  if ( pPattern )
754  {
755  const SfxItemSet& rSet = pPattern->GetItemSet();
756  const SfxPoolItem* pItem;
757  if ( rSet.GetItemState( ATTR_CONDITIONAL, true, &pItem ) == SfxItemState::SET )
758  {
759  const ScCondFormatIndexes& rIndex = pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData();
760  ScConditionalFormatList* pCondFormList = GetCondFormList( nTab );
761  if (!rIndex.empty() && pCondFormList)
762  {
763  for(const auto& rItem : rIndex)
764  {
765  const ScConditionalFormat* pForm = pCondFormList->GetFormat( rItem );
766  if ( pForm )
767  {
768  ScAddress aPos(nCol, nRow, nTab);
769  ScRefCellValue aCell(const_cast<ScDocument&>(*this), aPos);
770  const OUString& aStyle = pForm->GetCellStyle(aCell, aPos);
771  if (!aStyle.isEmpty())
772  {
773  SfxStyleSheetBase* pStyleSheet = mxPoolHelper->GetStylePool()->Find(
774  aStyle, SfxStyleFamily::Para );
775  if ( pStyleSheet && pStyleSheet->GetItemSet().GetItemState(
776  nWhich, true, &pItem ) == SfxItemState::SET )
777  return pItem;
778  }
779  }
780  }
781  }
782  }
783  return &rSet.Get( nWhich );
784  }
785  OSL_FAIL("no pattern");
786  return nullptr;
787 }
788 
789 const SfxItemSet* ScDocument::GetCondResult( SCCOL nCol, SCROW nRow, SCTAB nTab, ScRefCellValue* pCell ) const
790 {
791  ScConditionalFormatList* pFormatList = GetCondFormList(nTab);
792  if (!pFormatList)
793  return nullptr;
794 
795  ScAddress aPos(nCol, nRow, nTab);
796  ScRefCellValue aCell;
797  if( pCell == nullptr )
798  {
799  aCell.assign(const_cast<ScDocument&>(*this), aPos);
800  pCell = &aCell;
801  }
802  const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
803  const ScCondFormatIndexes& rIndex =
804  pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData();
805 
806  return GetCondResult(*pCell, aPos, *pFormatList, rIndex);
807 }
808 
810  ScRefCellValue& rCell, const ScAddress& rPos, const ScConditionalFormatList& rList,
811  const ScCondFormatIndexes& rIndex ) const
812 {
813  for (const auto& rItem : rIndex)
814  {
815  const ScConditionalFormat* pForm = rList.GetFormat(rItem);
816  if (!pForm)
817  continue;
818 
819  const OUString& aStyle = pForm->GetCellStyle(rCell, rPos);
820  if (!aStyle.isEmpty())
821  {
822  SfxStyleSheetBase* pStyleSheet =
823  mxPoolHelper->GetStylePool()->Find(aStyle, SfxStyleFamily::Para);
824 
825  if (pStyleSheet)
826  return &pStyleSheet->GetItemSet();
827 
828  // if style is not there, treat like no condition
829  }
830  }
831 
832  return nullptr;
833 }
834 
836  SCCOL nCol, SCROW nRow, SCTAB nTab ) const
837 {
838  sal_uInt32 nIndex = 0;
839  const ScCondFormatIndexes& rCondFormats = GetAttr(nCol, nRow, nTab, ATTR_CONDITIONAL)->GetCondFormatData();
840 
841  if(!rCondFormats.empty())
842  nIndex = rCondFormats[0];
843 
844  if (nIndex)
845  {
846  ScConditionalFormatList* pCondFormList = GetCondFormList(nTab);
847  if (pCondFormList)
848  return pCondFormList->GetFormat( nIndex );
849  else
850  {
851  OSL_FAIL("pCondFormList is 0");
852  }
853  }
854 
855  return nullptr;
856 }
857 
859 {
860  if(ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
861  return maTabs[nTab]->GetCondFormList();
862 
863  return nullptr;
864 }
865 
867 {
868  if(ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
869  maTabs[nTab]->SetCondFormList(pList);
870 }
871 
873 {
874  if ( pValidationList )
875  return pValidationList->GetData( nIndex );
876  else
877  return nullptr;
878 }
879 
881 {
882  if(ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
883  maTabs[nTab]->DeleteConditionalFormat(nOldIndex);
884 }
885 
887 {
888  return pDetOpList && pDetOpList->Count();
889 }
890 
892 {
893  if (!pDetOpList)
894  pDetOpList.reset(new ScDetOpList);
895 
896  pDetOpList->Append( rData );
897 }
898 
900 {
901  pDetOpList.reset(); // deletes also the entries
902 }
903 
904 void ScDocument::SetDetOpList(std::unique_ptr<ScDetOpList> pNew)
905 {
906  pDetOpList = std::move(pNew);
907 }
908 
909 // Comparison of Documents
910 
911 // Pfriemel-Factors
912 #define SC_DOCCOMP_MAXDIFF 256
913 #define SC_DOCCOMP_MINGOOD 128
914 #define SC_DOCCOMP_COLUMNS 10
915 #define SC_DOCCOMP_ROWS 100
916 
917 sal_uInt16 ScDocument::RowDifferences( SCROW nThisRow, SCTAB nThisTab,
918  ScDocument& rOtherDoc, SCROW nOtherRow, SCTAB nOtherTab,
919  SCCOL nMaxCol, const SCCOLROW* pOtherCols )
920 {
921  sal_uLong nDif = 0;
922  sal_uLong nUsed = 0;
923  for (SCCOL nThisCol=0; nThisCol<=nMaxCol; nThisCol++)
924  {
925  SCCOL nOtherCol;
926  if ( pOtherCols )
927  nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
928  else
929  nOtherCol = nThisCol;
930 
931  if (ValidCol(nOtherCol)) // only compare columns that are common to both docs
932  {
933  ScRefCellValue aThisCell(*this, ScAddress(nThisCol, nThisRow, nThisTab));
934  ScRefCellValue aOtherCell(rOtherDoc, ScAddress(nOtherCol, nOtherRow, nOtherTab));
935  if (!aThisCell.equalsWithoutFormat(aOtherCell))
936  {
937  if (!aThisCell.isEmpty() && !aOtherCell.isEmpty())
938  nDif += 3;
939  else
940  nDif += 4; // content <-> empty counts more
941  }
942 
943  if (!aThisCell.isEmpty() || !aOtherCell.isEmpty())
944  ++nUsed;
945  }
946  }
947 
948  if (nUsed > 0)
949  return static_cast<sal_uInt16>((nDif*64)/nUsed); // max.256 (SC_DOCCOMP_MAXDIFF)
950 
951  OSL_ENSURE(!nDif,"Diff without Used");
952  return 0;
953 }
954 
955 sal_uInt16 ScDocument::ColDifferences( SCCOL nThisCol, SCTAB nThisTab,
956  ScDocument& rOtherDoc, SCCOL nOtherCol, SCTAB nOtherTab,
957  SCROW nMaxRow, const SCCOLROW* pOtherRows )
958 {
959 
960  //TODO: optimize e.g. with iterator?
961 
962  sal_uLong nDif = 0;
963  sal_uLong nUsed = 0;
964  for (SCROW nThisRow=0; nThisRow<=nMaxRow; nThisRow++)
965  {
966  SCROW nOtherRow;
967  if ( pOtherRows )
968  nOtherRow = pOtherRows[nThisRow];
969  else
970  nOtherRow = nThisRow;
971 
972  if (ValidRow(nOtherRow)) // only compare rows that are common to both docs
973  {
974  ScRefCellValue aThisCell(*this, ScAddress(nThisCol, nThisRow, nThisTab));
975  ScRefCellValue aOtherCell(rOtherDoc, ScAddress(nOtherCol, nOtherRow, nOtherTab));
976  if (!aThisCell.equalsWithoutFormat(aOtherCell))
977  {
978  if (!aThisCell.isEmpty() && !aOtherCell.isEmpty())
979  nDif += 3;
980  else
981  nDif += 4; // content <-> empty counts more
982  }
983 
984  if (!aThisCell.isEmpty() || !aOtherCell.isEmpty())
985  ++nUsed;
986  }
987  }
988 
989  if (nUsed > 0)
990  return static_cast<sal_uInt16>((nDif*64)/nUsed); // max.256
991 
992  OSL_ENSURE(!nDif,"Diff without Used");
993  return 0;
994 }
995 
996 void ScDocument::FindOrder( SCCOLROW* pOtherRows, SCCOLROW nThisEndRow, SCCOLROW nOtherEndRow,
997  bool bColumns, ScDocument& rOtherDoc, SCTAB nThisTab, SCTAB nOtherTab,
998  SCCOLROW nEndCol, const SCCOLROW* pTranslate, ScProgress* pProgress, sal_uLong nProAdd )
999 {
1000  // bColumns=true: rows are columns and vice versa
1001 
1002  SCCOLROW nMaxCont; // continue by how much
1003  SCCOLROW nMinGood; // what is a hit (incl.)
1004  if ( bColumns )
1005  {
1006  nMaxCont = SC_DOCCOMP_COLUMNS; // 10 columns
1007  nMinGood = SC_DOCCOMP_MINGOOD;
1008 
1009  //TODO: additional pass with nMinGood = 0 ????
1010 
1011  }
1012  else
1013  {
1014  nMaxCont = SC_DOCCOMP_ROWS; // 100 rows
1015  nMinGood = SC_DOCCOMP_MINGOOD;
1016  }
1017  bool bUseTotal = bColumns && !pTranslate; // only for the 1st pass
1018 
1019  SCCOLROW nOtherRow = 0;
1020  sal_uInt16 nComp;
1021  SCCOLROW nThisRow;
1022  bool bTotal = false; // hold for several nThisRow
1023  SCCOLROW nUnknown = 0;
1024  for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++)
1025  {
1026  SCCOLROW nTempOther = nOtherRow;
1027  bool bFound = false;
1028  sal_uInt16 nBest = SC_DOCCOMP_MAXDIFF;
1029  SCCOLROW nMax = std::min( nOtherEndRow, static_cast<SCCOLROW>(( nTempOther + nMaxCont + nUnknown )) );
1030  for (SCCOLROW i=nTempOther; i<=nMax && nBest>0; i++) // stop at 0
1031  {
1032  if (bColumns)
1033  nComp = ColDifferences( static_cast<SCCOL>(nThisRow), nThisTab, rOtherDoc, static_cast<SCCOL>(i), nOtherTab, nEndCol, pTranslate );
1034  else
1035  nComp = RowDifferences( nThisRow, nThisTab, rOtherDoc, i, nOtherTab, static_cast<SCCOL>(nEndCol), pTranslate );
1036  if ( nComp < nBest && ( nComp <= nMinGood || bTotal ) )
1037  {
1038  nTempOther = i;
1039  nBest = nComp;
1040  bFound = true;
1041  }
1042  if ( nComp < SC_DOCCOMP_MAXDIFF || bFound )
1043  bTotal = false;
1044  else if ( i == nTempOther && bUseTotal )
1045  bTotal = true; // only at the very top
1046  }
1047  if ( bFound )
1048  {
1049  pOtherRows[nThisRow] = nTempOther;
1050  nOtherRow = nTempOther + 1;
1051  nUnknown = 0;
1052  }
1053  else
1054  {
1055  pOtherRows[nThisRow] = SCROW_MAX;
1056  ++nUnknown;
1057  }
1058 
1059  if (pProgress)
1060  pProgress->SetStateOnPercent(nProAdd+static_cast<sal_uLong>(nThisRow));
1061  }
1062 
1063  // fill in blocks that don't match
1064 
1065  SCROW nFillStart = 0;
1066  SCROW nFillPos = 0;
1067  bool bInFill = false;
1068  for (nThisRow = 0; nThisRow <= nThisEndRow+1; nThisRow++)
1069  {
1070  SCROW nThisOther = ( nThisRow <= nThisEndRow ) ? pOtherRows[nThisRow] : (nOtherEndRow+1);
1071  if ( ValidRow(nThisOther) )
1072  {
1073  if ( bInFill )
1074  {
1075  if ( nThisOther > nFillStart ) // is there something to distribute?
1076  {
1077  SCROW nDiff1 = nThisOther - nFillStart;
1078  SCROW nDiff2 = nThisRow - nFillPos;
1079  SCROW nMinDiff = std::min(nDiff1, nDiff2);
1080  for (SCROW i=0; i<nMinDiff; i++)
1081  pOtherRows[nFillPos+i] = nFillStart+i;
1082  }
1083 
1084  bInFill = false;
1085  }
1086  nFillStart = nThisOther + 1;
1087  nFillPos = nThisRow + 1;
1088  }
1089  else
1090  bInFill = true;
1091  }
1092 }
1093 
1095 {
1096  if (!pChangeTrack)
1097  return;
1098 
1099  SCTAB nThisCount = GetTableCount();
1100  SCTAB nOtherCount = rOtherDoc.GetTableCount();
1101  std::unique_ptr<SCTAB[]> pOtherTabs(new SCTAB[nThisCount]);
1102  SCTAB nThisTab;
1103 
1104  // compare tables with identical names
1105  OUString aThisName;
1106  OUString aOtherName;
1107  for (nThisTab=0; nThisTab<nThisCount; nThisTab++)
1108  {
1109  SCTAB nOtherTab = SCTAB_MAX;
1110  if (!IsScenario(nThisTab)) // skip scenarios
1111  {
1112  GetName( nThisTab, aThisName );
1113  for (SCTAB nTemp=0; nTemp<nOtherCount && nOtherTab>MAXTAB; nTemp++)
1114  if (!rOtherDoc.IsScenario(nTemp))
1115  {
1116  rOtherDoc.GetName( nTemp, aOtherName );
1117  if ( aThisName == aOtherName )
1118  nOtherTab = nTemp;
1119  }
1120  }
1121  pOtherTabs[nThisTab] = nOtherTab;
1122  }
1123  // fill in, so that un-named tables don't get lost
1124  SCTAB nFillStart = 0;
1125  SCTAB nFillPos = 0;
1126  bool bInFill = false;
1127  for (nThisTab = 0; nThisTab <= nThisCount; nThisTab++)
1128  {
1129  SCTAB nThisOther = ( nThisTab < nThisCount ) ? pOtherTabs[nThisTab] : nOtherCount;
1130  if ( ValidTab(nThisOther) )
1131  {
1132  if ( bInFill )
1133  {
1134  if ( nThisOther > nFillStart ) // is there something to distribute?
1135  {
1136  SCTAB nDiff1 = nThisOther - nFillStart;
1137  SCTAB nDiff2 = nThisTab - nFillPos;
1138  SCTAB nMinDiff = std::min(nDiff1, nDiff2);
1139  for (SCTAB i=0; i<nMinDiff; i++)
1140  if ( !IsScenario(nFillPos+i) && !rOtherDoc.IsScenario(nFillStart+i) )
1141  pOtherTabs[nFillPos+i] = nFillStart+i;
1142  }
1143 
1144  bInFill = false;
1145  }
1146  nFillStart = nThisOther + 1;
1147  nFillPos = nThisTab + 1;
1148  }
1149  else
1150  bInFill = true;
1151  }
1152 
1153  // compare tables in the original order
1154 
1155  for (nThisTab=0; nThisTab<nThisCount; nThisTab++)
1156  {
1157  SCTAB nOtherTab = pOtherTabs[nThisTab];
1158  if ( ValidTab(nOtherTab) )
1159  {
1160  SCCOL nThisEndCol = 0;
1161  SCROW nThisEndRow = 0;
1162  SCCOL nOtherEndCol = 0;
1163  SCROW nOtherEndRow = 0;
1164  GetCellArea( nThisTab, nThisEndCol, nThisEndRow );
1165  rOtherDoc.GetCellArea( nOtherTab, nOtherEndCol, nOtherEndRow );
1166  SCCOL nEndCol = std::max(nThisEndCol, nOtherEndCol);
1167  SCROW nEndRow = std::max(nThisEndRow, nOtherEndRow);
1168  SCCOL nThisCol;
1169  SCROW nThisRow;
1170  sal_uLong n1,n2; // for AppendDeleteRange
1171 
1172  //TODO: one Progress over all tables ???
1173 
1174  OUString aTabName;
1175  GetName( nThisTab, aTabName );
1176  OUString aTemplate = ScResId(STR_PROGRESS_COMPARING);
1177  sal_Int32 nIndex = 0;
1178  OUString aProText = aTemplate.getToken( 0, '#', nIndex ) +
1179  aTabName +
1180  aTemplate.getToken( 0, '#', nIndex );
1181  ScProgress aProgress( GetDocumentShell(), aProText, 3*nThisEndRow, true ); // 2x FindOrder, 1x here
1182  tools::Long nProgressStart = 2*nThisEndRow; // start for here
1183 
1184  std::unique_ptr<SCCOLROW[]> pTempRows(new SCCOLROW[nThisEndRow+1]);
1185  std::unique_ptr<SCCOLROW[]> pOtherRows(new SCCOLROW[nThisEndRow+1]);
1186  std::unique_ptr<SCCOLROW[]> pOtherCols(new SCCOLROW[nThisEndCol+1]);
1187 
1188  // find inserted/deleted columns/rows:
1189  // Two attempts:
1190  // 1) compare original rows (pTempRows)
1191  // 2) compare original columns (pOtherCols)
1192  // with this column order compare rows (pOtherRows)
1193 
1194  //TODO: compare columns twice with different nMinGood ???
1195 
1196  // 1
1197  FindOrder( pTempRows.get(), nThisEndRow, nOtherEndRow, false,
1198  rOtherDoc, nThisTab, nOtherTab, nEndCol, nullptr, &aProgress, 0 );
1199  // 2
1200  FindOrder( pOtherCols.get(), nThisEndCol, nOtherEndCol, true,
1201  rOtherDoc, nThisTab, nOtherTab, nEndRow, nullptr, nullptr, 0 );
1202  FindOrder( pOtherRows.get(), nThisEndRow, nOtherEndRow, false,
1203  rOtherDoc, nThisTab, nOtherTab, nThisEndCol,
1204  pOtherCols.get(), &aProgress, nThisEndRow );
1205 
1206  sal_uLong nMatch1 = 0; // pTempRows, no columns
1207  for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++)
1208  if (ValidRow(pTempRows[nThisRow]))
1209  nMatch1 += SC_DOCCOMP_MAXDIFF -
1210  RowDifferences( nThisRow, nThisTab, rOtherDoc, pTempRows[nThisRow],
1211  nOtherTab, nEndCol, nullptr );
1212 
1213  sal_uLong nMatch2 = 0; // pOtherRows, pOtherCols
1214  for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++)
1215  if (ValidRow(pOtherRows[nThisRow]))
1216  nMatch2 += SC_DOCCOMP_MAXDIFF -
1217  RowDifferences( nThisRow, nThisTab, rOtherDoc, pOtherRows[nThisRow],
1218  nOtherTab, nThisEndCol, pOtherCols.get() );
1219 
1220  if ( nMatch1 >= nMatch2 ) // without columns ?
1221  {
1222  // reset columns
1223  for (nThisCol = 0; nThisCol<=nThisEndCol; nThisCol++)
1224  pOtherCols[nThisCol] = nThisCol;
1225 
1226  // swap row-arrays (they get both deleted anyway)
1227  pTempRows.swap(pOtherRows);
1228  }
1229  else
1230  {
1231  // remains for pOtherCols, pOtherRows
1232  }
1233 
1234  // Generate Change-Actions
1235  // 1) columns from the right
1236  // 2) rows from below
1237  // 3) single cells in normal order
1238 
1239  // Actions for inserted/deleted columns
1240 
1241  SCCOL nLastOtherCol = static_cast<SCCOL>(nOtherEndCol + 1);
1242  // nThisEndCol ... 0
1243  for ( nThisCol = nThisEndCol+1; nThisCol > 0; )
1244  {
1245  --nThisCol;
1246  SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
1247  if ( ValidCol(nOtherCol) && nOtherCol+1 < nLastOtherCol )
1248  {
1249  // gap -> deleted
1250  ScRange aDelRange( nOtherCol+1, 0, nOtherTab,
1251  nLastOtherCol-1, MaxRow(), nOtherTab );
1252  pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1253  }
1254  if ( nOtherCol > MaxCol() ) // inserted
1255  {
1256  // combine
1257  if ( nThisCol == nThisEndCol || ValidCol(static_cast<SCCOL>(pOtherCols[nThisCol+1])) )
1258  {
1259  SCCOL nFirstNew = nThisCol;
1260  while ( nFirstNew > 0 && pOtherCols[nFirstNew-1] > MaxCol() )
1261  --nFirstNew;
1262  SCCOL nDiff = nThisCol - nFirstNew;
1263  ScRange aRange( nLastOtherCol, 0, nOtherTab,
1264  nLastOtherCol+nDiff, MaxRow(), nOtherTab );
1265  pChangeTrack->AppendInsert( aRange );
1266  }
1267  }
1268  else
1269  nLastOtherCol = nOtherCol;
1270  }
1271  if ( nLastOtherCol > 0 ) // deleted at the very top
1272  {
1273  ScRange aDelRange( 0, 0, nOtherTab,
1274  nLastOtherCol-1, MaxRow(), nOtherTab );
1275  pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1276  }
1277 
1278  // Actions for inserted/deleted rows
1279 
1280  SCROW nLastOtherRow = nOtherEndRow + 1;
1281  // nThisEndRow ... 0
1282  for ( nThisRow = nThisEndRow+1; nThisRow > 0; )
1283  {
1284  --nThisRow;
1285  SCROW nOtherRow = pOtherRows[nThisRow];
1286  if ( ValidRow(nOtherRow) && nOtherRow+1 < nLastOtherRow )
1287  {
1288  // gap -> deleted
1289  ScRange aDelRange( 0, nOtherRow+1, nOtherTab,
1290  MaxCol(), nLastOtherRow-1, nOtherTab );
1291  pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1292  }
1293  if ( nOtherRow > MaxRow() ) // inserted
1294  {
1295  // combine
1296  if ( nThisRow == nThisEndRow || ValidRow(pOtherRows[nThisRow+1]) )
1297  {
1298  SCROW nFirstNew = nThisRow;
1299  while ( nFirstNew > 0 && pOtherRows[nFirstNew-1] > MaxRow() )
1300  --nFirstNew;
1301  SCROW nDiff = nThisRow - nFirstNew;
1302  ScRange aRange( 0, nLastOtherRow, nOtherTab,
1303  MaxCol(), nLastOtherRow+nDiff, nOtherTab );
1304  pChangeTrack->AppendInsert( aRange );
1305  }
1306  }
1307  else
1308  nLastOtherRow = nOtherRow;
1309  }
1310  if ( nLastOtherRow > 0 ) // deleted at the very top
1311  {
1312  ScRange aDelRange( 0, 0, nOtherTab,
1313  MaxCol(), nLastOtherRow-1, nOtherTab );
1314  pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1315  }
1316 
1317  // walk rows to find single cells
1318 
1319  for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++)
1320  {
1321  SCROW nOtherRow = pOtherRows[nThisRow];
1322  for (nThisCol = 0; nThisCol <= nThisEndCol; nThisCol++)
1323  {
1324  SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
1325  ScAddress aThisPos( nThisCol, nThisRow, nThisTab );
1326  ScCellValue aThisCell;
1327  aThisCell.assign(*this, aThisPos);
1328  ScCellValue aOtherCell; // start empty
1329  if ( ValidCol(nOtherCol) && ValidRow(nOtherRow) )
1330  {
1331  ScAddress aOtherPos( nOtherCol, nOtherRow, nOtherTab );
1332  aOtherCell.assign(rOtherDoc, aOtherPos);
1333  }
1334 
1335  if (!aThisCell.equalsWithoutFormat(aOtherCell))
1336  {
1337  ScRange aRange( aThisPos );
1338  ScChangeActionContent* pAction = new ScChangeActionContent( aRange );
1339  pAction->SetOldValue(aOtherCell, &rOtherDoc, this);
1340  pAction->SetNewValue(aThisCell, this);
1341  pChangeTrack->Append( pAction );
1342  }
1343  }
1344  aProgress.SetStateOnPercent(nProgressStart+nThisRow);
1345  }
1346  }
1347  }
1348 }
1349 
1351 {
1353  FormulaGrammar::extractRefConvention( GetGrammar()));
1354  assert(pConv);
1355  return pConv ? pConv->getSpecialSymbol( ScCompiler::Convention::SHEET_SEPARATOR) : '.';
1356 }
1357 
1358 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
If set, cloned formula cells will start to listen to the document.
bool GetNextMarkedCell(SCCOL &rCol, SCROW &rRow, SCTAB nTab, const ScMarkData &rMark)
Definition: documen4.cxx:540
void Set(const ScAddress &rAdr, bool bNewRelCol, bool bNewRelRow, bool bNewRelTab)
Definition: address.hxx:920
void ClearDetectiveOperations()
Definition: documen4.cxx:899
bool equalsWithoutFormat(const ScCellValue &r) const
Definition: cellvalue.cxx:504
sal_Int32 nIndex
OUString getString() const
ScFormulaCell * SetFormulaCell(SCCOL nCol, SCROW nRow, ScFormulaCell *pCell)
Takes ownership of pCell.
Definition: table2.cxx:1703
void DeleteConditionalFormat(sal_uLong nIndex, SCTAB nTab)
Definition: documen4.cxx:880
ScAddress aStart
Definition: address.hxx:499
todo: It should be possible to have MarkArrays for each table, in order to enable "search all" across...
Definition: markdata.hxx:42
#define SC_DOCCOMP_MINGOOD
Definition: documen4.cxx:913
SCROW Row() const
Definition: address.hxx:261
SC_DLLPUBLIC ScRange toAbs(const ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:493
void MarkToMulti()
Definition: markdata.cxx:224
Single reference (one address) into the sheet.
Definition: refdata.hxx:29
std::unique_ptr< ContentProperties > pData
formula::FormulaToken * AddMatrixSingleReference(const ScSingleRefData &rRef)
ScSingleRefOpToken with ocMatRef.
Definition: token.cxx:2209
int SetError()
bool isEmpty() const
Definition: cellvalue.cxx:670
int n1
void CompileColRowNameFormula()
Definition: documen4.cxx:567
sal_uIntPtr sal_uLong
long Long
Character between sheet name and address.
Definition: compiler.hxx:241
sal_uInt16 GetSubformatIndex(double fNumber) const
void SetRelTab(SCTAB nVal)
Definition: refdata.cxx:99
ocOpen
void SetStateOnPercent(sal_uLong nVal)
Definition: progress.hxx:96
const SfxItemSet & GetItemSet() const
SC_DLLPUBLIC sal_Unicode GetSheetSeparator() const
Obtain the sheet separator corresponding to the document's grammar.
Definition: documen4.cxx:1350
virtual sal_Unicode getSpecialSymbol(SpecialSymbolType eSymType) const =0
bool IsExternalRef() const
SC_DLLPUBLIC sal_uLong AddCondFormat(std::unique_ptr< ScConditionalFormat > pNew, SCTAB nTab)
Definition: documen4.cxx:706
ScAddress aEnd
Definition: address.hxx:500
SC_DLLPUBLIC void InsertTableOp(const ScTabOpParam &rParam, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData &rMark)
Definition: documen4.cxx:356
virtual SfxItemSet & GetItemSet()
This is very similar to ScCellValue, except that it references the original value instead of copying ...
Definition: cellvalue.hxx:103
void SetRowRel(bool bVal)
Definition: refdata.hxx:66
ScConditionalFormat * GetFormat(sal_uInt32 nKey)
Definition: conditio.cxx:2069
const SCROW SCROW_MAX
Definition: address.hxx:55
bool Solver(SCCOL nFCol, SCROW nFRow, SCTAB nFTab, SCCOL nVCol, SCROW nVRow, SCTAB nVTab, const OUString &sValStr, double &nX)
(Goal Seek) Find a value of x that is a root of f(x)
Definition: documen4.cxx:70
bool IsCellMarked(SCCOL nCol, SCROW nRow, bool bNoSimple=false) const
Definition: markdata.cxx:285
bool GetNextSpellingCell(SCCOL &nCol, SCROW &nRow, SCTAB nTab, bool bInSel, const ScMarkData &rMark) const
Definition: documen4.cxx:531
SC_DLLPUBLIC const ScValidationData * GetValidationEntry(sal_uLong nIndex) const
Definition: documen4.cxx:872
void InvalidateTableArea()
Definition: documen4.cxx:577
static const Convention * GetRefConvention(formula::FormulaGrammar::AddressConvention eConv)
Definition: compiler.cxx:1983
bool equalsWithoutFormat(const ScRefCellValue &r) const
Definition: cellvalue.cxx:686
Store arbitrary cell value of any kind.
Definition: cellvalue.hxx:35
sal_uInt16 sal_Unicode
SC_DLLPUBLIC ScConditionalFormatList * GetCondFormList(SCTAB nTab) const
Definition: documen4.cxx:858
int n2
sal_Int32 SCCOLROW
a type capable of holding either SCCOL or SCROW
Definition: types.hxx:23
SC_DLLPUBLIC SCTAB GetTableCount() const
Definition: document.cxx:313
#define SC_DOCCOMP_MAXDIFF
Definition: documen4.cxx:912
double GetValue()
bool IsMultiMarked() const
Definition: markdata.hxx:82
SCTAB GetSelectCount() const
Definition: markdata.cxx:195
double GetRoundFractionValue(double fNumber) const
FormulaError GetErrCode()
static bool IsFuzzing()
virtual const ScSingleRefData * GetSingleRef() const
SCTAB Tab() const
Definition: address.hxx:270
void SetTabRel(bool bVal)
Definition: refdata.hxx:68
static AddressConvention extractRefConvention(const Grammar eGrammar)
sal_uInt16 ColDifferences(SCCOL nThisCol, SCTAB nThisTab, ScDocument &rOtherDoc, SCCOL nOtherCol, SCTAB nOtherTab, SCROW nMaxRow, const SCCOLROW *pOtherRows)
Definition: documen4.cxx:955
ScRefAddress aRefRowCell
Definition: paramisc.hxx:51
void ReplaceStyle(const SvxSearchItem &rSearchItem, SCCOL nCol, SCROW nRow, SCTAB nTab, const ScMarkData &rMark)
Definition: documen4.cxx:549
void MarkUsedExternalReferences()
Definition: documen3.cxx:637
sal_uInt16 GetLen() const
const SfxPoolItem & GetItem(sal_uInt16 nWhichP) const
Definition: patattr.hxx:70
void PutInOrder(T &nStart, T &nEnd)
Definition: address.hxx:953
ocSep
SC_DLLPUBLIC bool GetCellArea(SCTAB nTab, SCCOL &rEndCol, SCROW &rEndRow) const
Definition: document.cxx:1009
constexpr OUStringLiteral aData
SC_DLLPUBLIC void InsertMatrixFormula(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData &rMark, const OUString &rFormula, const ScTokenArray *p=nullptr, const formula::FormulaGrammar::Grammar=formula::FormulaGrammar::GRAM_DEFAULT)
Definition: documen4.cxx:258
A pretty assertion that checks that the relevant bits in the are not set on the document at entry an...
Definition: document.hxx:2661
void AddDetectiveOperation(const ScDetOpData &rData)
Definition: documen4.cxx:891
bool ValidCol(SCCOL nCol, SCCOL nMaxCol)
Definition: address.hxx:92
void SetMarkArea(const ScRange &rRange)
Definition: markdata.cxx:97
Parameter for data table aka multiple operations.
Definition: paramisc.hxx:45
static const OUString & GetNativeSymbol(OpCode eOp)
sal_uInt16 RowDifferences(SCROW nThisRow, SCTAB nThisTab, ScDocument &rOtherDoc, SCROW nOtherRow, SCTAB nOtherTab, SCCOL nMaxCol, const SCCOLROW *pOtherCols)
Definition: documen4.cxx:917
virtual const ScComplexRefData * GetDoubleRef() const
SfxItemState GetItemState(sal_uInt16 nWhich, bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
int i
ocTableOp
sal_Int16 SCCOL
Definition: types.hxx:21
SvNumberFormatter * GetFormatTable() const
const SvxPageUsage aArr[]
bool Interpret(SCROW nStartOffset=-1, SCROW nEndOffset=-1)
ScRefAddress aRefFormulaCell
Definition: paramisc.hxx:49
#define SC_DOCCOMP_ROWS
Definition: documen4.cxx:915
bool EqualEntries(const ScValidationData &r) const
Definition: validat.cxx:134
svExternalDoubleRef
SvNumFormatType
svExternalSingleRef
constexpr double deg2rad(double v)
void SetMatColsRows(SCCOL nCols, SCROW nRows)
iterator end()
Definition: markdata.cxx:953
#define SV_COUNTRY_LANGUAGE_OFFSET
bool empty() const
sal_Int32 GetMaxNumberStringLen(sal_uInt16 &nPrecision, SCTAB nTab, SCCOL nCol, SCROW nRowStart, SCROW nRowEnd) const
Maximum string length of numerical cells of a column, e.g.
Definition: documen4.cxx:598
const SCTAB MAXTAB
Definition: address.hxx:70
std::set< SCTAB >::const_iterator const_iterator
Definition: markdata.hxx:158
SC_DLLPUBLIC const SfxItemSet * GetCondResult(SCCOL nCol, SCROW nRow, SCTAB nTab, ScRefCellValue *pCell=nullptr) const
Definition: documen4.cxx:789
void CompileDBFormula()
Definition: documen4.cxx:557
sal_Int32 GetMaxStringLen(SCTAB nTab, SCCOL nCol, SCROW nRowStart, SCROW nRowEnd, rtl_TextEncoding eCharSet) const
Maximum string length of a column, e.g.
Definition: documen4.cxx:589
virtual sal_uInt16 GetIndex() const
SCCOL Col() const
Definition: address.hxx:266
double getResult()
Check getError() after (!) obtaining the result.
Definition: subtotal.cxx:129
XPropertyListType t
const OUString & GetCellStyle(ScRefCellValue &rCell, const ScAddress &rPos) const
Definition: conditio.cxx:1789
void SetOldValue(const ScCellValue &rCell, const ScDocument *pFromDoc, ScDocument *pToDoc, sal_uLong nFormat)
Definition: chgtrack.cxx:1332
static const sal_uInt16 UNLIMITED_PRECISION
SC_DLLPUBLIC double RoundValueAsShown(double fVal, sal_uInt32 nFormat, const ScInterpreterContext *pContext=nullptr) const
Definition: documen4.cxx:634
void SetDetOpList(std::unique_ptr< ScDetOpList > pNew)
Definition: documen4.cxx:904
void SetNewValue(const ScCellValue &rCell, ScDocument *pDoc)
Definition: chgtrack.cxx:1344
bool IsEmpty() const
Definition: validat.cxx:128
SC_DLLPUBLIC sal_uLong AddValidationEntry(const ScValidationData &rNew)
Definition: documen4.cxx:717
iterator begin()
Definition: markdata.cxx:948
sal_Int32 SCROW
Definition: types.hxx:17
To calculate a single subtotal function.
Definition: subtotal.hxx:60
const ScAddress & GetAddress() const
Definition: address.hxx:887
OUString ScResId(std::string_view aId)
Definition: scdll.cxx:89
SC_DLLPUBLIC void CompareDocument(ScDocument &rOtherDoc)
Definition: documen4.cxx:1094
sal_uInt16 GetFormatIntegerDigits(sal_uInt16 nIx=0) const
sal_uInt16 GetFormatPrecision(sal_uInt16 nIx=0) const
SC_DLLPUBLIC bool IsScenario(SCTAB nTab) const
Definition: documen3.cxx:432
const SfxPoolItem & Get(sal_uInt16 nWhich, bool bSrchInParent=true) const
const SvNumberformat * GetEntry(sal_uInt32 nKey) const
virtual const svl::SharedString & GetString() const
CellType
Definition: global.hxx:280
void assign(ScDocument &rDoc, const ScAddress &rPos)
Take cell value from specified position in specified document.
Definition: cellvalue.cxx:582
SCROW Row() const
Definition: address.hxx:896
void assign(const ScDocument &rDoc, const ScAddress &rPos)
Take cell value from specified position in specified document.
Definition: cellvalue.cxx:330
void InitFlags()
No default ctor, because used in ScRawToken union, set InitFlags!
Definition: refdata.hxx:54
ScRefAddress aRefColCell
Definition: paramisc.hxx:52
#define SC_DOCCOMP_COLUMNS
Definition: documen4.cxx:914
void 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)
Definition: documen4.cxx:996
SCCOL Col() const
Definition: address.hxx:892
SvNumFormatType GetMaskedType() const
constexpr TypedWhichId< ScCondFormatItem > ATTR_CONDITIONAL(154)
QPRO_FUNC_TYPE nType
Definition: qproform.cxx:400
bool GetSelectionFunction(ScSubTotalFunc eFunc, const ScAddress &rCursor, const ScMarkData &rMark, double &rResult)
Definition: documen4.cxx:609
void SetAddress(const ScSheetLimits &rLimits, const ScAddress &rAddr, const ScAddress &rPos)
Definition: refdata.cxx:213
Complex reference (a range) into the sheet.
Definition: refdata.hxx:122
bool ValidColRow(SCCOL nCol, SCROW nRow, SCCOL nMaxCol, SCROW nMaxRow)
Definition: address.hxx:114
bool ValidRow(SCROW nRow, SCROW nMaxRow)
Definition: address.hxx:98
bool getError() const
Definition: subtotal.hxx:69
#define SAL_WARN(area, stream)
SC_DLLPUBLIC ScConditionalFormat * GetCondFormat(SCCOL nCol, SCROW nRow, SCTAB nTab) const
Definition: documen4.cxx:835
OUString GetRefString(const ScDocument &rDocument, SCTAB nActTab, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1) const
Definition: address.cxx:2520
ScRefAddress aRefFormulaEnd
Definition: paramisc.hxx:50
sal_uInt32 GetKey() const
Definition: validat.hxx:160
ScSubTotalFunc
Definition: global.hxx:844
bool HasDetectiveOperations() const
Definition: documen4.cxx:886
const SCTAB SCTAB_MAX
Definition: address.hxx:57
StackVar GetType() const
void SetColRel(bool bVal)
Definition: refdata.hxx:64
ScTokenArray * GetCode()
Definition: rangenam.hxx:119
SC_DLLPUBLIC bool GetName(SCTAB nTab, OUString &rName) const
Definition: document.cxx:212
ScValidationData * Clone() const
Definition: validat.hxx:97
ocClose
bool ValidTab(SCTAB nTab)
Definition: address.hxx:104
bool setCacheTableReferenced(sal_uInt16 nFileId, const OUString &rTabName, size_t nSheets)
Set a table as referenced, used only during store-to-file.
sal_uInt16 GetThousandDivisorPrecision(sal_uInt16 nIx=0) const
std::unique_ptr< ScTokenArray > Clone() const
Definition: token.cxx:1921
void SetCondFormList(ScConditionalFormatList *pList, SCTAB nTab)
Definition: documen4.cxx:866
sal_Int16 SCTAB
Definition: types.hxx:22
const SfxPoolItem * GetEffItem(SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich) const
Definition: documen4.cxx:749