summaryrefslogtreecommitdiff
path: root/sc/source/core/data/documen4.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/data/documen4.cxx')
-rw-r--r--sc/source/core/data/documen4.cxx1198
1 files changed, 1198 insertions, 0 deletions
diff --git a/sc/source/core/data/documen4.cxx b/sc/source/core/data/documen4.cxx
new file mode 100644
index 000000000000..148cc367534c
--- /dev/null
+++ b/sc/source/core/data/documen4.cxx
@@ -0,0 +1,1198 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sc.hxx"
+
+
+
+// INCLUDE ---------------------------------------------------------------
+
+#include <svl/intitem.hxx>
+#include <svl/zforlist.hxx>
+#include <vcl/sound.hxx>
+#include <formula/token.hxx>
+
+#include "document.hxx"
+#include "table.hxx"
+#include "globstr.hrc"
+#include "subtotal.hxx"
+#include "docoptio.hxx"
+#include "interpre.hxx"
+#include "markdata.hxx"
+#include "validat.hxx"
+#include "scitems.hxx"
+#include "stlpool.hxx"
+#include "poolhelp.hxx"
+#include "detdata.hxx"
+#include "patattr.hxx"
+#include "chgtrack.hxx"
+#include "progress.hxx"
+#include "paramisc.hxx"
+#include "compiler.hxx"
+#include "externalrefmgr.hxx"
+
+using namespace formula;
+
+// -----------------------------------------------------------------------
+
+// Nach der Regula Falsi Methode
+BOOL ScDocument::Solver(SCCOL nFCol, SCROW nFRow, SCTAB nFTab,
+ SCCOL nVCol, SCROW nVRow, SCTAB nVTab,
+ const String& sValStr, double& nX)
+{
+ BOOL bRet = FALSE;
+ nX = 0.0;
+ if (ValidColRow(nFCol, nFRow) && ValidColRow(nVCol, nVRow) &&
+ VALIDTAB(nFTab) && VALIDTAB(nVTab) && pTab[nFTab] && pTab[nVTab])
+ {
+ CellType eFType, eVType;
+ GetCellType(nFCol, nFRow, nFTab, eFType);
+ GetCellType(nVCol, nVRow, nVTab, eVType);
+ // CELLTYPE_NOTE: no value, but referenced by formula
+ // #i108005# convert target value to number using default format,
+ // as previously done in ScInterpreter::GetDouble
+ double nTargetVal = 0.0;
+ sal_uInt32 nFIndex = 0;
+ if (eFType == CELLTYPE_FORMULA && (eVType == CELLTYPE_VALUE || eVType == CELLTYPE_NOTE) &&
+ GetFormatTable()->IsNumberFormat(sValStr, nFIndex, nTargetVal))
+ {
+ ScSingleRefData aRefData;
+ aRefData.InitFlags();
+ aRefData.nCol = nVCol;
+ aRefData.nRow = nVRow;
+ aRefData.nTab = nVTab;
+
+ ScTokenArray aArr;
+ aArr.AddOpCode( ocBackSolver );
+ aArr.AddOpCode( ocOpen );
+ aArr.AddSingleReference( aRefData );
+ aArr.AddOpCode( ocSep );
+
+ aRefData.nCol = nFCol;
+ aRefData.nRow = nFRow;
+ aRefData.nTab = nFTab;
+
+ aArr.AddSingleReference( aRefData );
+ aArr.AddOpCode( ocSep );
+ aArr.AddDouble( nTargetVal );
+ aArr.AddOpCode( ocClose );
+ aArr.AddOpCode( ocStop );
+
+ ScFormulaCell* pCell = new ScFormulaCell( this, ScAddress(), &aArr );
+
+ if (pCell)
+ {
+ // FIXME FIXME FIXME this might need to be reworked now that we have formula::FormulaErrorToken and ScFormulaResult, double check !!!
+ DBG_ERRORFILE("ScDocument::Solver: -> ScFormulaCell::GetValueAlways might need reimplementation");
+ pCell->Interpret();
+ USHORT nErrCode = pCell->GetErrCode();
+ nX = pCell->GetValueAlways();
+ if (nErrCode == 0) // kein fehler beim Rechnen
+ bRet = TRUE;
+ delete pCell;
+ }
+ }
+ }
+ return bRet;
+}
+
+void ScDocument::InsertMatrixFormula(SCCOL nCol1, SCROW nRow1,
+ SCCOL nCol2, SCROW nRow2,
+ const ScMarkData& rMark,
+ const String& rFormula,
+ const ScTokenArray* pArr,
+ const formula::FormulaGrammar::Grammar eGram )
+{
+ PutInOrder(nCol1, nCol2);
+ PutInOrder(nRow1, nRow2);
+ SCTAB i, nTab1;
+ SCCOL j;
+ SCROW k;
+ i = 0;
+ BOOL bStop = FALSE;
+ while (i <= MAXTAB && !bStop) // erste markierte Tabelle finden
+ {
+ if (pTab[i] && rMark.GetTableSelect(i))
+ bStop = TRUE;
+ else
+ i++;
+ }
+ nTab1 = i;
+ if (i == MAXTAB + 1)
+ {
+ Sound::Beep();
+ DBG_ERROR("ScDocument::InsertMatrixFormula Keine Tabelle markiert");
+ return;
+ }
+
+ ScFormulaCell* pCell;
+ ScAddress aPos( nCol1, nRow1, nTab1 );
+ if (pArr)
+ pCell = new ScFormulaCell( this, aPos, pArr, eGram, MM_FORMULA );
+ else
+ pCell = new ScFormulaCell( this, aPos, rFormula, eGram, MM_FORMULA );
+ pCell->SetMatColsRows( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1 );
+ for (i = 0; i <= MAXTAB; i++)
+ {
+ if (pTab[i] && rMark.GetTableSelect(i))
+ {
+ if (i == nTab1)
+ pTab[i]->PutCell(nCol1, nRow1, pCell);
+ else
+ pTab[i]->PutCell(nCol1, nRow1, pCell->CloneWithoutNote(*this, ScAddress( nCol1, nRow1, i), SC_CLONECELL_STARTLISTENING));
+ }
+ }
+
+ ScSingleRefData aRefData;
+ aRefData.InitFlags();
+ aRefData.nCol = nCol1;
+ aRefData.nRow = nRow1;
+ aRefData.nTab = nTab1;
+ aRefData.SetColRel( TRUE );
+ aRefData.SetRowRel( TRUE );
+ aRefData.SetTabRel( TRUE );
+ aRefData.CalcRelFromAbs( ScAddress( nCol1, nRow1, nTab1 ) );
+
+ ScTokenArray aArr;
+ ScToken* t = static_cast<ScToken*>(aArr.AddMatrixSingleReference( aRefData));
+
+ for (i = 0; i <= MAXTAB; i++)
+ {
+ if (pTab[i] && rMark.GetTableSelect(i))
+ {
+ pTab[i]->DoColResize( nCol1, nCol2, static_cast<SCSIZE>(nRow2 - nRow1 + 1) );
+ if (i != nTab1)
+ {
+ aRefData.nTab = i;
+ aRefData.nRelTab = i - nTab1;
+ t->GetSingleRef() = aRefData;
+ }
+ for (j = nCol1; j <= nCol2; j++)
+ {
+ for (k = nRow1; k <= nRow2; k++)
+ {
+ if (j != nCol1 || k != nRow1) // nicht in der ersten Zelle
+ {
+ // Array muss geklont werden, damit jede
+ // Zelle ein eigenes Array erhaelt!
+ aPos = ScAddress( j, k, i );
+ t->CalcRelFromAbs( aPos );
+ pCell = new ScFormulaCell( this, aPos, aArr.Clone(), eGram, MM_REFERENCE );
+ pTab[i]->PutCell(j, k, (ScBaseCell*) pCell);
+ }
+ }
+ }
+ }
+ }
+}
+
+void ScDocument::InsertTableOp(const ScTabOpParam& rParam, // Mehrfachoperation
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ const ScMarkData& rMark)
+{
+ PutInOrder(nCol1, nCol2);
+ PutInOrder(nRow1, nRow2);
+ SCTAB i, nTab1;
+ SCCOL j;
+ SCROW k;
+ i = 0;
+ BOOL bStop = FALSE;
+ while (i <= MAXTAB && !bStop) // erste markierte Tabelle finden
+ {
+ if (pTab[i] && rMark.GetTableSelect(i))
+ bStop = TRUE;
+ else
+ i++;
+ }
+ nTab1 = i;
+ if (i == MAXTAB + 1)
+ {
+ Sound::Beep();
+ DBG_ERROR("ScDocument::InsertTableOp: Keine Tabelle markiert");
+ return;
+ }
+
+ ScRefAddress aRef;
+ String aForString = '=';
+ aForString += ScCompiler::GetNativeSymbol(ocTableOp);
+ aForString += ScCompiler::GetNativeSymbol( ocOpen);
+
+ const String& sSep = ScCompiler::GetNativeSymbol( ocSep);
+ if (rParam.nMode == 0) // nur Spalte
+ {
+ aRef.Set( rParam.aRefFormulaCell.GetAddress(), TRUE, FALSE, FALSE );
+ aForString += aRef.GetRefString(this, nTab1);
+ aForString += sSep;
+ aForString += rParam.aRefColCell.GetRefString(this, nTab1);
+ aForString += sSep;
+ aRef.Set( nCol1, nRow1, nTab1, FALSE, TRUE, TRUE );
+ aForString += aRef.GetRefString(this, nTab1);
+ nCol1++;
+ nCol2 = Min( nCol2, (SCCOL)(rParam.aRefFormulaEnd.Col() -
+ rParam.aRefFormulaCell.Col() + nCol1 + 1));
+ }
+ else if (rParam.nMode == 1) // nur zeilenweise
+ {
+ aRef.Set( rParam.aRefFormulaCell.GetAddress(), FALSE, TRUE, FALSE );
+ aForString += aRef.GetRefString(this, nTab1);
+ aForString += sSep;
+ aForString += rParam.aRefRowCell.GetRefString(this, nTab1);
+ aForString += sSep;
+ aRef.Set( nCol1, nRow1, nTab1, TRUE, FALSE, TRUE );
+ aForString += aRef.GetRefString(this, nTab1);
+ nRow1++;
+ nRow2 = Min( nRow2, (SCROW)(rParam.aRefFormulaEnd.Row() -
+ rParam.aRefFormulaCell.Row() + nRow1 + 1));
+ }
+ else // beides
+ {
+ aForString += rParam.aRefFormulaCell.GetRefString(this, nTab1);
+ aForString += sSep;
+ aForString += rParam.aRefColCell.GetRefString(this, nTab1);
+ aForString += sSep;
+ aRef.Set( nCol1, nRow1 + 1, nTab1, FALSE, TRUE, TRUE );
+ aForString += aRef.GetRefString(this, nTab1);
+ aForString += sSep;
+ aForString += rParam.aRefRowCell.GetRefString(this, nTab1);
+ aForString += sSep;
+ aRef.Set( nCol1 + 1, nRow1, nTab1, TRUE, FALSE, TRUE );
+ aForString += aRef.GetRefString(this, nTab1);
+ nCol1++; nRow1++;
+ }
+ aForString += ScCompiler::GetNativeSymbol( ocClose);
+
+ ScFormulaCell aRefCell( this, ScAddress( nCol1, nRow1, nTab1 ), aForString,
+ formula::FormulaGrammar::GRAM_NATIVE, MM_NONE );
+ for( j = nCol1; j <= nCol2; j++ )
+ for( k = nRow1; k <= nRow2; k++ )
+ for (i = 0; i <= MAXTAB; i++)
+ if( pTab[i] && rMark.GetTableSelect(i) )
+ pTab[i]->PutCell( j, k, aRefCell.CloneWithoutNote( *this, ScAddress( j, k, i ), SC_CLONECELL_STARTLISTENING ) );
+}
+
+bool ScDocument::MarkUsedExternalReferences( ScTokenArray & rArr )
+{
+ bool bAllMarked = false;
+ if (rArr.GetLen())
+ {
+ ScExternalRefManager* pRefMgr = NULL;
+ rArr.Reset();
+ ScToken* t;
+ while (!bAllMarked && (t = static_cast<ScToken*>(rArr.GetNextReferenceOrName())) != NULL)
+ {
+ if (t->GetOpCode() == ocExternalRef)
+ {
+ if (!pRefMgr)
+ pRefMgr = GetExternalRefManager();
+ switch (t->GetType())
+ {
+ case svExternalSingleRef:
+ bAllMarked = pRefMgr->setCacheTableReferenced(
+ t->GetIndex(), t->GetString(), 1);
+ break;
+ case svExternalDoubleRef:
+ {
+ const ScComplexRefData& rRef = t->GetDoubleRef();
+ size_t nSheets = rRef.Ref2.nTab - rRef.Ref1.nTab + 1;
+ bAllMarked = pRefMgr->setCacheTableReferenced(
+ t->GetIndex(), t->GetString(), nSheets);
+ }
+ break;
+ case svExternalName:
+ /* TODO: external names aren't supported yet, but would
+ * have to be marked as well, if so. Mechanism would be
+ * different. */
+ DBG_ERRORFILE("ScDocument::MarkUsedExternalReferences: implement the svExternalName case!");
+ break;
+ default: break;
+ }
+ }
+ }
+ }
+ return bAllMarked;
+}
+
+BOOL ScDocument::GetNextSpellingCell(SCCOL& nCol, SCROW& nRow, SCTAB nTab,
+ BOOL bInSel, const ScMarkData& rMark) const
+{
+ if (ValidTab(nTab) && pTab[nTab])
+ return pTab[nTab]->GetNextSpellingCell( nCol, nRow, bInSel, rMark );
+ else
+ return FALSE;
+}
+
+BOOL ScDocument::GetNextMarkedCell( SCCOL& rCol, SCROW& rRow, SCTAB nTab,
+ const ScMarkData& rMark )
+{
+ if (ValidTab(nTab) && pTab[nTab])
+ return pTab[nTab]->GetNextMarkedCell( rCol, rRow, rMark );
+ else
+ return FALSE;
+}
+
+BOOL ScDocument::ReplaceStyle(const SvxSearchItem& rSearchItem,
+ SCCOL nCol, SCROW nRow, SCTAB nTab,
+ ScMarkData& rMark,
+ BOOL bIsUndoP)
+{
+ if (pTab[nTab])
+ return pTab[nTab]->ReplaceStyle(rSearchItem, nCol, nRow, rMark, bIsUndoP);
+ else
+ return FALSE;
+}
+
+void ScDocument::CompileDBFormula()
+{
+ for (SCTAB i=0; i<=MAXTAB; i++)
+ {
+ if (pTab[i]) pTab[i]->CompileDBFormula();
+ }
+}
+
+void ScDocument::CompileDBFormula( BOOL bCreateFormulaString )
+{
+ for (SCTAB i=0; i<=MAXTAB; i++)
+ {
+ if (pTab[i]) pTab[i]->CompileDBFormula( bCreateFormulaString );
+ }
+}
+
+void ScDocument::CompileNameFormula( BOOL bCreateFormulaString )
+{
+ if ( pCondFormList )
+ pCondFormList->CompileAll(); // nach ScNameDlg noetig
+
+ for (SCTAB i=0; i<=MAXTAB; i++)
+ {
+ if (pTab[i]) pTab[i]->CompileNameFormula( bCreateFormulaString );
+ }
+}
+
+void ScDocument::CompileColRowNameFormula()
+{
+ for (SCTAB i=0; i<=MAXTAB; i++)
+ {
+ if (pTab[i]) pTab[i]->CompileColRowNameFormula();
+ }
+}
+
+void ScDocument::DoColResize( SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCSIZE nAdd )
+{
+ if (ValidTab(nTab) && pTab[nTab])
+ pTab[nTab]->DoColResize( nCol1, nCol2, nAdd );
+ else
+ {
+ DBG_ERROR("DoColResize: falsche Tabelle");
+ }
+}
+
+void ScDocument::InvalidateTableArea()
+{
+ for (SCTAB nTab=0; nTab<=MAXTAB && pTab[nTab]; nTab++)
+ {
+ pTab[nTab]->InvalidateTableArea();
+ if ( pTab[nTab]->IsScenario() )
+ pTab[nTab]->InvalidateScenarioRanges();
+ }
+}
+
+sal_Int32 ScDocument::GetMaxStringLen( SCTAB nTab, SCCOL nCol,
+ SCROW nRowStart, SCROW nRowEnd, CharSet eCharSet ) const
+{
+ if (ValidTab(nTab) && pTab[nTab])
+ return pTab[nTab]->GetMaxStringLen( nCol, nRowStart, nRowEnd, eCharSet );
+ else
+ return 0;
+}
+
+xub_StrLen ScDocument::GetMaxNumberStringLen( sal_uInt16& nPrecision, SCTAB nTab,
+ SCCOL nCol,
+ SCROW nRowStart, SCROW nRowEnd ) const
+{
+ if (ValidTab(nTab) && pTab[nTab])
+ return pTab[nTab]->GetMaxNumberStringLen( nPrecision, nCol,
+ nRowStart, nRowEnd );
+ else
+ return 0;
+}
+
+BOOL ScDocument::GetSelectionFunction( ScSubTotalFunc eFunc,
+ const ScAddress& rCursor, const ScMarkData& rMark,
+ double& rResult )
+{
+ ScFunctionData aData(eFunc);
+
+ ScRange aSingle( rCursor );
+ if ( rMark.IsMarked() )
+ rMark.GetMarkArea(aSingle);
+
+ SCCOL nStartCol = aSingle.aStart.Col();
+ SCROW nStartRow = aSingle.aStart.Row();
+ SCCOL nEndCol = aSingle.aEnd.Col();
+ SCROW nEndRow = aSingle.aEnd.Row();
+
+ for (SCTAB nTab=0; nTab<=MAXTAB && !aData.bError; nTab++)
+ if (pTab[nTab] && rMark.GetTableSelect(nTab))
+ pTab[nTab]->UpdateSelectionFunction( aData,
+ nStartCol, nStartRow, nEndCol, nEndRow, rMark );
+
+ //! rMark an UpdateSelectionFunction uebergeben !!!!!
+
+ if (!aData.bError)
+ switch (eFunc)
+ {
+ case SUBTOTAL_FUNC_SUM:
+ rResult = aData.nVal;
+ break;
+ case SUBTOTAL_FUNC_CNT:
+ case SUBTOTAL_FUNC_CNT2:
+ rResult = aData.nCount;
+ break;
+ case SUBTOTAL_FUNC_AVE:
+ if (aData.nCount)
+ rResult = aData.nVal / (double) aData.nCount;
+ else
+ aData.bError = TRUE;
+ break;
+ case SUBTOTAL_FUNC_MAX:
+ case SUBTOTAL_FUNC_MIN:
+ if (aData.nCount)
+ rResult = aData.nVal;
+ else
+ aData.bError = TRUE;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ if (aData.bError)
+ rResult = 0.0;
+
+ return !aData.bError;
+}
+
+double ScDocument::RoundValueAsShown( double fVal, ULONG nFormat )
+{
+ short nType;
+ if ( (nType = GetFormatTable()->GetType( nFormat )) != NUMBERFORMAT_DATE
+ && nType != NUMBERFORMAT_TIME && nType != NUMBERFORMAT_DATETIME )
+ {
+ short nPrecision;
+ if ( nFormat )
+ {
+ nPrecision = (short)GetFormatTable()->GetFormatPrecision( nFormat );
+ switch ( nType )
+ {
+ case NUMBERFORMAT_PERCENT: // 0,41% == 0,0041
+ nPrecision += 2;
+ break;
+ case NUMBERFORMAT_SCIENTIFIC: // 1,23e-3 == 0,00123
+ {
+ if ( fVal > 0.0 )
+ nPrecision = sal::static_int_cast<short>( nPrecision - (short)floor( log10( fVal ) ) );
+ else if ( fVal < 0.0 )
+ nPrecision = sal::static_int_cast<short>( nPrecision - (short)floor( log10( -fVal ) ) );
+ break;
+ }
+ }
+ }
+ else
+ nPrecision = (short)GetDocOptions().GetStdPrecision();
+ double fRound = ::rtl::math::round( fVal, nPrecision );
+ if ( ::rtl::math::approxEqual( fVal, fRound ) )
+ return fVal; // durch Rundung hoechstens Fehler
+ else
+ return fRound;
+ }
+ else
+ return fVal;
+}
+
+//
+// bedingte Formate und Gueltigkeitsbereiche
+//
+
+ULONG ScDocument::AddCondFormat( const ScConditionalFormat& rNew )
+{
+ if (rNew.IsEmpty())
+ return 0; // leer ist immer 0
+
+ if (!pCondFormList)
+ pCondFormList = new ScConditionalFormatList;
+
+ ULONG nMax = 0;
+ USHORT nCount = pCondFormList->Count();
+ for (USHORT i=0; i<nCount; i++)
+ {
+ const ScConditionalFormat* pForm = (*pCondFormList)[i];
+ ULONG nKey = pForm->GetKey();
+ if ( pForm->EqualEntries( rNew ) )
+ return nKey;
+ if ( nKey > nMax )
+ nMax = nKey;
+ }
+
+ // Der Aufruf kann aus ScPatternAttr::PutInPool kommen, darum Clone (echte Kopie)
+
+ ULONG nNewKey = nMax + 1;
+ ScConditionalFormat* pInsert = rNew.Clone(this);
+ pInsert->SetKey( nNewKey );
+ pCondFormList->InsertNew( pInsert );
+ return nNewKey;
+}
+
+ULONG ScDocument::AddValidationEntry( const ScValidationData& rNew )
+{
+ if (rNew.IsEmpty())
+ return 0; // leer ist immer 0
+
+ if (!pValidationList)
+ pValidationList = new ScValidationDataList;
+
+ ULONG nMax = 0;
+ USHORT nCount = pValidationList->Count();
+ for (USHORT i=0; i<nCount; i++)
+ {
+ const ScValidationData* pData = (*pValidationList)[i];
+ ULONG nKey = pData->GetKey();
+ if ( pData->EqualEntries( rNew ) )
+ return nKey;
+ if ( nKey > nMax )
+ nMax = nKey;
+ }
+
+ // Der Aufruf kann aus ScPatternAttr::PutInPool kommen, darum Clone (echte Kopie)
+
+ ULONG nNewKey = nMax + 1;
+ ScValidationData* pInsert = rNew.Clone(this);
+ pInsert->SetKey( nNewKey );
+ pValidationList->InsertNew( pInsert );
+ return nNewKey;
+}
+
+const SfxPoolItem* ScDocument::GetEffItem(
+ SCCOL nCol, SCROW nRow, SCTAB nTab, USHORT nWhich ) const
+{
+ const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
+ if ( pPattern )
+ {
+ const SfxItemSet& rSet = pPattern->GetItemSet();
+ const SfxPoolItem* pItem;
+ if ( rSet.GetItemState( ATTR_CONDITIONAL, TRUE, &pItem ) == SFX_ITEM_SET )
+ {
+ ULONG nIndex = ((const SfxUInt32Item*)pItem)->GetValue();
+ if (nIndex && pCondFormList)
+ {
+ const ScConditionalFormat* pForm = pCondFormList->GetFormat( nIndex );
+ if ( pForm )
+ {
+ ScBaseCell* pCell = ((ScDocument*)this)->GetCell(ScAddress(nCol,nRow,nTab));
+ String aStyle = pForm->GetCellStyle( pCell, ScAddress(nCol, nRow, nTab) );
+ if (aStyle.Len())
+ {
+ SfxStyleSheetBase* pStyleSheet = xPoolHelper->GetStylePool()->Find(
+ aStyle, SFX_STYLE_FAMILY_PARA );
+ if ( pStyleSheet && pStyleSheet->GetItemSet().GetItemState(
+ nWhich, TRUE, &pItem ) == SFX_ITEM_SET )
+ return pItem;
+ }
+ }
+ }
+ }
+ return &rSet.Get( nWhich );
+ }
+ DBG_ERROR("kein Pattern");
+ return NULL;
+}
+
+const SfxItemSet* ScDocument::GetCondResult( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ const ScConditionalFormat* pForm = GetCondFormat( nCol, nRow, nTab );
+ if ( pForm )
+ {
+ ScBaseCell* pCell = ((ScDocument*)this)->GetCell(ScAddress(nCol,nRow,nTab));
+ String aStyle = pForm->GetCellStyle( pCell, ScAddress(nCol, nRow, nTab) );
+ if (aStyle.Len())
+ {
+ SfxStyleSheetBase* pStyleSheet = xPoolHelper->GetStylePool()->Find( aStyle, SFX_STYLE_FAMILY_PARA );
+ if ( pStyleSheet )
+ return &pStyleSheet->GetItemSet();
+ // if style is not there, treat like no condition
+ }
+ }
+ return NULL;
+}
+
+const ScConditionalFormat* ScDocument::GetCondFormat(
+ SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ ULONG nIndex = ((const SfxUInt32Item*)GetAttr(nCol,nRow,nTab,ATTR_CONDITIONAL))->GetValue();
+ if (nIndex)
+ {
+ if (pCondFormList)
+ return pCondFormList->GetFormat( nIndex );
+ else
+ {
+ DBG_ERROR("pCondFormList ist 0");
+ }
+ }
+
+ return NULL;
+}
+
+const ScValidationData* ScDocument::GetValidationEntry( ULONG nIndex ) const
+{
+ if ( pValidationList )
+ return pValidationList->GetData( nIndex );
+ else
+ return NULL;
+}
+
+void ScDocument::FindConditionalFormat( ULONG nKey, ScRangeList& rRanges )
+{
+ for (SCTAB i=0; i<=MAXTAB && pTab[i]; i++)
+ pTab[i]->FindConditionalFormat( nKey, rRanges );
+}
+
+void ScDocument::FindConditionalFormat( ULONG nKey, ScRangeList& rRanges, SCTAB nTab )
+{
+ if(VALIDTAB(nTab) && pTab[nTab])
+ pTab[nTab]->FindConditionalFormat( nKey, rRanges );
+}
+
+void ScDocument::ConditionalChanged( ULONG nKey )
+{
+ if ( nKey && pCondFormList && !bIsClip && !bIsUndo ) // nKey==0 -> noop
+ {
+ ScConditionalFormat* pForm = pCondFormList->GetFormat( nKey );
+ if (pForm)
+ pForm->InvalidateArea();
+ }
+}
+
+void ScDocument::SetCondFormList(ScConditionalFormatList* pNew)
+{
+ if (pCondFormList)
+ {
+ pCondFormList->DeleteAndDestroy( 0, pCondFormList->Count() );
+ delete pCondFormList;
+ }
+
+ pCondFormList = pNew;
+}
+
+//------------------------------------------------------------------------
+
+BOOL ScDocument::HasDetectiveOperations() const
+{
+ return pDetOpList && pDetOpList->Count();
+}
+
+void ScDocument::AddDetectiveOperation( const ScDetOpData& rData )
+{
+ if (!pDetOpList)
+ pDetOpList = new ScDetOpList;
+
+ pDetOpList->Append( new ScDetOpData( rData ) );
+}
+
+void ScDocument::ClearDetectiveOperations()
+{
+ delete pDetOpList; // loescht auch die Eintraege
+ pDetOpList = NULL;
+}
+
+void ScDocument::SetDetOpList(ScDetOpList* pNew)
+{
+ delete pDetOpList; // loescht auch die Eintraege
+ pDetOpList = pNew;
+}
+
+//------------------------------------------------------------------------
+//
+// Vergleich von Dokumenten
+//
+//------------------------------------------------------------------------
+
+// Pfriemel-Faktoren
+#define SC_DOCCOMP_MAXDIFF 256
+#define SC_DOCCOMP_MINGOOD 128
+#define SC_DOCCOMP_COLUMNS 10
+#define SC_DOCCOMP_ROWS 100
+
+
+USHORT ScDocument::RowDifferences( SCROW nThisRow, SCTAB nThisTab,
+ ScDocument& rOtherDoc, SCROW nOtherRow, SCTAB nOtherTab,
+ SCCOL nMaxCol, SCCOLROW* pOtherCols )
+{
+ ULONG nDif = 0;
+ ULONG nUsed = 0;
+ for (SCCOL nThisCol=0; nThisCol<=nMaxCol; nThisCol++)
+ {
+ SCCOL nOtherCol;
+ if ( pOtherCols )
+ nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
+ else
+ nOtherCol = nThisCol;
+
+ if (ValidCol(nOtherCol)) // nur Spalten vergleichen, die in beiden Dateien sind
+ {
+ const ScBaseCell* pThisCell = GetCell( ScAddress( nThisCol, nThisRow, nThisTab ) );
+ const ScBaseCell* pOtherCell = rOtherDoc.GetCell( ScAddress( nOtherCol, nOtherRow, nOtherTab ) );
+ if (!ScBaseCell::CellEqual( pThisCell, pOtherCell ))
+ {
+ if ( pThisCell && pOtherCell )
+ nDif += 3;
+ else
+ nDif += 4; // Inhalt <-> leer zaehlt mehr
+ }
+
+ if ( ( pThisCell && pThisCell->GetCellType()!=CELLTYPE_NOTE ) ||
+ ( pOtherCell && pOtherCell->GetCellType()!=CELLTYPE_NOTE ) )
+ ++nUsed;
+ }
+ }
+
+ if (nUsed > 0)
+ return static_cast<USHORT>((nDif*64)/nUsed); // max.256 (SC_DOCCOMP_MAXDIFF)
+
+ DBG_ASSERT(!nDif,"Diff ohne Used");
+ return 0;
+}
+
+USHORT ScDocument::ColDifferences( SCCOL nThisCol, SCTAB nThisTab,
+ ScDocument& rOtherDoc, SCCOL nOtherCol, SCTAB nOtherTab,
+ SCROW nMaxRow, SCCOLROW* pOtherRows )
+{
+ //! optimieren mit Iterator oder so
+
+ ULONG nDif = 0;
+ ULONG nUsed = 0;
+ for (SCROW nThisRow=0; nThisRow<=nMaxRow; nThisRow++)
+ {
+ SCROW nOtherRow;
+ if ( pOtherRows )
+ nOtherRow = pOtherRows[nThisRow];
+ else
+ nOtherRow = nThisRow;
+
+ if (ValidRow(nOtherRow)) // nur Zeilen vergleichen, die in beiden Dateien sind
+ {
+ const ScBaseCell* pThisCell = GetCell( ScAddress( nThisCol, nThisRow, nThisTab ) );
+ const ScBaseCell* pOtherCell = rOtherDoc.GetCell( ScAddress( nOtherCol, nOtherRow, nOtherTab ) );
+ if (!ScBaseCell::CellEqual( pThisCell, pOtherCell ))
+ {
+ if ( pThisCell && pOtherCell )
+ nDif += 3;
+ else
+ nDif += 4; // Inhalt <-> leer zaehlt mehr
+ }
+
+ if ( ( pThisCell && pThisCell->GetCellType()!=CELLTYPE_NOTE ) ||
+ ( pOtherCell && pOtherCell->GetCellType()!=CELLTYPE_NOTE ) )
+ ++nUsed;
+ }
+ }
+
+ if (nUsed > 0)
+ return static_cast<USHORT>((nDif*64)/nUsed); // max.256
+
+ DBG_ASSERT(!nDif,"Diff ohne Used");
+ return 0;
+}
+
+void ScDocument::FindOrder( SCCOLROW* pOtherRows, SCCOLROW nThisEndRow, SCCOLROW nOtherEndRow,
+ BOOL bColumns, ScDocument& rOtherDoc, SCTAB nThisTab, SCTAB nOtherTab,
+ SCCOLROW nEndCol, SCCOLROW* pTranslate, ScProgress* pProgress, ULONG nProAdd )
+{
+ // bColumns=TRUE: Zeilen sind Spalten und umgekehrt
+
+ SCCOLROW nMaxCont; // wieviel weiter
+ SCCOLROW nMinGood; // was ist ein Treffer (incl.)
+ if ( bColumns )
+ {
+ nMaxCont = SC_DOCCOMP_COLUMNS; // 10 Spalten
+ nMinGood = SC_DOCCOMP_MINGOOD;
+ //! Extra Durchgang mit nMinGood = 0 ????
+ }
+ else
+ {
+ nMaxCont = SC_DOCCOMP_ROWS; // 100 Zeilen
+ nMinGood = SC_DOCCOMP_MINGOOD;
+ }
+ BOOL bUseTotal = bColumns && !pTranslate; // nur beim ersten Durchgang
+
+
+ SCCOLROW nOtherRow = 0;
+ USHORT nComp;
+ SCCOLROW nThisRow;
+ BOOL bTotal = FALSE; // ueber verschiedene nThisRow beibehalten
+ SCCOLROW nUnknown = 0;
+ for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++)
+ {
+ SCCOLROW nTempOther = nOtherRow;
+ BOOL bFound = FALSE;
+ USHORT nBest = SC_DOCCOMP_MAXDIFF;
+ SCCOLROW nMax = Min( nOtherEndRow, static_cast<SCCOLROW>(( nTempOther + nMaxCont + nUnknown )) );
+ for (SCCOLROW i=nTempOther; i<=nMax && nBest>0; i++) // bei 0 abbrechen
+ {
+ if (bColumns)
+ nComp = ColDifferences( static_cast<SCCOL>(nThisRow), nThisTab, rOtherDoc, static_cast<SCCOL>(i), nOtherTab, nEndCol, pTranslate );
+ else
+ nComp = RowDifferences( nThisRow, nThisTab, rOtherDoc, i, nOtherTab, static_cast<SCCOL>(nEndCol), pTranslate );
+ if ( nComp < nBest && ( nComp <= nMinGood || bTotal ) )
+ {
+ nTempOther = i;
+ nBest = nComp;
+ bFound = TRUE;
+ }
+ if ( nComp < SC_DOCCOMP_MAXDIFF || bFound )
+ bTotal = FALSE;
+ else if ( i == nTempOther && bUseTotal )
+ bTotal = TRUE; // nur ganz oben
+ }
+ if ( bFound )
+ {
+ pOtherRows[nThisRow] = nTempOther;
+ nOtherRow = nTempOther + 1;
+ nUnknown = 0;
+ }
+ else
+ {
+ pOtherRows[nThisRow] = SCROW_MAX;
+ ++nUnknown;
+ }
+
+ if (pProgress)
+ pProgress->SetStateOnPercent(nProAdd+static_cast<ULONG>(nThisRow));
+ }
+
+ // Bloecke ohne Uebereinstimmung ausfuellen
+
+ SCROW nFillStart = 0;
+ SCROW nFillPos = 0;
+ BOOL bInFill = FALSE;
+ for (nThisRow = 0; nThisRow <= nThisEndRow+1; nThisRow++)
+ {
+ SCROW nThisOther = ( nThisRow <= nThisEndRow ) ? pOtherRows[nThisRow] : (nOtherEndRow+1);
+ if ( ValidRow(nThisOther) )
+ {
+ if ( bInFill )
+ {
+ if ( nThisOther > nFillStart ) // ist was zu verteilen da?
+ {
+ SCROW nDiff1 = nThisOther - nFillStart;
+ SCROW nDiff2 = nThisRow - nFillPos;
+ SCROW nMinDiff = Min(nDiff1, nDiff2);
+ for (SCROW i=0; i<nMinDiff; i++)
+ pOtherRows[nFillPos+i] = nFillStart+i;
+ }
+
+ bInFill = FALSE;
+ }
+ nFillStart = nThisOther + 1;
+ nFillPos = nThisRow + 1;
+ }
+ else
+ bInFill = TRUE;
+ }
+}
+
+void ScDocument::CompareDocument( ScDocument& rOtherDoc )
+{
+ if (!pChangeTrack)
+ return;
+
+ SCTAB nThisCount = GetTableCount();
+ SCTAB nOtherCount = rOtherDoc.GetTableCount();
+ SCTAB* pOtherTabs = new SCTAB[nThisCount];
+ SCTAB nThisTab;
+
+ // Tabellen mit gleichen Namen vergleichen
+ String aThisName;
+ String aOtherName;
+ for (nThisTab=0; nThisTab<nThisCount; nThisTab++)
+ {
+ SCTAB nOtherTab = SCTAB_MAX;
+ if (!IsScenario(nThisTab)) // Szenarien weglassen
+ {
+ GetName( nThisTab, aThisName );
+ for (SCTAB nTemp=0; nTemp<nOtherCount && nOtherTab>MAXTAB; nTemp++)
+ if (!rOtherDoc.IsScenario(nTemp))
+ {
+ rOtherDoc.GetName( nTemp, aOtherName );
+ if ( aThisName == aOtherName )
+ nOtherTab = nTemp;
+ }
+ }
+ pOtherTabs[nThisTab] = nOtherTab;
+ }
+ // auffuellen, damit einzeln umbenannte Tabellen nicht wegfallen
+ SCTAB nFillStart = 0;
+ SCTAB nFillPos = 0;
+ BOOL bInFill = FALSE;
+ for (nThisTab = 0; nThisTab <= nThisCount; nThisTab++)
+ {
+ SCTAB nThisOther = ( nThisTab < nThisCount ) ? pOtherTabs[nThisTab] : nOtherCount;
+ if ( ValidTab(nThisOther) )
+ {
+ if ( bInFill )
+ {
+ if ( nThisOther > nFillStart ) // ist was zu verteilen da?
+ {
+ SCTAB nDiff1 = nThisOther - nFillStart;
+ SCTAB nDiff2 = nThisTab - nFillPos;
+ SCTAB nMinDiff = Min(nDiff1, nDiff2);
+ for (SCTAB i=0; i<nMinDiff; i++)
+ if ( !IsScenario(nFillPos+i) && !rOtherDoc.IsScenario(nFillStart+i) )
+ pOtherTabs[nFillPos+i] = nFillStart+i;
+ }
+
+ bInFill = FALSE;
+ }
+ nFillStart = nThisOther + 1;
+ nFillPos = nThisTab + 1;
+ }
+ else
+ bInFill = TRUE;
+ }
+
+ //
+ // Tabellen in der gefundenen Reihenfolge vergleichen
+ //
+
+ for (nThisTab=0; nThisTab<nThisCount; nThisTab++)
+ {
+ SCTAB nOtherTab = pOtherTabs[nThisTab];
+ if ( ValidTab(nOtherTab) )
+ {
+ SCCOL nThisEndCol = 0;
+ SCROW nThisEndRow = 0;
+ SCCOL nOtherEndCol = 0;
+ SCROW nOtherEndRow = 0;
+ GetCellArea( nThisTab, nThisEndCol, nThisEndRow );
+ rOtherDoc.GetCellArea( nOtherTab, nOtherEndCol, nOtherEndRow );
+ SCCOL nEndCol = Max(nThisEndCol, nOtherEndCol);
+ SCROW nEndRow = Max(nThisEndRow, nOtherEndRow);
+ SCCOL nThisCol;
+ SCROW nThisRow;
+ ULONG n1,n2; // fuer AppendDeleteRange
+
+ //! ein Progress ueber alle Tabellen ???
+ String aTabName;
+ GetName( nThisTab, aTabName );
+ String aTemplate = ScGlobal::GetRscString(STR_PROGRESS_COMPARING);
+ String aProText = aTemplate.GetToken( 0, '#' );
+ aProText += aTabName;
+ aProText += aTemplate.GetToken( 1, '#' );
+ ScProgress aProgress( GetDocumentShell(),
+ aProText, 3*nThisEndRow ); // 2x FindOrder, 1x hier
+ long nProgressStart = 2*nThisEndRow; // start fuer hier
+
+ SCCOLROW* pTempRows = new SCCOLROW[nThisEndRow+1];
+ SCCOLROW* pOtherRows = new SCCOLROW[nThisEndRow+1];
+ SCCOLROW* pOtherCols = new SCCOLROW[nThisEndCol+1];
+
+ // eingefuegte/geloeschte Spalten/Zeilen finden:
+ // Zwei Versuche:
+ // 1) Original Zeilen vergleichen (pTempRows)
+ // 2) Original Spalten vergleichen (pOtherCols)
+ // mit dieser Spaltenreihenfolge Zeilen vergleichen (pOtherRows)
+
+ //! Spalten vergleichen zweimal mit unterschiedlichem nMinGood ???
+
+ // 1
+ FindOrder( pTempRows, nThisEndRow, nOtherEndRow, FALSE,
+ rOtherDoc, nThisTab, nOtherTab, nEndCol, NULL, &aProgress, 0 );
+ // 2
+ FindOrder( pOtherCols, nThisEndCol, nOtherEndCol, TRUE,
+ rOtherDoc, nThisTab, nOtherTab, nEndRow, NULL, NULL, 0 );
+ FindOrder( pOtherRows, nThisEndRow, nOtherEndRow, FALSE,
+ rOtherDoc, nThisTab, nOtherTab, nThisEndCol,
+ pOtherCols, &aProgress, nThisEndRow );
+
+ ULONG nMatch1 = 0; // pTempRows, keine Spalten
+ for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++)
+ if (ValidRow(pTempRows[nThisRow]))
+ nMatch1 += SC_DOCCOMP_MAXDIFF -
+ RowDifferences( nThisRow, nThisTab, rOtherDoc, pTempRows[nThisRow],
+ nOtherTab, nEndCol, NULL );
+
+ ULONG nMatch2 = 0; // pOtherRows, pOtherCols
+ for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++)
+ if (ValidRow(pOtherRows[nThisRow]))
+ nMatch2 += SC_DOCCOMP_MAXDIFF -
+ RowDifferences( nThisRow, nThisTab, rOtherDoc, pOtherRows[nThisRow],
+ nOtherTab, nThisEndCol, pOtherCols );
+
+ if ( nMatch1 >= nMatch2 ) // ohne Spalten ?
+ {
+ // Spalten zuruecksetzen
+ for (nThisCol = 0; nThisCol<=nThisEndCol; nThisCol++)
+ pOtherCols[nThisCol] = nThisCol;
+
+ // Zeilenarrays vertauschen (geloescht werden sowieso beide)
+ SCCOLROW* pSwap = pTempRows;
+ pTempRows = pOtherRows;
+ pOtherRows = pSwap;
+ }
+ else
+ {
+ // bleibt bei pOtherCols, pOtherRows
+ }
+
+
+ // Change-Actions erzeugen
+ // 1) Spalten von rechts
+ // 2) Zeilen von unten
+ // 3) einzelne Zellen in normaler Reihenfolge
+
+ // Actions fuer eingefuegte/geloeschte Spalten
+
+ SCCOL nLastOtherCol = static_cast<SCCOL>(nOtherEndCol + 1);
+ // nThisEndCol ... 0
+ for ( nThisCol = nThisEndCol+1; nThisCol > 0; )
+ {
+ --nThisCol;
+ SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
+ if ( ValidCol(nOtherCol) && nOtherCol+1 < nLastOtherCol )
+ {
+ // Luecke -> geloescht
+ ScRange aDelRange( nOtherCol+1, 0, nOtherTab,
+ nLastOtherCol-1, MAXROW, nOtherTab );
+ pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
+ }
+ if ( nOtherCol > MAXCOL ) // eingefuegt
+ {
+ // zusammenfassen
+ if ( nThisCol == nThisEndCol || ValidCol(static_cast<SCCOL>(pOtherCols[nThisCol+1])) )
+ {
+ SCCOL nFirstNew = static_cast<SCCOL>(nThisCol);
+ while ( nFirstNew > 0 && pOtherCols[nFirstNew-1] > MAXCOL )
+ --nFirstNew;
+ SCCOL nDiff = nThisCol - nFirstNew;
+ ScRange aRange( nLastOtherCol, 0, nOtherTab,
+ nLastOtherCol+nDiff, MAXROW, nOtherTab );
+ pChangeTrack->AppendInsert( aRange );
+ }
+ }
+ else
+ nLastOtherCol = nOtherCol;
+ }
+ if ( nLastOtherCol > 0 ) // ganz oben geloescht
+ {
+ ScRange aDelRange( 0, 0, nOtherTab,
+ nLastOtherCol-1, MAXROW, nOtherTab );
+ pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
+ }
+
+ // Actions fuer eingefuegte/geloeschte Zeilen
+
+ SCROW nLastOtherRow = nOtherEndRow + 1;
+ // nThisEndRow ... 0
+ for ( nThisRow = nThisEndRow+1; nThisRow > 0; )
+ {
+ --nThisRow;
+ SCROW nOtherRow = pOtherRows[nThisRow];
+ if ( ValidRow(nOtherRow) && nOtherRow+1 < nLastOtherRow )
+ {
+ // Luecke -> geloescht
+ ScRange aDelRange( 0, nOtherRow+1, nOtherTab,
+ MAXCOL, nLastOtherRow-1, nOtherTab );
+ pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
+ }
+ if ( nOtherRow > MAXROW ) // eingefuegt
+ {
+ // zusammenfassen
+ if ( nThisRow == nThisEndRow || ValidRow(pOtherRows[nThisRow+1]) )
+ {
+ SCROW nFirstNew = nThisRow;
+ while ( nFirstNew > 0 && pOtherRows[nFirstNew-1] > MAXROW )
+ --nFirstNew;
+ SCROW nDiff = nThisRow - nFirstNew;
+ ScRange aRange( 0, nLastOtherRow, nOtherTab,
+ MAXCOL, nLastOtherRow+nDiff, nOtherTab );
+ pChangeTrack->AppendInsert( aRange );
+ }
+ }
+ else
+ nLastOtherRow = nOtherRow;
+ }
+ if ( nLastOtherRow > 0 ) // ganz oben geloescht
+ {
+ ScRange aDelRange( 0, 0, nOtherTab,
+ MAXCOL, nLastOtherRow-1, nOtherTab );
+ pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
+ }
+
+ // Zeilen durchgehen um einzelne Zellen zu finden
+
+ for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++)
+ {
+ SCROW nOtherRow = pOtherRows[nThisRow];
+ for (nThisCol = 0; nThisCol <= nThisEndCol; nThisCol++)
+ {
+ SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
+ ScAddress aThisPos( nThisCol, nThisRow, nThisTab );
+ const ScBaseCell* pThisCell = GetCell( aThisPos );
+ const ScBaseCell* pOtherCell = NULL;
+ if ( ValidCol(nOtherCol) && ValidRow(nOtherRow) )
+ {
+ ScAddress aOtherPos( nOtherCol, nOtherRow, nOtherTab );
+ pOtherCell = rOtherDoc.GetCell( aOtherPos );
+ }
+ if ( !ScBaseCell::CellEqual( pThisCell, pOtherCell ) )
+ {
+ ScRange aRange( aThisPos );
+ ScChangeActionContent* pAction = new ScChangeActionContent( aRange );
+ pAction->SetOldValue( pOtherCell, &rOtherDoc, this );
+ pAction->SetNewValue( pThisCell, this );
+ pChangeTrack->Append( pAction );
+ }
+ }
+ aProgress.SetStateOnPercent(nProgressStart+nThisRow);
+ }
+
+ delete[] pOtherCols;
+ delete[] pOtherRows;
+ delete[] pTempRows;
+ }
+ }
+
+ //! Inhalt von eingefuegten / geloeschten Tabellen ???
+ //! Aktionen fuer eingefuegte / geloeschte Tabellen ???
+
+ delete[] pOtherTabs;
+}
+
+
+
+
+