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