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