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