xref: /AOO41X/main/sc/source/core/data/documen4.cxx (revision 8809db7a87f97847b57a57f4cd2b0104b2b83182)
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
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 
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 
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 
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 
336 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 
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 
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 
365 void ScDocument::CompileDBFormula()
366 {
367     for (SCTAB i=0; i<=MAXTAB; i++)
368     {
369         if (pTab[i]) pTab[i]->CompileDBFormula();
370     }
371 }
372 
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 
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 
392 void ScDocument::CompileColRowNameFormula()
393 {
394     for (SCTAB i=0; i<=MAXTAB; i++)
395     {
396         if (pTab[i]) pTab[i]->CompileColRowNameFormula();
397     }
398 }
399 
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 
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 
420 sal_Int32 ScDocument::GetMaxStringLen( SCTAB nTab, SCCOL nCol,
421         SCROW nRowStart, SCROW nRowEnd, CharSet eCharSet ) const
422 {
423     if (ValidTab(nTab) && pTab[nTab])
424         return pTab[nTab]->GetMaxStringLen( nCol, nRowStart, nRowEnd, eCharSet );
425     else
426         return 0;
427 }
428 
429 xub_StrLen ScDocument::GetMaxNumberStringLen( sal_uInt16& nPrecision, SCTAB nTab,
430                                     SCCOL nCol,
431                                     SCROW nRowStart, SCROW nRowEnd ) const
432 {
433     if (ValidTab(nTab) && pTab[nTab])
434         return pTab[nTab]->GetMaxNumberStringLen( nPrecision, nCol,
435             nRowStart, nRowEnd );
436     else
437         return 0;
438 }
439 
440 sal_Bool ScDocument::GetSelectionFunction( ScSubTotalFunc eFunc,
441                                         const ScAddress& rCursor, const ScMarkData& rMark,
442                                         double& rResult )
443 {
444     ScFunctionData aData(eFunc);
445 
446     ScRange aSingle( rCursor );
447     if ( rMark.IsMarked() )
448         rMark.GetMarkArea(aSingle);
449 
450     SCCOL nStartCol = aSingle.aStart.Col();
451     SCROW nStartRow = aSingle.aStart.Row();
452     SCCOL nEndCol = aSingle.aEnd.Col();
453     SCROW nEndRow = aSingle.aEnd.Row();
454 
455     for (SCTAB nTab=0; nTab<=MAXTAB && !aData.bError; nTab++)
456         if (pTab[nTab] && rMark.GetTableSelect(nTab))
457             pTab[nTab]->UpdateSelectionFunction( aData,
458                             nStartCol, nStartRow, nEndCol, nEndRow, rMark );
459 
460             //! rMark an UpdateSelectionFunction uebergeben !!!!!
461 
462     if (!aData.bError)
463         switch (eFunc)
464         {
465             case SUBTOTAL_FUNC_SUM:
466                 rResult = aData.nVal;
467                 break;
468             case SUBTOTAL_FUNC_CNT:
469             case SUBTOTAL_FUNC_CNT2:
470                 rResult = aData.nCount;
471                 break;
472             case SUBTOTAL_FUNC_AVE:
473                 if (aData.nCount)
474                     rResult = aData.nVal / (double) aData.nCount;
475                 else
476                     aData.bError = sal_True;
477                 break;
478             case SUBTOTAL_FUNC_MAX:
479             case SUBTOTAL_FUNC_MIN:
480                 if (aData.nCount)
481                     rResult = aData.nVal;
482                 else
483                     aData.bError = sal_True;
484                 break;
485             default:
486             {
487                 // added to avoid warnings
488             }
489         }
490 
491     if (aData.bError)
492         rResult = 0.0;
493 
494     return !aData.bError;
495 }
496 
497 double ScDocument::RoundValueAsShown( double fVal, sal_uLong nFormat )
498 {
499     short nType;
500     if ( (nType = GetFormatTable()->GetType( nFormat )) != NUMBERFORMAT_DATE
501       && nType != NUMBERFORMAT_TIME && nType != NUMBERFORMAT_DATETIME )
502     {
503         short nPrecision;
504         if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
505         {
506             nPrecision = (short)GetFormatTable()->GetFormatPrecision( nFormat );
507             switch ( nType )
508             {
509                 case NUMBERFORMAT_PERCENT:      // 0,41% == 0,0041
510                     nPrecision += 2;
511                     break;
512                 case NUMBERFORMAT_SCIENTIFIC:   // 1,23e-3 == 0,00123
513                 {
514                     if ( fVal > 0.0 )
515                         nPrecision = sal::static_int_cast<short>( nPrecision - (short)floor( log10( fVal ) ) );
516                     else if ( fVal < 0.0 )
517                         nPrecision = sal::static_int_cast<short>( nPrecision - (short)floor( log10( -fVal ) ) );
518                     break;
519                 }
520             }
521         }
522         else
523         {
524             nPrecision = (short)GetDocOptions().GetStdPrecision();
525             // #i115512# no rounding for automatic decimals
526             if (nPrecision == static_cast<short>(SvNumberFormatter::UNLIMITED_PRECISION))
527                 return fVal;
528         }
529         double fRound = ::rtl::math::round( fVal, nPrecision );
530         if ( ::rtl::math::approxEqual( fVal, fRound ) )
531             return fVal;        // durch Rundung hoechstens Fehler
532         else
533             return fRound;
534     }
535     else
536         return fVal;
537 }
538 
539 //
540 //          bedingte Formate und Gueltigkeitsbereiche
541 //
542 
543 sal_uLong ScDocument::AddCondFormat( const ScConditionalFormat& rNew )
544 {
545     if (rNew.IsEmpty())
546         return 0;                   // leer ist immer 0
547 
548     if (!pCondFormList)
549         pCondFormList = new ScConditionalFormatList;
550 
551     sal_uLong nMax = 0;
552     sal_uInt16 nCount = pCondFormList->Count();
553     for (sal_uInt16 i=0; i<nCount; i++)
554     {
555         const ScConditionalFormat* pForm = (*pCondFormList)[i];
556         sal_uLong nKey = pForm->GetKey();
557         if ( pForm->EqualEntries( rNew ) )
558             return nKey;
559         if ( nKey > nMax )
560             nMax = nKey;
561     }
562 
563     // Der Aufruf kann aus ScPatternAttr::PutInPool kommen, darum Clone (echte Kopie)
564 
565     sal_uLong nNewKey = nMax + 1;
566     ScConditionalFormat* pInsert = rNew.Clone(this);
567     pInsert->SetKey( nNewKey );
568     pCondFormList->InsertNew( pInsert );
569     return nNewKey;
570 }
571 
572 sal_uLong ScDocument::AddValidationEntry( const ScValidationData& rNew )
573 {
574     if (rNew.IsEmpty())
575         return 0;                   // leer ist immer 0
576 
577     if (!pValidationList)
578         pValidationList = new ScValidationDataList;
579 
580     sal_uLong nMax = 0;
581     sal_uInt16 nCount = pValidationList->Count();
582     for (sal_uInt16 i=0; i<nCount; i++)
583     {
584         const ScValidationData* pData = (*pValidationList)[i];
585         sal_uLong nKey = pData->GetKey();
586         if ( pData->EqualEntries( rNew ) )
587             return nKey;
588         if ( nKey > nMax )
589             nMax = nKey;
590     }
591 
592     // Der Aufruf kann aus ScPatternAttr::PutInPool kommen, darum Clone (echte Kopie)
593 
594     sal_uLong nNewKey = nMax + 1;
595     ScValidationData* pInsert = rNew.Clone(this);
596     pInsert->SetKey( nNewKey );
597     pValidationList->InsertNew( pInsert );
598     return nNewKey;
599 }
600 
601 const SfxPoolItem* ScDocument::GetEffItem(
602                         SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich ) const
603 {
604     const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
605     if ( pPattern )
606     {
607         const SfxItemSet& rSet = pPattern->GetItemSet();
608         const SfxPoolItem* pItem;
609         if ( rSet.GetItemState( ATTR_CONDITIONAL, sal_True, &pItem ) == SFX_ITEM_SET )
610         {
611             sal_uLong nIndex = ((const SfxUInt32Item*)pItem)->GetValue();
612             if (nIndex && pCondFormList)
613             {
614                 const ScConditionalFormat* pForm = pCondFormList->GetFormat( nIndex );
615                 if ( pForm )
616                 {
617                     ScBaseCell* pCell = ((ScDocument*)this)->GetCell(ScAddress(nCol,nRow,nTab));
618                     String aStyle = pForm->GetCellStyle( pCell, ScAddress(nCol, nRow, nTab) );
619                     if (aStyle.Len())
620                     {
621                         SfxStyleSheetBase* pStyleSheet = xPoolHelper->GetStylePool()->Find(
622                                                                 aStyle, SFX_STYLE_FAMILY_PARA );
623                         if ( pStyleSheet && pStyleSheet->GetItemSet().GetItemState(
624                                                 nWhich, sal_True, &pItem ) == SFX_ITEM_SET )
625                             return pItem;
626                     }
627                 }
628             }
629         }
630         return &rSet.Get( nWhich );
631     }
632     DBG_ERROR("kein Pattern");
633     return NULL;
634 }
635 
636 const SfxItemSet* ScDocument::GetCondResult( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
637 {
638     const ScConditionalFormat* pForm = GetCondFormat( nCol, nRow, nTab );
639     if ( pForm )
640     {
641         ScBaseCell* pCell = ((ScDocument*)this)->GetCell(ScAddress(nCol,nRow,nTab));
642         String aStyle = pForm->GetCellStyle( pCell, ScAddress(nCol, nRow, nTab) );
643         if (aStyle.Len())
644         {
645             SfxStyleSheetBase* pStyleSheet = xPoolHelper->GetStylePool()->Find( aStyle, SFX_STYLE_FAMILY_PARA );
646             if ( pStyleSheet )
647                 return &pStyleSheet->GetItemSet();
648             // if style is not there, treat like no condition
649         }
650     }
651     return NULL;
652 }
653 
654 const ScConditionalFormat* ScDocument::GetCondFormat(
655                             SCCOL nCol, SCROW nRow, SCTAB nTab ) const
656 {
657     sal_uLong nIndex = ((const SfxUInt32Item*)GetAttr(nCol,nRow,nTab,ATTR_CONDITIONAL))->GetValue();
658     if (nIndex)
659     {
660         if (pCondFormList)
661             return pCondFormList->GetFormat( nIndex );
662         else
663         {
664             DBG_ERROR("pCondFormList ist 0");
665         }
666     }
667 
668     return NULL;
669 }
670 
671 const ScValidationData* ScDocument::GetValidationEntry( sal_uLong nIndex ) const
672 {
673     if ( pValidationList )
674         return pValidationList->GetData( nIndex );
675     else
676         return NULL;
677 }
678 
679 void ScDocument::FindConditionalFormat( sal_uLong nKey, ScRangeList& rRanges )
680 {
681     for (SCTAB i=0; i<=MAXTAB && pTab[i]; i++)
682         pTab[i]->FindConditionalFormat( nKey, rRanges );
683 }
684 
685 void ScDocument::FindConditionalFormat( sal_uLong nKey, ScRangeList& rRanges, SCTAB nTab )
686 {
687     if(VALIDTAB(nTab) && pTab[nTab])
688         pTab[nTab]->FindConditionalFormat( nKey, rRanges );
689 }
690 
691 void ScDocument::ConditionalChanged( sal_uLong nKey )
692 {
693     if ( nKey && pCondFormList && !bIsClip && !bIsUndo )        // nKey==0 -> noop
694     {
695         ScConditionalFormat* pForm = pCondFormList->GetFormat( nKey );
696         if (pForm)
697             pForm->InvalidateArea();
698     }
699 }
700 
701 void ScDocument::SetCondFormList(ScConditionalFormatList* pNew)
702 {
703     if (pCondFormList)
704     {
705         pCondFormList->DeleteAndDestroy( 0, pCondFormList->Count() );
706         delete pCondFormList;
707     }
708 
709     pCondFormList = pNew;
710 }
711 
712 //------------------------------------------------------------------------
713 
714 sal_Bool ScDocument::HasDetectiveOperations() const
715 {
716     return pDetOpList && pDetOpList->Count();
717 }
718 
719 void ScDocument::AddDetectiveOperation( const ScDetOpData& rData )
720 {
721     if (!pDetOpList)
722         pDetOpList = new ScDetOpList;
723 
724     pDetOpList->Append( new ScDetOpData( rData ) );
725 }
726 
727 void ScDocument::ClearDetectiveOperations()
728 {
729     delete pDetOpList;      // loescht auch die Eintraege
730     pDetOpList = NULL;
731 }
732 
733 void ScDocument::SetDetOpList(ScDetOpList* pNew)
734 {
735     delete pDetOpList;      // loescht auch die Eintraege
736     pDetOpList = pNew;
737 }
738 
739 //------------------------------------------------------------------------
740 //
741 //      Vergleich von Dokumenten
742 //
743 //------------------------------------------------------------------------
744 
745 //  Pfriemel-Faktoren
746 #define SC_DOCCOMP_MAXDIFF  256
747 #define SC_DOCCOMP_MINGOOD  128
748 #define SC_DOCCOMP_COLUMNS  10
749 #define SC_DOCCOMP_ROWS     100
750 
751 
752 sal_uInt16 ScDocument::RowDifferences( SCROW nThisRow, SCTAB nThisTab,
753                                     ScDocument& rOtherDoc, SCROW nOtherRow, SCTAB nOtherTab,
754                                     SCCOL nMaxCol, SCCOLROW* pOtherCols )
755 {
756     sal_uLong nDif = 0;
757     sal_uLong nUsed = 0;
758     for (SCCOL nThisCol=0; nThisCol<=nMaxCol; nThisCol++)
759     {
760         SCCOL nOtherCol;
761         if ( pOtherCols )
762             nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
763         else
764             nOtherCol = nThisCol;
765 
766         if (ValidCol(nOtherCol))    // nur Spalten vergleichen, die in beiden Dateien sind
767         {
768             const ScBaseCell* pThisCell = GetCell( ScAddress( nThisCol, nThisRow, nThisTab ) );
769             const ScBaseCell* pOtherCell = rOtherDoc.GetCell( ScAddress( nOtherCol, nOtherRow, nOtherTab ) );
770             if (!ScBaseCell::CellEqual( pThisCell, pOtherCell ))
771             {
772                 if ( pThisCell && pOtherCell )
773                     nDif += 3;
774                 else
775                     nDif += 4;      // Inhalt <-> leer zaehlt mehr
776             }
777 
778             if ( ( pThisCell  && pThisCell->GetCellType()!=CELLTYPE_NOTE ) ||
779                  ( pOtherCell && pOtherCell->GetCellType()!=CELLTYPE_NOTE ) )
780                 ++nUsed;
781         }
782     }
783 
784     if (nUsed > 0)
785         return static_cast<sal_uInt16>((nDif*64)/nUsed);            // max.256 (SC_DOCCOMP_MAXDIFF)
786 
787     DBG_ASSERT(!nDif,"Diff ohne Used");
788     return 0;
789 }
790 
791 sal_uInt16 ScDocument::ColDifferences( SCCOL nThisCol, SCTAB nThisTab,
792                                     ScDocument& rOtherDoc, SCCOL nOtherCol, SCTAB nOtherTab,
793                                     SCROW nMaxRow, SCCOLROW* pOtherRows )
794 {
795     //! optimieren mit Iterator oder so
796 
797     sal_uLong nDif = 0;
798     sal_uLong nUsed = 0;
799     for (SCROW nThisRow=0; nThisRow<=nMaxRow; nThisRow++)
800     {
801         SCROW nOtherRow;
802         if ( pOtherRows )
803             nOtherRow = pOtherRows[nThisRow];
804         else
805             nOtherRow = nThisRow;
806 
807         if (ValidRow(nOtherRow))    // nur Zeilen vergleichen, die in beiden Dateien sind
808         {
809             const ScBaseCell* pThisCell = GetCell( ScAddress( nThisCol, nThisRow, nThisTab ) );
810             const ScBaseCell* pOtherCell = rOtherDoc.GetCell( ScAddress( nOtherCol, nOtherRow, nOtherTab ) );
811             if (!ScBaseCell::CellEqual( pThisCell, pOtherCell ))
812             {
813                 if ( pThisCell && pOtherCell )
814                     nDif += 3;
815                 else
816                     nDif += 4;      // Inhalt <-> leer zaehlt mehr
817             }
818 
819             if ( ( pThisCell  && pThisCell->GetCellType()!=CELLTYPE_NOTE ) ||
820                  ( pOtherCell && pOtherCell->GetCellType()!=CELLTYPE_NOTE ) )
821                 ++nUsed;
822         }
823     }
824 
825     if (nUsed > 0)
826         return static_cast<sal_uInt16>((nDif*64)/nUsed);    // max.256
827 
828     DBG_ASSERT(!nDif,"Diff ohne Used");
829     return 0;
830 }
831 
832 void ScDocument::FindOrder( SCCOLROW* pOtherRows, SCCOLROW nThisEndRow, SCCOLROW nOtherEndRow,
833                             sal_Bool bColumns, ScDocument& rOtherDoc, SCTAB nThisTab, SCTAB nOtherTab,
834                             SCCOLROW nEndCol, SCCOLROW* pTranslate, ScProgress* pProgress, sal_uLong nProAdd )
835 {
836     //  bColumns=sal_True: Zeilen sind Spalten und umgekehrt
837 
838     SCCOLROW nMaxCont;                      // wieviel weiter
839     SCCOLROW nMinGood;                      // was ist ein Treffer (incl.)
840     if ( bColumns )
841     {
842         nMaxCont = SC_DOCCOMP_COLUMNS;      // 10 Spalten
843         nMinGood = SC_DOCCOMP_MINGOOD;
844         //! Extra Durchgang mit nMinGood = 0 ????
845     }
846     else
847     {
848         nMaxCont = SC_DOCCOMP_ROWS;         // 100 Zeilen
849         nMinGood = SC_DOCCOMP_MINGOOD;
850     }
851     sal_Bool bUseTotal = bColumns && !pTranslate;       // nur beim ersten Durchgang
852 
853 
854     SCCOLROW nOtherRow = 0;
855     sal_uInt16 nComp;
856     SCCOLROW nThisRow;
857     sal_Bool bTotal = sal_False;        // ueber verschiedene nThisRow beibehalten
858     SCCOLROW nUnknown = 0;
859     for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++)
860     {
861         SCCOLROW nTempOther = nOtherRow;
862         sal_Bool bFound = sal_False;
863         sal_uInt16 nBest = SC_DOCCOMP_MAXDIFF;
864         SCCOLROW nMax = Min( nOtherEndRow, static_cast<SCCOLROW>(( nTempOther + nMaxCont + nUnknown )) );
865         for (SCCOLROW i=nTempOther; i<=nMax && nBest>0; i++)    // bei 0 abbrechen
866         {
867             if (bColumns)
868                 nComp = ColDifferences( static_cast<SCCOL>(nThisRow), nThisTab, rOtherDoc, static_cast<SCCOL>(i), nOtherTab, nEndCol, pTranslate );
869             else
870                 nComp = RowDifferences( nThisRow, nThisTab, rOtherDoc, i, nOtherTab, static_cast<SCCOL>(nEndCol), pTranslate );
871             if ( nComp < nBest && ( nComp <= nMinGood || bTotal ) )
872             {
873                 nTempOther = i;
874                 nBest = nComp;
875                 bFound = sal_True;
876             }
877             if ( nComp < SC_DOCCOMP_MAXDIFF || bFound )
878                 bTotal = sal_False;
879             else if ( i == nTempOther && bUseTotal )
880                 bTotal = sal_True;                          // nur ganz oben
881         }
882         if ( bFound )
883         {
884             pOtherRows[nThisRow] = nTempOther;
885             nOtherRow = nTempOther + 1;
886             nUnknown = 0;
887         }
888         else
889         {
890             pOtherRows[nThisRow] = SCROW_MAX;
891             ++nUnknown;
892         }
893 
894         if (pProgress)
895             pProgress->SetStateOnPercent(nProAdd+static_cast<sal_uLong>(nThisRow));
896     }
897 
898     //  Bloecke ohne Uebereinstimmung ausfuellen
899 
900     SCROW nFillStart = 0;
901     SCROW nFillPos = 0;
902     sal_Bool bInFill = sal_False;
903     for (nThisRow = 0; nThisRow <= nThisEndRow+1; nThisRow++)
904     {
905         SCROW nThisOther = ( nThisRow <= nThisEndRow ) ? pOtherRows[nThisRow] : (nOtherEndRow+1);
906         if ( ValidRow(nThisOther) )
907         {
908             if ( bInFill )
909             {
910                 if ( nThisOther > nFillStart )      // ist was zu verteilen da?
911                 {
912                     SCROW nDiff1 = nThisOther - nFillStart;
913                     SCROW nDiff2 = nThisRow   - nFillPos;
914                     SCROW nMinDiff = Min(nDiff1, nDiff2);
915                     for (SCROW i=0; i<nMinDiff; i++)
916                         pOtherRows[nFillPos+i] = nFillStart+i;
917                 }
918 
919                 bInFill = sal_False;
920             }
921             nFillStart = nThisOther + 1;
922             nFillPos = nThisRow + 1;
923         }
924         else
925             bInFill = sal_True;
926     }
927 }
928 
929 void ScDocument::CompareDocument( ScDocument& rOtherDoc )
930 {
931     if (!pChangeTrack)
932         return;
933 
934     SCTAB nThisCount = GetTableCount();
935     SCTAB nOtherCount = rOtherDoc.GetTableCount();
936     SCTAB* pOtherTabs = new SCTAB[nThisCount];
937     SCTAB nThisTab;
938 
939     //  Tabellen mit gleichen Namen vergleichen
940     String aThisName;
941     String aOtherName;
942     for (nThisTab=0; nThisTab<nThisCount; nThisTab++)
943     {
944         SCTAB nOtherTab = SCTAB_MAX;
945         if (!IsScenario(nThisTab))  // Szenarien weglassen
946         {
947             GetName( nThisTab, aThisName );
948             for (SCTAB nTemp=0; nTemp<nOtherCount && nOtherTab>MAXTAB; nTemp++)
949                 if (!rOtherDoc.IsScenario(nTemp))
950                 {
951                     rOtherDoc.GetName( nTemp, aOtherName );
952                     if ( aThisName == aOtherName )
953                         nOtherTab = nTemp;
954                 }
955         }
956         pOtherTabs[nThisTab] = nOtherTab;
957     }
958     //  auffuellen, damit einzeln umbenannte Tabellen nicht wegfallen
959     SCTAB nFillStart = 0;
960     SCTAB nFillPos = 0;
961     sal_Bool bInFill = sal_False;
962     for (nThisTab = 0; nThisTab <= nThisCount; nThisTab++)
963     {
964         SCTAB nThisOther = ( nThisTab < nThisCount ) ? pOtherTabs[nThisTab] : nOtherCount;
965         if ( ValidTab(nThisOther) )
966         {
967             if ( bInFill )
968             {
969                 if ( nThisOther > nFillStart )      // ist was zu verteilen da?
970                 {
971                     SCTAB nDiff1 = nThisOther - nFillStart;
972                     SCTAB nDiff2 = nThisTab   - nFillPos;
973                     SCTAB nMinDiff = Min(nDiff1, nDiff2);
974                     for (SCTAB i=0; i<nMinDiff; i++)
975                         if ( !IsScenario(nFillPos+i) && !rOtherDoc.IsScenario(nFillStart+i) )
976                             pOtherTabs[nFillPos+i] = nFillStart+i;
977                 }
978 
979                 bInFill = sal_False;
980             }
981             nFillStart = nThisOther + 1;
982             nFillPos = nThisTab + 1;
983         }
984         else
985             bInFill = sal_True;
986     }
987 
988     //
989     //  Tabellen in der gefundenen Reihenfolge vergleichen
990     //
991 
992     for (nThisTab=0; nThisTab<nThisCount; nThisTab++)
993     {
994         SCTAB nOtherTab = pOtherTabs[nThisTab];
995         if ( ValidTab(nOtherTab) )
996         {
997             SCCOL nThisEndCol = 0;
998             SCROW nThisEndRow = 0;
999             SCCOL nOtherEndCol = 0;
1000             SCROW nOtherEndRow = 0;
1001             GetCellArea( nThisTab, nThisEndCol, nThisEndRow );
1002             rOtherDoc.GetCellArea( nOtherTab, nOtherEndCol, nOtherEndRow );
1003             SCCOL nEndCol = Max(nThisEndCol, nOtherEndCol);
1004             SCROW nEndRow = Max(nThisEndRow, nOtherEndRow);
1005             SCCOL nThisCol;
1006             SCROW nThisRow;
1007             sal_uLong n1,n2;    // fuer AppendDeleteRange
1008 
1009             //! ein Progress ueber alle Tabellen ???
1010             String aTabName;
1011             GetName( nThisTab, aTabName );
1012             String aTemplate = ScGlobal::GetRscString(STR_PROGRESS_COMPARING);
1013             String aProText = aTemplate.GetToken( 0, '#' );
1014             aProText += aTabName;
1015             aProText += aTemplate.GetToken( 1, '#' );
1016             ScProgress aProgress( GetDocumentShell(),
1017                                         aProText, 3*nThisEndRow );  // 2x FindOrder, 1x hier
1018             long nProgressStart = 2*nThisEndRow;                    // start fuer hier
1019 
1020             SCCOLROW* pTempRows = new SCCOLROW[nThisEndRow+1];
1021             SCCOLROW* pOtherRows = new SCCOLROW[nThisEndRow+1];
1022             SCCOLROW* pOtherCols = new SCCOLROW[nThisEndCol+1];
1023 
1024             //  eingefuegte/geloeschte Spalten/Zeilen finden:
1025             //  Zwei Versuche:
1026             //  1) Original Zeilen vergleichen                          (pTempRows)
1027             //  2) Original Spalten vergleichen                         (pOtherCols)
1028             //     mit dieser Spaltenreihenfolge Zeilen vergleichen     (pOtherRows)
1029 
1030             //! Spalten vergleichen zweimal mit unterschiedlichem nMinGood ???
1031 
1032             // 1
1033             FindOrder( pTempRows, nThisEndRow, nOtherEndRow, sal_False,
1034                         rOtherDoc, nThisTab, nOtherTab, nEndCol, NULL, &aProgress, 0 );
1035             // 2
1036             FindOrder( pOtherCols, nThisEndCol, nOtherEndCol, sal_True,
1037                         rOtherDoc, nThisTab, nOtherTab, nEndRow, NULL, NULL, 0 );
1038             FindOrder( pOtherRows, nThisEndRow, nOtherEndRow, sal_False,
1039                         rOtherDoc, nThisTab, nOtherTab, nThisEndCol,
1040                         pOtherCols, &aProgress, nThisEndRow );
1041 
1042             sal_uLong nMatch1 = 0;  // pTempRows, keine Spalten
1043             for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++)
1044                 if (ValidRow(pTempRows[nThisRow]))
1045                     nMatch1 += SC_DOCCOMP_MAXDIFF -
1046                                RowDifferences( nThisRow, nThisTab, rOtherDoc, pTempRows[nThisRow],
1047                                                 nOtherTab, nEndCol, NULL );
1048 
1049             sal_uLong nMatch2 = 0;  // pOtherRows, pOtherCols
1050             for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++)
1051                 if (ValidRow(pOtherRows[nThisRow]))
1052                     nMatch2 += SC_DOCCOMP_MAXDIFF -
1053                                RowDifferences( nThisRow, nThisTab, rOtherDoc, pOtherRows[nThisRow],
1054                                                 nOtherTab, nThisEndCol, pOtherCols );
1055 
1056             if ( nMatch1 >= nMatch2 )           // ohne Spalten ?
1057             {
1058                 //  Spalten zuruecksetzen
1059                 for (nThisCol = 0; nThisCol<=nThisEndCol; nThisCol++)
1060                     pOtherCols[nThisCol] = nThisCol;
1061 
1062                 //  Zeilenarrays vertauschen (geloescht werden sowieso beide)
1063                 SCCOLROW* pSwap = pTempRows;
1064                 pTempRows = pOtherRows;
1065                 pOtherRows = pSwap;
1066             }
1067             else
1068             {
1069                 //  bleibt bei pOtherCols, pOtherRows
1070             }
1071 
1072 
1073             //  Change-Actions erzeugen
1074             //  1) Spalten von rechts
1075             //  2) Zeilen von unten
1076             //  3) einzelne Zellen in normaler Reihenfolge
1077 
1078             //  Actions fuer eingefuegte/geloeschte Spalten
1079 
1080             SCCOL nLastOtherCol = static_cast<SCCOL>(nOtherEndCol + 1);
1081             //  nThisEndCol ... 0
1082             for ( nThisCol = nThisEndCol+1; nThisCol > 0; )
1083             {
1084                 --nThisCol;
1085                 SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
1086                 if ( ValidCol(nOtherCol) && nOtherCol+1 < nLastOtherCol )
1087                 {
1088                     // Luecke -> geloescht
1089                     ScRange aDelRange( nOtherCol+1, 0, nOtherTab,
1090                                         nLastOtherCol-1, MAXROW, nOtherTab );
1091                     pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1092                 }
1093                 if ( nOtherCol > MAXCOL )                       // eingefuegt
1094                 {
1095                     //  zusammenfassen
1096                     if ( nThisCol == nThisEndCol || ValidCol(static_cast<SCCOL>(pOtherCols[nThisCol+1])) )
1097                     {
1098                         SCCOL nFirstNew = static_cast<SCCOL>(nThisCol);
1099                         while ( nFirstNew > 0 && pOtherCols[nFirstNew-1] > MAXCOL )
1100                             --nFirstNew;
1101                         SCCOL nDiff = nThisCol - nFirstNew;
1102                         ScRange aRange( nLastOtherCol, 0, nOtherTab,
1103                                         nLastOtherCol+nDiff, MAXROW, nOtherTab );
1104                         pChangeTrack->AppendInsert( aRange );
1105                     }
1106                 }
1107                 else
1108                     nLastOtherCol = nOtherCol;
1109             }
1110             if ( nLastOtherCol > 0 )                            // ganz oben geloescht
1111             {
1112                 ScRange aDelRange( 0, 0, nOtherTab,
1113                                     nLastOtherCol-1, MAXROW, nOtherTab );
1114                 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1115             }
1116 
1117             //  Actions fuer eingefuegte/geloeschte Zeilen
1118 
1119             SCROW nLastOtherRow = nOtherEndRow + 1;
1120             //  nThisEndRow ... 0
1121             for ( nThisRow = nThisEndRow+1; nThisRow > 0; )
1122             {
1123                 --nThisRow;
1124                 SCROW nOtherRow = pOtherRows[nThisRow];
1125                 if ( ValidRow(nOtherRow) && nOtherRow+1 < nLastOtherRow )
1126                 {
1127                     // Luecke -> geloescht
1128                     ScRange aDelRange( 0, nOtherRow+1, nOtherTab,
1129                                         MAXCOL, nLastOtherRow-1, nOtherTab );
1130                     pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1131                 }
1132                 if ( nOtherRow > MAXROW )                       // eingefuegt
1133                 {
1134                     //  zusammenfassen
1135                     if ( nThisRow == nThisEndRow || ValidRow(pOtherRows[nThisRow+1]) )
1136                     {
1137                         SCROW nFirstNew = nThisRow;
1138                         while ( nFirstNew > 0 && pOtherRows[nFirstNew-1] > MAXROW )
1139                             --nFirstNew;
1140                         SCROW nDiff = nThisRow - nFirstNew;
1141                         ScRange aRange( 0, nLastOtherRow, nOtherTab,
1142                                         MAXCOL, nLastOtherRow+nDiff, nOtherTab );
1143                         pChangeTrack->AppendInsert( aRange );
1144                     }
1145                 }
1146                 else
1147                     nLastOtherRow = nOtherRow;
1148             }
1149             if ( nLastOtherRow > 0 )                            // ganz oben geloescht
1150             {
1151                 ScRange aDelRange( 0, 0, nOtherTab,
1152                                     MAXCOL, nLastOtherRow-1, nOtherTab );
1153                 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1154             }
1155 
1156             //  Zeilen durchgehen um einzelne Zellen zu finden
1157 
1158             for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++)
1159             {
1160                 SCROW nOtherRow = pOtherRows[nThisRow];
1161                 for (nThisCol = 0; nThisCol <= nThisEndCol; nThisCol++)
1162                 {
1163                     SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
1164                     ScAddress aThisPos( nThisCol, nThisRow, nThisTab );
1165                     const ScBaseCell* pThisCell = GetCell( aThisPos );
1166                     const ScBaseCell* pOtherCell = NULL;
1167                     if ( ValidCol(nOtherCol) && ValidRow(nOtherRow) )
1168                     {
1169                         ScAddress aOtherPos( nOtherCol, nOtherRow, nOtherTab );
1170                         pOtherCell = rOtherDoc.GetCell( aOtherPos );
1171                     }
1172                     if ( !ScBaseCell::CellEqual( pThisCell, pOtherCell ) )
1173                     {
1174                         ScRange aRange( aThisPos );
1175                         ScChangeActionContent* pAction = new ScChangeActionContent( aRange );
1176                         pAction->SetOldValue( pOtherCell, &rOtherDoc, this );
1177                         pAction->SetNewValue( pThisCell, this );
1178                         pChangeTrack->Append( pAction );
1179                     }
1180                 }
1181                 aProgress.SetStateOnPercent(nProgressStart+nThisRow);
1182             }
1183 
1184             delete[] pOtherCols;
1185             delete[] pOtherRows;
1186             delete[] pTempRows;
1187         }
1188     }
1189 
1190     //! Inhalt von eingefuegten / geloeschten Tabellen ???
1191     //! Aktionen fuer eingefuegte / geloeschte Tabellen ???
1192 
1193     delete[] pOtherTabs;
1194 }
1195 
1196 
1197 
1198 
1199 
1200