1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sc.hxx"
26
27
28
29 // INCLUDE ---------------------------------------------------------------
30
31 #include <svl/intitem.hxx>
32 #include <svl/zforlist.hxx>
33 #include <vcl/sound.hxx>
34 #include <formula/token.hxx>
35
36 #include "document.hxx"
37 #include "table.hxx"
38 #include "globstr.hrc"
39 #include "subtotal.hxx"
40 #include "docoptio.hxx"
41 #include "interpre.hxx"
42 #include "markdata.hxx"
43 #include "validat.hxx"
44 #include "scitems.hxx"
45 #include "stlpool.hxx"
46 #include "poolhelp.hxx"
47 #include "detdata.hxx"
48 #include "patattr.hxx"
49 #include "chgtrack.hxx"
50 #include "progress.hxx"
51 #include "paramisc.hxx"
52 #include "compiler.hxx"
53 #include "externalrefmgr.hxx"
54
55 using namespace formula;
56
57 // -----------------------------------------------------------------------
58
59 // Nach der Regula Falsi Methode
Solver(SCCOL nFCol,SCROW nFRow,SCTAB nFTab,SCCOL nVCol,SCROW nVRow,SCTAB nVTab,const String & sValStr,double & nX)60 sal_Bool ScDocument::Solver(SCCOL nFCol, SCROW nFRow, SCTAB nFTab,
61 SCCOL nVCol, SCROW nVRow, SCTAB nVTab,
62 const String& sValStr, double& nX)
63 {
64 sal_Bool bRet = sal_False;
65 nX = 0.0;
66 if (ValidColRow(nFCol, nFRow) && ValidColRow(nVCol, nVRow) &&
67 VALIDTAB(nFTab) && VALIDTAB(nVTab) && pTab[nFTab] && pTab[nVTab])
68 {
69 CellType eFType, eVType;
70 GetCellType(nFCol, nFRow, nFTab, eFType);
71 GetCellType(nVCol, nVRow, nVTab, eVType);
72 // CELLTYPE_NOTE: no value, but referenced by formula
73 // #i108005# convert target value to number using default format,
74 // as previously done in ScInterpreter::GetDouble
75 double nTargetVal = 0.0;
76 sal_uInt32 nFIndex = 0;
77 if (eFType == CELLTYPE_FORMULA && (eVType == CELLTYPE_VALUE || eVType == CELLTYPE_NOTE) &&
78 GetFormatTable()->IsNumberFormat(sValStr, nFIndex, nTargetVal))
79 {
80 ScSingleRefData aRefData;
81 aRefData.InitFlags();
82 aRefData.nCol = nVCol;
83 aRefData.nRow = nVRow;
84 aRefData.nTab = nVTab;
85
86 ScTokenArray aArr;
87 aArr.AddOpCode( ocBackSolver );
88 aArr.AddOpCode( ocOpen );
89 aArr.AddSingleReference( aRefData );
90 aArr.AddOpCode( ocSep );
91
92 aRefData.nCol = nFCol;
93 aRefData.nRow = nFRow;
94 aRefData.nTab = nFTab;
95
96 aArr.AddSingleReference( aRefData );
97 aArr.AddOpCode( ocSep );
98 aArr.AddDouble( nTargetVal );
99 aArr.AddOpCode( ocClose );
100 aArr.AddOpCode( ocStop );
101
102 ScFormulaCell* pCell = new ScFormulaCell( this, ScAddress(), &aArr );
103
104 if (pCell)
105 {
106 // FIXME FIXME FIXME this might need to be reworked now that we have formula::FormulaErrorToken and ScFormulaResult, double check !!!
107 DBG_ERRORFILE("ScDocument::Solver: -> ScFormulaCell::GetValueAlways might need reimplementation");
108 pCell->Interpret();
109 sal_uInt16 nErrCode = pCell->GetErrCode();
110 nX = pCell->GetValueAlways();
111 if (nErrCode == 0) // kein fehler beim Rechnen
112 bRet = sal_True;
113 delete pCell;
114 }
115 }
116 }
117 return bRet;
118 }
119
InsertMatrixFormula(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,const ScMarkData & rMark,const String & rFormula,const ScTokenArray * pArr,const formula::FormulaGrammar::Grammar eGram)120 void ScDocument::InsertMatrixFormula(SCCOL nCol1, SCROW nRow1,
121 SCCOL nCol2, SCROW nRow2,
122 const ScMarkData& rMark,
123 const String& rFormula,
124 const ScTokenArray* pArr,
125 const formula::FormulaGrammar::Grammar eGram )
126 {
127 PutInOrder(nCol1, nCol2);
128 PutInOrder(nRow1, nRow2);
129 SCTAB i, nTab1;
130 SCCOL j;
131 SCROW k;
132 i = 0;
133 sal_Bool bStop = sal_False;
134 while (i <= MAXTAB && !bStop) // erste markierte Tabelle finden
135 {
136 if (pTab[i] && rMark.GetTableSelect(i))
137 bStop = sal_True;
138 else
139 i++;
140 }
141 nTab1 = i;
142 if (i == MAXTAB + 1)
143 {
144 Sound::Beep();
145 DBG_ERROR("ScDocument::InsertMatrixFormula Keine Tabelle markiert");
146 return;
147 }
148
149 ScFormulaCell* pCell;
150 ScAddress aPos( nCol1, nRow1, nTab1 );
151 if (pArr)
152 pCell = new ScFormulaCell( this, aPos, pArr, eGram, MM_FORMULA );
153 else
154 pCell = new ScFormulaCell( this, aPos, rFormula, eGram, MM_FORMULA );
155 pCell->SetMatColsRows( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1 );
156 for (i = 0; i <= MAXTAB; i++)
157 {
158 if (pTab[i] && rMark.GetTableSelect(i))
159 {
160 if (i == nTab1)
161 pTab[i]->PutCell(nCol1, nRow1, pCell);
162 else
163 pTab[i]->PutCell(nCol1, nRow1, pCell->CloneWithoutNote(*this, ScAddress( nCol1, nRow1, i), SC_CLONECELL_STARTLISTENING));
164 }
165 }
166
167 ScSingleRefData aRefData;
168 aRefData.InitFlags();
169 aRefData.nCol = nCol1;
170 aRefData.nRow = nRow1;
171 aRefData.nTab = nTab1;
172 aRefData.SetColRel( sal_True );
173 aRefData.SetRowRel( sal_True );
174 aRefData.SetTabRel( sal_True );
175 aRefData.CalcRelFromAbs( ScAddress( nCol1, nRow1, nTab1 ) );
176
177 ScTokenArray aArr;
178 ScToken* t = static_cast<ScToken*>(aArr.AddMatrixSingleReference( aRefData));
179
180 for (i = 0; i <= MAXTAB; i++)
181 {
182 if (pTab[i] && rMark.GetTableSelect(i))
183 {
184 pTab[i]->DoColResize( nCol1, nCol2, static_cast<SCSIZE>(nRow2 - nRow1 + 1) );
185 if (i != nTab1)
186 {
187 aRefData.nTab = i;
188 aRefData.nRelTab = i - nTab1;
189 t->GetSingleRef() = aRefData;
190 }
191 for (j = nCol1; j <= nCol2; j++)
192 {
193 for (k = nRow1; k <= nRow2; k++)
194 {
195 if (j != nCol1 || k != nRow1) // nicht in der ersten Zelle
196 {
197 // Array muss geklont werden, damit jede
198 // Zelle ein eigenes Array erhaelt!
199 aPos = ScAddress( j, k, i );
200 t->CalcRelFromAbs( aPos );
201 pCell = new ScFormulaCell( this, aPos, aArr.Clone(), eGram, MM_REFERENCE );
202 pTab[i]->PutCell(j, k, (ScBaseCell*) pCell);
203 }
204 }
205 }
206 }
207 }
208 }
209
InsertTableOp(const ScTabOpParam & rParam,SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,const ScMarkData & rMark)210 void ScDocument::InsertTableOp(const ScTabOpParam& rParam, // Mehrfachoperation
211 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
212 const ScMarkData& rMark)
213 {
214 PutInOrder(nCol1, nCol2);
215 PutInOrder(nRow1, nRow2);
216 SCTAB i, nTab1;
217 SCCOL j;
218 SCROW k;
219 i = 0;
220 sal_Bool bStop = sal_False;
221 while (i <= MAXTAB && !bStop) // erste markierte Tabelle finden
222 {
223 if (pTab[i] && rMark.GetTableSelect(i))
224 bStop = sal_True;
225 else
226 i++;
227 }
228 nTab1 = i;
229 if (i == MAXTAB + 1)
230 {
231 Sound::Beep();
232 DBG_ERROR("ScDocument::InsertTableOp: Keine Tabelle markiert");
233 return;
234 }
235
236 ScRefAddress aRef;
237 String aForString = '=';
238 aForString += ScCompiler::GetNativeSymbol(ocTableOp);
239 aForString += ScCompiler::GetNativeSymbol( ocOpen);
240
241 const String& sSep = ScCompiler::GetNativeSymbol( ocSep);
242 if (rParam.nMode == 0) // nur Spalte
243 {
244 aRef.Set( rParam.aRefFormulaCell.GetAddress(), sal_True, sal_False, sal_False );
245 aForString += aRef.GetRefString(this, nTab1);
246 aForString += sSep;
247 aForString += rParam.aRefColCell.GetRefString(this, nTab1);
248 aForString += sSep;
249 aRef.Set( nCol1, nRow1, nTab1, sal_False, sal_True, sal_True );
250 aForString += aRef.GetRefString(this, nTab1);
251 nCol1++;
252 nCol2 = Min( nCol2, (SCCOL)(rParam.aRefFormulaEnd.Col() -
253 rParam.aRefFormulaCell.Col() + nCol1 + 1));
254 }
255 else if (rParam.nMode == 1) // nur zeilenweise
256 {
257 aRef.Set( rParam.aRefFormulaCell.GetAddress(), sal_False, sal_True, sal_False );
258 aForString += aRef.GetRefString(this, nTab1);
259 aForString += sSep;
260 aForString += rParam.aRefRowCell.GetRefString(this, nTab1);
261 aForString += sSep;
262 aRef.Set( nCol1, nRow1, nTab1, sal_True, sal_False, sal_True );
263 aForString += aRef.GetRefString(this, nTab1);
264 nRow1++;
265 nRow2 = Min( nRow2, (SCROW)(rParam.aRefFormulaEnd.Row() -
266 rParam.aRefFormulaCell.Row() + nRow1 + 1));
267 }
268 else // beides
269 {
270 aForString += rParam.aRefFormulaCell.GetRefString(this, nTab1);
271 aForString += sSep;
272 aForString += rParam.aRefColCell.GetRefString(this, nTab1);
273 aForString += sSep;
274 aRef.Set( nCol1, nRow1 + 1, nTab1, sal_False, sal_True, sal_True );
275 aForString += aRef.GetRefString(this, nTab1);
276 aForString += sSep;
277 aForString += rParam.aRefRowCell.GetRefString(this, nTab1);
278 aForString += sSep;
279 aRef.Set( nCol1 + 1, nRow1, nTab1, sal_True, sal_False, sal_True );
280 aForString += aRef.GetRefString(this, nTab1);
281 nCol1++; nRow1++;
282 }
283 aForString += ScCompiler::GetNativeSymbol( ocClose);
284
285 ScFormulaCell aRefCell( this, ScAddress( nCol1, nRow1, nTab1 ), aForString,
286 formula::FormulaGrammar::GRAM_NATIVE, MM_NONE );
287 for( j = nCol1; j <= nCol2; j++ )
288 for( k = nRow1; k <= nRow2; k++ )
289 for (i = 0; i <= MAXTAB; i++)
290 if( pTab[i] && rMark.GetTableSelect(i) )
291 pTab[i]->PutCell( j, k, aRefCell.CloneWithoutNote( *this, ScAddress( j, k, i ), SC_CLONECELL_STARTLISTENING ) );
292 }
293
MarkUsedExternalReferences(ScTokenArray & rArr)294 bool ScDocument::MarkUsedExternalReferences( ScTokenArray & rArr )
295 {
296 bool bAllMarked = false;
297 if (rArr.GetLen())
298 {
299 ScExternalRefManager* pRefMgr = NULL;
300 rArr.Reset();
301 ScToken* t;
302 while (!bAllMarked && (t = static_cast<ScToken*>(rArr.GetNextReferenceOrName())) != NULL)
303 {
304 if (t->GetOpCode() == ocExternalRef)
305 {
306 if (!pRefMgr)
307 pRefMgr = GetExternalRefManager();
308 switch (t->GetType())
309 {
310 case svExternalSingleRef:
311 bAllMarked = pRefMgr->setCacheTableReferenced(
312 t->GetIndex(), t->GetString(), 1);
313 break;
314 case svExternalDoubleRef:
315 {
316 const ScComplexRefData& rRef = t->GetDoubleRef();
317 size_t nSheets = rRef.Ref2.nTab - rRef.Ref1.nTab + 1;
318 bAllMarked = pRefMgr->setCacheTableReferenced(
319 t->GetIndex(), t->GetString(), nSheets);
320 }
321 break;
322 case svExternalName:
323 /* TODO: external names aren't supported yet, but would
324 * have to be marked as well, if so. Mechanism would be
325 * different. */
326 DBG_ERRORFILE("ScDocument::MarkUsedExternalReferences: implement the svExternalName case!");
327 break;
328 default: break;
329 }
330 }
331 }
332 }
333 return bAllMarked;
334 }
335
GetNextSpellingCell(SCCOL & nCol,SCROW & nRow,SCTAB nTab,sal_Bool bInSel,const ScMarkData & rMark) const336 sal_Bool ScDocument::GetNextSpellingCell(SCCOL& nCol, SCROW& nRow, SCTAB nTab,
337 sal_Bool bInSel, const ScMarkData& rMark) const
338 {
339 if (ValidTab(nTab) && pTab[nTab])
340 return pTab[nTab]->GetNextSpellingCell( nCol, nRow, bInSel, rMark );
341 else
342 return sal_False;
343 }
344
GetNextMarkedCell(SCCOL & rCol,SCROW & rRow,SCTAB nTab,const ScMarkData & rMark)345 sal_Bool ScDocument::GetNextMarkedCell( SCCOL& rCol, SCROW& rRow, SCTAB nTab,
346 const ScMarkData& rMark )
347 {
348 if (ValidTab(nTab) && pTab[nTab])
349 return pTab[nTab]->GetNextMarkedCell( rCol, rRow, rMark );
350 else
351 return sal_False;
352 }
353
ReplaceStyle(const SvxSearchItem & rSearchItem,SCCOL nCol,SCROW nRow,SCTAB nTab,ScMarkData & rMark,sal_Bool bIsUndoP)354 sal_Bool ScDocument::ReplaceStyle(const SvxSearchItem& rSearchItem,
355 SCCOL nCol, SCROW nRow, SCTAB nTab,
356 ScMarkData& rMark,
357 sal_Bool bIsUndoP)
358 {
359 if (pTab[nTab])
360 return pTab[nTab]->ReplaceStyle(rSearchItem, nCol, nRow, rMark, bIsUndoP);
361 else
362 return sal_False;
363 }
364
CompileDBFormula()365 void ScDocument::CompileDBFormula()
366 {
367 for (SCTAB i=0; i<=MAXTAB; i++)
368 {
369 if (pTab[i]) pTab[i]->CompileDBFormula();
370 }
371 }
372
CompileDBFormula(sal_Bool bCreateFormulaString)373 void ScDocument::CompileDBFormula( sal_Bool bCreateFormulaString )
374 {
375 for (SCTAB i=0; i<=MAXTAB; i++)
376 {
377 if (pTab[i]) pTab[i]->CompileDBFormula( bCreateFormulaString );
378 }
379 }
380
CompileNameFormula(sal_Bool bCreateFormulaString)381 void ScDocument::CompileNameFormula( sal_Bool bCreateFormulaString )
382 {
383 if ( pCondFormList )
384 pCondFormList->CompileAll(); // nach ScNameDlg noetig
385
386 for (SCTAB i=0; i<=MAXTAB; i++)
387 {
388 if (pTab[i]) pTab[i]->CompileNameFormula( bCreateFormulaString );
389 }
390 }
391
CompileColRowNameFormula()392 void ScDocument::CompileColRowNameFormula()
393 {
394 for (SCTAB i=0; i<=MAXTAB; i++)
395 {
396 if (pTab[i]) pTab[i]->CompileColRowNameFormula();
397 }
398 }
399
DoColResize(SCTAB nTab,SCCOL nCol1,SCCOL nCol2,SCSIZE nAdd)400 void ScDocument::DoColResize( SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCSIZE nAdd )
401 {
402 if (ValidTab(nTab) && pTab[nTab])
403 pTab[nTab]->DoColResize( nCol1, nCol2, nAdd );
404 else
405 {
406 DBG_ERROR("DoColResize: falsche Tabelle");
407 }
408 }
409
InvalidateTableArea()410 void ScDocument::InvalidateTableArea()
411 {
412 for (SCTAB nTab=0; nTab<=MAXTAB && pTab[nTab]; nTab++)
413 {
414 pTab[nTab]->InvalidateTableArea();
415 if ( pTab[nTab]->IsScenario() )
416 pTab[nTab]->InvalidateScenarioRanges();
417 }
418 }
419
GetLastAttrCell(SCTAB nTab,SCCOL & rEndCol,SCROW & rEndRow) const420 void ScDocument::GetLastAttrCell( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const
421 {
422 if ( ValidTab( nTab ) && pTab[nTab] )
423 {
424 pTab[nTab]->GetLastAttrCell( rEndCol, rEndRow );
425 }
426 }
427
GetMaxStringLen(SCTAB nTab,SCCOL nCol,SCROW nRowStart,SCROW nRowEnd,CharSet eCharSet) const428 sal_Int32 ScDocument::GetMaxStringLen( SCTAB nTab, SCCOL nCol,
429 SCROW nRowStart, SCROW nRowEnd, CharSet eCharSet ) const
430 {
431 if (ValidTab(nTab) && pTab[nTab])
432 return pTab[nTab]->GetMaxStringLen( nCol, nRowStart, nRowEnd, eCharSet );
433 else
434 return 0;
435 }
436
GetMaxNumberStringLen(sal_uInt16 & nPrecision,SCTAB nTab,SCCOL nCol,SCROW nRowStart,SCROW nRowEnd) const437 xub_StrLen ScDocument::GetMaxNumberStringLen( sal_uInt16& nPrecision, SCTAB nTab,
438 SCCOL nCol,
439 SCROW nRowStart, SCROW nRowEnd ) const
440 {
441 if (ValidTab(nTab) && pTab[nTab])
442 return pTab[nTab]->GetMaxNumberStringLen( nPrecision, nCol,
443 nRowStart, nRowEnd );
444 else
445 return 0;
446 }
447
GetSelectionFunction(ScSubTotalFunc eFunc,const ScAddress & rCursor,const ScMarkData & rMark,double & rResult)448 sal_Bool ScDocument::GetSelectionFunction( ScSubTotalFunc eFunc,
449 const ScAddress& rCursor, const ScMarkData& rMark,
450 double& rResult )
451 {
452 ScFunctionData aData(eFunc);
453
454 ScRange aSingle( rCursor );
455 if ( rMark.IsMarked() )
456 rMark.GetMarkArea(aSingle);
457
458 SCCOL nStartCol = aSingle.aStart.Col();
459 SCROW nStartRow = aSingle.aStart.Row();
460 SCCOL nEndCol = aSingle.aEnd.Col();
461 SCROW nEndRow = aSingle.aEnd.Row();
462
463 for (SCTAB nTab=0; nTab<=MAXTAB && !aData.bError; nTab++)
464 if (pTab[nTab] && rMark.GetTableSelect(nTab))
465 pTab[nTab]->UpdateSelectionFunction( aData,
466 nStartCol, nStartRow, nEndCol, nEndRow, rMark );
467
468 //! rMark an UpdateSelectionFunction uebergeben !!!!!
469
470 if (!aData.bError)
471 switch (eFunc)
472 {
473 case SUBTOTAL_FUNC_SUM:
474 rResult = aData.nVal;
475 break;
476 case SUBTOTAL_FUNC_CNT:
477 case SUBTOTAL_FUNC_CNT2:
478 rResult = aData.nCount;
479 break;
480 case SUBTOTAL_FUNC_AVE:
481 if (aData.nCount)
482 rResult = aData.nVal / (double) aData.nCount;
483 else
484 aData.bError = sal_True;
485 break;
486 case SUBTOTAL_FUNC_MAX:
487 case SUBTOTAL_FUNC_MIN:
488 if (aData.nCount)
489 rResult = aData.nVal;
490 else
491 aData.bError = sal_True;
492 break;
493 default:
494 {
495 // added to avoid warnings
496 }
497 }
498
499 if (aData.bError)
500 rResult = 0.0;
501
502 return !aData.bError;
503 }
504
RoundValueAsShown(double fVal,sal_uLong nFormat)505 double ScDocument::RoundValueAsShown( double fVal, sal_uLong nFormat )
506 {
507 short nType;
508 if ( (nType = GetFormatTable()->GetType( nFormat )) != NUMBERFORMAT_DATE
509 && nType != NUMBERFORMAT_TIME && nType != NUMBERFORMAT_DATETIME )
510 {
511 short nPrecision;
512 if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
513 {
514 nPrecision = (short)GetFormatTable()->GetFormatPrecision( nFormat );
515 switch ( nType )
516 {
517 case NUMBERFORMAT_PERCENT: // 0,41% == 0,0041
518 nPrecision += 2;
519 break;
520 case NUMBERFORMAT_SCIENTIFIC: // 1,23e-3 == 0,00123
521 {
522 if ( fVal > 0.0 )
523 nPrecision = sal::static_int_cast<short>( nPrecision - (short)floor( log10( fVal ) ) );
524 else if ( fVal < 0.0 )
525 nPrecision = sal::static_int_cast<short>( nPrecision - (short)floor( log10( -fVal ) ) );
526 break;
527 }
528 }
529 }
530 else
531 {
532 nPrecision = (short)GetDocOptions().GetStdPrecision();
533 // #i115512# no rounding for automatic decimals
534 if (nPrecision == static_cast<short>(SvNumberFormatter::UNLIMITED_PRECISION))
535 return fVal;
536 }
537 double fRound = ::rtl::math::round( fVal, nPrecision );
538 if ( ::rtl::math::approxEqual( fVal, fRound ) )
539 return fVal; // durch Rundung hoechstens Fehler
540 else
541 return fRound;
542 }
543 else
544 return fVal;
545 }
546
547 //
548 // bedingte Formate und Gueltigkeitsbereiche
549 //
550
AddCondFormat(const ScConditionalFormat & rNew)551 sal_uLong ScDocument::AddCondFormat( const ScConditionalFormat& rNew )
552 {
553 if (rNew.IsEmpty())
554 return 0; // leer ist immer 0
555
556 if (!pCondFormList)
557 pCondFormList = new ScConditionalFormatList;
558
559 sal_uLong nMax = 0;
560 sal_uInt16 nCount = pCondFormList->Count();
561 for (sal_uInt16 i=0; i<nCount; i++)
562 {
563 const ScConditionalFormat* pForm = (*pCondFormList)[i];
564 sal_uLong nKey = pForm->GetKey();
565 if ( pForm->EqualEntries( rNew ) )
566 return nKey;
567 if ( nKey > nMax )
568 nMax = nKey;
569 }
570
571 // Der Aufruf kann aus ScPatternAttr::PutInPool kommen, darum Clone (echte Kopie)
572
573 sal_uLong nNewKey = nMax + 1;
574 ScConditionalFormat* pInsert = rNew.Clone(this);
575 pInsert->SetKey( nNewKey );
576 pCondFormList->InsertNew( pInsert );
577 return nNewKey;
578 }
579
AddValidationEntry(const ScValidationData & rNew)580 sal_uLong ScDocument::AddValidationEntry( const ScValidationData& rNew )
581 {
582 if (rNew.IsEmpty())
583 return 0; // leer ist immer 0
584
585 if (!pValidationList)
586 pValidationList = new ScValidationDataList;
587
588 sal_uLong nMax = 0;
589 sal_uInt16 nCount = pValidationList->Count();
590 for (sal_uInt16 i=0; i<nCount; i++)
591 {
592 const ScValidationData* pData = (*pValidationList)[i];
593 sal_uLong nKey = pData->GetKey();
594 if ( pData->EqualEntries( rNew ) )
595 return nKey;
596 if ( nKey > nMax )
597 nMax = nKey;
598 }
599
600 // Der Aufruf kann aus ScPatternAttr::PutInPool kommen, darum Clone (echte Kopie)
601
602 sal_uLong nNewKey = nMax + 1;
603 ScValidationData* pInsert = rNew.Clone(this);
604 pInsert->SetKey( nNewKey );
605 pValidationList->InsertNew( pInsert );
606 return nNewKey;
607 }
608
GetEffItem(SCCOL nCol,SCROW nRow,SCTAB nTab,sal_uInt16 nWhich) const609 const SfxPoolItem* ScDocument::GetEffItem(
610 SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich ) const
611 {
612 const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
613 if ( pPattern )
614 {
615 const SfxItemSet& rSet = pPattern->GetItemSet();
616 const SfxPoolItem* pItem;
617 if ( rSet.GetItemState( ATTR_CONDITIONAL, sal_True, &pItem ) == SFX_ITEM_SET )
618 {
619 sal_uLong nIndex = ((const SfxUInt32Item*)pItem)->GetValue();
620 if (nIndex && pCondFormList)
621 {
622 const ScConditionalFormat* pForm = pCondFormList->GetFormat( nIndex );
623 if ( pForm )
624 {
625 ScBaseCell* pCell = ((ScDocument*)this)->GetCell(ScAddress(nCol,nRow,nTab));
626 String aStyle = pForm->GetCellStyle( pCell, ScAddress(nCol, nRow, nTab) );
627 if (aStyle.Len())
628 {
629 SfxStyleSheetBase* pStyleSheet = xPoolHelper->GetStylePool()->Find(
630 aStyle, SFX_STYLE_FAMILY_PARA );
631 if ( pStyleSheet && pStyleSheet->GetItemSet().GetItemState(
632 nWhich, sal_True, &pItem ) == SFX_ITEM_SET )
633 return pItem;
634 }
635 }
636 }
637 }
638 return &rSet.Get( nWhich );
639 }
640 DBG_ERROR("kein Pattern");
641 return NULL;
642 }
643
GetCondResult(SCCOL nCol,SCROW nRow,SCTAB nTab) const644 const SfxItemSet* ScDocument::GetCondResult( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
645 {
646 const ScConditionalFormat* pForm = GetCondFormat( nCol, nRow, nTab );
647 if ( pForm )
648 {
649 ScBaseCell* pCell = ((ScDocument*)this)->GetCell(ScAddress(nCol,nRow,nTab));
650 String aStyle = pForm->GetCellStyle( pCell, ScAddress(nCol, nRow, nTab) );
651 if (aStyle.Len())
652 {
653 SfxStyleSheetBase* pStyleSheet = xPoolHelper->GetStylePool()->Find( aStyle, SFX_STYLE_FAMILY_PARA );
654 if ( pStyleSheet )
655 return &pStyleSheet->GetItemSet();
656 // if style is not there, treat like no condition
657 }
658 }
659 return NULL;
660 }
661
GetCondFormat(SCCOL nCol,SCROW nRow,SCTAB nTab) const662 const ScConditionalFormat* ScDocument::GetCondFormat(
663 SCCOL nCol, SCROW nRow, SCTAB nTab ) const
664 {
665 sal_uLong nIndex = ((const SfxUInt32Item*)GetAttr(nCol,nRow,nTab,ATTR_CONDITIONAL))->GetValue();
666 if (nIndex)
667 {
668 if (pCondFormList)
669 return pCondFormList->GetFormat( nIndex );
670 else
671 {
672 DBG_ERROR("pCondFormList ist 0");
673 }
674 }
675
676 return NULL;
677 }
678
GetValidationEntry(sal_uLong nIndex) const679 const ScValidationData* ScDocument::GetValidationEntry( sal_uLong nIndex ) const
680 {
681 if ( pValidationList )
682 return pValidationList->GetData( nIndex );
683 else
684 return NULL;
685 }
686
FindConditionalFormat(sal_uLong nKey,ScRangeList & rRanges)687 void ScDocument::FindConditionalFormat( sal_uLong nKey, ScRangeList& rRanges )
688 {
689 for (SCTAB i=0; i<=MAXTAB && pTab[i]; i++)
690 pTab[i]->FindConditionalFormat( nKey, rRanges );
691 }
692
FindConditionalFormat(sal_uLong nKey,ScRangeList & rRanges,SCTAB nTab)693 void ScDocument::FindConditionalFormat( sal_uLong nKey, ScRangeList& rRanges, SCTAB nTab )
694 {
695 if(VALIDTAB(nTab) && pTab[nTab])
696 pTab[nTab]->FindConditionalFormat( nKey, rRanges );
697 }
698
ConditionalChanged(sal_uLong nKey)699 void ScDocument::ConditionalChanged( sal_uLong nKey )
700 {
701 if ( nKey && pCondFormList && !bIsClip && !bIsUndo ) // nKey==0 -> noop
702 {
703 ScConditionalFormat* pForm = pCondFormList->GetFormat( nKey );
704 if (pForm)
705 pForm->InvalidateArea();
706 }
707 }
708
SetCondFormList(ScConditionalFormatList * pNew)709 void ScDocument::SetCondFormList(ScConditionalFormatList* pNew)
710 {
711 if (pCondFormList)
712 {
713 pCondFormList->DeleteAndDestroy( 0, pCondFormList->Count() );
714 delete pCondFormList;
715 }
716
717 pCondFormList = pNew;
718 }
719
720 //------------------------------------------------------------------------
721
HasDetectiveOperations() const722 sal_Bool ScDocument::HasDetectiveOperations() const
723 {
724 return pDetOpList && pDetOpList->Count();
725 }
726
AddDetectiveOperation(const ScDetOpData & rData)727 void ScDocument::AddDetectiveOperation( const ScDetOpData& rData )
728 {
729 if (!pDetOpList)
730 pDetOpList = new ScDetOpList;
731
732 pDetOpList->Append( new ScDetOpData( rData ) );
733 }
734
ClearDetectiveOperations()735 void ScDocument::ClearDetectiveOperations()
736 {
737 delete pDetOpList; // loescht auch die Eintraege
738 pDetOpList = NULL;
739 }
740
SetDetOpList(ScDetOpList * pNew)741 void ScDocument::SetDetOpList(ScDetOpList* pNew)
742 {
743 delete pDetOpList; // loescht auch die Eintraege
744 pDetOpList = pNew;
745 }
746
747 //------------------------------------------------------------------------
748 //
749 // Vergleich von Dokumenten
750 //
751 //------------------------------------------------------------------------
752
753 // Pfriemel-Faktoren
754 #define SC_DOCCOMP_MAXDIFF 256
755 #define SC_DOCCOMP_MINGOOD 128
756 #define SC_DOCCOMP_COLUMNS 10
757 #define SC_DOCCOMP_ROWS 100
758
759
RowDifferences(SCROW nThisRow,SCTAB nThisTab,ScDocument & rOtherDoc,SCROW nOtherRow,SCTAB nOtherTab,SCCOL nMaxCol,SCCOLROW * pOtherCols)760 sal_uInt16 ScDocument::RowDifferences( SCROW nThisRow, SCTAB nThisTab,
761 ScDocument& rOtherDoc, SCROW nOtherRow, SCTAB nOtherTab,
762 SCCOL nMaxCol, SCCOLROW* pOtherCols )
763 {
764 sal_uLong nDif = 0;
765 sal_uLong nUsed = 0;
766 for (SCCOL nThisCol=0; nThisCol<=nMaxCol; nThisCol++)
767 {
768 SCCOL nOtherCol;
769 if ( pOtherCols )
770 nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
771 else
772 nOtherCol = nThisCol;
773
774 if (ValidCol(nOtherCol)) // nur Spalten vergleichen, die in beiden Dateien sind
775 {
776 const ScBaseCell* pThisCell = GetCell( ScAddress( nThisCol, nThisRow, nThisTab ) );
777 const ScBaseCell* pOtherCell = rOtherDoc.GetCell( ScAddress( nOtherCol, nOtherRow, nOtherTab ) );
778 if (!ScBaseCell::CellEqual( pThisCell, pOtherCell ))
779 {
780 if ( pThisCell && pOtherCell )
781 nDif += 3;
782 else
783 nDif += 4; // Inhalt <-> leer zaehlt mehr
784 }
785
786 if ( ( pThisCell && pThisCell->GetCellType()!=CELLTYPE_NOTE ) ||
787 ( pOtherCell && pOtherCell->GetCellType()!=CELLTYPE_NOTE ) )
788 ++nUsed;
789 }
790 }
791
792 if (nUsed > 0)
793 return static_cast<sal_uInt16>((nDif*64)/nUsed); // max.256 (SC_DOCCOMP_MAXDIFF)
794
795 DBG_ASSERT(!nDif,"Diff ohne Used");
796 return 0;
797 }
798
ColDifferences(SCCOL nThisCol,SCTAB nThisTab,ScDocument & rOtherDoc,SCCOL nOtherCol,SCTAB nOtherTab,SCROW nMaxRow,SCCOLROW * pOtherRows)799 sal_uInt16 ScDocument::ColDifferences( SCCOL nThisCol, SCTAB nThisTab,
800 ScDocument& rOtherDoc, SCCOL nOtherCol, SCTAB nOtherTab,
801 SCROW nMaxRow, SCCOLROW* pOtherRows )
802 {
803 //! optimieren mit Iterator oder so
804
805 sal_uLong nDif = 0;
806 sal_uLong nUsed = 0;
807 for (SCROW nThisRow=0; nThisRow<=nMaxRow; nThisRow++)
808 {
809 SCROW nOtherRow;
810 if ( pOtherRows )
811 nOtherRow = pOtherRows[nThisRow];
812 else
813 nOtherRow = nThisRow;
814
815 if (ValidRow(nOtherRow)) // nur Zeilen vergleichen, die in beiden Dateien sind
816 {
817 const ScBaseCell* pThisCell = GetCell( ScAddress( nThisCol, nThisRow, nThisTab ) );
818 const ScBaseCell* pOtherCell = rOtherDoc.GetCell( ScAddress( nOtherCol, nOtherRow, nOtherTab ) );
819 if (!ScBaseCell::CellEqual( pThisCell, pOtherCell ))
820 {
821 if ( pThisCell && pOtherCell )
822 nDif += 3;
823 else
824 nDif += 4; // Inhalt <-> leer zaehlt mehr
825 }
826
827 if ( ( pThisCell && pThisCell->GetCellType()!=CELLTYPE_NOTE ) ||
828 ( pOtherCell && pOtherCell->GetCellType()!=CELLTYPE_NOTE ) )
829 ++nUsed;
830 }
831 }
832
833 if (nUsed > 0)
834 return static_cast<sal_uInt16>((nDif*64)/nUsed); // max.256
835
836 DBG_ASSERT(!nDif,"Diff ohne Used");
837 return 0;
838 }
839
FindOrder(SCCOLROW * pOtherRows,SCCOLROW nThisEndRow,SCCOLROW nOtherEndRow,sal_Bool bColumns,ScDocument & rOtherDoc,SCTAB nThisTab,SCTAB nOtherTab,SCCOLROW nEndCol,SCCOLROW * pTranslate,ScProgress * pProgress,sal_uLong nProAdd)840 void ScDocument::FindOrder( SCCOLROW* pOtherRows, SCCOLROW nThisEndRow, SCCOLROW nOtherEndRow,
841 sal_Bool bColumns, ScDocument& rOtherDoc, SCTAB nThisTab, SCTAB nOtherTab,
842 SCCOLROW nEndCol, SCCOLROW* pTranslate, ScProgress* pProgress, sal_uLong nProAdd )
843 {
844 // bColumns=sal_True: Zeilen sind Spalten und umgekehrt
845
846 SCCOLROW nMaxCont; // wieviel weiter
847 SCCOLROW nMinGood; // was ist ein Treffer (incl.)
848 if ( bColumns )
849 {
850 nMaxCont = SC_DOCCOMP_COLUMNS; // 10 Spalten
851 nMinGood = SC_DOCCOMP_MINGOOD;
852 //! Extra Durchgang mit nMinGood = 0 ????
853 }
854 else
855 {
856 nMaxCont = SC_DOCCOMP_ROWS; // 100 Zeilen
857 nMinGood = SC_DOCCOMP_MINGOOD;
858 }
859 sal_Bool bUseTotal = bColumns && !pTranslate; // nur beim ersten Durchgang
860
861
862 SCCOLROW nOtherRow = 0;
863 sal_uInt16 nComp;
864 SCCOLROW nThisRow;
865 sal_Bool bTotal = sal_False; // ueber verschiedene nThisRow beibehalten
866 SCCOLROW nUnknown = 0;
867 for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++)
868 {
869 SCCOLROW nTempOther = nOtherRow;
870 sal_Bool bFound = sal_False;
871 sal_uInt16 nBest = SC_DOCCOMP_MAXDIFF;
872 SCCOLROW nMax = Min( nOtherEndRow, static_cast<SCCOLROW>(( nTempOther + nMaxCont + nUnknown )) );
873 for (SCCOLROW i=nTempOther; i<=nMax && nBest>0; i++) // bei 0 abbrechen
874 {
875 if (bColumns)
876 nComp = ColDifferences( static_cast<SCCOL>(nThisRow), nThisTab, rOtherDoc, static_cast<SCCOL>(i), nOtherTab, nEndCol, pTranslate );
877 else
878 nComp = RowDifferences( nThisRow, nThisTab, rOtherDoc, i, nOtherTab, static_cast<SCCOL>(nEndCol), pTranslate );
879 if ( nComp < nBest && ( nComp <= nMinGood || bTotal ) )
880 {
881 nTempOther = i;
882 nBest = nComp;
883 bFound = sal_True;
884 }
885 if ( nComp < SC_DOCCOMP_MAXDIFF || bFound )
886 bTotal = sal_False;
887 else if ( i == nTempOther && bUseTotal )
888 bTotal = sal_True; // nur ganz oben
889 }
890 if ( bFound )
891 {
892 pOtherRows[nThisRow] = nTempOther;
893 nOtherRow = nTempOther + 1;
894 nUnknown = 0;
895 }
896 else
897 {
898 pOtherRows[nThisRow] = SCROW_MAX;
899 ++nUnknown;
900 }
901
902 if (pProgress)
903 pProgress->SetStateOnPercent(nProAdd+static_cast<sal_uLong>(nThisRow));
904 }
905
906 // Bloecke ohne Uebereinstimmung ausfuellen
907
908 SCROW nFillStart = 0;
909 SCROW nFillPos = 0;
910 sal_Bool bInFill = sal_False;
911 for (nThisRow = 0; nThisRow <= nThisEndRow+1; nThisRow++)
912 {
913 SCROW nThisOther = ( nThisRow <= nThisEndRow ) ? pOtherRows[nThisRow] : (nOtherEndRow+1);
914 if ( ValidRow(nThisOther) )
915 {
916 if ( bInFill )
917 {
918 if ( nThisOther > nFillStart ) // ist was zu verteilen da?
919 {
920 SCROW nDiff1 = nThisOther - nFillStart;
921 SCROW nDiff2 = nThisRow - nFillPos;
922 SCROW nMinDiff = Min(nDiff1, nDiff2);
923 for (SCROW i=0; i<nMinDiff; i++)
924 pOtherRows[nFillPos+i] = nFillStart+i;
925 }
926
927 bInFill = sal_False;
928 }
929 nFillStart = nThisOther + 1;
930 nFillPos = nThisRow + 1;
931 }
932 else
933 bInFill = sal_True;
934 }
935 }
936
CompareDocument(ScDocument & rOtherDoc)937 void ScDocument::CompareDocument( ScDocument& rOtherDoc )
938 {
939 if (!pChangeTrack)
940 return;
941
942 SCTAB nThisCount = GetTableCount();
943 SCTAB nOtherCount = rOtherDoc.GetTableCount();
944 SCTAB* pOtherTabs = new SCTAB[nThisCount];
945 SCTAB nThisTab;
946
947 // Tabellen mit gleichen Namen vergleichen
948 String aThisName;
949 String aOtherName;
950 for (nThisTab=0; nThisTab<nThisCount; nThisTab++)
951 {
952 SCTAB nOtherTab = SCTAB_MAX;
953 if (!IsScenario(nThisTab)) // Szenarien weglassen
954 {
955 GetName( nThisTab, aThisName );
956 for (SCTAB nTemp=0; nTemp<nOtherCount && nOtherTab>MAXTAB; nTemp++)
957 if (!rOtherDoc.IsScenario(nTemp))
958 {
959 rOtherDoc.GetName( nTemp, aOtherName );
960 if ( aThisName == aOtherName )
961 nOtherTab = nTemp;
962 }
963 }
964 pOtherTabs[nThisTab] = nOtherTab;
965 }
966 // auffuellen, damit einzeln umbenannte Tabellen nicht wegfallen
967 SCTAB nFillStart = 0;
968 SCTAB nFillPos = 0;
969 sal_Bool bInFill = sal_False;
970 for (nThisTab = 0; nThisTab <= nThisCount; nThisTab++)
971 {
972 SCTAB nThisOther = ( nThisTab < nThisCount ) ? pOtherTabs[nThisTab] : nOtherCount;
973 if ( ValidTab(nThisOther) )
974 {
975 if ( bInFill )
976 {
977 if ( nThisOther > nFillStart ) // ist was zu verteilen da?
978 {
979 SCTAB nDiff1 = nThisOther - nFillStart;
980 SCTAB nDiff2 = nThisTab - nFillPos;
981 SCTAB nMinDiff = Min(nDiff1, nDiff2);
982 for (SCTAB i=0; i<nMinDiff; i++)
983 if ( !IsScenario(nFillPos+i) && !rOtherDoc.IsScenario(nFillStart+i) )
984 pOtherTabs[nFillPos+i] = nFillStart+i;
985 }
986
987 bInFill = sal_False;
988 }
989 nFillStart = nThisOther + 1;
990 nFillPos = nThisTab + 1;
991 }
992 else
993 bInFill = sal_True;
994 }
995
996 //
997 // Tabellen in der gefundenen Reihenfolge vergleichen
998 //
999
1000 for (nThisTab=0; nThisTab<nThisCount; nThisTab++)
1001 {
1002 SCTAB nOtherTab = pOtherTabs[nThisTab];
1003 if ( ValidTab(nOtherTab) )
1004 {
1005 SCCOL nThisEndCol = 0;
1006 SCROW nThisEndRow = 0;
1007 SCCOL nOtherEndCol = 0;
1008 SCROW nOtherEndRow = 0;
1009 GetCellArea( nThisTab, nThisEndCol, nThisEndRow );
1010 rOtherDoc.GetCellArea( nOtherTab, nOtherEndCol, nOtherEndRow );
1011 SCCOL nEndCol = Max(nThisEndCol, nOtherEndCol);
1012 SCROW nEndRow = Max(nThisEndRow, nOtherEndRow);
1013 SCCOL nThisCol;
1014 SCROW nThisRow;
1015 sal_uLong n1,n2; // fuer AppendDeleteRange
1016
1017 //! ein Progress ueber alle Tabellen ???
1018 String aTabName;
1019 GetName( nThisTab, aTabName );
1020 String aTemplate = ScGlobal::GetRscString(STR_PROGRESS_COMPARING);
1021 String aProText = aTemplate.GetToken( 0, '#' );
1022 aProText += aTabName;
1023 aProText += aTemplate.GetToken( 1, '#' );
1024 ScProgress aProgress( GetDocumentShell(),
1025 aProText, 3*nThisEndRow ); // 2x FindOrder, 1x hier
1026 long nProgressStart = 2*nThisEndRow; // start fuer hier
1027
1028 SCCOLROW* pTempRows = new SCCOLROW[nThisEndRow+1];
1029 SCCOLROW* pOtherRows = new SCCOLROW[nThisEndRow+1];
1030 SCCOLROW* pOtherCols = new SCCOLROW[nThisEndCol+1];
1031
1032 // eingefuegte/geloeschte Spalten/Zeilen finden:
1033 // Zwei Versuche:
1034 // 1) Original Zeilen vergleichen (pTempRows)
1035 // 2) Original Spalten vergleichen (pOtherCols)
1036 // mit dieser Spaltenreihenfolge Zeilen vergleichen (pOtherRows)
1037
1038 //! Spalten vergleichen zweimal mit unterschiedlichem nMinGood ???
1039
1040 // 1
1041 FindOrder( pTempRows, nThisEndRow, nOtherEndRow, sal_False,
1042 rOtherDoc, nThisTab, nOtherTab, nEndCol, NULL, &aProgress, 0 );
1043 // 2
1044 FindOrder( pOtherCols, nThisEndCol, nOtherEndCol, sal_True,
1045 rOtherDoc, nThisTab, nOtherTab, nEndRow, NULL, NULL, 0 );
1046 FindOrder( pOtherRows, nThisEndRow, nOtherEndRow, sal_False,
1047 rOtherDoc, nThisTab, nOtherTab, nThisEndCol,
1048 pOtherCols, &aProgress, nThisEndRow );
1049
1050 sal_uLong nMatch1 = 0; // pTempRows, keine Spalten
1051 for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++)
1052 if (ValidRow(pTempRows[nThisRow]))
1053 nMatch1 += SC_DOCCOMP_MAXDIFF -
1054 RowDifferences( nThisRow, nThisTab, rOtherDoc, pTempRows[nThisRow],
1055 nOtherTab, nEndCol, NULL );
1056
1057 sal_uLong nMatch2 = 0; // pOtherRows, pOtherCols
1058 for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++)
1059 if (ValidRow(pOtherRows[nThisRow]))
1060 nMatch2 += SC_DOCCOMP_MAXDIFF -
1061 RowDifferences( nThisRow, nThisTab, rOtherDoc, pOtherRows[nThisRow],
1062 nOtherTab, nThisEndCol, pOtherCols );
1063
1064 if ( nMatch1 >= nMatch2 ) // ohne Spalten ?
1065 {
1066 // Spalten zuruecksetzen
1067 for (nThisCol = 0; nThisCol<=nThisEndCol; nThisCol++)
1068 pOtherCols[nThisCol] = nThisCol;
1069
1070 // Zeilenarrays vertauschen (geloescht werden sowieso beide)
1071 SCCOLROW* pSwap = pTempRows;
1072 pTempRows = pOtherRows;
1073 pOtherRows = pSwap;
1074 }
1075 else
1076 {
1077 // bleibt bei pOtherCols, pOtherRows
1078 }
1079
1080
1081 // Change-Actions erzeugen
1082 // 1) Spalten von rechts
1083 // 2) Zeilen von unten
1084 // 3) einzelne Zellen in normaler Reihenfolge
1085
1086 // Actions fuer eingefuegte/geloeschte Spalten
1087
1088 SCCOL nLastOtherCol = static_cast<SCCOL>(nOtherEndCol + 1);
1089 // nThisEndCol ... 0
1090 for ( nThisCol = nThisEndCol+1; nThisCol > 0; )
1091 {
1092 --nThisCol;
1093 SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
1094 if ( ValidCol(nOtherCol) && nOtherCol+1 < nLastOtherCol )
1095 {
1096 // Luecke -> geloescht
1097 ScRange aDelRange( nOtherCol+1, 0, nOtherTab,
1098 nLastOtherCol-1, MAXROW, nOtherTab );
1099 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1100 }
1101 if ( nOtherCol > MAXCOL ) // eingefuegt
1102 {
1103 // zusammenfassen
1104 if ( nThisCol == nThisEndCol || ValidCol(static_cast<SCCOL>(pOtherCols[nThisCol+1])) )
1105 {
1106 SCCOL nFirstNew = static_cast<SCCOL>(nThisCol);
1107 while ( nFirstNew > 0 && pOtherCols[nFirstNew-1] > MAXCOL )
1108 --nFirstNew;
1109 SCCOL nDiff = nThisCol - nFirstNew;
1110 ScRange aRange( nLastOtherCol, 0, nOtherTab,
1111 nLastOtherCol+nDiff, MAXROW, nOtherTab );
1112 pChangeTrack->AppendInsert( aRange );
1113 }
1114 }
1115 else
1116 nLastOtherCol = nOtherCol;
1117 }
1118 if ( nLastOtherCol > 0 ) // ganz oben geloescht
1119 {
1120 ScRange aDelRange( 0, 0, nOtherTab,
1121 nLastOtherCol-1, MAXROW, nOtherTab );
1122 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1123 }
1124
1125 // Actions fuer eingefuegte/geloeschte Zeilen
1126
1127 SCROW nLastOtherRow = nOtherEndRow + 1;
1128 // nThisEndRow ... 0
1129 for ( nThisRow = nThisEndRow+1; nThisRow > 0; )
1130 {
1131 --nThisRow;
1132 SCROW nOtherRow = pOtherRows[nThisRow];
1133 if ( ValidRow(nOtherRow) && nOtherRow+1 < nLastOtherRow )
1134 {
1135 // Luecke -> geloescht
1136 ScRange aDelRange( 0, nOtherRow+1, nOtherTab,
1137 MAXCOL, nLastOtherRow-1, nOtherTab );
1138 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1139 }
1140 if ( nOtherRow > MAXROW ) // eingefuegt
1141 {
1142 // zusammenfassen
1143 if ( nThisRow == nThisEndRow || ValidRow(pOtherRows[nThisRow+1]) )
1144 {
1145 SCROW nFirstNew = nThisRow;
1146 while ( nFirstNew > 0 && pOtherRows[nFirstNew-1] > MAXROW )
1147 --nFirstNew;
1148 SCROW nDiff = nThisRow - nFirstNew;
1149 ScRange aRange( 0, nLastOtherRow, nOtherTab,
1150 MAXCOL, nLastOtherRow+nDiff, nOtherTab );
1151 pChangeTrack->AppendInsert( aRange );
1152 }
1153 }
1154 else
1155 nLastOtherRow = nOtherRow;
1156 }
1157 if ( nLastOtherRow > 0 ) // ganz oben geloescht
1158 {
1159 ScRange aDelRange( 0, 0, nOtherTab,
1160 MAXCOL, nLastOtherRow-1, nOtherTab );
1161 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1162 }
1163
1164 // Zeilen durchgehen um einzelne Zellen zu finden
1165
1166 for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++)
1167 {
1168 SCROW nOtherRow = pOtherRows[nThisRow];
1169 for (nThisCol = 0; nThisCol <= nThisEndCol; nThisCol++)
1170 {
1171 SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
1172 ScAddress aThisPos( nThisCol, nThisRow, nThisTab );
1173 const ScBaseCell* pThisCell = GetCell( aThisPos );
1174 const ScBaseCell* pOtherCell = NULL;
1175 if ( ValidCol(nOtherCol) && ValidRow(nOtherRow) )
1176 {
1177 ScAddress aOtherPos( nOtherCol, nOtherRow, nOtherTab );
1178 pOtherCell = rOtherDoc.GetCell( aOtherPos );
1179 }
1180 if ( !ScBaseCell::CellEqual( pThisCell, pOtherCell ) )
1181 {
1182 ScRange aRange( aThisPos );
1183 ScChangeActionContent* pAction = new ScChangeActionContent( aRange );
1184 pAction->SetOldValue( pOtherCell, &rOtherDoc, this );
1185 pAction->SetNewValue( pThisCell, this );
1186 pChangeTrack->Append( pAction );
1187 }
1188 }
1189 aProgress.SetStateOnPercent(nProgressStart+nThisRow);
1190 }
1191
1192 delete[] pOtherCols;
1193 delete[] pOtherRows;
1194 delete[] pTempRows;
1195 }
1196 }
1197
1198 //! Inhalt von eingefuegten / geloeschten Tabellen ???
1199 //! Aktionen fuer eingefuegte / geloeschte Tabellen ???
1200
1201 delete[] pOtherTabs;
1202 }
1203
1204
1205
1206
1207
1208