xref: /aoo41x/main/sc/source/core/data/documen4.cxx (revision b3f79822)
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