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"
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
)
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
;
85 aRefData
.nCol
= nVCol
;
86 aRefData
.nRow
= nVRow
;
87 aRefData
.nTab
= nVTab
;
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
);
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");
112 USHORT nErrCode
= pCell
->GetErrCode();
113 nX
= pCell
->GetValueAlways();
114 if (nErrCode
== 0) // kein fehler beim Rechnen
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
);
137 while (i
<= MAXTAB
&& !bStop
) // erste markierte Tabelle finden
139 if (pTab
[i
] && rMark
.GetTableSelect(i
))
148 DBG_ERROR("ScDocument::InsertMatrixFormula Keine Tabelle markiert");
152 ScFormulaCell
* pCell
;
153 ScAddress
aPos( nCol1
, nRow1
, nTab1
);
155 pCell
= new ScFormulaCell( this, aPos
, pArr
, eGram
, MM_FORMULA
);
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
))
164 pTab
[i
]->PutCell(nCol1
, nRow1
, pCell
);
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
) );
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) );
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
);
224 while (i
<= MAXTAB
&& !bStop
) // erste markierte Tabelle finden
226 if (pTab
[i
] && rMark
.GetTableSelect(i
))
235 DBG_ERROR("ScDocument::InsertTableOp: Keine Tabelle markiert");
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
);
250 aForString
+= rParam
.aRefColCell
.GetRefString(this, nTab1
);
252 aRef
.Set( nCol1
, nRow1
, nTab1
, FALSE
, TRUE
, TRUE
);
253 aForString
+= aRef
.GetRefString(this, nTab1
);
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
);
263 aForString
+= rParam
.aRefRowCell
.GetRefString(this, nTab1
);
265 aRef
.Set( nCol1
, nRow1
, nTab1
, TRUE
, FALSE
, TRUE
);
266 aForString
+= aRef
.GetRefString(this, nTab1
);
268 nRow2
= Min( nRow2
, (SCROW
)(rParam
.aRefFormulaEnd
.Row() -
269 rParam
.aRefFormulaCell
.Row() + nRow1
+ 1));
273 aForString
+= rParam
.aRefFormulaCell
.GetRefString(this, nTab1
);
275 aForString
+= rParam
.aRefColCell
.GetRefString(this, nTab1
);
277 aRef
.Set( nCol1
, nRow1
+ 1, nTab1
, FALSE
, TRUE
, TRUE
);
278 aForString
+= aRef
.GetRefString(this, nTab1
);
280 aForString
+= rParam
.aRefRowCell
.GetRefString(this, nTab1
);
282 aRef
.Set( nCol1
+ 1, nRow1
, nTab1
, TRUE
, FALSE
, TRUE
);
283 aForString
+= aRef
.GetRefString(this, nTab1
);
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;
302 ScExternalRefManager
* pRefMgr
= NULL
;
305 while (!bAllMarked
&& (t
= static_cast<ScToken
*>(rArr
.GetNextReferenceOrName())) != NULL
)
307 if (t
->GetOpCode() == ocExternalRef
)
310 pRefMgr
= GetExternalRefManager();
311 switch (t
->GetType())
313 case svExternalSingleRef
:
314 bAllMarked
= pRefMgr
->setCacheTableReferenced(
315 t
->GetIndex(), t
->GetString(), 1);
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
);
326 /* TODO: external names aren't supported yet, but would
327 * have to be marked as well, if so. Mechanism would be
329 DBG_ERRORFILE("ScDocument::MarkUsedExternalReferences: implement the svExternalName case!");
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
);
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
);
357 BOOL
ScDocument::ReplaceStyle(const SvxSearchItem
& rSearchItem
,
358 SCCOL nCol
, SCROW nRow
, SCTAB nTab
,
363 return pTab
[nTab
]->ReplaceStyle(rSearchItem
, nCol
, nRow
, rMark
, bIsUndoP
);
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
)
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
);
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
);
432 xub_StrLen
ScDocument::GetMaxNumberStringLen( USHORT
& nPrecision
, SCTAB nTab
,
434 SCROW nRowStart
, SCROW nRowEnd
) const
436 if (ValidTab(nTab
) && pTab
[nTab
])
437 return pTab
[nTab
]->GetMaxNumberStringLen( nPrecision
, nCol
,
438 nRowStart
, nRowEnd
);
443 BOOL
ScDocument::GetSelectionFunction( ScSubTotalFunc eFunc
,
444 const ScAddress
& rCursor
, const ScMarkData
& rMark
,
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 !!!!!
468 case SUBTOTAL_FUNC_SUM
:
469 rResult
= aData
.nVal
;
471 case SUBTOTAL_FUNC_CNT
:
472 case SUBTOTAL_FUNC_CNT2
:
473 rResult
= aData
.nCount
;
475 case SUBTOTAL_FUNC_AVE
:
477 rResult
= aData
.nVal
/ (double) aData
.nCount
;
481 case SUBTOTAL_FUNC_MAX
:
482 case SUBTOTAL_FUNC_MIN
:
484 rResult
= aData
.nVal
;
490 // added to avoid warnings
497 return !aData
.bError
;
500 double ScDocument::RoundValueAsShown( double fVal
, ULONG nFormat
)
503 if ( (nType
= GetFormatTable()->GetType( nFormat
)) != NUMBERFORMAT_DATE
504 && nType
!= NUMBERFORMAT_TIME
&& nType
!= NUMBERFORMAT_DATETIME
)
509 nPrecision
= (short)GetFormatTable()->GetFormatPrecision( nFormat
);
512 case NUMBERFORMAT_PERCENT
: // 0,41% == 0,0041
515 case NUMBERFORMAT_SCIENTIFIC
: // 1,23e-3 == 0,00123
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
) ) );
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
538 // bedingte Formate und Gueltigkeitsbereiche
541 ULONG
ScDocument::AddCondFormat( const ScConditionalFormat
& rNew
)
544 return 0; // leer ist immer 0
547 pCondFormList
= new ScConditionalFormatList
;
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
) )
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
);
570 ULONG
ScDocument::AddValidationEntry( const ScValidationData
& rNew
)
573 return 0; // leer ist immer 0
575 if (!pValidationList
)
576 pValidationList
= new ScValidationDataList
;
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
) )
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
);
599 const SfxPoolItem
* ScDocument::GetEffItem(
600 SCCOL nCol
, SCROW nRow
, SCTAB nTab
, USHORT nWhich
) const
602 const ScPatternAttr
* pPattern
= GetPattern( nCol
, nRow
, nTab
);
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
);
615 ScBaseCell
* pCell
= ((ScDocument
*)this)->GetCell(ScAddress(nCol
,nRow
,nTab
));
616 String aStyle
= pForm
->GetCellStyle( pCell
, ScAddress(nCol
, nRow
, nTab
) );
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
)
628 return &rSet
.Get( nWhich
);
630 DBG_ERROR("kein Pattern");
634 const SfxItemSet
* ScDocument::GetCondResult( SCCOL nCol
, SCROW nRow
, SCTAB nTab
) const
636 const ScConditionalFormat
* pForm
= GetCondFormat( nCol
, nRow
, nTab
);
639 ScBaseCell
* pCell
= ((ScDocument
*)this)->GetCell(ScAddress(nCol
,nRow
,nTab
));
640 String aStyle
= pForm
->GetCellStyle( pCell
, ScAddress(nCol
, nRow
, nTab
) );
643 SfxStyleSheetBase
* pStyleSheet
= xPoolHelper
->GetStylePool()->Find( aStyle
, SFX_STYLE_FAMILY_PARA
);
645 return &pStyleSheet
->GetItemSet();
646 // if style is not there, treat like no condition
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();
659 return pCondFormList
->GetFormat( nIndex
);
662 DBG_ERROR("pCondFormList ist 0");
669 const ScValidationData
* ScDocument::GetValidationEntry( ULONG nIndex
) const
671 if ( pValidationList
)
672 return pValidationList
->GetData( nIndex
);
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
);
695 pForm
->InvalidateArea();
699 void ScDocument::SetCondFormList(ScConditionalFormatList
* pNew
)
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
)
720 pDetOpList
= new ScDetOpList
;
722 pDetOpList
->Append( new ScDetOpData( rData
) );
725 void ScDocument::ClearDetectiveOperations()
727 delete pDetOpList
; // loescht auch die Eintraege
731 void ScDocument::SetDetOpList(ScDetOpList
* pNew
)
733 delete pDetOpList
; // loescht auch die Eintraege
737 //------------------------------------------------------------------------
739 // Vergleich von Dokumenten
741 //------------------------------------------------------------------------
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
)
756 for (SCCOL nThisCol
=0; nThisCol
<=nMaxCol
; nThisCol
++)
760 nOtherCol
= static_cast<SCCOL
>(pOtherCols
[nThisCol
]);
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
)
773 nDif
+= 4; // Inhalt <-> leer zaehlt mehr
776 if ( ( pThisCell
&& pThisCell
->GetCellType()!=CELLTYPE_NOTE
) ||
777 ( pOtherCell
&& pOtherCell
->GetCellType()!=CELLTYPE_NOTE
) )
783 return static_cast<USHORT
>((nDif
*64)/nUsed
); // max.256 (SC_DOCCOMP_MAXDIFF)
785 DBG_ASSERT(!nDif
,"Diff ohne Used");
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
797 for (SCROW nThisRow
=0; nThisRow
<=nMaxRow
; nThisRow
++)
801 nOtherRow
= pOtherRows
[nThisRow
];
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
)
814 nDif
+= 4; // Inhalt <-> leer zaehlt mehr
817 if ( ( pThisCell
&& pThisCell
->GetCellType()!=CELLTYPE_NOTE
) ||
818 ( pOtherCell
&& pOtherCell
->GetCellType()!=CELLTYPE_NOTE
) )
824 return static_cast<USHORT
>((nDif
*64)/nUsed
); // max.256
826 DBG_ASSERT(!nDif
,"Diff ohne Used");
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.)
840 nMaxCont
= SC_DOCCOMP_COLUMNS
; // 10 Spalten
841 nMinGood
= SC_DOCCOMP_MINGOOD
;
842 //! Extra Durchgang mit nMinGood = 0 ????
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;
855 BOOL bTotal
= FALSE
; // ueber verschiedene nThisRow beibehalten
856 SCCOLROW nUnknown
= 0;
857 for (nThisRow
= 0; nThisRow
<= nThisEndRow
; nThisRow
++)
859 SCCOLROW nTempOther
= nOtherRow
;
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
866 nComp
= ColDifferences( static_cast<SCCOL
>(nThisRow
), nThisTab
, rOtherDoc
, static_cast<SCCOL
>(i
), nOtherTab
, nEndCol
, pTranslate
);
868 nComp
= RowDifferences( nThisRow
, nThisTab
, rOtherDoc
, i
, nOtherTab
, static_cast<SCCOL
>(nEndCol
), pTranslate
);
869 if ( nComp
< nBest
&& ( nComp
<= nMinGood
|| bTotal
) )
875 if ( nComp
< SC_DOCCOMP_MAXDIFF
|| bFound
)
877 else if ( i
== nTempOther
&& bUseTotal
)
878 bTotal
= TRUE
; // nur ganz oben
882 pOtherRows
[nThisRow
] = nTempOther
;
883 nOtherRow
= nTempOther
+ 1;
888 pOtherRows
[nThisRow
] = SCROW_MAX
;
893 pProgress
->SetStateOnPercent(nProAdd
+static_cast<ULONG
>(nThisRow
));
896 // Bloecke ohne Uebereinstimmung ausfuellen
898 SCROW nFillStart
= 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
) )
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
;
919 nFillStart
= nThisOther
+ 1;
920 nFillPos
= nThisRow
+ 1;
927 void ScDocument::CompareDocument( ScDocument
& rOtherDoc
)
932 SCTAB nThisCount
= GetTableCount();
933 SCTAB nOtherCount
= rOtherDoc
.GetTableCount();
934 SCTAB
* pOtherTabs
= new SCTAB
[nThisCount
];
937 // Tabellen mit gleichen Namen vergleichen
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
)
954 pOtherTabs
[nThisTab
] = nOtherTab
;
956 // auffuellen, damit einzeln umbenannte Tabellen nicht wegfallen
957 SCTAB nFillStart
= 0;
959 BOOL bInFill
= FALSE
;
960 for (nThisTab
= 0; nThisTab
<= nThisCount
; nThisTab
++)
962 SCTAB nThisOther
= ( nThisTab
< nThisCount
) ? pOtherTabs
[nThisTab
] : nOtherCount
;
963 if ( ValidTab(nThisOther
) )
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
;
979 nFillStart
= nThisOther
+ 1;
980 nFillPos
= nThisTab
+ 1;
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
);
1005 ULONG n1
,n2
; // fuer AppendDeleteRange
1007 //! ein Progress ueber alle Tabellen ???
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:
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 ???
1031 FindOrder( pTempRows
, nThisEndRow
, nOtherEndRow
, FALSE
,
1032 rOtherDoc
, nThisTab
, nOtherTab
, nEndCol
, NULL
, &aProgress
, 0 );
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
;
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; )
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
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
)
1099 SCCOL nDiff
= nThisCol
- nFirstNew
;
1100 ScRange
aRange( nLastOtherCol
, 0, nOtherTab
,
1101 nLastOtherCol
+nDiff
, MAXROW
, nOtherTab
);
1102 pChangeTrack
->AppendInsert( aRange
);
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; )
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
1133 if ( nThisRow
== nThisEndRow
|| ValidRow(pOtherRows
[nThisRow
+1]) )
1135 SCROW nFirstNew
= nThisRow
;
1136 while ( nFirstNew
> 0 && pOtherRows
[nFirstNew
-1] > MAXROW
)
1138 SCROW nDiff
= nThisRow
- nFirstNew
;
1139 ScRange
aRange( 0, nLastOtherRow
, nOtherTab
,
1140 MAXCOL
, nLastOtherRow
+nDiff
, nOtherTab
);
1141 pChangeTrack
->AppendInsert( aRange
);
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
;
1188 //! Inhalt von eingefuegten / geloeschten Tabellen ???
1189 //! Aktionen fuer eingefuegte / geloeschte Tabellen ???
1191 delete[] pOtherTabs
;