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