merge the formfield patch from ooo-build
[ooovba.git] / sc / source / core / data / documen4.cxx
blobaa90414052f1b04185277ea5af975d94914d6e4e
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: documen4.cxx,v $
10 * $Revision: 1.23.102.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
36 // INCLUDE ---------------------------------------------------------------
38 #include <svtools/intitem.hxx>
39 #include <svtools/zforlist.hxx>
40 #include <vcl/sound.hxx>
41 #include <formula/token.hxx>
43 #include "document.hxx"
44 #include "table.hxx"
45 #include "globstr.hrc"
46 #include "subtotal.hxx"
47 #include "docoptio.hxx"
48 #include "interpre.hxx"
49 #include "markdata.hxx"
50 #include "validat.hxx"
51 #include "scitems.hxx"
52 #include "stlpool.hxx"
53 #include "poolhelp.hxx"
54 #include "detdata.hxx"
55 #include "patattr.hxx"
56 #include "chgtrack.hxx"
57 #include "progress.hxx"
58 #include "paramisc.hxx"
59 #include "compiler.hxx"
60 #include "externalrefmgr.hxx"
62 using namespace formula;
64 // -----------------------------------------------------------------------
66 // Nach der Regula Falsi Methode
67 BOOL ScDocument::Solver(SCCOL nFCol, SCROW nFRow, SCTAB nFTab,
68 SCCOL nVCol, SCROW nVRow, SCTAB nVTab,
69 const String& sValStr, double& nX)
71 BOOL bRet = FALSE;
72 nX = 0.0;
73 if (ValidColRow(nFCol, nFRow) && ValidColRow(nVCol, nVRow) &&
74 VALIDTAB(nFTab) && VALIDTAB(nVTab) && pTab[nFTab] && pTab[nVTab])
76 CellType eFType, eVType;
77 GetCellType(nFCol, nFRow, nFTab, eFType);
78 GetCellType(nVCol, nVRow, nVTab, eVType);
79 // CELLTYPE_NOTE: kein Value aber von Formel referiert
80 if (eFType == CELLTYPE_FORMULA && (eVType == CELLTYPE_VALUE
81 || eVType == CELLTYPE_NOTE) )
83 ScSingleRefData aRefData;
84 aRefData.InitFlags();
85 aRefData.nCol = nVCol;
86 aRefData.nRow = nVRow;
87 aRefData.nTab = nVTab;
89 ScTokenArray aArr;
90 aArr.AddOpCode( ocBackSolver );
91 aArr.AddOpCode( ocOpen );
92 aArr.AddSingleReference( aRefData );
93 aArr.AddOpCode( ocSep );
95 aRefData.nCol = nFCol;
96 aRefData.nRow = nFRow;
97 aRefData.nTab = nFTab;
99 aArr.AddSingleReference( aRefData );
100 aArr.AddOpCode( ocSep );
101 aArr.AddString( sValStr.GetBuffer() );
102 aArr.AddOpCode( ocClose );
103 aArr.AddOpCode( ocStop );
105 ScFormulaCell* pCell = new ScFormulaCell( this, ScAddress(), &aArr );
107 if (pCell)
109 // FIXME FIXME FIXME this might need to be reworked now that we have formula::FormulaErrorToken and ScFormulaResult, double check !!!
110 DBG_ERRORFILE("ScDocument::Solver: -> ScFormulaCell::GetValueAlways might need reimplementation");
111 pCell->Interpret();
112 USHORT nErrCode = pCell->GetErrCode();
113 nX = pCell->GetValueAlways();
114 if (nErrCode == 0) // kein fehler beim Rechnen
115 bRet = TRUE;
116 delete pCell;
120 return bRet;
123 void ScDocument::InsertMatrixFormula(SCCOL nCol1, SCROW nRow1,
124 SCCOL nCol2, SCROW nRow2,
125 const ScMarkData& rMark,
126 const String& rFormula,
127 const ScTokenArray* pArr,
128 const formula::FormulaGrammar::Grammar eGram )
130 PutInOrder(nCol1, nCol2);
131 PutInOrder(nRow1, nRow2);
132 SCTAB i, nTab1;
133 SCCOL j;
134 SCROW k;
135 i = 0;
136 BOOL bStop = FALSE;
137 while (i <= MAXTAB && !bStop) // erste markierte Tabelle finden
139 if (pTab[i] && rMark.GetTableSelect(i))
140 bStop = TRUE;
141 else
142 i++;
144 nTab1 = i;
145 if (i == MAXTAB + 1)
147 Sound::Beep();
148 DBG_ERROR("ScDocument::InsertMatrixFormula Keine Tabelle markiert");
149 return;
152 ScFormulaCell* pCell;
153 ScAddress aPos( nCol1, nRow1, nTab1 );
154 if (pArr)
155 pCell = new ScFormulaCell( this, aPos, pArr, eGram, MM_FORMULA );
156 else
157 pCell = new ScFormulaCell( this, aPos, rFormula, eGram, MM_FORMULA );
158 pCell->SetMatColsRows( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1 );
159 for (i = 0; i <= MAXTAB; i++)
161 if (pTab[i] && rMark.GetTableSelect(i))
163 if (i == nTab1)
164 pTab[i]->PutCell(nCol1, nRow1, pCell);
165 else
166 pTab[i]->PutCell(nCol1, nRow1, pCell->CloneWithoutNote(*this, ScAddress( nCol1, nRow1, i), SC_CLONECELL_STARTLISTENING));
170 ScSingleRefData aRefData;
171 aRefData.InitFlags();
172 aRefData.nCol = nCol1;
173 aRefData.nRow = nRow1;
174 aRefData.nTab = nTab1;
175 aRefData.SetColRel( TRUE );
176 aRefData.SetRowRel( TRUE );
177 aRefData.SetTabRel( TRUE );
178 aRefData.CalcRelFromAbs( ScAddress( nCol1, nRow1, nTab1 ) );
180 ScTokenArray aArr;
181 ScToken* t = static_cast<ScToken*>(aArr.AddMatrixSingleReference( aRefData));
183 for (i = 0; i <= MAXTAB; i++)
185 if (pTab[i] && rMark.GetTableSelect(i))
187 pTab[i]->DoColResize( nCol1, nCol2, static_cast<SCSIZE>(nRow2 - nRow1 + 1) );
188 if (i != nTab1)
190 aRefData.nTab = i;
191 aRefData.nRelTab = i - nTab1;
192 t->GetSingleRef() = aRefData;
194 for (j = nCol1; j <= nCol2; j++)
196 for (k = nRow1; k <= nRow2; k++)
198 if (j != nCol1 || k != nRow1) // nicht in der ersten Zelle
200 // Array muss geklont werden, damit jede
201 // Zelle ein eigenes Array erhaelt!
202 aPos = ScAddress( j, k, i );
203 t->CalcRelFromAbs( aPos );
204 pCell = new ScFormulaCell( this, aPos, aArr.Clone(), eGram, MM_REFERENCE );
205 pTab[i]->PutCell(j, k, (ScBaseCell*) pCell);
213 void ScDocument::InsertTableOp(const ScTabOpParam& rParam, // Mehrfachoperation
214 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
215 const ScMarkData& rMark)
217 PutInOrder(nCol1, nCol2);
218 PutInOrder(nRow1, nRow2);
219 SCTAB i, nTab1;
220 SCCOL j;
221 SCROW k;
222 i = 0;
223 BOOL bStop = FALSE;
224 while (i <= MAXTAB && !bStop) // erste markierte Tabelle finden
226 if (pTab[i] && rMark.GetTableSelect(i))
227 bStop = TRUE;
228 else
229 i++;
231 nTab1 = i;
232 if (i == MAXTAB + 1)
234 Sound::Beep();
235 DBG_ERROR("ScDocument::InsertTableOp: Keine Tabelle markiert");
236 return;
239 ScRefAddress aRef;
240 String aForString = '=';
241 aForString += ScCompiler::GetNativeSymbol(ocTableOp);
242 aForString += ScCompiler::GetNativeSymbol( ocOpen);
244 const String& sSep = ScCompiler::GetNativeSymbol( ocSep);
245 if (rParam.nMode == 0) // nur Spalte
247 aRef.Set( rParam.aRefFormulaCell.GetAddress(), TRUE, FALSE, FALSE );
248 aForString += aRef.GetRefString(this, nTab1);
249 aForString += sSep;
250 aForString += rParam.aRefColCell.GetRefString(this, nTab1);
251 aForString += sSep;
252 aRef.Set( nCol1, nRow1, nTab1, FALSE, TRUE, TRUE );
253 aForString += aRef.GetRefString(this, nTab1);
254 nCol1++;
255 nCol2 = Min( nCol2, (SCCOL)(rParam.aRefFormulaEnd.Col() -
256 rParam.aRefFormulaCell.Col() + nCol1 + 1));
258 else if (rParam.nMode == 1) // nur zeilenweise
260 aRef.Set( rParam.aRefFormulaCell.GetAddress(), FALSE, TRUE, FALSE );
261 aForString += aRef.GetRefString(this, nTab1);
262 aForString += sSep;
263 aForString += rParam.aRefRowCell.GetRefString(this, nTab1);
264 aForString += sSep;
265 aRef.Set( nCol1, nRow1, nTab1, TRUE, FALSE, TRUE );
266 aForString += aRef.GetRefString(this, nTab1);
267 nRow1++;
268 nRow2 = Min( nRow2, (SCROW)(rParam.aRefFormulaEnd.Row() -
269 rParam.aRefFormulaCell.Row() + nRow1 + 1));
271 else // beides
273 aForString += rParam.aRefFormulaCell.GetRefString(this, nTab1);
274 aForString += sSep;
275 aForString += rParam.aRefColCell.GetRefString(this, nTab1);
276 aForString += sSep;
277 aRef.Set( nCol1, nRow1 + 1, nTab1, FALSE, TRUE, TRUE );
278 aForString += aRef.GetRefString(this, nTab1);
279 aForString += sSep;
280 aForString += rParam.aRefRowCell.GetRefString(this, nTab1);
281 aForString += sSep;
282 aRef.Set( nCol1 + 1, nRow1, nTab1, TRUE, FALSE, TRUE );
283 aForString += aRef.GetRefString(this, nTab1);
284 nCol1++; nRow1++;
286 aForString += ScCompiler::GetNativeSymbol( ocClose);
288 ScFormulaCell aRefCell( this, ScAddress( nCol1, nRow1, nTab1 ), aForString,
289 formula::FormulaGrammar::GRAM_NATIVE, MM_NONE );
290 for( j = nCol1; j <= nCol2; j++ )
291 for( k = nRow1; k <= nRow2; k++ )
292 for (i = 0; i <= MAXTAB; i++)
293 if( pTab[i] && rMark.GetTableSelect(i) )
294 pTab[i]->PutCell( j, k, aRefCell.CloneWithoutNote( *this, ScAddress( j, k, i ), SC_CLONECELL_STARTLISTENING ) );
297 bool ScDocument::MarkUsedExternalReferences( ScTokenArray & rArr )
299 bool bAllMarked = false;
300 if (rArr.GetLen())
302 ScExternalRefManager* pRefMgr = NULL;
303 rArr.Reset();
304 ScToken* t;
305 while (!bAllMarked && (t = static_cast<ScToken*>(rArr.GetNextReferenceOrName())) != NULL)
307 if (t->GetOpCode() == ocExternalRef)
309 if (!pRefMgr)
310 pRefMgr = GetExternalRefManager();
311 switch (t->GetType())
313 case svExternalSingleRef:
314 bAllMarked = pRefMgr->setCacheTableReferenced(
315 t->GetIndex(), t->GetString(), 1);
316 break;
317 case svExternalDoubleRef:
319 const ScComplexRefData& rRef = t->GetDoubleRef();
320 size_t nSheets = rRef.Ref2.nTab - rRef.Ref1.nTab + 1;
321 bAllMarked = pRefMgr->setCacheTableReferenced(
322 t->GetIndex(), t->GetString(), nSheets);
324 break;
325 case svExternalName:
326 /* TODO: external names aren't supported yet, but would
327 * have to be marked as well, if so. Mechanism would be
328 * different. */
329 DBG_ERRORFILE("ScDocument::MarkUsedExternalReferences: implement the svExternalName case!");
330 break;
331 default: break;
336 return bAllMarked;
339 BOOL ScDocument::GetNextSpellingCell(SCCOL& nCol, SCROW& nRow, SCTAB nTab,
340 BOOL bInSel, const ScMarkData& rMark) const
342 if (ValidTab(nTab) && pTab[nTab])
343 return pTab[nTab]->GetNextSpellingCell( nCol, nRow, bInSel, rMark );
344 else
345 return FALSE;
348 BOOL ScDocument::GetNextMarkedCell( SCCOL& rCol, SCROW& rRow, SCTAB nTab,
349 const ScMarkData& rMark )
351 if (ValidTab(nTab) && pTab[nTab])
352 return pTab[nTab]->GetNextMarkedCell( rCol, rRow, rMark );
353 else
354 return FALSE;
357 BOOL ScDocument::ReplaceStyle(const SvxSearchItem& rSearchItem,
358 SCCOL nCol, SCROW nRow, SCTAB nTab,
359 ScMarkData& rMark,
360 BOOL bIsUndoP)
362 if (pTab[nTab])
363 return pTab[nTab]->ReplaceStyle(rSearchItem, nCol, nRow, rMark, bIsUndoP);
364 else
365 return FALSE;
368 void ScDocument::CompileDBFormula()
370 for (SCTAB i=0; i<=MAXTAB; i++)
372 if (pTab[i]) pTab[i]->CompileDBFormula();
376 void ScDocument::CompileDBFormula( BOOL bCreateFormulaString )
378 for (SCTAB i=0; i<=MAXTAB; i++)
380 if (pTab[i]) pTab[i]->CompileDBFormula( bCreateFormulaString );
384 void ScDocument::CompileNameFormula( BOOL bCreateFormulaString )
386 if ( pCondFormList )
387 pCondFormList->CompileAll(); // nach ScNameDlg noetig
389 for (SCTAB i=0; i<=MAXTAB; i++)
391 if (pTab[i]) pTab[i]->CompileNameFormula( bCreateFormulaString );
395 void ScDocument::CompileColRowNameFormula()
397 for (SCTAB i=0; i<=MAXTAB; i++)
399 if (pTab[i]) pTab[i]->CompileColRowNameFormula();
403 void ScDocument::DoColResize( SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCSIZE nAdd )
405 if (ValidTab(nTab) && pTab[nTab])
406 pTab[nTab]->DoColResize( nCol1, nCol2, nAdd );
407 else
409 DBG_ERROR("DoColResize: falsche Tabelle");
413 void ScDocument::InvalidateTableArea()
415 for (SCTAB nTab=0; nTab<=MAXTAB && pTab[nTab]; nTab++)
417 pTab[nTab]->InvalidateTableArea();
418 if ( pTab[nTab]->IsScenario() )
419 pTab[nTab]->InvalidateScenarioRanges();
423 sal_Int32 ScDocument::GetMaxStringLen( SCTAB nTab, SCCOL nCol,
424 SCROW nRowStart, SCROW nRowEnd, CharSet eCharSet ) const
426 if (ValidTab(nTab) && pTab[nTab])
427 return pTab[nTab]->GetMaxStringLen( nCol, nRowStart, nRowEnd, eCharSet );
428 else
429 return 0;
432 xub_StrLen ScDocument::GetMaxNumberStringLen( USHORT& nPrecision, SCTAB nTab,
433 SCCOL nCol,
434 SCROW nRowStart, SCROW nRowEnd ) const
436 if (ValidTab(nTab) && pTab[nTab])
437 return pTab[nTab]->GetMaxNumberStringLen( nPrecision, nCol,
438 nRowStart, nRowEnd );
439 else
440 return 0;
443 BOOL ScDocument::GetSelectionFunction( ScSubTotalFunc eFunc,
444 const ScAddress& rCursor, const ScMarkData& rMark,
445 double& rResult )
447 ScFunctionData aData(eFunc);
449 ScRange aSingle( rCursor );
450 if ( rMark.IsMarked() )
451 rMark.GetMarkArea(aSingle);
453 SCCOL nStartCol = aSingle.aStart.Col();
454 SCROW nStartRow = aSingle.aStart.Row();
455 SCCOL nEndCol = aSingle.aEnd.Col();
456 SCROW nEndRow = aSingle.aEnd.Row();
458 for (SCTAB nTab=0; nTab<=MAXTAB && !aData.bError; nTab++)
459 if (pTab[nTab] && rMark.GetTableSelect(nTab))
460 pTab[nTab]->UpdateSelectionFunction( aData,
461 nStartCol, nStartRow, nEndCol, nEndRow, rMark );
463 //! rMark an UpdateSelectionFunction uebergeben !!!!!
465 if (!aData.bError)
466 switch (eFunc)
468 case SUBTOTAL_FUNC_SUM:
469 rResult = aData.nVal;
470 break;
471 case SUBTOTAL_FUNC_CNT:
472 case SUBTOTAL_FUNC_CNT2:
473 rResult = aData.nCount;
474 break;
475 case SUBTOTAL_FUNC_AVE:
476 if (aData.nCount)
477 rResult = aData.nVal / (double) aData.nCount;
478 else
479 aData.bError = TRUE;
480 break;
481 case SUBTOTAL_FUNC_MAX:
482 case SUBTOTAL_FUNC_MIN:
483 if (aData.nCount)
484 rResult = aData.nVal;
485 else
486 aData.bError = TRUE;
487 break;
488 default:
490 // added to avoid warnings
494 if (aData.bError)
495 rResult = 0.0;
497 return !aData.bError;
500 double ScDocument::RoundValueAsShown( double fVal, ULONG nFormat )
502 short nType;
503 if ( (nType = GetFormatTable()->GetType( nFormat )) != NUMBERFORMAT_DATE
504 && nType != NUMBERFORMAT_TIME && nType != NUMBERFORMAT_DATETIME )
506 short nPrecision;
507 if ( nFormat )
509 nPrecision = (short)GetFormatTable()->GetFormatPrecision( nFormat );
510 switch ( nType )
512 case NUMBERFORMAT_PERCENT: // 0,41% == 0,0041
513 nPrecision += 2;
514 break;
515 case NUMBERFORMAT_SCIENTIFIC: // 1,23e-3 == 0,00123
517 if ( fVal > 0.0 )
518 nPrecision = sal::static_int_cast<short>( nPrecision - (short)floor( log10( fVal ) ) );
519 else if ( fVal < 0.0 )
520 nPrecision = sal::static_int_cast<short>( nPrecision - (short)floor( log10( -fVal ) ) );
521 break;
525 else
526 nPrecision = (short)GetDocOptions().GetStdPrecision();
527 double fRound = ::rtl::math::round( fVal, nPrecision );
528 if ( ::rtl::math::approxEqual( fVal, fRound ) )
529 return fVal; // durch Rundung hoechstens Fehler
530 else
531 return fRound;
533 else
534 return fVal;
538 // bedingte Formate und Gueltigkeitsbereiche
541 ULONG ScDocument::AddCondFormat( const ScConditionalFormat& rNew )
543 if (rNew.IsEmpty())
544 return 0; // leer ist immer 0
546 if (!pCondFormList)
547 pCondFormList = new ScConditionalFormatList;
549 ULONG nMax = 0;
550 USHORT nCount = pCondFormList->Count();
551 for (USHORT i=0; i<nCount; i++)
553 const ScConditionalFormat* pForm = (*pCondFormList)[i];
554 ULONG nKey = pForm->GetKey();
555 if ( pForm->EqualEntries( rNew ) )
556 return nKey;
557 if ( nKey > nMax )
558 nMax = nKey;
561 // Der Aufruf kann aus ScPatternAttr::PutInPool kommen, darum Clone (echte Kopie)
563 ULONG nNewKey = nMax + 1;
564 ScConditionalFormat* pInsert = rNew.Clone(this);
565 pInsert->SetKey( nNewKey );
566 pCondFormList->InsertNew( pInsert );
567 return nNewKey;
570 ULONG ScDocument::AddValidationEntry( const ScValidationData& rNew )
572 if (rNew.IsEmpty())
573 return 0; // leer ist immer 0
575 if (!pValidationList)
576 pValidationList = new ScValidationDataList;
578 ULONG nMax = 0;
579 USHORT nCount = pValidationList->Count();
580 for (USHORT i=0; i<nCount; i++)
582 const ScValidationData* pData = (*pValidationList)[i];
583 ULONG nKey = pData->GetKey();
584 if ( pData->EqualEntries( rNew ) )
585 return nKey;
586 if ( nKey > nMax )
587 nMax = nKey;
590 // Der Aufruf kann aus ScPatternAttr::PutInPool kommen, darum Clone (echte Kopie)
592 ULONG nNewKey = nMax + 1;
593 ScValidationData* pInsert = rNew.Clone(this);
594 pInsert->SetKey( nNewKey );
595 pValidationList->InsertNew( pInsert );
596 return nNewKey;
599 const SfxPoolItem* ScDocument::GetEffItem(
600 SCCOL nCol, SCROW nRow, SCTAB nTab, USHORT nWhich ) const
602 const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
603 if ( pPattern )
605 const SfxItemSet& rSet = pPattern->GetItemSet();
606 const SfxPoolItem* pItem;
607 if ( rSet.GetItemState( ATTR_CONDITIONAL, TRUE, &pItem ) == SFX_ITEM_SET )
609 ULONG nIndex = ((const SfxUInt32Item*)pItem)->GetValue();
610 if (nIndex && pCondFormList)
612 const ScConditionalFormat* pForm = pCondFormList->GetFormat( nIndex );
613 if ( pForm )
615 ScBaseCell* pCell = ((ScDocument*)this)->GetCell(ScAddress(nCol,nRow,nTab));
616 String aStyle = pForm->GetCellStyle( pCell, ScAddress(nCol, nRow, nTab) );
617 if (aStyle.Len())
619 SfxStyleSheetBase* pStyleSheet = xPoolHelper->GetStylePool()->Find(
620 aStyle, SFX_STYLE_FAMILY_PARA );
621 if ( pStyleSheet && pStyleSheet->GetItemSet().GetItemState(
622 nWhich, TRUE, &pItem ) == SFX_ITEM_SET )
623 return pItem;
628 return &rSet.Get( nWhich );
630 DBG_ERROR("kein Pattern");
631 return NULL;
634 const SfxItemSet* ScDocument::GetCondResult( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
636 const ScConditionalFormat* pForm = GetCondFormat( nCol, nRow, nTab );
637 if ( pForm )
639 ScBaseCell* pCell = ((ScDocument*)this)->GetCell(ScAddress(nCol,nRow,nTab));
640 String aStyle = pForm->GetCellStyle( pCell, ScAddress(nCol, nRow, nTab) );
641 if (aStyle.Len())
643 SfxStyleSheetBase* pStyleSheet = xPoolHelper->GetStylePool()->Find( aStyle, SFX_STYLE_FAMILY_PARA );
644 if ( pStyleSheet )
645 return &pStyleSheet->GetItemSet();
646 // if style is not there, treat like no condition
649 return NULL;
652 const ScConditionalFormat* ScDocument::GetCondFormat(
653 SCCOL nCol, SCROW nRow, SCTAB nTab ) const
655 ULONG nIndex = ((const SfxUInt32Item*)GetAttr(nCol,nRow,nTab,ATTR_CONDITIONAL))->GetValue();
656 if (nIndex)
658 if (pCondFormList)
659 return pCondFormList->GetFormat( nIndex );
660 else
662 DBG_ERROR("pCondFormList ist 0");
666 return NULL;
669 const ScValidationData* ScDocument::GetValidationEntry( ULONG nIndex ) const
671 if ( pValidationList )
672 return pValidationList->GetData( nIndex );
673 else
674 return NULL;
677 void ScDocument::FindConditionalFormat( ULONG nKey, ScRangeList& rRanges )
679 for (SCTAB i=0; i<=MAXTAB && pTab[i]; i++)
680 pTab[i]->FindConditionalFormat( nKey, rRanges );
683 void ScDocument::FindConditionalFormat( ULONG nKey, ScRangeList& rRanges, SCTAB nTab )
685 if(VALIDTAB(nTab) && pTab[nTab])
686 pTab[nTab]->FindConditionalFormat( nKey, rRanges );
689 void ScDocument::ConditionalChanged( ULONG nKey )
691 if ( nKey && pCondFormList && !bIsClip && !bIsUndo ) // nKey==0 -> noop
693 ScConditionalFormat* pForm = pCondFormList->GetFormat( nKey );
694 if (pForm)
695 pForm->InvalidateArea();
699 void ScDocument::SetCondFormList(ScConditionalFormatList* pNew)
701 if (pCondFormList)
703 pCondFormList->DeleteAndDestroy( 0, pCondFormList->Count() );
704 delete pCondFormList;
707 pCondFormList = pNew;
710 //------------------------------------------------------------------------
712 BOOL ScDocument::HasDetectiveOperations() const
714 return pDetOpList && pDetOpList->Count();
717 void ScDocument::AddDetectiveOperation( const ScDetOpData& rData )
719 if (!pDetOpList)
720 pDetOpList = new ScDetOpList;
722 pDetOpList->Append( new ScDetOpData( rData ) );
725 void ScDocument::ClearDetectiveOperations()
727 delete pDetOpList; // loescht auch die Eintraege
728 pDetOpList = NULL;
731 void ScDocument::SetDetOpList(ScDetOpList* pNew)
733 delete pDetOpList; // loescht auch die Eintraege
734 pDetOpList = pNew;
737 //------------------------------------------------------------------------
739 // Vergleich von Dokumenten
741 //------------------------------------------------------------------------
743 // Pfriemel-Faktoren
744 #define SC_DOCCOMP_MAXDIFF 256
745 #define SC_DOCCOMP_MINGOOD 128
746 #define SC_DOCCOMP_COLUMNS 10
747 #define SC_DOCCOMP_ROWS 100
750 USHORT ScDocument::RowDifferences( SCROW nThisRow, SCTAB nThisTab,
751 ScDocument& rOtherDoc, SCROW nOtherRow, SCTAB nOtherTab,
752 SCCOL nMaxCol, SCCOLROW* pOtherCols )
754 ULONG nDif = 0;
755 ULONG nUsed = 0;
756 for (SCCOL nThisCol=0; nThisCol<=nMaxCol; nThisCol++)
758 SCCOL nOtherCol;
759 if ( pOtherCols )
760 nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
761 else
762 nOtherCol = nThisCol;
764 if (ValidCol(nOtherCol)) // nur Spalten vergleichen, die in beiden Dateien sind
766 const ScBaseCell* pThisCell = GetCell( ScAddress( nThisCol, nThisRow, nThisTab ) );
767 const ScBaseCell* pOtherCell = rOtherDoc.GetCell( ScAddress( nOtherCol, nOtherRow, nOtherTab ) );
768 if (!ScBaseCell::CellEqual( pThisCell, pOtherCell ))
770 if ( pThisCell && pOtherCell )
771 nDif += 3;
772 else
773 nDif += 4; // Inhalt <-> leer zaehlt mehr
776 if ( ( pThisCell && pThisCell->GetCellType()!=CELLTYPE_NOTE ) ||
777 ( pOtherCell && pOtherCell->GetCellType()!=CELLTYPE_NOTE ) )
778 ++nUsed;
782 if (nUsed > 0)
783 return static_cast<USHORT>((nDif*64)/nUsed); // max.256 (SC_DOCCOMP_MAXDIFF)
785 DBG_ASSERT(!nDif,"Diff ohne Used");
786 return 0;
789 USHORT ScDocument::ColDifferences( SCCOL nThisCol, SCTAB nThisTab,
790 ScDocument& rOtherDoc, SCCOL nOtherCol, SCTAB nOtherTab,
791 SCROW nMaxRow, SCCOLROW* pOtherRows )
793 //! optimieren mit Iterator oder so
795 ULONG nDif = 0;
796 ULONG nUsed = 0;
797 for (SCROW nThisRow=0; nThisRow<=nMaxRow; nThisRow++)
799 SCROW nOtherRow;
800 if ( pOtherRows )
801 nOtherRow = pOtherRows[nThisRow];
802 else
803 nOtherRow = nThisRow;
805 if (ValidRow(nOtherRow)) // nur Zeilen vergleichen, die in beiden Dateien sind
807 const ScBaseCell* pThisCell = GetCell( ScAddress( nThisCol, nThisRow, nThisTab ) );
808 const ScBaseCell* pOtherCell = rOtherDoc.GetCell( ScAddress( nOtherCol, nOtherRow, nOtherTab ) );
809 if (!ScBaseCell::CellEqual( pThisCell, pOtherCell ))
811 if ( pThisCell && pOtherCell )
812 nDif += 3;
813 else
814 nDif += 4; // Inhalt <-> leer zaehlt mehr
817 if ( ( pThisCell && pThisCell->GetCellType()!=CELLTYPE_NOTE ) ||
818 ( pOtherCell && pOtherCell->GetCellType()!=CELLTYPE_NOTE ) )
819 ++nUsed;
823 if (nUsed > 0)
824 return static_cast<USHORT>((nDif*64)/nUsed); // max.256
826 DBG_ASSERT(!nDif,"Diff ohne Used");
827 return 0;
830 void ScDocument::FindOrder( SCCOLROW* pOtherRows, SCCOLROW nThisEndRow, SCCOLROW nOtherEndRow,
831 BOOL bColumns, ScDocument& rOtherDoc, SCTAB nThisTab, SCTAB nOtherTab,
832 SCCOLROW nEndCol, SCCOLROW* pTranslate, ScProgress* pProgress, ULONG nProAdd )
834 // bColumns=TRUE: Zeilen sind Spalten und umgekehrt
836 SCCOLROW nMaxCont; // wieviel weiter
837 SCCOLROW nMinGood; // was ist ein Treffer (incl.)
838 if ( bColumns )
840 nMaxCont = SC_DOCCOMP_COLUMNS; // 10 Spalten
841 nMinGood = SC_DOCCOMP_MINGOOD;
842 //! Extra Durchgang mit nMinGood = 0 ????
844 else
846 nMaxCont = SC_DOCCOMP_ROWS; // 100 Zeilen
847 nMinGood = SC_DOCCOMP_MINGOOD;
849 BOOL bUseTotal = bColumns && !pTranslate; // nur beim ersten Durchgang
852 SCCOLROW nOtherRow = 0;
853 USHORT nComp;
854 SCCOLROW nThisRow;
855 BOOL bTotal = FALSE; // ueber verschiedene nThisRow beibehalten
856 SCCOLROW nUnknown = 0;
857 for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++)
859 SCCOLROW nTempOther = nOtherRow;
860 BOOL bFound = FALSE;
861 USHORT nBest = SC_DOCCOMP_MAXDIFF;
862 SCCOLROW nMax = Min( nOtherEndRow, static_cast<SCCOLROW>(( nTempOther + nMaxCont + nUnknown )) );
863 for (SCCOLROW i=nTempOther; i<=nMax && nBest>0; i++) // bei 0 abbrechen
865 if (bColumns)
866 nComp = ColDifferences( static_cast<SCCOL>(nThisRow), nThisTab, rOtherDoc, static_cast<SCCOL>(i), nOtherTab, nEndCol, pTranslate );
867 else
868 nComp = RowDifferences( nThisRow, nThisTab, rOtherDoc, i, nOtherTab, static_cast<SCCOL>(nEndCol), pTranslate );
869 if ( nComp < nBest && ( nComp <= nMinGood || bTotal ) )
871 nTempOther = i;
872 nBest = nComp;
873 bFound = TRUE;
875 if ( nComp < SC_DOCCOMP_MAXDIFF || bFound )
876 bTotal = FALSE;
877 else if ( i == nTempOther && bUseTotal )
878 bTotal = TRUE; // nur ganz oben
880 if ( bFound )
882 pOtherRows[nThisRow] = nTempOther;
883 nOtherRow = nTempOther + 1;
884 nUnknown = 0;
886 else
888 pOtherRows[nThisRow] = SCROW_MAX;
889 ++nUnknown;
892 if (pProgress)
893 pProgress->SetStateOnPercent(nProAdd+static_cast<ULONG>(nThisRow));
896 // Bloecke ohne Uebereinstimmung ausfuellen
898 SCROW nFillStart = 0;
899 SCROW nFillPos = 0;
900 BOOL bInFill = FALSE;
901 for (nThisRow = 0; nThisRow <= nThisEndRow+1; nThisRow++)
903 SCROW nThisOther = ( nThisRow <= nThisEndRow ) ? pOtherRows[nThisRow] : (nOtherEndRow+1);
904 if ( ValidRow(nThisOther) )
906 if ( bInFill )
908 if ( nThisOther > nFillStart ) // ist was zu verteilen da?
910 SCROW nDiff1 = nThisOther - nFillStart;
911 SCROW nDiff2 = nThisRow - nFillPos;
912 SCROW nMinDiff = Min(nDiff1, nDiff2);
913 for (SCROW i=0; i<nMinDiff; i++)
914 pOtherRows[nFillPos+i] = nFillStart+i;
917 bInFill = FALSE;
919 nFillStart = nThisOther + 1;
920 nFillPos = nThisRow + 1;
922 else
923 bInFill = TRUE;
927 void ScDocument::CompareDocument( ScDocument& rOtherDoc )
929 if (!pChangeTrack)
930 return;
932 SCTAB nThisCount = GetTableCount();
933 SCTAB nOtherCount = rOtherDoc.GetTableCount();
934 SCTAB* pOtherTabs = new SCTAB[nThisCount];
935 SCTAB nThisTab;
937 // Tabellen mit gleichen Namen vergleichen
938 String aThisName;
939 String aOtherName;
940 for (nThisTab=0; nThisTab<nThisCount; nThisTab++)
942 SCTAB nOtherTab = SCTAB_MAX;
943 if (!IsScenario(nThisTab)) // Szenarien weglassen
945 GetName( nThisTab, aThisName );
946 for (SCTAB nTemp=0; nTemp<nOtherCount && nOtherTab>MAXTAB; nTemp++)
947 if (!rOtherDoc.IsScenario(nTemp))
949 rOtherDoc.GetName( nTemp, aOtherName );
950 if ( aThisName == aOtherName )
951 nOtherTab = nTemp;
954 pOtherTabs[nThisTab] = nOtherTab;
956 // auffuellen, damit einzeln umbenannte Tabellen nicht wegfallen
957 SCTAB nFillStart = 0;
958 SCTAB nFillPos = 0;
959 BOOL bInFill = FALSE;
960 for (nThisTab = 0; nThisTab <= nThisCount; nThisTab++)
962 SCTAB nThisOther = ( nThisTab < nThisCount ) ? pOtherTabs[nThisTab] : nOtherCount;
963 if ( ValidTab(nThisOther) )
965 if ( bInFill )
967 if ( nThisOther > nFillStart ) // ist was zu verteilen da?
969 SCTAB nDiff1 = nThisOther - nFillStart;
970 SCTAB nDiff2 = nThisTab - nFillPos;
971 SCTAB nMinDiff = Min(nDiff1, nDiff2);
972 for (SCTAB i=0; i<nMinDiff; i++)
973 if ( !IsScenario(nFillPos+i) && !rOtherDoc.IsScenario(nFillStart+i) )
974 pOtherTabs[nFillPos+i] = nFillStart+i;
977 bInFill = FALSE;
979 nFillStart = nThisOther + 1;
980 nFillPos = nThisTab + 1;
982 else
983 bInFill = TRUE;
987 // Tabellen in der gefundenen Reihenfolge vergleichen
990 for (nThisTab=0; nThisTab<nThisCount; nThisTab++)
992 SCTAB nOtherTab = pOtherTabs[nThisTab];
993 if ( ValidTab(nOtherTab) )
995 SCCOL nThisEndCol = 0;
996 SCROW nThisEndRow = 0;
997 SCCOL nOtherEndCol = 0;
998 SCROW nOtherEndRow = 0;
999 GetCellArea( nThisTab, nThisEndCol, nThisEndRow );
1000 rOtherDoc.GetCellArea( nOtherTab, nOtherEndCol, nOtherEndRow );
1001 SCCOL nEndCol = Max(nThisEndCol, nOtherEndCol);
1002 SCROW nEndRow = Max(nThisEndRow, nOtherEndRow);
1003 SCCOL nThisCol;
1004 SCROW nThisRow;
1005 ULONG n1,n2; // fuer AppendDeleteRange
1007 //! ein Progress ueber alle Tabellen ???
1008 String aTabName;
1009 GetName( nThisTab, aTabName );
1010 String aTemplate = ScGlobal::GetRscString(STR_PROGRESS_COMPARING);
1011 String aProText = aTemplate.GetToken( 0, '#' );
1012 aProText += aTabName;
1013 aProText += aTemplate.GetToken( 1, '#' );
1014 ScProgress aProgress( GetDocumentShell(),
1015 aProText, 3*nThisEndRow ); // 2x FindOrder, 1x hier
1016 long nProgressStart = 2*nThisEndRow; // start fuer hier
1018 SCCOLROW* pTempRows = new SCCOLROW[nThisEndRow+1];
1019 SCCOLROW* pOtherRows = new SCCOLROW[nThisEndRow+1];
1020 SCCOLROW* pOtherCols = new SCCOLROW[nThisEndCol+1];
1022 // eingefuegte/geloeschte Spalten/Zeilen finden:
1023 // Zwei Versuche:
1024 // 1) Original Zeilen vergleichen (pTempRows)
1025 // 2) Original Spalten vergleichen (pOtherCols)
1026 // mit dieser Spaltenreihenfolge Zeilen vergleichen (pOtherRows)
1028 //! Spalten vergleichen zweimal mit unterschiedlichem nMinGood ???
1030 // 1
1031 FindOrder( pTempRows, nThisEndRow, nOtherEndRow, FALSE,
1032 rOtherDoc, nThisTab, nOtherTab, nEndCol, NULL, &aProgress, 0 );
1033 // 2
1034 FindOrder( pOtherCols, nThisEndCol, nOtherEndCol, TRUE,
1035 rOtherDoc, nThisTab, nOtherTab, nEndRow, NULL, NULL, 0 );
1036 FindOrder( pOtherRows, nThisEndRow, nOtherEndRow, FALSE,
1037 rOtherDoc, nThisTab, nOtherTab, nThisEndCol,
1038 pOtherCols, &aProgress, nThisEndRow );
1040 ULONG nMatch1 = 0; // pTempRows, keine Spalten
1041 for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++)
1042 if (ValidRow(pTempRows[nThisRow]))
1043 nMatch1 += SC_DOCCOMP_MAXDIFF -
1044 RowDifferences( nThisRow, nThisTab, rOtherDoc, pTempRows[nThisRow],
1045 nOtherTab, nEndCol, NULL );
1047 ULONG nMatch2 = 0; // pOtherRows, pOtherCols
1048 for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++)
1049 if (ValidRow(pOtherRows[nThisRow]))
1050 nMatch2 += SC_DOCCOMP_MAXDIFF -
1051 RowDifferences( nThisRow, nThisTab, rOtherDoc, pOtherRows[nThisRow],
1052 nOtherTab, nThisEndCol, pOtherCols );
1054 if ( nMatch1 >= nMatch2 ) // ohne Spalten ?
1056 // Spalten zuruecksetzen
1057 for (nThisCol = 0; nThisCol<=nThisEndCol; nThisCol++)
1058 pOtherCols[nThisCol] = nThisCol;
1060 // Zeilenarrays vertauschen (geloescht werden sowieso beide)
1061 SCCOLROW* pSwap = pTempRows;
1062 pTempRows = pOtherRows;
1063 pOtherRows = pSwap;
1065 else
1067 // bleibt bei pOtherCols, pOtherRows
1071 // Change-Actions erzeugen
1072 // 1) Spalten von rechts
1073 // 2) Zeilen von unten
1074 // 3) einzelne Zellen in normaler Reihenfolge
1076 // Actions fuer eingefuegte/geloeschte Spalten
1078 SCCOL nLastOtherCol = static_cast<SCCOL>(nOtherEndCol + 1);
1079 // nThisEndCol ... 0
1080 for ( nThisCol = nThisEndCol+1; nThisCol > 0; )
1082 --nThisCol;
1083 SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
1084 if ( ValidCol(nOtherCol) && nOtherCol+1 < nLastOtherCol )
1086 // Luecke -> geloescht
1087 ScRange aDelRange( nOtherCol+1, 0, nOtherTab,
1088 nLastOtherCol-1, MAXROW, nOtherTab );
1089 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1091 if ( nOtherCol > MAXCOL ) // eingefuegt
1093 // zusammenfassen
1094 if ( nThisCol == nThisEndCol || ValidCol(static_cast<SCCOL>(pOtherCols[nThisCol+1])) )
1096 SCCOL nFirstNew = static_cast<SCCOL>(nThisCol);
1097 while ( nFirstNew > 0 && pOtherCols[nFirstNew-1] > MAXCOL )
1098 --nFirstNew;
1099 SCCOL nDiff = nThisCol - nFirstNew;
1100 ScRange aRange( nLastOtherCol, 0, nOtherTab,
1101 nLastOtherCol+nDiff, MAXROW, nOtherTab );
1102 pChangeTrack->AppendInsert( aRange );
1105 else
1106 nLastOtherCol = nOtherCol;
1108 if ( nLastOtherCol > 0 ) // ganz oben geloescht
1110 ScRange aDelRange( 0, 0, nOtherTab,
1111 nLastOtherCol-1, MAXROW, nOtherTab );
1112 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1115 // Actions fuer eingefuegte/geloeschte Zeilen
1117 SCROW nLastOtherRow = nOtherEndRow + 1;
1118 // nThisEndRow ... 0
1119 for ( nThisRow = nThisEndRow+1; nThisRow > 0; )
1121 --nThisRow;
1122 SCROW nOtherRow = pOtherRows[nThisRow];
1123 if ( ValidRow(nOtherRow) && nOtherRow+1 < nLastOtherRow )
1125 // Luecke -> geloescht
1126 ScRange aDelRange( 0, nOtherRow+1, nOtherTab,
1127 MAXCOL, nLastOtherRow-1, nOtherTab );
1128 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1130 if ( nOtherRow > MAXROW ) // eingefuegt
1132 // zusammenfassen
1133 if ( nThisRow == nThisEndRow || ValidRow(pOtherRows[nThisRow+1]) )
1135 SCROW nFirstNew = nThisRow;
1136 while ( nFirstNew > 0 && pOtherRows[nFirstNew-1] > MAXROW )
1137 --nFirstNew;
1138 SCROW nDiff = nThisRow - nFirstNew;
1139 ScRange aRange( 0, nLastOtherRow, nOtherTab,
1140 MAXCOL, nLastOtherRow+nDiff, nOtherTab );
1141 pChangeTrack->AppendInsert( aRange );
1144 else
1145 nLastOtherRow = nOtherRow;
1147 if ( nLastOtherRow > 0 ) // ganz oben geloescht
1149 ScRange aDelRange( 0, 0, nOtherTab,
1150 MAXCOL, nLastOtherRow-1, nOtherTab );
1151 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
1154 // Zeilen durchgehen um einzelne Zellen zu finden
1156 for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++)
1158 SCROW nOtherRow = pOtherRows[nThisRow];
1159 for (nThisCol = 0; nThisCol <= nThisEndCol; nThisCol++)
1161 SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
1162 ScAddress aThisPos( nThisCol, nThisRow, nThisTab );
1163 const ScBaseCell* pThisCell = GetCell( aThisPos );
1164 const ScBaseCell* pOtherCell = NULL;
1165 if ( ValidCol(nOtherCol) && ValidRow(nOtherRow) )
1167 ScAddress aOtherPos( nOtherCol, nOtherRow, nOtherTab );
1168 pOtherCell = rOtherDoc.GetCell( aOtherPos );
1170 if ( !ScBaseCell::CellEqual( pThisCell, pOtherCell ) )
1172 ScRange aRange( aThisPos );
1173 ScChangeActionContent* pAction = new ScChangeActionContent( aRange );
1174 pAction->SetOldValue( pOtherCell, &rOtherDoc, this );
1175 pAction->SetNewValue( pThisCell, this );
1176 pChangeTrack->Append( pAction );
1179 aProgress.SetStateOnPercent(nProgressStart+nThisRow);
1182 delete[] pOtherCols;
1183 delete[] pOtherRows;
1184 delete[] pTempRows;
1188 //! Inhalt von eingefuegten / geloeschten Tabellen ???
1189 //! Aktionen fuer eingefuegte / geloeschte Tabellen ???
1191 delete[] pOtherTabs;