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